Part of #118 (dedx_extra.c migration) · Phase C. This is the highest-value item — a real bug that affects every consumer, currently worked around only in dedx_web.
Bug
dedx_get_inverse_stp() in src/dedx_tools.c uses find_min() to locate the Bragg-peak energy. For a monotonically decreasing stopping-power curve (e.g. proton on water, ICRU49), find_min() returns -1, which becomes x2 = -1 in the bisection — producing negative intermediate energies and a spurious dedx_get_stp() error instead of a valid inverse.
Separately, the side semantics are inconsistent: the documented API is side = 0 (low-energy branch) / side = 1 (high-energy branch), but the implementation maps side < 0 → low branch, so neither 0 nor 1 ever selects the ascending branch — both currently return the descending-branch result.
The web project already worked around both in wasm/dedx_extra.c (dedx_get_inverse_stp_flat) with a robust strategy:
- Sample STP on a log-spaced energy grid to locate the peak.
- If the peak is at the leftmost sample → monotone curve → bisect the single descending branch over the full range.
- If the peak is interior →
side == 0 selects [emin, e_peak] (ascending), side == 1 selects [e_peak, emax] (descending), with a guard for requested STP below STP(emin).
Proposed change
- Port the sampled-peak + branch-aware bisection into the core
dedx_get_inverse_stp() so all consumers (CLI, Python, WASM) get correct results, and fix the side ∈ {0,1} mapping to match the documented contract.
- Add a Bragg-peak STP tool to
dedx_tools.h (the web dedx_get_bragg_peak_stp samples ~300 log-spaced energies and returns the max), e.g.:
double dedx_get_bragg_peak_stp(dedx_workspace *ws, dedx_config *config, int *err);
Acceptance criteria
Reference
dedx_web wasm/dedx_extra.c dedx_get_inverse_stp_flat (lines ~264–396) — the proven implementation to port.
Filed via Claude Code as part of the dedx_extra.c → libdedx migration plan.
Part of #118 (dedx_extra.c migration) · Phase C. This is the highest-value item — a real bug that affects every consumer, currently worked around only in dedx_web.
Bug
dedx_get_inverse_stp()insrc/dedx_tools.cusesfind_min()to locate the Bragg-peak energy. For a monotonically decreasing stopping-power curve (e.g. proton on water, ICRU49),find_min()returns-1, which becomesx2 = -1in the bisection — producing negative intermediate energies and a spuriousdedx_get_stp()error instead of a valid inverse.Separately, the
sidesemantics are inconsistent: the documented API isside = 0(low-energy branch) /side = 1(high-energy branch), but the implementation mapsside < 0→ low branch, so neither0nor1ever selects the ascending branch — both currently return the descending-branch result.The web project already worked around both in
wasm/dedx_extra.c(dedx_get_inverse_stp_flat) with a robust strategy:side == 0selects[emin, e_peak](ascending),side == 1selects[e_peak, emax](descending), with a guard for requested STP belowSTP(emin).Proposed change
dedx_get_inverse_stp()so all consumers (CLI, Python, WASM) get correct results, and fix theside ∈ {0,1}mapping to match the documented contract.dedx_tools.h(the webdedx_get_bragg_peak_stpsamples ~300 log-spaced energies and returns the max), e.g.:Acceptance criteria
STP(emax)andSTP(emin)— no error, no negative energy.side = 0andside = 1return the ascending vs descending branch respectively for a curve with an interior Bragg peak.dedx_get_bragg_peak_stp()declared indedx_tools.h+ test.MeV cm²/g,MeV/nucl).Reference
dedx_web
wasm/dedx_extra.cdedx_get_inverse_stp_flat(lines ~264–396) — the proven implementation to port.Filed via Claude Code as part of the
dedx_extra.c→ libdedx migration plan.