Path Integral Quantum Monte Carlo
Public Member Functions | Data Fields
PathIntegralMonteCarlo Class Reference

The main driver class for the entire path integral monte carlo program. More...

#include <pimc.h>

Public Member Functions

 PathIntegralMonteCarlo (boost::ptr_vector< Path > &, MTRand &, boost::ptr_vector< move_vector > &, boost::ptr_vector< estimator_vector > &, const bool, uint32 binSize=100)
 Constructor. More...
 
 ~PathIntegralMonteCarlo ()
 Destructor.
 
void equilStepDiagonal ()
 Diagonal Equilibration. More...
 
bool equilStepRelaxmu ()
 Relax the chemical potential to find a target number of particles. More...
 
bool equilStepRelaxC0 ()
 Relax the worm constant C0 until we have found the desired diagonal fraction. More...
 
void equilStep (const uint32, const bool, const bool)
 Equilibration. More...
 
void step ()
 PIMC step. More...
 
void finalOutput ()
 Output simulation statistics to disk. More...
 
void saveState (const int finalSave=0)
 Save the state of the simulation to disk, including all path, worm, move and estimator data.
 
void outputPDB ()
 Output the worldline configuration to disk using PDB , suitable for plotting using vmd. More...
 
void printWormState ()
 
string printHistogram ()
 Output a histogram to the terminal. More...
 

Data Fields

int numStoredBins
 Number of stored estimators.
 
int numDiagonal
 Number of consecutive diagonal configs.
 
int numConfig
 Number of configurations;.
 
int numCoMAttempted
 Number of Center of Mass moves.
 
int prevNumCoMAttempted
 Number of Center of Mass moves attempted.
 
int numCoMAccepted
 Number of equil CoM moves accepted.
 
int numDisplaceAttempted
 Number of equil Displace moves.
 
int numDisplaceAccepted
 Number of equil Displace moves accepted.
 
int numMuAttempted
 Number of moves between mu adjustments.
 
int numNAttempted
 The number of particle measurements.
 
int numStepsAttempted
 Number of steps for relaxing C0.
 

Detailed Description

The main driver class for the entire path integral monte carlo program.

Holds the path, action, move and estimator objects along with methods which actually perform the monte carlo sampling procedure.

Definition at line 37 of file pimc.h.

Constructor & Destructor Documentation

◆ PathIntegralMonteCarlo()

PathIntegralMonteCarlo::PathIntegralMonteCarlo ( boost::ptr_vector< Path > &  _pathPtrVec,
MTRand &  _random,
boost::ptr_vector< move_vector > &  _movePtrVec,
boost::ptr_vector< estimator_vector > &  _estimatorPtrVec,
const bool  _startWithState,
uint32  _binSize = 100 
)

Constructor.

Here we initialize all data structures, moves and estimators that will be required with peforming a path integral quantum monte carlo simulation. The initialization depends on whether or not we are restarting, or starting from a user supplied state.

Definition at line 27 of file pimc.cpp.

