Minimal, modern re-adaptation of Tom Marsh’s lcurve code. The goal is to make binary-star light-curve modelling easy to build, easy to script and easy to extend on any recent Linux / macOS box.
- Stand-alone C++17/20 implementation – no ancient Fortran, no IRAF
- Fast static‐geometry engine with OpenMP parallelism
- Optional mass–ratio / velocity-scale / radius-scale Bayesian priors
- Command-line tools for direct evaluation, Nelder–Mead optimisation and full MCMC sampling
- Interactive, live-updating plots via gnuplot-iostream
# 1. Clone
git clone https://github.com/yourname/lcurve_re.git
cd lcurve_re
# 2. Configure & build (uses CMake ≥ 3.14)
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
# 3. Run one of the tools
./build/lcurve_re path/to/config.json # single light-curve evaluation
./build/mcmc_solver path/to/config.json # Metropolis-Hastings / MCMC
./build/simplex path/to/config.json # Nelder–Mead optimiserAll executables understand exactly one argument – the JSON configuration file described below.
| Executable | Purpose |
|---|---|
lcurve_re (main.cpp) |
Evaluate a model, scale it to the data and write the best-fit curve |
mcmc_solver |
Metropolis–Hastings sampler with live χ² plot & colourful progress bar |
simplex |
Lightweight Nelder–Mead optimiser (no priors, no chains) |
All binaries live in build/ after compilation.
• The five numbers in each model_parameters entry are
value range dstep vary(0|1) defined(0|1) – identical to the
original lcurve files.
• If autoscale : true a single scale factor (or four individual
factors when iscale=true in the model) is fitted to minimise χ².
• plot_device : "none" turns off all gnuplot calls – useful for
headless clusters.
• plot_device : "stream" also avoids launching gnuplot and emits live,
line-delimited plot frames on standard output. Each frame starts with
@@LCURVE_PLOT@@ and contains a compact lcurve.plot.v1 JSON object with
x, flux, error, model, residual, and progress metadata. Parent
applications can strip these records from normal terminal output and draw
them with their native plotting widget. plot_update_interval controls
LM/MCMC frame cadence; error_plot_update_interval controls the short
error-refinement MCMC cadence.
lcurve_re/
│
├─ new_scripts/ » small command-line front-ends
│ ├─ mcmc_solver.cpp
│ ├─ simplex.cpp
│ └─ test.cpp
│
├─ src/ » reusable library
│ ├─ lcurve_base/ – light-curve engine (modernised Tom Marsh code)
│ ├─ lroche_base/ – Roche geometry, stream & eclipse maths
│ ├─ mass_ratio_pdf – fast 2-D KDE grid for inclination-conditioned priors
│ └─ new_subs.* – lightweight replacement of TRM’s “subs” helpers
│
├─ main.cpp » minimal evaluator
└─ CMakeLists.txt » build recipe
Run-time
• gnuplot (≥ 5.0) – only for graphical plot_device values; none and
stream never launch it
Build-time
• CMake ≥ 3.14
• A C++17 (or later) compiler (gcc-10+, clang-11+, MSVC 2019)
• nlohmann/json (header-only, bundled as a git submodule)
• gnuplot-iostream (header-only, bundled)
• Optional: OpenMP for multi-threaded geometry / KDE
All third-party headers are vendored – nothing is fetched at build time.
When an NVIDIA CUDA compiler is installed, CMake builds optional GPU kernels for the batched stellar flux and whole Roche-grid/eclipsing calculation:
cmake -B build-cuda -DCMAKE_BUILD_TYPE=Release \
-DGNUPLOT_IOSTREAM_INCLUDE_DIR=/path/to/header/parent \
-DLCURVE_ENABLE_CUDA=ON
cmake --build build-cuda -j$(nproc)
LCURVE_CUDA=1 ./build-cuda/lcurve_mcmc config.jsonCUDA is runtime opt-in; without LCURVE_CUDA=1 the same binary uses the
original CPU/OpenMP path. The accelerated path uses FP32 for per-face work and
FP64 for accumulation and numerically sensitive tiny-star eclipse boundaries.
LCURVE_CUDA_PRECISION=double selects FP64 flux arithmetic, while
LCURVE_CUDA_GRID=0 disables only whole-grid offload. If the driver or device
is unavailable, execution falls back to the CPU automatically.
Use LCURVE_CUDA_GRID_VALIDATE=1 with the direct evaluator to compare every
GPU eclipse interval against the CPU implementation for a model. This is a
validation mode and is intentionally slow.
-
Prepare a first-guess configuration
cp examples/template.json myrun.json -
Optimise with Nelder–Mead
./build/simplex myrun.json
-
Run a full MCMC with priors
./build/mcmc_solver myrun.json
-
Inspect
chain_out.txtin your favourite corner-plotter, tweak steps / priors and repeat.
Original algorithms © Tom Marsh, 2001-2023.
Re-written C++17 adaption, helpers and build scripts © 2025 Fabian
Mattig, MIT licence. See LICENSE files for full text.
Happy modelling! – Fabian
{ /* --- required --------------------------------------------------- */ "data_file_path" : "lightcurve.dat", // or "none" for synthetic data "output_file_path" : "fit_out.dat", /* --- global switches -------------------------------------------- */ "autoscale" : true, // let the code re-scale the model to χ² min "plot_device" : "qt", // qt | wxt | x11 | stream | none "seed" : -123456, // RNG seed (negative = random) /* --- noise / fake data block ------------------------------------ */ "noise" : 1.5e-3, // Gaussian σ added to ‘data’ for writing /* --- MCMC -------------------------------------------------------- */ "mcmc_steps" : 40000, "mcmc_burn_in" : 10000, "use_priors" : true, "priors" : { "vrad1_obs" : "186.2 2.0 0", // mean ±1σ [km s⁻¹] "m1" : "0.82 0.17 0", // M☉ "m2_min" : "1.30 0.27 0", // M☉ "r1" : "0.309 0.020 0" // R☉ }, /* --- model ------------------------------------------------------- */ "model_parameters" : { "q" : "1.4 0.5 0.01 1 1", // value range dstep vary defined "iangle" : "65.0 5 0.1 1 1", "r1" : "0.014 0.01 0.0001 0 1", "... many more see src/model.cpp ..." } }