C++ 临界温度的伊辛模型模拟

C++ 临界温度的伊辛模型模拟,c++,montecarlo,C++,Montecarlo,我正在写一个二维伊辛模型的模拟。该模型的行为与预测的一样,除了一件事:临界温度大约为3.5,而它应该接近2/ln(2+sqrt(2)) 项目是一个生成数据的C++程序,和一个执行程序的shell脚本。可以找到完整的代码。还有lattice.cpp #include <iostream> #include "include/lattice.h" using namespace std; /* Copy assignment operator, too long to include

我正在写一个二维伊辛模型的模拟。该模型的行为与预测的一样,除了一件事:临界温度大约为3.5,而它应该接近
2/ln(2+sqrt(2))

项目是一个生成数据的C++程序,和一个执行程序的shell脚本。可以找到完整的代码。还有lattice.cpp

#include <iostream>
#include "include/lattice.h"

using namespace std;

/*
Copy assignment operator, too long to include in the header.
*/
lattice &lattice::operator=(const lattice &other) {
  size_ = other.size_;
  spins_ = other.spins_;
  J_ = other.J_;
  H_ = other.H_;
  delete spins_;
  return *this;
}

void lattice::print() {
  unsigned int area = size_ * size_;
  for (unsigned int i = 0; i < area; i++) {
    cout << to_symbol(spins_->at(i));
    if (i % size_ == size_ - 1)
      cout << endl;
  }
  cout << endl;
}

/*
Computes the energy associated with a spin at the given point.

It is explicitly float as that would allow the compiler to make use of multiple
registers instead of keeping track of unneeded precision.  (typically J, H ~ 1).
*/
float lattice::compute_point_energy(int row, int col) {
  int accumulator = get(row + 1, col) + get(row - 1, col) + get(row, col - 1) +
                    get(row, col + 1);
  return -get(row, col) * (accumulator * J_ + H_);
}

/*
Computes total magnetisation in O(n^2). Thread safe
*/
int lattice::total_magnetisation() {
  int sum = 0;
  #pragma omp parallel for reduction(+ : sum)
  for (unsigned int i = 0; i < size_ * size_; i++) {
    sum += spins_->at(i);
  }
  return sum;
}

int inline to_periodic(int row, int col, int size) {
  if (row < 0 || row >= size)
    row = abs(size - abs(row));
  if (col < 0 || col >= size)
    col = abs(size - abs(col));
  return row * size + col;
}
#包括
#包括“include/lattice.h”
使用名称空间std;
/*
复制分配运算符,太长,无法包含在标头中。
*/
晶格和晶格::运算符=(常数晶格和其他){
尺寸=其他尺寸;
自旋=其他。自旋;
J_u=other.J_u;
H_uu=other.H_u;
删除自旋;
归还*这个;
}
void lattice::print(){
无符号整数区域=大小*大小;
for(无符号整数i=0;i=大小)
col=abs(尺寸-abs(col));
返回行*大小+列;
}
具有晶格.h

#ifndef lattice_h
#define lattice_h

#include <cmath>
#include <vector>

/* Converts spin up/down to easily printable symbols. */
char inline to_symbol(int in) { return in == -1 ? '-' : '+'; }

/* Converts given pair of indices to those with periodic boundary conditions. */
int inline to_periodic(int row, int col, int size) {
  if (row < 0 || row >= size)
    row = abs(size - abs(row));
  if (col < 0 || col >= size)
    col = abs(size - abs(col));
  return row * size + col;
}

class lattice {
private:
  unsigned int size_;
  // vector<bool> would be more space efficient, but it would not allow
  // multithreading
  std::vector<short> *spins_;
  float J_;
  float H_;

public:
  lattice() noexcept : size_(0), spins_(NULL), J_(1.0), H_(0.0) {}
  lattice(int new_size, double new_J, double new_H) noexcept
      : size_(new_size), spins_(new std::vector<short>(size_ * size_, 1)),
        J_(new_J), H_(new_H) {}
  lattice(const lattice &other) noexcept
      : lattice(other.size_, other.J_, other.H_) {
#pragma omp parallel for
    for (unsigned int i = 0; i < size_ * size_; i++)
      spins_->at(i) = other.spins_->at(i);
  }
  lattice &operator=(const lattice &);

  ~lattice() { delete spins_; }
  void print();
  short get(int row, int col) {
    return spins_->at(to_periodic(row, col, size_));
  }
  unsigned int get_size() { return size_; }
  void flip(int row, int col) { spins_->at(to_periodic(row, col, size_)) *= -1; }
  int total_magnetisation();
  float compute_point_energy(int row, int col);
};