30  :
31  random(_random),
32  binSize(_binSize),
33  Npaths(_pathPtrVec.size()),
34  pathPtrVec(_pathPtrVec),
35  path(pathPtrVec.front()),
36  movePtrVec(_movePtrVec),
37  move(movePtrVec.front()),
38  estimatorPtrVec(_estimatorPtrVec),
39  estimator(estimatorPtrVec.front())
40 {
41  /* Are we starting from a saved state? */
42  startWithState = _startWithState;
43 
44  /* Initialize stateStrings */
45  stateStrings.resize(Npaths);
46 
47  /* We keep simulating until we have stored a certain number of measurements */
48  numStoredBins = 0;
49 
50  /* Initialize the number of sweeps and updates per sweep*/
51  numSteps = 0;
52 
53  /* This is required in case we have zero particles in the simulation */
54  numUpdates = int(ceil(max(constants()->initialNumParticles(),1)*constants()->numTimeSlices() /
55  (1.0*constants()->Mbar())));
56  if (numUpdates < 100)
57  numUpdates = 100;
58 
59  /* Calculate the number of sweeps to make sure we touch every bead */
60  numImagTimeSweeps = int(ceil((1.0*constants()->numTimeSlices()/(1.0*constants()->Mbar()))));
61 
62  /* These counters are used in the equilibration process */
63  numConfig = 0;
64  numDiagonal = 0;
65  numCoMAttempted = 200;
66  prevNumCoMAttempted = 200;
67  numCoMAccepted = 0;
70  numNAttempted = 0;
71 
72  numStepsAttempted = 2000*numUpdates;
73  numMuAttempted = 2000*numUpdates;
74 
75  relaxmuMessage = false;
76  relaxC0Message = false;
77  equilMessage = false;
78  equilODMessage = false;
79 
80  /* The target diagonal fraction (hard coded) */
81  targetDiagFrac = 0.70;
82 
83  /* Do we need to shift C0 up or down? */
84  sgnDiagFrac = 0;
85  shiftC0 = 0.25;
86 
87  /* Have we found the desired C0 value for the diagonal fraction */
88  foundC0 = false;
89 
90  /* The target/initial number of particles */
92 
93  /* Number probability distribution used when relaxing chemical potential.
94  * We assume the maximum possible number is 2000 particles */
95  PN.resize(2000);
96  PN = 0;
97  foundmu = false;
98  muFactor = 1.0;
99  sgnAveN = 0;
100 
101  /* Used for optimization of μ search */
102  bestPN = 0;
103  bestmu = constants()->mu();
104  bestDiffAveN = 5000;
105 
106  /* Intialize the config number to zero */
107  configNumber = 0;
108 
109  /* Determine the cumulative attempt move probabilities, and the indices of
110  * various diagonal moves */
111  double cumDiagProb = 0.0;
112  double cumOffDiagProb = 0.0;
113  string moveName;
114 
115  for (auto movePtr = move.begin(); movePtr != move.end(); ++movePtr) {
116 
117  /* Get the namne of the move and check if it is the generic diagonal
118  * move */
119  moveName = movePtr->getName();
120  if ((moveName == "bisection") || (moveName == "staging"))
121  moveName = "diagonal";
122 
123  /* Accumulate the diagonal moves */
124  if ( (movePtr->operateOnConfig == DIAGONAL) || movePtr->operateOnConfig == ANY ) {
125  cumDiagProb += constants()->attemptProb(moveName);
126  attemptDiagProb.push_back(cumDiagProb);
127  }
128  else
129  attemptDiagProb.push_back(cumDiagProb);
130 
131  /* Accumulate the off-diagonal moves */
132  if ( (movePtr->operateOnConfig == OFFDIAGONAL) || movePtr->operateOnConfig == ANY) {
133  cumOffDiagProb += constants()->attemptProb(moveName);
134  attemptOffDiagProb.push_back(cumOffDiagProb);
135  }
136  else
137  attemptOffDiagProb.push_back(cumOffDiagProb);
138 
139  /* Find the indices of moves in case we need them */
140  moveIndex[moveName] = std::distance(move.begin(), movePtr);
141  }
142 
143  /* Make sure the move cumulative probability arrays add up to 1 */
144  attemptDiagProb.back() = 1.0 + EPS;
145  attemptOffDiagProb.back() = 1.0 + EPS;
146 
147  /* If we are restarting, or loading a state from disk, do so now. */
148  if (startWithState || constants()->restart())
149  loadState();
150 
151  /* Setup all the estimators for measurement i/o */
152  for (auto &&estPtr : estimatorPtrVec)
153  for (auto &est : estPtr)
154  est.prepare();
155 
156  /* Make a list of estimator names for the 0th estimator */
157  for (auto estimatorPtr = estimator.begin(); estimatorPtr != estimator.end(); ++estimatorPtr)
158  estimatorIndex[estimatorPtr->getName()] = std::distance(estimator.begin(), estimatorPtr);
159 }
double mu() const
Get chemical potential.
Definition: constants.h:43
int initialNumParticles()
Get initial number of particles.
Definition: constants.h:100
int prevNumCoMAttempted
Number of Center of Mass moves attempted.
Definition: pimc.h:68
int numDisplaceAttempted
Number of equil Displace moves.
Definition: pimc.h:70
int numDisplaceAccepted
Number of equil Displace moves accepted.
Definition: pimc.h:71
int numConfig
Number of configurations;.
Definition: pimc.h:66
int numStepsAttempted
Number of steps for relaxing C0.
Definition: pimc.h:74
int numCoMAttempted
Number of Center of Mass moves.
Definition: pimc.h:67
int numCoMAccepted
Number of equil CoM moves accepted.
Definition: pimc.h:69
int numNAttempted
The number of particle measurements.
Definition: pimc.h:73
int numDiagonal
Number of consecutive diagonal configs.
Definition: pimc.h:65
int numStoredBins
Number of stored estimators.
Definition: pimc.h:64
int numMuAttempted
Number of moves between mu adjustments.
Definition: pimc.h:72
Ttype & max(Ttype &x, Ttype &y)
Maximum of two inputs.
Definition: common.h:146
#define EPS
A small number.
Definition: common.h:94
ConstantParameters * constants()
Global public access to the constants.
Definition: constants.h:201
+ Here is the call graph for this function:

Member Function Documentation

◆ equilStep()

void PathIntegralMonteCarlo::equilStep ( const uint32  iStep,
const bool  relaxC0,
const bool  relaxmu 
)

Equilibration.

The equilibration method, where we perform fully diagonal moves 20% of the time, finding an optimal, COM step, then if it is desired, we attempt to find an optimal value of μ and C0.

Definition at line 561 of file pimc.cpp.

