Skip to content

VMC program architecture

VMC is organized as a small executable around a reusable simulation library. main() owns configuration, parameter optimization, concurrent production tasks, result aggregation, and terminal/file setup. Each Simulation owns the state and components needed for one sampling stream.

main()
|
+-- Config::from_file("config.cfg")
+-- JastrowOptimizer::optimize() short Simulation::run() calls for candidate b
+-- open output/vmc.bin
+-- launch Num_Threads asynchronous production tasks
| |
| +-- Simulation::run() initialize_positions -> warmup -> measure
| +-- write_init/frame/done [task 0 only]
|
+-- aggregate task summaries
+-- print final measurements

The per-phase internals of Simulation::run() (warmup and measurement steps) are described under One simulation stream below.

The optimizer finishes before production tasks are launched. Its candidate evaluations create Simulation objects without output writers, so optimizer trajectories are not written to vmc.bin.

ComponentResponsibilityPrimary source
main()Load configuration, optimize b, seed and launch production tasks, aggregate results, write terminal summarysrc/main.cpp
ConfigParse keys, supply defaults, validate selected values, compute step counts and initial proposal sizesrc/config/config.hpp
JastrowOptimizerScan candidate b values with short simulations and select a resultsrc/optimizer/jastrow_optimizer.cu
SimulationOwn one particle configuration and run initialization, warmup, and measurementsrc/simulation/simulation.cu, .cuh
ParticlesStore positions and wavefunction derivative arrays in aligned structure-of-arrays memorysrc/particles/particles.cuh
WaveFunctionCombine the plane-wave Slater component and Padé–Jastrow component; maintain derivative cachessrc/wavefunction/wavefunction.cu, .cuh
SlaterPlaneWaveBuild plane-wave determinant rows, compute determinant ratios, and update/restore cached statesrc/slater_plane_wave/
JastrowPadeEvaluate the Jastrow term, move delta, and derivativessrc/jastrow_pade/
EnergyTrackerMaintain real- and reciprocal-space interaction state and evaluate kinetic plus potential energysrc/energy_tracking/energy_tracking.cu, .cuh
BlockingAnalysisForm block means and estimate a standard errorsrc/blocking_analysis/blocking_analysis.cpp, .hpp
WalkerRNGChoose particles, uniform acceptance values, and proposed displacementssrc/utilities/random.cuh
OutputWriterAbstract init/frame/done records; binary implementation serializes task 0src/output_writer/output_writer.cpp, .hpp

The Simulation constructor creates its particles, wavefunction, blocking analysis, energy tracker, optional output writer, and RNG. initialize_positions() draws coordinates uniformly inside the box until the evaluated log wavefunction is finite, then initializes the energy tracker’s structure factors and real- and reciprocal-space terms.

warmup() performs Num_Particles × Warmup_Sweeps proposed moves. After each particle-sized batch it adjusts the proposal step size toward a 50% acceptance target. Warmup moves update simulation state but are not sent to the energy blocks or output writer.

Calling this phase “equilibration” expresses its intended role; the code does not prove that a selected warmup length is sufficient for every configuration. [general-VMC-knowledge]

For each of Num_Particles × Measure_Sweeps steps, measure():

  1. Proposes and accepts or rejects one particle move.
  2. Updates wavefunction derivatives when necessary.
  3. Evaluates total energy.
  4. Adds the energy to the running mean and blocking analysis.
  5. Writes a frame when an output writer is attached.

The returned MeasurementSummary contains a mean energy, an optional standard error, and an acceptance fraction.

metropolis_step() selects one particle and proposes displacements in all three coordinates. Coordinates are wrapped into [0, box_length). It calculates the Slater determinant ratio and change in the Jastrow value, then compares the corresponding log probability ratio with a uniform random draw.

On acceptance, it commits the Slater update and incrementally updates the interaction-energy state. On rejection, it restores the cached Slater row and old coordinates.

The wavefunction derivative arrays feed EnergyTracker::kinetic_energy(). EnergyTracker::potential_energy() adds cached real-space and reciprocal-space terms to correction and background terms. eval_total_energy() returns their sum.

Every measured energy enters BlockingAnalysis. A standard error becomes available after two complete blocks. In main(), production-task means are averaged; available task errors are combined as sqrt(sum(task_error²)) / Num_Threads.

This describes the implemented arithmetic. The assumptions required for that quantity to be a reliable uncertainty belong in the missing method/statistics documentation.

Num_Threads controls the number of std::async(std::launch::async, ...) production tasks. The master RNG generates a separate seed value for each task. The code does not use MPI.

Only task 0 receives BinOutputWriter; therefore:

  • output/vmc.bin is a single task’s sequence of frames.
  • The final terminal energy and acceptance rate aggregate all tasks.
  • The file does not contain the final cross-task aggregate.

Separate seeds show that the tasks are intended as distinct sampling streams; statistical independence is an assumption rather than something control flow alone can prove. [assumption]

CMake builds the same library sources in one of two modes:

  • With CUDA disabled, .cu files are compiled as C++ and take their CPU paths.
  • With CUDA enabled and a CUDA compiler available, CMake defines VMC_CUDA_BACKEND and links CUDA runtime, cuSOLVER, and cuBLAS.

The selected backend and numeric precision are compile-time properties, not runtime CLI options.

Start with src/main.cpp and src/simulation/simulation.cu, then follow the component paths in the table above. Build-time backend selection is in CMakeLists.txt.