# NMR Random Walk Simulation Simulate solid-state NMR spectra and stimulated echo (STE) decays using random walk models. # Build Requires CMake 3.18+, a C++17 compiler, and OpenMP. ```bash cmake -B build cmake --build build ``` The executable is `build/src/rwsim`. # Running ```bash ./build/src/rwsim /path/to/config.txt MotionModel DistributionModel --ste --spectrum -ARG1 1 -ARG2 2 ``` Three positional arguments are required: the path to a configuration file, the motion model name, and the distribution model name. Set `--ste` and/or `--spectrum` to run stimulated echo or spectrum simulations. Any parameter from the configuration file can be overridden on the command line, e.g. `-tau 1e-3`. Use `-seed 42` for reproducible results. Without a seed, a random one is used. **Output files are overwritten if a simulation with the same parameters is run again.** ## Configuration file Change delta, eta, mixing times, echo times, etc. in this file. If a parameter appears multiple times, only the last value is used. ### General parameters | Parameter | Description | |------------|----------------------------| | num_walker | Number of trajectories | | delta | Anisotropy parameter in Hz | | eta | Asymmetry parameter | ### Distribution of correlation times | Model | CLI name | Parameters | |-------|----------|------------| | Delta distribution (same tau for all walkers) | `Delta` | `tau` (jump time in s) | | Log-normal distribution | `LogNormal` | `tau` (peak time in s), `sigma` (width) | ### Motion models | Model | CLI name | Parameters | |-------|----------|------------| | Isotropic random jump | `RandomJump` | — | | Isotropic jump by fixed angle | `IsotropicAngle` | `angle` (degrees) | | Bimodal angle distribution | `BimodalAngle` | `angle1`, `angle2` (degrees), `probability1` (0–1) | | Tetrahedral 4-site jump | `FourSiteTetrahedral` | — | | Octahedral 6-site jump (C3) | `SixSiteOctahedralC3` | — | | N-site jump on cone | `NSiteConeJump` | `num_sites`, `chi` (cone angle, degrees) | | Random jump on cone | `RandomJumpOnCone` | `angle` (cone angle) | | Wobbling in cone | `ConeWobble` | `angle` (cone angle) | ### Spectrum parameters | Parameter | Description | |--------------|---------------------------------| | dwell_time | Acquisition dwell time in s | | num_acq | Number of acquisition points | | techo_start | First echo time in s | | techo_stop | Last echo time in s | | techo_steps | Number of echo times | ### STE parameters | Parameter | Description | |-------------|---------------------------------| | tevo_start | First evolution time in s | | tevo_stop | Last evolution time in s | | tevo_steps | Number of evolution times | | tmix_start | First mixing time in s | | tmix_stop | Last mixing time in s | | tmix_steps | Number of mixing times (log-spaced) | | tpulse4 | Delay of 4th pulse in s | # Architecture The simulation is built around three abstractions: ## Dynamics `Dynamics` (`src/dynamics/base.h`) produces trajectory steps — pairs of `{dt, omega}` representing a waiting time and the NMR frequency after a jump. This is the core interface for defining physical models. **`DecoupledDynamics`** wraps a separate motion model and time distribution, calling them independently. This covers all existing models. To implement a **coupled model** where motion and time are interdependent (e.g. jump angle affects waiting time, or position-dependent dynamics), implement the `Dynamics` interface directly: ```cpp class MyModel : public Dynamics { void initialize(std::mt19937_64& rng) override { /* set initial state */ } Step next(std::mt19937_64& rng) override { // draw angle, compute rate from angle, draw wait time from rate return {dt, omega}; } // ... clone(), setParameters(), etc. }; ``` ## Experiment `Experiment` (`src/experiments/base.h`) defines how trajectory data is turned into observables: - `setup(params)` — configure time axes, allocate accumulators - `accumulate(trajectory)` — process one walker's trajectory - `save(directory)` — write results to files - `clone()` / `merge()` — for thread-parallel accumulation Existing experiments: `SpectrumExperiment`, `STEExperiment`. ## Simulation runner `run_simulation()` (`src/sims.h`) connects a `Dynamics` and an `Experiment`: 1. Sets parameters on both 2. Generates trajectories in parallel (OpenMP, one clone per thread) 3. Each thread accumulates into its own experiment clone 4. Merges thread-local results 5. Saves output The walker loop is parallelized with OpenMP using static scheduling for deterministic reproducibility with a given seed and thread count.