561  {
562 
563  /* How far are we into the equilibration? */
564  double equilFrac = (1.0*iStep) / (1.0*constants()->numEqSteps());
565 
566  /* We begin by attempting to perform a purely diagonal equilibration */
567  if (equilFrac < 0.1 && !startWithState) {
568  if (iStep == 0) {
569  numRemainingSteps = constants()->numEqSteps()/10;
570  if (numRemainingSteps == 0)
571  numRemainingSteps = 1;
572  barStepSize = numRemainingSteps/44;
573  if (!barStepSize)
574  barStepSize = 1;
575  cout << "[" << std::flush;
576  numBars = 0;
577  }
579 
580  /* Output a progress bar */
581  if ((iStep > 0) && (iStep % barStepSize == 0) && (numBars < 44)) {
582  cout << "▇" << std::flush;
583  numBars++;
584  }
585 
586  if (iStep == numRemainingSteps-1) {
587  for (int nb = numBars; nb < 44; nb++)
588  cout << "▇" << std::flush;
589  cout << "] \t. Diagonal Pre-Equilibration." << endl << std::flush;
590  }
591 
592  }
593  else if ((equilFrac < 0.2) && (relaxmu || relaxC0)) {
594 
595  if (!equilODMessage) {
596  equilODMessage = true;
597  cout << "[";
598  numRemainingSteps = int(0.2*constants()->numEqSteps())-iStep;
599  barStepSize = numRemainingSteps/44;
600  if (!barStepSize)
601  barStepSize = 1;
602  numBars = 0;
603  }
604 
605  /* Output a progress bar */
606  if ((iStep % barStepSize == 0) && (numBars < 44)) {
607  cout << "▇" << std::flush;
608  numBars++;
609  }
610 
611  if (iStep == constants()->numEqSteps()/5-1) {
612  for (int nb = numBars; nb < 44; nb++)
613  cout << "▇" << std::flush;
614  cout << "] \t. Off-Diagonal Pre-Equilibration." << endl << std::flush;
615  }
616 
617  /* If we are performing any parameter relaxtion, we do some additional
618  * off-diagonal pre-equilibration */
619  for (int n = 0; n < numUpdates; n++) {
620 
621  /* Generate random number and run through all moves */
622  double x = random.rand();
623  string mName;
624  for(uint32 p=0;p<Npaths;p++){
625  mName = update(x,n,p);
626  }
627  }
628  }
629  else {
630  /* Perform possible parameter relaxation schemes */
631  if (relaxmu && !foundmu)
632  foundmu = equilStepRelaxmu();
633  else if (relaxC0 && !foundC0) {
634  foundC0 = equilStepRelaxC0();
635  }
636  /* Otherwise (or after converged) equilibrate */
637  else {
638  if (!equilMessage) {
639  equilMessage = true;
640  cout << format("[PIMCID: %s] - Equilibration Stage.") % constants()->id() << endl << "[";
641  numRemainingSteps = constants()->numEqSteps()-iStep;
642  barStepSize = numRemainingSteps/44;
643  if (!barStepSize)
644  barStepSize = 1;
645  numBars = 0;
646  }
647 
648  /* Output a progress bar */
649  if ((iStep % barStepSize == 0) && (numBars < 44)) {
650  cout << "▇" << std::flush;
651  numBars++;
652  }
653 
654  /* This is the regular equilibration portion after we have the desired
655  * value of C0 and μ. */
656  for (int n = 0; n < numUpdates; n++) {
657 
658  /* Generate random number and run through all moves */
659  double x = random.rand();
660  string mName;
661  for(uint32 p=0;p<Npaths;p++){
662  mName = update(x,n,p);
663  }
664  } // numUpdates
665  }
666 
667  } // equilfrac > 0.2
668 
669  /* Save a state every binsize equilibrium steps provided we are diagonal*/
670  if ( path.worm.isConfigDiagonal && (iStep > 0) && (iStep % binSize) == 0)
671  saveState();
672 
673  if ((iStep == constants()->numEqSteps()-1) && equilMessage){
674  for (int nb = numBars; nb < 44; nb++)
675  cout << "▇" << std::flush;
676  cout << "]" << endl;
677  }
678 }
string id()
Get simulation UUID.
Definition: constants.h:102
uint32 numEqSteps()
Get the number of equilibration steps.
Definition: constants.h:103
bool equilStepRelaxC0()
Relax the worm constant C0 until we have found the desired diagonal fraction.
Definition: pimc.cpp:412
bool equilStepRelaxmu()
Relax the chemical potential to find a target number of particles.
Definition: pimc.cpp:292
void saveState(const int finalSave=0)
Save the state of the simulation to disk, including all path, worm, move and estimator data.
Definition: pimc.cpp:804
void equilStepDiagonal()
Diagonal Equilibration.
Definition: pimc.cpp:198
Worm worm
Details on the worm.
Definition: path.h:44
bool isConfigDiagonal
Stores the diagonality of the configuration.
Definition: worm.h:39
unsigned long uint32
Unsigned integer type, at least 32 bits.
Definition: common.h:105
+ Here is the call graph for this function:

◆ equilStepDiagonal()

void PathIntegralMonteCarlo::equilStepDiagonal ( )

Diagonal Equilibration.

The diagonal-only portion of the equilibration.

Definition at line 198 of file pimc.cpp.

