HILA
Loading...
Searching...
No Matches
suN_gauge_gf.cpp
1#include "hila.h"
2#include "gauge/staples.h"
9
10#ifndef NCOLOR
11#define NCOLOR 3
12#endif
13
14#ifndef BCOPEN
15#define BCOPEN -1
16#endif
17
18#if BCOPEN==-1
19#include "gauge/polyakov.h"
21#endif
22
23using ftype = double;
25
26// define a struct to hold the input parameters: this
27// makes it simpler to pass the values around
28struct parameters {
29 ftype beta; // inverse gauge coupling
30 int n_traj; // number of trajectories to generate
31 int n_therm; // number of thermalization trajectories (counts only accepted traj.)
32 int n_update; // number of heat-bath sweeps per "trajectory"
33 int n_overrelax; // number of overrelaxation sweeps between heat-batch sweeps
34 int gflow_freq; // number of trajectories between gflow measurements
35 ftype gflow_max_l; // flow scale at which gradient flow stops
36 ftype gflow_l_step; // flow scale interval between flow measurements
37 ftype gflow_a_accu; // desired absolute accuracy of gradient flow integration steps
38 ftype gflow_r_accu; // desired relative accuracy of gradient flow integration steps
39 int n_save; // number of trajectories between config. check point
40 std::string config_file;
41 ftype time_offset;
42};
43
44///////////////////////////////////////////////////////////////////////////////////
45// heat-bath functions
46
47/**
48 * @brief Wrapper function to updated GaugeField per direction
49 * @details Computes first staplesum, then uses computed result to evolve GaugeField either with
50 * over relaxation or heat bath
51 *
52 * @tparam group
53 * @param U GaugeField to evolve
54 * @param p parameter struct
55 * @param par Parity
56 * @param d Direction to evolve
57 * @param relax If true evolves GaugeField with over relaxation if false then with heat bath
58 * @param plaqw plaquette weights
59 */
60template <typename group>
61void update_parity_dir(GaugeField<group> &U, const parameters &p, Parity par, Direction d,
62 bool relax, const plaqw_t<ftype> &plaqw) {
63
64 static hila::timer hb_timer("Heatbath");
65 static hila::timer or_timer("Overrelax");
66 static hila::timer staples_timer("Staplesum");
67
68 double accr = 0.0;
69
70 Field<group> staples;
71
72 staples_timer.start();
73
74 staplesum(U, staples, d, plaqw, par);
75
76 staples_timer.stop();
77
78 if (relax) {
79
80 or_timer.start();
81
82 onsites(par) {
83 if(plaqw[d][d][X] != 0) {
84#ifdef SUN_OVERRELAX_dFJ
85 suN_overrelax_dFJ(U[d][X], staples[X], p.beta);
86#else
87 //suN_overrelax(U[d][X], staples[X]);
88 suN_overrelax(U[d][X], staples[X], p.beta);
89#endif
90 }
91 }
92 or_timer.stop();
93
94 } else {
95
96 hb_timer.start();
97 onsites(par) {
98 if (plaqw[d][d][X] != 0) {
99 suN_heatbath(U[d][X], staples[X], p.beta);
100 }
101 }
102 hb_timer.stop();
103
104 }
105}
106
107/**
108 * @brief Wrapper update function
109 * @details Gauge Field update sweep with randomly chosen parities and directions
110 *
111 * @tparam group
112 * @param U GaugeField to update
113 * @param p Parameter struct
114 * @param plaqw plaquette weights
115 */
116template <typename group>
117void update(GaugeField<group> &U, const parameters &p, const plaqw_t<ftype> &plaqw) {
118 for (int i = 0; i < 2 * NDIM; ++i) {
119 int tdp = hila::broadcast((int)(hila::random() * 2 * NDIM));
120 int tdir = tdp / 2;
121 int tpar = 1 + (tdp % 2);
122 bool relax = hila::broadcast((int)(hila::random() * (1 + p.n_overrelax)) != 0);
123 // hila::out0 << " " << Parity(tpar) << " -- " << Direction(tdir);
124 update_parity_dir(U, p, Parity(tpar), Direction(tdir), relax, plaqw);
125 }
126 // hila::out0 << "\n";
127}
128
129/**
130 * @brief Evolve gauge field
131 * @details Evolution happens by means of heat bath and overrelaxation. For each heatbath update
132 * (p.n_update) we do on average also p.n_overrelax overrelaxation updates.
133 *
134 * @tparam group
135 * @param U
136 * @param p
137 */
138template <typename group>
139void do_trajectory(GaugeField<group> &U, const plaqw_t<ftype> &plaqw, const parameters &p) {
140 for (int n = 0; n < p.n_update; n++) {
141 for (int i = 0; i <= p.n_overrelax; i++) {
142 update(U, p, plaqw);
143 //hila::out0 << relax << "\n";
144 }
145 }
147}
148
149// heat-bath functions
150///////////////////////////////////////////////////////////////////////////////////
151// measurement functions
152
153template <typename group>
154std::vector<double> measure_s_wplaq_ps(const GaugeField<group> &U, Direction obd,
155 const plaqw_t<ftype> &plaqw) {
156 // measure the total Wilson plaquette action for the gauge field U
157 int obdlsize = lattice.size(obd);
158 ReductionVector<double> plaql(obdlsize);
159 plaql = 0.0;
160 plaql.allreduce(false).delayed(true);
161 double normf = 2.0 / (NDIM * (NDIM - 1) * lattice.volume() / obdlsize);
162
163 foralldir(dir1) foralldir(dir2) if (dir1 < dir2) {
164 U[dir2].start_gather(dir1, ALL);
165 U[dir1].start_gather(dir2, ALL);
166 onsites(ALL) {
167 double tplq = (1.0 - real(trace(U[dir1][X] * U[dir2][X + dir1] *
168 (U[dir2][X] * U[dir1][X + dir2]).dagger())) /
169 group::size()) *
170 plaqw[dir1][dir2][X] * normf;
171 int obdind = X.coordinate(obd);
172 if (dir1 != obd && dir2 != obd) {
173 plaql[obdind] += tplq;
174 } else {
175 if (obdind > 0 && obdind < obdlsize - 1) {
176 if (obdind == 1) {
177 plaql[obdind] += tplq;
178 plaql[obdind + 1] += 0.5 * tplq;
179 } else if (obdind == obdlsize - 2) {
180 plaql[obdind] += 0.5 * tplq;
181 plaql[obdind + 1] += tplq;
182 } else {
183 plaql[obdind] += 0.5 * tplq;
184 plaql[obdind + 1] += 0.5 * tplq;
185 }
186 }
187 }
188 }
189 }
190 plaql.reduce();
191 return plaql.vector();
192}
193
194/**
195 * @brief Measure Polyakov lines to direction dir
196 * @details Naive implementation, includes extra communication
197 * @tparam T GaugeField Group
198 * @param U GaugeField to measure
199 * @param dir Direction
200 * @return Complex<double>
201 */
202template <typename T>
203std::vector<Complex<double>> measure_polyakov_ps(const GaugeField<T> &U, Direction obd,
204 Direction dir = Direction(NDIM - 1)) {
205
206 Field<T> polyakov = U[dir];
207
208 // mult links so that polyakov[X.dir == 0] contains the polyakov loop
209 for (int plane = lattice.size(dir) - 2; plane >= 0; plane--) {
210
211 // safe_access(polyakov) pragma allows the expression below, otherwise
212 // hilapp would reject it because X and X+dir can refer to the same
213 // site on different "iterations" of the loop. However, here this
214 // is restricted on single dir-plane so it works but we must tell it to hilapp.
215
216#pragma hila safe_access(polyakov)
217 onsites(ALL) {
218 if (X.coordinate(dir) == plane) {
219 polyakov[X] = U[dir][X] * polyakov[X + dir];
220 }
221 }
222 }
223
224 ReductionVector<Complex<double>> ploopl(lattice.size(obd));
225 ploopl = 0.0;
226 ploopl.allreduce(false).delayed(true);
227
228 int obdlsize = lattice.size(obd);
229 double normf = 1.0 / (lattice.volume() / (lattice.size(dir) * obdlsize));
230
231 onsites(ALL) if (X.coordinate(dir) == 0) {
232 ploopl[X.coordinate(obd)] += trace(polyakov[X]) * normf;
233 }
234 ploopl.reduce();
235
236 return ploopl.vector();
237}
238
239template <typename group>
240void measure_stuff(const GaugeField<group> &U, const plaqw_t<ftype> &plaqw) {
241 // perform measurements on current gauge and momentum pair (U, E) and
242 // print results in formatted form to standard output
243 static bool first = true;
244#if BCOPEN >= 0 && BCOPEN < NDIM
245 if (first) {
246 // print legend for measurement output
247 hila::out0 << "LMPLAQPS: plaq[1] plaq[2] plaq[3] plaq[4] ...\n";
248 hila::out0 << "LMPOLPS: P[1].re P[1].im P[2].re P[2].im ...\n";
249 first = false;
250 }
251 Direction obd = Direction(BCOPEN);
252 auto plaql = measure_s_wplaq_ps(U, obd, plaqw);
253 auto polyl = measure_polyakov_ps(U, obd, e_t);
254 hila::out0 << "MPLAQPS";
255 for (int i = 1; i < lattice.size(obd); ++i) {
256 hila::out0 << string_format(" % 0.6e", plaql[i]);
257 }
258 hila::out0 << '\n';
259 hila::out0 << "MPOLPS ";
260 for (int i = 1; i < lattice.size(obd); ++i) {
261 hila::out0 << string_format(" % 0.6e % 0.6e", polyl[i].real(), polyl[i].imag());
262 }
263 hila::out0 << '\n';
264#else
265 if (first) {
266 // print legend for measurement output
267 hila::out0 << "LMEAS: plaq P.real P.imag\n";
268 first = false;
269 }
270 auto plaq = measure_s_wplaq(U) / (lattice.volume() * NDIM * (NDIM - 1) / 2);
271 auto poly = measure_polyakov(U, e_t);
272 hila::out0 << string_format("MEAS % 0.6e % 0.6e % 0.6e", plaq, poly.real(), poly.imag())
273 << '\n';
274#endif
275}
276
277// end measurement functions
278///////////////////////////////////////////////////////////////////////////////////
279// load/save config functions
280
281template <typename group>
282void checkpoint(const GaugeField<group> &U, int trajectory, const parameters &p) {
283 double t = hila::gettime();
284 // name of config with extra suffix
285 std::string config_file =
286 p.config_file + "_" + std::to_string(abs((trajectory + 1) / p.n_save) % 2);
287 // save config
288 U.config_write(config_file);
289 // write run_status file
290 if (hila::myrank() == 0) {
291 std::ofstream outf;
292 outf.open("run_status", std::ios::out | std::ios::trunc);
293 outf << "trajectory " << trajectory + 1 << '\n';
294 outf << "seed " << static_cast<uint64_t>(hila::random() * (1UL << 61)) << '\n';
295 outf << "time " << hila::gettime() << '\n';
296 // write config name to status file:
297 outf << "config name " << config_file << '\n';
298 outf.close();
299 }
300 std::stringstream msg;
301 msg << "Checkpointing, time " << hila::gettime() - t;
302 hila::timestamp(msg.str().c_str());
303}
304
305template <typename group>
306bool restore_checkpoint(GaugeField<group> &U, int &trajectory, parameters &p) {
307 uint64_t seed;
308 bool ok = true;
309 p.time_offset = 0;
310 hila::input status;
311 if (status.open("run_status", false, false)) {
312 hila::out0 << "RESTORING FROM CHECKPOINT:\n";
313 trajectory = status.get("trajectory");
314 seed = status.get("seed");
315 p.time_offset = status.get("time");
316 // get config name with suffix from status file:
317 std::string config_file = status.get("config name");
318 status.close();
319 hila::seed_random(seed);
320 U.config_read(config_file);
321 ok = true;
322 } else {
323 std::ifstream in;
324 in.open(p.config_file, std::ios::in | std::ios::binary);
325 if (in.is_open()) {
326 in.close();
327 hila::out0 << "READING initial config\n";
328 U.config_read(p.config_file);
329 ok = true;
330 } else {
331 ok = false;
332 }
333 }
334 return ok;
335}
336
337// end load/save config functions
338///////////////////////////////////////////////////////////////////////////////////
339
340
341int main(int argc, char **argv) {
342
343 // hila::initialize should be called as early as possible
344 hila::initialize(argc, argv);
345
346 // hila provides an input class hila::input, which is
347 // a convenient way to read in parameters from input files.
348 // parameters are presented as key - value pairs, as an example
349 // " lattice size 64, 64, 64, 64"
350 // is read below.
351 //
352 // Values are broadcast to all MPI nodes.
353 //
354 // .get() -method can read many different input types,
355 // see file "input.h" for documentation
356
357 hila::out0 << "SU(" << mygroup::size() << ") heat-bath with overrelaxation\n";
358
359 hila::out0 << "Using floating point epsilon: " << fp<ftype>::epsilon << "\n";
360
361 parameters p;
362
363 hila::input par("parameters");
364
365 CoordinateVector lsize;
366 // reads NDIM numbers
367 lsize = par.get("lattice size");
368 // inverse gauge coupling
369 p.beta = par.get("beta");
370 // number of trajectories
371 p.n_traj = par.get("number of trajectories");
372 // number of heat-bath (HB) sweeps per trajectory
373 p.n_update = par.get("updates in trajectory");
374 // number of overrelaxation sweeps petween HB sweeps
375 p.n_overrelax = par.get("overrelax steps");
376 // number of thermalization trajectories
377 p.n_therm = par.get("thermalization trajs");
378 // wilson flow frequency (number of traj. between w. flow measurement)
379 p.gflow_freq = par.get("gflow freq");
380 // wilson flow max. flow-distance
381 p.gflow_max_l = par.get("gflow max lambda");
382 // wilson flow flow-distance step size
383 p.gflow_l_step = par.get("gflow lambda step");
384 // wilson flow absolute accuracy (per integration step)
385 p.gflow_a_accu = par.get("gflow abs. accuracy");
386 // wilson flow relative accuracy (per integration step)
387 p.gflow_r_accu = par.get("gflow rel. accuracy");
388 // random seed = 0 -> get seed from time
389 long seed = par.get("random seed");
390 // save config and checkpoint
391 p.n_save = par.get("trajs/saved");
392 // measure surface properties and print "profile"
393 p.config_file = par.get("config name");
394
395 par.close(); // file is closed also when par goes out of scope
396
397 // set up the lattice
398 lattice.setup(lsize);
399
400 // We need random number here
401 hila::seed_random(seed);
402
403 // Alloc gauge field and momenta (E)
406
407 plaqw_t<ftype> plaqw;
408 foralldir(d1) {
409 foralldir(d2) {
410 onsites(ALL) {
411 plaqw[d1][d2][X] = 1.0;
412 }
413 }
414 }
415
416#if BCOPEN>=0 && BCOPEN<NDIM
417 Direction obd = Direction(BCOPEN);
418 hila::out0 << "Using open boundary conditions in " << obd << "-direction\n";
419 foralldir(d1) {
420 foralldir(d2) {
421 onsites(ALL) {
422 if (X.coordinate(obd) == 0) {
423 plaqw[d1][d2][X] = 0;
424 }
425 if (X.coordinate(obd) == 1) {
426 if (d1 != obd && d2 != obd) {
427 plaqw[d1][d2][X] = 0.5;
428 }
429 }
430 if (X.coordinate(obd) == lattice.size(obd) - 1) {
431 if (d1 != obd && d2 != obd) {
432 plaqw[d1][d2][X] = 0.5;
433 } else {
434 plaqw[d1][d2][X] = 0;
435 }
436 }
437 }
438 }
439 }
440#endif //END BCOPEN
441
442 // use negative trajectory for thermal
443 int start_traj = -p.n_therm;
444
445 if (!restore_checkpoint(U, start_traj, p)) {
446 U = 1;
447 }
448
449
450 hila::timer update_timer("Updates");
451 hila::timer measure_timer("Measurements");
452 hila::timer gf_timer("Gradient Flow");
453
454 ftype t_step0 = 0;
455 for (int trajectory = start_traj; trajectory < p.n_traj; ++trajectory) {
456
457 ftype ttime = hila::gettime();
458
459 update_timer.start();
460
461 do_trajectory(U, plaqw, p);
462
463 // put sync here in order to get approx gpu timing
464 hila::synchronize_threads();
465 update_timer.stop();
466
467 measure_timer.start();
468
469 hila::out0 << "Measure_start " << trajectory << '\n';
470
471 measure_stuff(U, plaqw);
472
473 hila::out0 << "Measure_end " << trajectory << '\n';
474
475 measure_timer.stop();
476
477 if (trajectory >= 0) {
478
479 if (p.gflow_freq > 0 && trajectory % p.gflow_freq == 0) {
480
481
482 int gtrajectory = trajectory / p.gflow_freq;
483 if (p.gflow_l_step > 0) {
484
485 gf_timer.start();
486
487 int nflow_steps = (int)(p.gflow_max_l / p.gflow_l_step);
488 ftype gftime = hila::gettime();
489 hila::out0 << "Gflow_start " << gtrajectory << '\n';
490
492
493 ftype t_step = t_step0;
494 measure_gradient_flow_stuff(V, (ftype)0.0, t_step);
495 t_step = do_gradient_flow_adapt(V, (ftype)0.0, p.gflow_l_step, p.gflow_a_accu,
496 p.gflow_r_accu, t_step);
497 measure_gradient_flow_stuff(V, p.gflow_l_step, t_step);
498 t_step0 = t_step;
499
500 for (int i = 1; i < nflow_steps; ++i) {
501
502 t_step =
503 do_gradient_flow_adapt(V, i * p.gflow_l_step, (i + 1) * p.gflow_l_step,
504 p.gflow_a_accu, p.gflow_r_accu, t_step);
505
506 measure_gradient_flow_stuff(V, (i + 1) * p.gflow_l_step, t_step);
507 }
508
509 hila::out0 << "Gflow_end " << gtrajectory << " time " << std::setprecision(3)
510 << hila::gettime() - gftime << '\n';
511
512 gf_timer.stop();
513 }
514 }
515 }
516
517 if (p.n_save > 0 && (trajectory + 1) % p.n_save == 0) {
518 checkpoint(U, trajectory, p);
519 }
520 }
521
523}
Array< n, m, hila::arithmetic_type< T > > imag(const Array< n, m, T > &arg)
Return imaginary part of Array.
Definition array.h:703
Array< n, m, hila::arithmetic_type< T > > real(const Array< n, m, T > &arg)
Return real part of Array.
Definition array.h:689
The field class implements the standard methods for accessing Fields. Hilapp replaces the parity acce...
Definition field.h:62
Gauge field class.
Definition gaugefield.h:23
void reunitarize_gauge()
Reunitarize Gauge Field consisting of matrices.
Definition gaugefield.h:95
void config_write(const std::string &filename) const
config_write writes the gauge field to file, with additional "verifying" header
Definition gaugefield.h:162
void setup(const CoordinateVector &siz)
lattice.setup(CoordinateVector size) - set up the base lattice. Called at the beginning of the progra...
Definition lattice.h:447
int size(Direction d) const
lattice.size() -> CoordinateVector or lattice.size(d) -> int returns the dimensions of the lattice,...
Definition lattice.h:433
int64_t volume() const
lattice.volume() returns lattice volume Can be used inside onsites()-loops
Definition lattice.h:424
static constexpr int size()
Returns size of Vector or square Matrix.
Definition matrix.h:248
Class for SU(N) matrix.
Definition sun_matrix.h:110
hila::input - Class for parsing runtime parameter files.
Definition input.h:53
void close()
Closes input parameter file.
Definition input.cpp:94
returntype get(const std::string &key)
Get next value in stack of read in input string from parameters file.
Definition input.h:273
bool open(const std::string &fname, bool use_cmdline=true, bool exit_on_error=true)
Open file that parameters are read from.
Definition input.cpp:27
Complex< T > dagger(const Complex< T > &val)
Return dagger of Complex number.
Definition cmplx.h:1302
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1266
Parity
Parity enum with values EVEN, ODD, ALL; refers to parity of the site. Parity of site (x,...
#define foralldir(d)
Macro to loop over (all) Direction(s)
Definition coordinates.h:80
Direction
Enumerator for direction that assigns integer to direction to be interpreted as unit vector.
Definition coordinates.h:34
constexpr Parity ALL
bit pattern: 011
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:121
int myrank()
rank of this node
Definition com_mpi.cpp:237
std::ostream out0
This writes output only from main process (node 0)
void initialize(int argc, char **argv)
Initial setup routines.
void seed_random(uint64_t seed, bool device_rng=true)
Seed random generators with 64-bit unsigned value. On MPI shuffles the seed so that different MPI ran...
Definition random.cpp:86
T broadcast(T &var, int rank=0)
Broadcast the value of var to all MPI ranks from rank (default=0).
Definition com_mpi.h:170
double gettime()
Definition timing.cpp:167
void finishrun()
Normal, controlled exit - all nodes must call this. Prints timing information and information about c...
Complex< double > measure_polyakov(const GaugeField< T > &U, Direction dir=Direction(NDIM - 1))
Measure Polyakov lines to direction dir.
Definition polyakov.h:17
void update(GaugeField< group > &U, const parameters &p, bool relax)
Wrapper update function.
Definition suN_gauge.cpp:73
void update_parity_dir(GaugeField< group > &U, const parameters &p, Parity par, Direction d, bool relax)
Wrapper function to updated GaugeField per direction.
void do_trajectory(GaugeField< group > &U, const parameters &p)
Evolve gauge field.
void measure_stuff(const GaugeField< group > &U, const parameters &p)
Measures Polyakov lines and Wilson action.
Definition suN_gauge.cpp:41
double suN_heatbath(SU< N, T > &U, const SU< N, T > &staple, double beta)
heatbath
int suN_overrelax(SU< N, T > &U, const SU< N, T > &staple, Btype beta)
overrelaxation using matrix obtained from max_staple_sum_rot() routine