The Heisenberg Example. But threaded!

The O(3) Heisenberg model is a slightly more advanced model on a continuous state space. This demo also gives an example of how to inject a self-defined Wolff Cluster update for your self-defined model into MARQOV. Additionally we show in this example how to use the MARQOV scheduler in order to have automatic scheduling of the jobs derived from your parameter space sampling.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <iostream>
#include <array>
#include <tuple>
#include <vector>

//include the MARQOV library
#undef MPIMARQOV
#include "libmarqov.h"

//include the RegularLattice
#include "../src/lattice/regular_hypercubic.h"

//include some predefined observables, e.g. the magnetization and the energy
#include "../src/hamiltonian/util/observables.h"


// Define interaction term for the Heisenberg model
class MyHeisenberg_interaction
{
	public:
		MyHeisenberg_interaction(const double J) : J(J) {}
		std::array<double, 3> get (const std::array<double, 3>& phi) {return phi;};
        const double J;
};

class MySimpleHeisenberg
{
	public:
		
		// The spin dimension of the Heisenberg or O(3) model
		constexpr static int SymD = 3;

		// Define the state vector that this model will use.
		// For the Heisenberg model this is a three-dimensional unit vector
		typedef std::array<double, SymD> StateVector;

		// Parameters
		double J; // The coupling constant
		const std::string name; // every Hamiltonian MUST have a name, this is required for the HDF5 output

		// Hamiltonian terms
		// here this is only the canonical O(3) interaction, defined above
		std::array<MyHeisenberg_interaction*, 1> interactions = {new MyHeisenberg_interaction(J)};
        
		// Constructor
		MySimpleHeisenberg(double J) : J(J), name("MySimpleHeisenberg"), obs_e(*this){}
		~MySimpleHeisenberg() {delete interactions[0];}

		// Observables
		Magnetization  obs_m;
		Energy<MySimpleHeisenberg>  obs_e{*this};
                std::pair<Magnetization, Energy<MySimpleHeisenberg> > observables{obs_m, obs_e};
};

namespace MARQOV
{
    template <class Lattice>
    struct Wolff<MySimpleHeisenberg, Lattice>
    {
        std::vector<int> cstack = std::vector<int>(4096/sizeof(int), 0);///< the size of the stack is meant to be preserved across different cluster processes.
        
        template <class RNG, class StateSpace>
        inline int move(const MySimpleHeisenberg& ham, const Lattice& grid, StateSpace& statespace, RNG& rng, double beta, int rsite)
        {
            typedef MySimpleHeisenberg Hamiltonian;
            typedef typename Hamiltonian::StateVector StateVector;
            constexpr static int SymD = Hamiltonian::SymD;
            
            int q = 0;
            auto& seed = statespace[rsite];
            cstack[q] = rsite;
            
            int rdir = rng.integer(SymD); // random Cartesian direction
            seed[rdir] *= -1;
            
            int clustersize = 1;
            int current = 0;
            
            // plain Heisenberg model has only one interaction term
            const auto gcpl = ham.interactions[0]->J; 
            
            while (q>=0)
            {
                current = cstack[q];
                q--;
                
                const auto nbrs = grid.nbrs(0, current);
                if(q + nbrs.size() > cstack.size()) 
                    cstack.resize(2*cstack.size());
                for (std::size_t i = 0; i < nbrs.size(); ++i)
                {
                    const auto currentnbr = nbrs[i];
                    StateVector& candidate = statespace[currentnbr];
                    
                    const auto lcpl = statespace[current][rdir] * candidate[rdir];
                    const auto cpl  = gcpl*lcpl;
                    
                    if (wolff_update_accepted(cpl, beta, rng))
                    {
                        q++;
                        clustersize++;
                        cstack[q] = currentnbr;
                        candidate[rdir] = -candidate[rdir];
                    }
                }
            }
            return clustersize;
        }
    };
}

using namespace std;
using namespace MARQOV;

int main()
{
	std::cout<<"Welcome to the MARQOV Heisenberg test case! ";
	std::cout<<"This test is the minimum example that features a self-defined Heisenberg model and utlizes ";
	std::cout<<"the multithread scheduler."<<std::endl;
    // Initialize the lattice
	int L = 64;
	int dim = 3;
    RegularHypercubic mylatt(L, dim);

	// Set Monte Carlo parameters using MARQOV::Config
	MARQOV::Config mp("./"); // output path 
	mp.setnmetro(5); // number of Metropolis sweeps per EMCS
	mp.setncluster(10); // number of Wolff updates per EMCS
	mp.setwarmupsteps(5); // number of EMCS for warmup
	mp.setgameloopsteps(3); // number of EMCS for production
	mp.setloglevel(RELEASE); // set the log level

	// Set the Hamiltonian parameters, J, and the inverse temperature beta
    double beta = 0.66;
    double J = -1;
    auto hp = make_tuple(beta, J);

    // Let's create some parameters tuples, a temperature scan for the scheduler to work on

    // Prepare an array of parameter tuples
    auto args = make_tuple(std::ref(mylatt), mp, hp);
    vector<decltype(args)> v;

	// Fill
    for(int j = 0; j < 4; ++j)
    {
        hp = make_tuple(beta+j*0.1, J);
        v.push_back(make_tuple(std::ref(mylatt), mp, hp));
    }

    // Set up the MARQOV schedular
    auto sched = makeScheduler<MySimpleHeisenberg, RegularHypercubic>(args, 10);
    sched.setloglevel(RELEASE);
	// Feed parameters to the scheduler which creates the simulations
    for(auto p : v)
		sched.createSimfromParameter(p);
	// Run
    sched.start();
}