198  {
199 
200  for (int n = 0; n < constants()->initialNumParticles(); n++) {
201 
202  double x = random.rand();
203 
204  /* Here we do the diagonal pre-equilibration moves, and allow for
205  * optimization of simulation parameters */
206  if (x < constants()->attemptProb("center of mass")) {
207 
208  /* index for the center of mass */
209  int index = moveIndex["center of mass"];
210 
211  /* perform the CoM move */
212  for (auto & cmove : movePtrVec)
213  cmove.at(index).attemptMove();
214 
215  /* We check how many CoM moves we have tried. Every 200 moves, we see if we need
216  * to adjust comDelta, provided we are in the pre-equilibration diagonal state. */
217  if ( (move.at(index).getNumAttempted() > 0)
218  && (move.at(index).getNumAttempted() > prevNumCoMAttempted)
219  && (move.at(index).getNumAttempted() % numCoMAttempted == 0)
220  && (constants()->comDelta() < 0.5*blitz::min(path.boxPtr->side)) ) {
221 
222  numCoMAccepted = move.at(index).getNumAccepted() - numCoMAccepted;
223  double CoMRatio = 1.0*numCoMAccepted / numCoMAttempted;
224  if (CoMRatio < 0.2)
225  constants()->shiftCoMDelta(-0.6);
226  else if (CoMRatio < 0.3)
227  constants()->shiftCoMDelta(-0.4);
228  else if (CoMRatio < 0.4)
229  constants()->shiftCoMDelta(-0.2);
230  else if (CoMRatio > 0.6)
231  constants()->shiftCoMDelta(0.2);
232  else if (CoMRatio > 0.7)
233  constants()->shiftCoMDelta(0.4);
234  else if (CoMRatio > 0.8)
235  constants()->shiftCoMDelta(0.6);
236 
237  /* Reset the counters */
238  numCoMAccepted = move.at(index).getNumAccepted();
239  prevNumCoMAttempted = move.at(index).getNumAttempted();
240  } // CoM Delta Shift
241 
242  } // Center of mass move
243  /* Now try a displace move */
244  else if (x < constants()->attemptProb("center of mass") + constants()->attemptProb("displace")) {
245 
246  int index = moveIndex["displace"];
247  for (auto & cmove : movePtrVec)
248  cmove.at(index).attemptMove();
249 
250  /* We check how many displace moves we have tried. Every numDisplaceAttempted moves, we see if we need
251  * to adjust delta, provided we are in the pre-equilibration diagonal state. */
252  if ( (move.at(index).getNumAttempted() > 0)
253  && (move.at(index).getNumAttempted() % numDisplaceAttempted == 0) ) {
254 
255  numDisplaceAccepted = move.at(index).getNumAccepted() - numDisplaceAccepted;
256  double displaceRatio = 1.0*numDisplaceAccepted / numDisplaceAttempted;
257  if (displaceRatio < 0.2)
258  constants()->shiftDisplaceDelta(-0.6);
259  else if (displaceRatio < 0.3)
260  constants()->shiftDisplaceDelta(-0.4);
261  else if (displaceRatio < 0.4)
262  constants()->shiftDisplaceDelta(-0.2);
263  else if (displaceRatio > 0.6)
265  else if (displaceRatio > 0.7)
267  else if (displaceRatio > 0.8)
269 
270  //cout << "delta: " << constants()->delta() << " " << displaceRatio << endl;
271  /* Reset the counters */
272  numDisplaceAccepted = move.at(index).getNumAccepted();
273  }
274  } // displace move
275  else {
276  /* Attemp a diagonal path update*/
277  for (int sweep = 0; sweep < numImagTimeSweeps; sweep++){
278  for (auto &cmove : movePtrVec)
279  cmove.at(moveIndex["diagonal"]).attemptMove();
280  }
281  }
282 
283  } // initNumParticles
284 }
void shiftCoMDelta(double frac)
Shift the CoM move size.
Definition: constants.h:129
void shiftDisplaceDelta(double frac)
Shift the displace move size.
Definition: constants.h:130
double comDelta() const
Get center of mass shift.
Definition: constants.h:53
dVec side
The linear dimensions of the box.
Definition: container.h:31
const Container * boxPtr
A constant reference to the container class.
Definition: path.h:43
Ttype & min(Ttype &x, Ttype &y)
Minimum of two inputs.
Definition: common.h:142
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ equilStepRelaxC0()

bool PathIntegralMonteCarlo::equilStepRelaxC0 ( )

Relax the worm constant C0 until we have found the desired diagonal fraction.

This portion of the equilibration attempts to find the appropriate value of the worm constant C0 until we have arrived at a desired diagonal fraction fixed to be 75% here.

Definition at line 412 of file pimc.cpp.

