Skip to content

Add projected range support and a capability-aware range API (CSDA already exists) #129

@grzanka

Description

@grzanka

Summary

Introduce projected range to libdedx and wrap it (together with the already-existing CSDA range) behind a small, capability-aware transport-quantities API. The design keeps CSDA computed-by-integration (as today), adds projected range as authoritative tabulated data only, and makes the library report explicitly when projected range is unavailable rather than ever returning an approximation.

This is the upstream/core half of the work; the WASM contract + UI half is tracked downstream in aptg/dedx_web (companion issue linked below).

Current state (research findings)

  • CSDA range already exists. dedx_get_csda() (src/dedx_tools.c:167) computes it by adaptive Gaussian quadrature of 1/S(E) over [E_min, E]. Wrappers: dedx_get_csda_range_table() (src/dedx_wrappers.c:215), dedx_get_inverse_csda(), dedx_get_inverse_stp(). This is purely a function of the stopping-power curve and works uniformly for every program including custom compounds — it should stay computed.
  • Projected range does not exist anywhere in the codebase (no projected/detour references).
  • Data model stores stopping power only. dedx_embedded_program_data (src/dedx_embedded_data.h) and the raw data/raw/*.dat files keep a single value column per (ion, target, energy); no range columns are retained.
  • ESTAR/electrons: dedx_estar.h data is present but the path is hard-disabled (dedx.c:587DEDX_ERR_ESTAR_NOT_IMPL).
  • ICRU90: already wired (dedx_embedded_icru90_p/_a/_C).

Physics constraint that drives the design

Quantity Definition Derivable from S(E) alone?
CSDA range ∫₀ᴱ dE'/S(E') — total path length Yes (program-agnostic)
Projected range endpoint projected onto initial direction = R_CSDA × detour No — needs multiple-scattering info

Authoritative projected-range data availability is non-uniform, and the API must reflect that:

Program CSDA Projected Detour
PSTAR (protons) ✅ NIST ✅ NIST
ASTAR (alphas) ✅ NIST ✅ NIST
ESTAR (electrons) ✅ NIST ❌ NIST tabulates CSDA + radiation yield only
MSTAR / ICRU73 / Bethe computed ❌ none tabulated
ICRU90 report tables per-ion, needs verification

Decisions taken (from design review)

  1. No approximation fallback. Programs/ions without authoritative projected-range data report DEDX_RANGE_UNAVAILABLE. We do not return projected = CSDA for heavy ions. Callers (web UI) grey out the quantity, mirroring the existing ESTAR-greyout pattern.
  2. Phased delivery. This issue's deliverable is Phase 1 (API + data plumbing + PSTAR/ASTAR tabulated projected range). Electron support and any computed scattering-power detour model are explicit future phases.

Proposed design

1. Capability-aware transport API

New public surface (in a new dedx_range.h, or extending dedx_tools.h) with an explicit method out-parameter so a tabulated value is never confused with an approximation:

typedef enum {
    DEDX_RANGE_UNAVAILABLE = 0, /* no authoritative data / no model        */
    DEDX_RANGE_TABULATED,       /* interpolated from authoritative table    */
    DEDX_RANGE_CSDA_INTEGRAL,   /* computed ∫dE/S (CSDA)                    */
    DEDX_RANGE_DETOUR_MODEL     /* computed via scattering model (future)   */
} dedx_range_method;

double dedx_get_csda_range     (dedx_workspace*, dedx_config*, float E, int *err);
double dedx_get_projected_range(dedx_workspace*, dedx_config*, float E,
                                dedx_range_method *method, int *err);
double dedx_get_detour_factor  (dedx_workspace*, dedx_config*, float E,
                                dedx_range_method *method, int *err);

/* Query without computing — lets a GUI grey out UI ahead of time */
int dedx_supports_projected_range(int program, int ion, int target);
  • dedx_get_csda() stays as a thin backward-compatible alias of dedx_get_csda_range().
  • Mirror the existing CSDA surface: add dedx_get_projected_range_table(), inverse projected-range, and flat (workspace-free) wrappers, so the downstream WASM integration is a copy of the CSDA path.
  • When projected range is unavailable: set method = DEDX_RANGE_UNAVAILABLE, return a sentinel (e.g. -1) and a non-fatal error code (new DEDX_ERR_PROJECTED_RANGE_UNAVAILABLE).

2. Provider resolution order (per program/ion/target)

  1. Tabulated projected range if embedded (PSTAR, ASTAR; ICRU90 where verified).
  2. Detour modelfuture phase, not in scope here.
  3. Otherwise DEDX_RANGE_UNAVAILABLE.

3. Backward-compatible data-model extension

Add optional parallel payload to dedx_embedded_program_data (NULL when absent, so embedding is incremental and never breaks existing programs):

const float *projected_range; /* [ion][target][energy], or NULL */
/* (optionally) const float *csda_range; for NIST cross-checks */
  • Extend tools/dat2c.py / tools/pdf2dat.py to emit *_proj.h headers from the NIST projected-range column, with provenance comments and reproducible regeneration documented in data/README.md.
  • Re-extract PSTAR/ASTAR projected-range columns into new data/raw/*proj*.dat inputs.

4. Tests & validation

  • Assert tabulated projected-range values against NIST reference points (same style as the existing PSTAR H₂O 100 MeV check).
  • Assert dedx_supports_projected_range() returns 0 for ESTAR/MSTAR/ICRU73/Bethe and custom compounds, and that those paths cleanly return UNAVAILABLE (no crash, no silent CSDA substitution).
  • Verify dedx_get_csda()dedx_get_csda_range() (no regression).

Scope of this issue (Phase 1)

  • New capability-aware range API (dedx_get_csda_range, dedx_get_projected_range, dedx_get_detour_factor, dedx_supports_projected_range) + dedx_get_csda alias
  • dedx_range_method enum + DEDX_ERR_PROJECTED_RANGE_UNAVAILABLE
  • Table + inverse + flat wrappers mirroring the CSDA surface
  • Optional projected_range payload in the embedded data model (NULL-safe)
  • Data pipeline: extract + embed PSTAR & ASTAR projected range with provenance
  • UNAVAILABLE for all programs/ions without authoritative data
  • Tests vs NIST reference values + capability-query tests
  • Doc updates (data/README.md, public header docs)

Future phases (explicitly out of scope here)

  • Phase 2: ICRU90 projected range where the report provides it; Python binding parity (relates to Expand the Python binding to cover the full C API and publish wheels #117).
  • Phase 3 (larger): un-gate ESTAR/electrons and add a scattering-power detour model (Highland/Lewis; ICRU 35 for electrons) so electrons get a real projected range — deferred because no NIST projected-range source exists for electrons.

Downstream

WASM API contract + web UI integration is tracked in the companion aptg/dedx_web issue (will be cross-linked once filed). Relates to the upstream-migration tracking in #118.

https://claude.ai/code/session_01SmjfogDVtB59qhxnfyXGeZ

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions