-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
356 lines (329 loc) · 11.1 KB
/
main.cpp
File metadata and controls
356 lines (329 loc) · 11.1 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*
Program to solve the two-dimensional Ising model
with zero external field using MPI
The coupling constant J = 1
Boltzmann's constant = 1, temperature has thus dimension energy
Metropolis sampling is used. Periodic boundary conditions.
The code needs an output file on the command line and the variables mcs, nspins,
initial temp, final temp and temp step.
*/
#include "mpi.h"
#include <cmath>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
using namespace std;
// output file
ofstream ofile;
// inline function for periodic boundary conditions
inline int periodic(int i, int limit, int add) {
return (i+limit+add) % (limit);
}
// Function to initialise energy and magnetization
void initialize(int, int **, double&, double&);
// The metropolis algorithm
void Metropolis(int, long&, int **, double&, double&, double *);
// prints to file the results of the calculations
void output(int, int, double, double *);
// Matrix memory allocation
// allocate space for a matrix
void **matrix(int, int, int);
// free space for a matrix
void free_matrix(void **);
// ran2 for uniform deviates, initialize with negative seed.
double ran2(long *);
// Main program begins here
int main(int argc, char* argv[])
{
char *outfilename;
long idum;
int **spin_matrix, n_spins, mcs, my_rank, numprocs;
double w[17], average[5], total_average[5],
initial_temp, final_temp, E, M, temp_step;
// MPI initializations
MPI_Init (&argc, &argv);
MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
if (my_rank == 0 && argc <= 1) {
cout << "Bad Usage: " << argv[0] <<
" read output file" << endl;
exit(1);
}
if (my_rank == 0 && argc > 1) {
outfilename = argv[1];
ofile.open(outfilename);
}
n_spins = atoi(argv[2]);
mcs = atoi(argv[3]);
initial_temp = atof(argv[4]);
final_temp = atof(argv[5]);
temp_step = atof(argv[6]);
//n_spins = 2; mcs = 1000000; initial_temp = 1; final_temp = 1; temp_step =1;
/*
Determine number of intervall which are used by all processes
myloop_begin gives the starting point on process my_rank
myloop_end gives the end point for summation on process my_rank
*/
int no_intervalls = mcs/numprocs;
int myloop_begin = my_rank*no_intervalls + 1;
int myloop_end = (my_rank+1)*no_intervalls;
if ( (my_rank == numprocs-1) &&( myloop_end < mcs) ) myloop_end = mcs;
// broadcast to all nodes common variables
MPI_Bcast (&n_spins, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast (&initial_temp, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast (&final_temp, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast (&temp_step, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
// Allocate memory for spin matrix
spin_matrix = (int**) matrix(n_spins, n_spins, sizeof(int));
// every node has its own seed for the random numbers, this is important else
// if one starts with the same seed, one ends with the same random numbers
srand(time(NULL));
idum = -1-my_rank; // random starting point
// Start Monte Carlo sampling by looping over T first
double TimeStart, TimeEnd, TotalTime;
TimeStart = MPI_Wtime();
for ( double temperature = initial_temp; temperature <= final_temp; temperature+=temp_step){
// initialise energy and magnetization
E = M = 0.;
// initialise array for expectation values
initialize(n_spins, spin_matrix, E, M);
// setup array for possible energy changes
for( int de =-8; de <= 8; de++) w[de+8] = 0;
for( int de =-8; de <= 8; de+=4) w[de+8] = exp(-de/temperature);
for( int i = 0; i < 5; i++) average[i] = 0.;
for( int i = 0; i < 5; i++) total_average[i] = 0.;
// start Monte Carlo computation
for (int cycles = myloop_begin; cycles <= myloop_end; cycles++){
Metropolis(n_spins, idum, spin_matrix, E, M, w);
// update expectation values for local node
average[0] += E;
average[1] += E*E;
average[2] += M;
average[3] += M*M;
average[4] += fabs(M);
}
// Find total average
for( int i =0; i < 5; i++){
MPI_Reduce(&average[i], &total_average[i], 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
}
// print results
if ( my_rank == 0) {
output(n_spins, mcs, temperature, total_average);
}
}
free_matrix((void **) spin_matrix); // free memory
ofile.close(); // close output file
TimeEnd = MPI_Wtime();
TotalTime = TimeEnd-TimeStart;
if ( my_rank == 0) {
cout << "Time = " << TotalTime << " on number of processors: " << numprocs << endl;
}
// End MPI
MPI_Finalize ();
return 0;
}
// function to initialise energy, spin matrix and magnetization
void initialize(int n_spins, int **spin_matrix,
double& E, double& M)
{
// setup spin matrix and intial magnetization
srand(time(NULL));
for(int y =0; y < n_spins; y++) {
for (int x= 0; x < n_spins; x++){
//spin_matrix[y][x] = 1; // spin orientation for the ground state
double invers_period = 1.0/RAND_MAX;
if (double(rand())*invers_period > 0.5) {
spin_matrix[y][x] = 1.0;
}
else {
spin_matrix[y][x] = -1.0;
}
M += (double)spin_matrix[y][x];
//cout << spin_matrix[y][x] << endl;
}
}
// setup initial energy
for(int y =0; y < n_spins; y++) {
for (int x= 0; x < n_spins; x++){
E -= (double) spin_matrix[y][x]*
(spin_matrix[periodic(y,n_spins,-1)][x] +
spin_matrix[y][periodic(x,n_spins,-1)]);
}
}
}// end function initialise
// implementation of the Metropolis algorithm
void Metropolis(int n_spins, long& idum, int **spin_matrix, double& E, double&M, double *w)
{
// loop over all spins
for(int y =0; y < n_spins; y++)
{
for (int x= 0; x < n_spins; x++)
{
// select a random spin element in the lattice
int ix = (int) (ran2(&idum)*(double)n_spins);
int iy = (int) (ran2(&idum)*(double)n_spins);
int deltaE = 2*spin_matrix[iy][ix]*
(spin_matrix[iy][periodic(ix,n_spins,-1)]+
spin_matrix[periodic(iy,n_spins,-1)][ix] +
spin_matrix[iy][periodic(ix,n_spins,1)] +
spin_matrix[periodic(iy,n_spins,1)][ix]);
// determine if the new state is to be accepted or rejected according to the metropolis algorithm
if ( ran2(&idum) <= w[deltaE+8] )
{
spin_matrix[iy][ix] *= -1.0; // flip one spin and accept new spin config
// update magnetization and energy values for the new configuration
M += (double) 2*spin_matrix[iy][ix];
E += (double) deltaE;
}
}
}
} // end of Metropolis sampling over spins
void output(int n_spins, int mcs, double temperature, double *total_average)
{
double norm = 1.0/((double) (mcs)); // divided by total number of cycles
double Etotal_average = total_average[0]*norm;
double E2total_average = total_average[1]*norm;
double Mtotal_average = total_average[2]*norm;
double M2total_average = total_average[3]*norm;
double Mabstotal_average = total_average[4]*norm;
// all expectation values are per spin, divide by 1/n_spins/n_spins
double Evariance = (E2total_average- Etotal_average*Etotal_average)/n_spins/n_spins;
double Mvariance = (M2total_average - Mabstotal_average*Mabstotal_average)/n_spins/n_spins;
ofile << setiosflags(ios::showpoint | ios::uppercase);
ofile << setw(15) << setprecision(8) << temperature;
ofile << setw(15) << setprecision(8) << Etotal_average/n_spins/n_spins;
ofile << setw(15) << setprecision(8) << Evariance/temperature/temperature;
ofile << setw(15) << setprecision(8) << Mtotal_average/n_spins/n_spins;
ofile << setw(15) << setprecision(8) << Mvariance/temperature;
ofile << setw(15) << setprecision(8) << Mabstotal_average/n_spins/n_spins << endl;
cout << "T: " << temperature;
cout << ", <E>: " << Etotal_average/n_spins/n_spins;
cout << ", C_V: " << Evariance/temperature/temperature;
cout << ", <M>: " << Mtotal_average/n_spins/n_spins;
cout << ", Chi: " << Mvariance/temperature;
cout << ", |M|: " << Mabstotal_average/n_spins/n_spins << endl;
} // end output function
/*
** The function
** ran2()
** is a long periode (> 2 x 10^18) random number generator of
** L'Ecuyer and Bays-Durham shuffle and added safeguards.
** Call with idum a negative integer to initialize; thereafter,
** do not alter idum between sucessive deviates in a
** sequence. RNMX should approximate the largest floating point value
** that is less than 1.
** The function returns a uniform deviate between 0.0 and 1.0
** (exclusive of end-point values).
*/
#define IM1 2147483563
#define IM2 2147483399
#define AM (1.0/IM1)
#define IMM1 (IM1-1)
#define IA1 40014
#define IA2 40692
#define IQ1 53668
#define IQ2 52774
#define IR1 12211
#define IR2 3791
#define NTAB 32
#define NDIV (1+IMM1/NTAB)
#define EPS 1.2e-7
#define RNMX (1.0-EPS)
double ran2(long *idum)
{
int j;
long k;
static long idum2 = 123456789;
static long iy=0;
static long iv[NTAB];
double temp;
if(*idum <= 0) {
if(-(*idum) < 1) *idum = 1;
else *idum = -(*idum);
idum2 = (*idum);
for(j = NTAB + 7; j >= 0; j--) {
k = (*idum)/IQ1;
*idum = IA1*(*idum - k*IQ1) - k*IR1;
if(*idum < 0) *idum += IM1;
if(j < NTAB) iv[j] = *idum;
}
iy=iv[0];
}
k = (*idum)/IQ1;
*idum = IA1*(*idum - k*IQ1) - k*IR1;
if(*idum < 0) *idum += IM1;
k = idum2/IQ2;
idum2 = IA2*(idum2 - k*IQ2) - k*IR2;
if(idum2 < 0) idum2 += IM2;
j = iy/NDIV;
iy = iv[j] - idum2;
iv[j] = *idum;
if(iy < 1) iy += IMM1;
if((temp = AM*iy) > RNMX) return RNMX;
else return temp;
}
#undef IM1
#undef IM2
#undef AM
#undef IMM1
#undef IA1
#undef IA2
#undef IQ1
#undef IQ2
#undef IR1
#undef IR2
#undef NTAB
#undef NDIV
#undef EPS
#undef RNMX
// End: function ran2()
/*
* The function
* void **matrix()
* reserves dynamic memory for a two-dimensional matrix
* using the C++ command new . No initialization of the elements.
* Input data:
* int row - number of rows
* int col - number of columns
* int num_bytes- number of bytes for each
* element
* Returns a void **pointer to the reserved memory location.
*/
void **matrix(int row, int col, int num_bytes)
{
int i, num;
char **pointer, *ptr;
pointer = new(nothrow) char* [row];
if(!pointer) {
cout << "Exception handling: Memory allocation failed";
cout << " for "<< row << "row addresses !" << endl;
return NULL;
}
i = (row * col * num_bytes)/sizeof(char);
pointer[0] = new(nothrow) char [i];
if(!pointer[0]) {
cout << "Exception handling: Memory allocation failed";
cout << " for address to " << i << " characters !" << endl;
return NULL;
}
ptr = pointer[0];
num = col * num_bytes;
for(i = 0; i < row; i++, ptr += num ) {
pointer[i] = ptr;
}
return (void **)pointer;
} // end: function void **matrix()
/*
* The function
* void free_matrix()
* releases the memory reserved by the function matrix()
*for the two-dimensional matrix[][]
* Input data:
* void far **matr - pointer to the matrix
*/
void free_matrix(void **matr)
{
delete [] (char *) matr[0];
delete [] matr;
} // End: function free_matrix()