412  {
413 
414  /* Print a message when starting the relaxation */
415  if (!relaxC0Message) {
416  relaxC0Message = true;
417  cout << format("[PIMCID: %s] - Relax Worm Constant.\n") % constants()->id() << endl;
418  }
419 
420  for (int n = 0; n < numUpdates; n++) {
421 
422  /* Generate random number and run through all moves */
423  double x = random.rand();
424  string mName;
425  for(uint32 p=0; p<Npaths; p++) {
426  mName = update(x,n,p);
427  }
428 
429  /* We accumulate the number of diagonal configurations */
430  numConfig++;
431  if (path.worm.isConfigDiagonal)
432  ++numDiagonal;
433 
434  /* Every numStepsAttempted steps, we check the diagonal fraction and update the
435  * worm constant. */
436  if ( numConfig == numStepsAttempted) {
437 
438  double diagFrac = 1.0*numDiagonal / (1.0*numConfig);
439 
440  if ( (diagFrac > (targetDiagFrac-0.05)) && (diagFrac <= (targetDiagFrac+0.05)) ) {
441  cout << format("\nConverged on C0 = %8.5f\n\n") % constants()->C0();
442  /* for (int i = 0; i < diagFracVals.size(); i++) */
443  /* cout << format("%12.5f%12.5f\n") % C0Vals[i] % diagFracVals[i]; */
444  return true;
445  }
446  else {
447  cout << format("%4.2f\t%8.5f\t") % diagFrac % constants()->C0();
448 
449  /* Store the values of C0 and the diagonal fraciton */
450  C0Vals.push_back(constants()->C0());
451  diagFracVals.push_back(diagFrac);
452 
453  /* Perform an iterative linaer regression and suggest a new
454  * value */
455  constants()->setC0(linearRegressionC0());
456 
457  cout << format("%8.5f\t%5d\t%8.6f\n") % constants()->C0()
458  % path.getTrueNumParticles()
459  % (1.0*path.getTrueNumParticles()/path.boxPtr->volume);
460 
461  /* Reset the counters */
462  numDiagonal = 0;
463  numConfig = 0;
464 
465  } // Haven't converged yet
466 
467  } // numConfig == numStepsAttempted
468 
469  } // numUpdates
470 
471  return false;
472 }
void setC0(double _C0)
Set the value of C0.
Definition: constants.h:127
double C0() const
Get worm factor C0.
Definition: constants.h:49
double volume
The volume of the container in A^3.
Definition: container.h:35
int getTrueNumParticles() const
The number of active particles.
Definition: path.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ equilStepRelaxmu()

bool PathIntegralMonteCarlo::equilStepRelaxmu ( )

Relax the chemical potential to find a target number of particles.

This portion of the equilibration attempts to find the appropriate chemical potential to converge onto a target number of particles.

Definition at line 292 of file pimc.cpp.

292  {
293 
294  /* Print a message when starting the relaxation */
295  if (!relaxmuMessage) {
296  relaxmuMessage = true;
297  cout << format("[PIMCID: %s] - Relax Chemical Potential.") % constants()->id() << endl;
298  }
299 
300  double shiftmu = 1.0;
301  double mu;
302 
303  for (int n = 0; n < numUpdates; n++) {
304 
305  /* Generate random number and run through all moves */
306  double x = random.rand();
307  string mName;
308  for(uint32 p=0; p<Npaths; p++) {
309  mName = update(x,n,p);
310  }
311 
312  /* Relax the chemical potential to get a desired average number of
313  * particles */
314  int cN = round(1.0*path.worm.getNumBeadsOn()/constants()->numTimeSlices());
315  int curSize = PN.size();
316  if (cN >= curSize) {
317  PN.resizeAndPreserve(cN+1);
318  for (int n = curSize; n < cN; n++)
319  PN(n) = 0;
320  }
321 
322  PN(cN)++;
323  numNAttempted += 1;
324 
325  if ( (numNAttempted == numMuAttempted) && (N0 > 0) ) {
326 
327  /* Output the distribution to the terminal*/
328  cout << printHistogram();
329 
330  /* Compute the peak loation and average number of particles */
331  int peakN = blitz::maxIndex(PN)[0];
332  firstIndex i;
333  int aveN = round(blitz::sum(i*PN)/blitz::sum(PN));
334 
335  /* If we have shifted the peak to the desired value, exit */
336  if (peakN == N0) {
337  cout << format("Converged on μ = %8.5f\n\n") % constants()->mu();
338  return true;
339  }
340  else {
341  string method;
342 
343  /* We make sure the peak is in a window 10 particles away from
344  * the target. */
345  bool peakInWindow = ((peakN >= N0-5) && (peakN <= N0+5));
346 
347  if ((PN(N0) > 0) && (PN(N0+1) > 0) && (PN(N0-1) > 0) && peakInWindow) {
348  method = "Equal";
349  mu = constants()->mu() - 0.5*constants()->T()*log(1.0*PN(N0+1)/PN(N0-1));
350  }
351  else if ((PN(N0) > 0) && (PN(N0+1) > 0) && peakInWindow) {
352  method = "Down";
353  mu = constants()->mu() - constants()->T()*log(1.0*PN(N0+1)/PN(N0));
354  }
355  else if ((PN(N0) > 0) && (PN(N0-1) > 0) && peakInWindow) {
356  method = "Up";
357  mu = constants()->mu() - constants()->T()*log(1.0*PN(N0)/PN(N0-1));
358  }
359  else {
360  if (!inWindow) {
361  cout << "Reseting μ to previous best value." << endl;
362  mu = bestmu;
363  }
364  else {
365  if (peakN > N0) {
366  mu = constants()->mu() - shiftmu*muFactor;
367  method = "Down";
368  }
369  else {
370  mu = constants()->mu() + shiftmu*muFactor;
371  method = "Up";
372  }
373 
374  /* Determine if we need to alter the muFactor. */
375  int sgn = ((N0-aveN) > 0) - ((N0-aveN) < 0);
376  if (sgnAveN != 0) {
377  if (sgn == sgnAveN)
378  muFactor *= 1.618;
379  else
380  muFactor /= 1.61803399;
381  }
382  sgnAveN = sgn;
383  }
384  }
385 
386  cout << format("Shifting %5s:%10.2f%10.2f%10d%10d%10d") % method %
387  constants()->mu() % mu % PN(N0-1) % PN(N0) % PN(N0+1);
388 
389  constants()->setmu(mu);
390 
391  cout << format("%12d%12d%12d\n") % peakN % aveN % path.getTrueNumParticles();
392 
393  numNAttempted = 0;
394  PN = 0;
395 
396  } // haven't moved peak yet
397 
398  } // numNAttempted == numMuAttempted
399  } // numupdates
400 
401  return false;
402 }
void setmu(double _mu)
Set the value of the chemical potential.
Definition: constants.h:121
double T() const
Get temperature.
Definition: constants.h:41
string printHistogram()
Output a histogram to the terminal.
Definition: pimc.cpp:1361
int getNumBeadsOn() const
Return the number of active beads.
Definition: worm.h:88
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ finalOutput()