#endif
#ifndef lattice\u h
#定义格
#包括
#包括
/*将旋转向上/向下转换为易于打印的符号*/
字符内联到_符号(int in){return in==-1?'-':'+';}
/*将给定的索引对转换为具有周期性边界条件的索引对*/
int内联到_周期(int行、int列、int大小){
如果(行<0 | |行>=大小)
行=abs(尺寸-abs(行));
如果(列<0 | |列>=大小)
col=abs(尺寸-abs(col));
返回行*大小+列;
}
类格{
私人:
无符号整数大小;
//向量将更节省空间,但它不允许
//多线程
标准::矢量*自旋;
浮动J_;
浮动H_;
公众:
lattice()noexcept:size_0(0)、spins_0(NULL)、J_0(1.0)、H_0(0.0){
晶格(int new_size,双new_J,双new_H)无例外
:size_u(新大小),spins_u(新标准::向量(大小*size_u,1)),
J_u(新的),H_u(新的{}
晶格(常数晶格和其他)无例外
:晶格(other.size_ux、other.J_x、other.H_x){
#pragma-omp并行
for(无符号整数i=0;i在(i)=其他。自旋->在(i);
}
格&算子=(常数格&);
~lattice(){delete spins_;}
作废打印();
短get(整数行,整数列){
返回自旋->在(到周期(行、列、大小);
}
无符号int get_size(){return size_;}
void flip(int row,int col){spins->at(to_periodic(row,col,size))*=-1;}
int总磁化率();
浮点计算点能量(整数行,整数列);
};
#恩迪夫
模拟。cpp

#include <iostream>
#include <math.h>
#include "include/simulation.h"

using namespace std;

/*
Advances the simulation a given number of steps, and updates/prints the statistics
into the given file pointer.

Defaults to stdout.

The number of time_steps is explcitly unsigned, so that linters/IDEs remind
the end user of the file that extra care needs to be taken, as well as to allow
advancing the simulation a larger number of times.
*/
void simulation::advance(unsigned int time_steps, FILE *output) {
  unsigned int area = spin_lattice_.get_size() * spin_lattice_.get_size();
  for (unsigned int i = 0; i < time_steps; i++) {
    // If we don't update mean_energy_ every time, we might get incorrect
    // thermodynamic behaviour.
    total_energy_ = compute_energy(spin_lattice_);
    double temperature_delta = total_energy_/area - mean_energy_;
    if (abs(temperature_delta) < 1/area){
      cerr<<temperature_delta<<"! Reached equilibrium "<<endl;
    }
    temperature_ += temperature_delta;
    mean_energy_ = total_energy_ / area;
    if (time_ % print_interval_ == 0) {
      total_magnetisation_ = spin_lattice_.total_magnetisation();
      mean_magnetisation_ = total_magnetisation_ / area;
      print_status(output);
    }
    advance();
  }
}

/*
Advances the simulation a single step.

DOES NOT KEEP TRACK OF STATISTICS. Hence private.
*/
void simulation::advance() {
  #pragma omp parallel for collapse(2)
  for (unsigned int row = 0; row < spin_lattice_.get_size(); row++) {
    for (unsigned int col = 0; col < spin_lattice_.get_size(); col++) {
      double dE = compute_dE(row, col);
      double p = r_.random_uniform();
      float rnd = rand() / (RAND_MAX + 1.);
      if (exp(-dE / temperature_) > rnd) {
        spin_lattice_.flip(row, col);
      }
    }
  }
  time_++;
}

/*
Computes change in energy due to flipping one single spin.

The function returns a single-precision floating-point number, as data cannot under
most circumstances make use of greater precision than that (save J is set to a
non-machine-representable value).

The code modifies the spin lattice, as an alternative (copying the neighborhood
of a given point), would make the code run slower by a factor of 2.25
*/
float simulation::compute_dE(int row, int col) {
  float e_0 =  spin_lattice_.compute_point_energy(row, col);
  return -4*e_0;


}
/*
Computes the total energy associated with spins in the spin_lattice_.

I originally used this function to test the code that tracked energy as the lattice
itself was modified, but that code turned out to be only marginally faster, and
not thread-safe. This is due to a race condition: when one thread uses a neighborhood
of a point, while another thread was computing the energy of one such point in
the neighborhood of (row, col).
*/
double simulation::compute_energy(lattice &other) {
  double energy_sum = 0;
  unsigned int max = other.get_size();
  #pragma omp parallel for reduction(+ : energy_sum)
  for (unsigned int i = 0; i < max; i++) {
    for (unsigned int j = 0; j < max; j++) {
      energy_sum += other.compute_point_energy(i, j);
    }
  }
  return energy_sum;
}

void simulation::set_to_chequerboard(int step){
  if (time_ !=0){
    return;
  }else{
    for (unsigned int i=0; i< spin_lattice_.get_size(); ++i){
      for (unsigned int j=0; j<spin_lattice_.get_size(); ++j){
        if ((i/step)%2-(j/step)%2==0){
          spin_lattice_.flip(i, j);
        }
      }
    }
  }
}
#ifndef simulation_h
#define simulation_h

#include "lattice.h"
#include "rng.h"
#include <gsl/gsl_rng.h>

/*
The logic of the entire simulation of the Ising model of magnetism.

This simulation will run and print statistics at a given time interval.
A simulation can be advanced a single time step, or many at a time,
*/
class simulation {
private:
  unsigned int time_ = 0;  // Current time of the simulation.
  rng r_ = rng();
  lattice spin_lattice_;
  double temperature_;
  double mean_magnetisation_ = 1;
  double mean_energy_;
  double total_magnetisation_;
  double total_energy_;
  unsigned int print_interval_ = 1;
  void advance();

public:
  void set_print_interval(unsigned int new_print_interval) { print_interval_ = new_print_interval; }

  simulation(int new_size, double new_temp, double new_J, double new_H)
      : time_(0), spin_lattice_(lattice(new_size, new_J, new_H)), temperature_(new_temp),
        mean_energy_(new_J * (-4)), total_magnetisation_(new_size * new_size),
        total_energy_(compute_energy(spin_lattice_)) {}

  void print_status(FILE *f) {
    f = f==NULL? stdout : f;
    fprintf(f, "%4d\t%e \t%e\t%e\n", time_, mean_magnetisation_,
            mean_energy_, temperature_);
  }
  void advance(unsigned int time_steps, FILE *output);
  double compute_energy(lattice &other);
  float compute_dE(int row, int col);
  void set_to_chequerboard(int step);
  void print_lattice(){
    spin_lattice_.print();
  };
  // void load_custom(const lattice& custom);
};

#endif
#包括
#包括
#包括“include/simulation.h”
使用名称空间std;
/*
将模拟推进给定数量的步骤,并更新/打印统计信息
输入给定的文件指针。
默认为标准输出。
时间步数明显无符号,因此linter/ide提醒
文件的最终用户需要格外小心,并允许
将模拟推进更多次。
*/
void simulation::advance(无符号整数时间步长,文件*输出){
无符号整数区域=自旋\晶格\获取\大小()*自旋\晶格\获取\大小();
for(无符号整数i=0;i<时间步长;i++){
//如果我们不每次更新mean_energy,我们可能会出错
//热力学行为。
总能量=计算能量(自旋晶格);
双温δ=总能量/面积-平均能量;
if(abs(温度增量)<1/面积){

cerr我在您的代码中发现了一些问题:

  • compute\u dE
    方法返回错误的能量,因为因子2不应该存在。伊辛系统的哈密顿量为

    当您有效地使用

  • compute\u energy
    方法返回了错误的能量。该方法应该只在每个自旋对上迭代一次。类似这样的方法应该可以做到:

    for(无符号整数i=0;i

  • 你使用的是动态更新的温度,而不是目标温度。我真的不明白这样做的目的


你应该在你的问题中添加
lattice.h
simulation.h
标题。我不认为这是问题所在。首先,答案不会偏移2倍,如果有任何偏移小于50%(即1单位J)第二,当我将
compute dE
更改为其一半时,系统停止会聚。它并没有按照我所希望的那样移动,而是完全改变了行为。你能解释更多吗?更新:你是对的。不完全正确,但你推动了我正确的方向。
compute dE
的关闭系数是3/2,而不是2。还有dou由于自旋翻转,我们可以改变点能量,但也可以改变周围四个自旋的相互作用能量,另一个点能量(不是两个,因为重复计数)事实上,真正的问题是你不使用输入温度,但是,出于某种原因,你在整个模拟过程中更新温度,并在接受率中使用。这是不对的。我将据此编辑答案。我不确定能量反馈。我假设系统被隔离,因此能量被释放rom对齐旋转会加热系统。如果不是这样,我愿意更新代码…好吧,你使用的是与恒温系统相关的验收规则,这意味着你将系统隐式耦合到一个热浴。我怀疑,如果在模拟过程中温度是可变的ion,您必须使用不同的接受规则。