Files
cpp/README.md
2026-03-08 13:04:40 +01:00

129 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` (01) |
| 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.