void PathIntegralMonteCarlo::finalOutput ( )

Output simulation statistics to disk.

We perform this once at the very end of the simulation. It saves the details of accetance probabilities as well as estimators to a file.

Definition at line 752 of file pimc.cpp.

752  {
753 
754  /* Output the acceptance data to the log file */
755  communicate()->file("log")->stream() << endl;
756  communicate()->file("log")->stream() << endl;
757 
758  communicate()->file("log")->stream() << "---------- Begin Acceptance Data ---------------" << endl;
759  communicate()->file("log")->stream() << endl;
760  communicate()->file("log")->stream() << format("%-29s\t:\t%7.5f\n") % "Total Rate"
761  % move.front().getTotAcceptanceRatio();
762  communicate()->file("log")->stream() << endl;
763 
764  /* Ouptut all the move acceptance information to disk */
765  string moveName;
766  for (auto &cmove : move){
767 
768  moveName = cmove.getName();
769 
770  /* We only output levels for those moves which have a variable size */
771  if (cmove.variableLength) {
772  for (int n = 0; n <= constants()->b(); n++) {
773  communicate()->file("log")->stream() << format("%-12s Level %-10d\t:\t%7.5f\t(%d/%d)\n")
774  % moveName % n % cmove.getAcceptanceRatioLevel(n)
775  % cmove.numAcceptedLevel(n) % cmove.numAttemptedLevel(n);
776  }
777  }
778  communicate()->file("log")->stream() << format("%-29s\t:\t%7.5f\t(%d/%d)\n") % moveName
779  % cmove.getAcceptanceRatio() % cmove.numAccepted % cmove.numAttempted;
780  communicate()->file("log")->stream() << endl;
781 
782  }
783  communicate()->file("log")->stream() << "---------- End Acceptance Data -----------------" << endl;
784 
785  communicate()->file("log")->stream() << endl;
786  communicate()->file("log")->stream() << endl;
787 
788  /* Output the estimator statistics to the log file */
789  communicate()->file("log")->stream() << "---------- Begin Estimator Data ----------------" << endl;
790  communicate()->file("log")->stream() << endl;
791  for (auto &cestimator : estimator) {
792  communicate()->file("log")->stream() << format("%-33s\t:\t%16d\t%16d\n") % cestimator.getName()
793  % cestimator.getNumSampled() % cestimator.getTotNumAccumulated();
794 
795  }
796  communicate()->file("log")->stream() << endl;
797  communicate()->file("log")->stream() << "---------- End Estimator Data ------------------" << endl;
798 }
File * file(string type)
Get method returning file object.
Definition: communicator.h:85
int b()
Get bisection level.
Definition: constants.h:98
Communicator * communicate()
Global public access to the communcator singleton.
Definition: communicator.h:121
+ Here is the call graph for this function:

◆ outputPDB()

void PathIntegralMonteCarlo::outputPDB ( )

Output the worldline configuration to disk using PDB , suitable for plotting using vmd.

We must post-process the final pdb file and split it up due to connectivity changes.

See also
For the PDB specification: http://www.wwpdb.org/documentation/format32/v3.2.html

Definition at line 1161 of file pimc.cpp.

1161  {
1162 
1163  numParticles = path.getNumParticles();
1164  int numTimeSlices = path.numTimeSlices;
1165 
1166  configNumber++;
1167 
1168  /* We go through all beads, and find the start and end bead for each
1169  * worldline, adding them to an array */
1170  Array <beadLocator,1> startBead,endBead;
1171  startBead.resize(numParticles);
1172  endBead.resize(numParticles);
1173 
1174  /* We sort the output by the number of beads in a worldline */
1175  Array <int,1> wlLength(numParticles);
1176  wlLength = 0;
1177 
1178  /* This is the index-beadNumber mapping array */
1179  Array <int,2> beadNum(numTimeSlices,numParticles);
1180  beadNum = 0;
1181 
1182  int numWorldLines = 0;
1183 
1184  /* Get the list of beads that are active in the simulation */
1185  Array <bool,2> doBead(numTimeSlices,numParticles);
1186  doBead = cast<bool>(path.worm.getBeads());
1187 
1188  /* We go through each particle/worldline */
1189  int nwl = 0;
1190  int beadNumber = 0;
1191  for (int n = 0; n < numParticles; n++) {
1192 
1193  /* The initial bead to be moved */
1194  startBead(nwl) = 0,n;
1195 
1196  /* We make sure we don't try to touch the same worldline twice */
1197  if (doBead(startBead(nwl))) {
1198 
1199  /* Check to see if the start bead is on a worm. If it is, we start
1200  * at the worm tail and end at its head. */
1201  if (path.worm.foundBead(path,startBead(nwl))) {
1202  startBead(nwl) = path.worm.tail;
1203  endBead(nwl) = path.next(path.worm.head);
1204  }
1205  /* Otherwise, we loop around until we find the initial bead */
1206  else
1207  endBead(nwl) = startBead(nwl);
1208 
1209  /* Mark the beads as touched and increment the number of worldlines */
1210  beadLocator beadIndex;
1211  beadIndex = startBead(nwl);
1212  int length = 1;
1213  do {
1214  doBead(beadIndex) = false;
1215  beadNum(beadIndex) = beadNumber;
1216  beadNumber++;
1217  length++;
1218  beadIndex = path.next(beadIndex);
1219  } while (!all(beadIndex==endBead(nwl)));
1220 
1221  /* We label each trajectory by the number of particles it contains.
1222  * a worm is always given label 0 */
1223  if ((length % numTimeSlices) == 0)
1224  wlLength(nwl) = length/numTimeSlices;
1225  else
1226  wlLength(nwl) = 0;
1227 
1228  nwl++;
1229  } // doBead
1230 
1231  } // n
1232  numWorldLines = nwl;
1233 
1234  /* Output the PDB header */
1235  communicate()->file("wl")->stream() << format("REMARK [CONFIG %04d]\n") % configNumber;
1236 
1237  /* Output the unit cell information. It is always cubic. Everything is scaled by
1238  * an overall factor for better visualization. */
1239 
1240  double scale = 10.0;
1241  int i;
1242  communicate()->file("wl")->stream() << format("%-6s") % "CRYST1";
1243  for (i = 0; i < NDIM; i++)
1244  communicate()->file("wl")->stream() << format("%9.3f") % (scale*path.boxPtr->side[i]);
1245  while (i < 3) {
1246  communicate()->file("wl")->stream() << format("%9.3f") % 1.0;
1247  i++;
1248  }
1249  communicate()->file("wl")->stream() << format("%7.2f%7.2f%7.2f %-11s%4d\n") % 90.0 % 90.0 % 90.0 % "P 1" % 1;
1250 
1251  /* We output the atom block */
1252  beadLocator beadIndex;
1253  for (int n = 0; n < numWorldLines; n++) {
1254  beadIndex = startBead(n);
1255  do {
1256  /* We give the zero-time-slice bead a special name */
1257  if (beadIndex[0] == 0) {
1258  communicate()->file("wl")->stream() << format("%-6s%5d %-4s %03d %9s") % "ATOM"
1259  % beadNum(beadIndex) % "H0" % wlLength(n) % " ";
1260  }
1261  else {
1262  communicate()->file("wl")->stream() << format("%-6s%5d %-4s %03d %9s") % "ATOM"
1263  % beadNum(beadIndex) % "HE" % wlLength(n) % " ";
1264  }
1265 
1266  /* Output the coordinates in 3D */
1267  for (i = 0; i < NDIM; i++) {
1268  communicate()->file("wl")->stream() << format("%8.3f") % (scale*path(beadIndex)[i]);
1269  }
1270  while (i < 3) {
1271  communicate()->file("wl")->stream() << format("%8.3f") % 0.0;
1272  i++;
1273  }
1274  communicate()->file("wl")->stream() << format("%14s\n") % "HE";
1275 
1276  beadIndex = path.next(beadIndex);
1277  } while (!all(beadIndex==endBead(n)));
1278  }
1279  communicate()->file("wl")->stream() <<("TER\n");
1280 
1281  /* Now output the connect block */
1282  for (int n = 0; n < numWorldLines; n++) {
1283  beadIndex = startBead(n);
1284  do {
1285  communicate()->file("wl")->stream() << format("%-6s%5d") % "CONECT" % beadNum(beadIndex);
1286  beadLocator prevIndex,nextIndex;
1287  prevIndex = path.prev(beadIndex);
1288  nextIndex = path.next(beadIndex);
1289 
1290  /* We make sure that we don't connect beads linked by periodic bondary
1291  * conditions */
1292 
1293  /* Check the previous bead */
1294  if (path.worm.beadOn(prevIndex)) {
1295  dVec sep;
1296  sep = path(beadIndex) - path(prevIndex);
1297  if (dot(sep,sep) < path.boxPtr->rcut2)
1298  communicate()->file("wl")->stream() << format("%5d") % beadNum(prevIndex);
1299  }
1300 
1301  /* Now the next bead */
1302  if (path.worm.beadOn(nextIndex)) {
1303  dVec sep;
1304  sep = path(nextIndex) - path(beadIndex);
1305  if (dot(sep,sep) < path.boxPtr->rcut2)
1306  communicate()->file("wl")->stream() << format("%5d") % beadNum(nextIndex);
1307  }
1308  communicate()->file("wl")->stream() << endl;
1309 
1310  beadIndex = path.next(beadIndex);
1311  } while (!all(beadIndex==endBead(n)));
1312  }
1313  communicate()->file("wl")->stream() <<("END\n");
1314 
1315  /* Free up memory */
1316  startBead.free();
1317  endBead.free();
1318  wlLength.free();
1319  beadNum.free();
1320  doBead.free();
1321 }
double rcut2
The smallest separation squared.
Definition: container.h:36
int getNumParticles() const
Get the size of the worldline array.
Definition: path.h:51
const int numTimeSlices
A local constant copy of the number of time slices.
Definition: path.h:37
beadLocator & next(int slice, int ptcl)
Move one link forward in imaginary time.
Definition: path.h:86
beadLocator & prev(int slice, int ptcl)
Move one link backward in imaginary time.
Definition: path.h:95
beadLocator tail
The coordinates of the worm tail.
Definition: worm.h:32
beadLocator head
The coordinates of the worm head.
Definition: worm.h:31
int beadOn(int, int) const
Safely get a bead (int indexed)
Definition: worm.h:148
bool foundBead(const Path &, const beadLocator &)
Test to see if a supplied bead is located on a worm.
Definition: worm.cpp:124
const Array< unsigned int, 2 > & getBeads() const
Return the bead list.
Definition: worm.h:84
#define NDIM
Number of spatial dimnsions.
Definition: common.h:71
TinyVector< int, 2 > beadLocator
time-slice,bead-number world line index
Definition: common.h:117
TinyVector< double, NDIM > dVec
A NDIM-vector of type double.
Definition: common.h:111
+ Here is the call graph for this function:

◆ printHistogram()

string PathIntegralMonteCarlo::printHistogram ( )

Output a histogram to the terminal.

Useful for visualizing distributions.

Definition at line 1361 of file pimc.cpp.

1361  {
1362 
1363  string histogramDisplay = "\n";
1364 
1365  /* Setup the window of the histogram to be analyzed.*/
1366  int start,end,window;
1367  window = 5;
1368 
1369  start = N0-window;
1370  if (start < 0)
1371  start = 0;
1372  end = N0 + window;
1373 
1374  /* We make sure we haven't moved past the end of the probability
1375  * distribution array */
1376  if (end > PN.size()-1)
1377  end = PN.size()-1;
1378 
1379  double factor = 50.0/blitz::max(PN);
1380  int sumPN = 0;
1381  for (int n = start; n <= end; n++) {
1382  int numStars = int(PN(n)*factor);
1383  histogramDisplay += str(format("%03d|") % n);
1384  for (int i = 0; i < numStars; i++)
1385  histogramDisplay += "▇";
1386  histogramDisplay += "\n";
1387  sumPN += PN(n);
1388  }
1389  histogramDisplay += "\n";
1390 
1391  /* if (sumPN > bestPN) { */
1392  /* bestPN = sumPN; */
1393  /* bestmu = constants()->mu(); */
1394  /* cout << "setting mu" << endl; */
1395  /* } */
1396 
1397  firstIndex i;
1398  double aveN = (blitz::sum(i*PN)/blitz::sum(PN));
1399 
1400  double diffAveN = abs(N0-aveN);
1401 
1402  if (diffAveN < bestDiffAveN) {
1403  bestDiffAveN = diffAveN;
1404  bestmu = constants()->mu();
1405  }
1406 
1407  inWindow = (sumPN > 0);
1408  if (bestPN == 0)
1409  inWindow = true;
1410 
1411  return histogramDisplay;
1412 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ step()

void PathIntegralMonteCarlo::step ( )

PIMC step.

This method performs the metropolis sampling, which is actually a complicated multi-step operation which consists of various types of moves.
They can in general occur at different frequencies. We also measure all estimators.

Definition at line 688 of file pimc.cpp.

688  {
689 
690  string moveName;
691 
692  /* perform updates on each set of paths */
693  for (uint32 pIdx=0; pIdx<Npaths; pIdx++) {
694 
695  /* We run through all moves, making sure that we could have touched each bead at least once */
696  for (int n = 0; n < numUpdates ; n++) {
697  moveName = update(random.rand(),n,pIdx);
698  }
699 
700  /* Perform all measurements */
701  for (auto& est : estimatorPtrVec[pIdx])
702  est.sample();
703 
704  /* Every binSize measurements, we output averages to disk and record the
705  * state of the simulation on disk. */
706  if (estimatorPtrVec[pIdx].size() > 0){
707  if (estimatorPtrVec[pIdx].front().getNumAccumulated() >= binSize) {
708 
709  for (auto& est : estimatorPtrVec[pIdx]) {
710  /* cout << est.getNumAccumulated() << endl; */
711  if (est.getNumAccumulated() >= binSize)
712  est.output();
713  }
714  if(Npaths==1)
715  saveState();
716  if (pIdx == 0)
717  ++numStoredBins;
718  }
719  }
720  }
721 
723  if(estimatorPtrVec.size() > Npaths) {
724 
725  /* Multi-Path estimators are at the end of the estimator vectors */
726  for (auto& est : estimatorPtrVec.back())
727  est.sample();
728 
729  /* Every binSize measurements, we output averages to disk and record the
730  * state of the simulation on disk. */
731  if (estimatorPtrVec.back().front().getNumAccumulated() >= binSize) {
732 
733  for (auto& est : estimatorPtrVec.back())
734  if (est.getNumAccumulated() >= binSize)
735  est.output();
736 
737  /* Save to disk or store a state file */
738  saveState();
739 
740  if (estimator.size() == 0)
741  ++numStoredBins;
742  }
743  }
744 }
+ Here is the call graph for this function:

The documentation for this class was generated from the following files: