Add intensity_map_predict_gap with corrected 3-parameter fit
CI for csaxs_bec / test (push) Successful in 1m33s

Undulator gap predictor for cSAXS, regenerated from plot_intensity_map.py.
Calibrated on the operating locus (per-energy lowest-gap odd/even flux
maxima, harmonics labelled from the global resonance fit): 3-parameter
pure-exponential field model, gap-residual RMS ~14 um.

Supersedes the earlier 4-parameter (quadratic) constants: c2 was
insignificant (0.2 sigma) and left E_inf degenerate (+/-11 keV).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 17:48:05 +02:00
co-authored by Claude Opus 4.8
parent 82b03274b7
commit 078cd1580d
@@ -0,0 +1,26 @@
"""Undulator gap predictor emitted by plot_intensity_map.py.
Edit the fitted constants in the signature to retune."""
import numpy as np
def predict_gap(energy, n=3, gap_min=5.0,
E_inf=3.2878, c0=2.46086, c1=-0.468091, c2=0.0):
"""Undulator gap [mm] to place `energy` [keV] on harmonic `n`.
Fitted constants are the defaults below; edit them to retune.
Returns NaN where the energy is unreachable on that harmonic."""
energy = np.asarray(energy, float)
arg = E_inf * n / energy - 1.0 # required K^2/2; must be > 0
with np.errstate(invalid="ignore", divide="ignore"):
y = np.log(arg)
if abs(c2) < 1e-12:
g = (y - c0) / c1
else:
disc = c1 * c1 - 4.0 * c2 * (c0 - y)
sq = np.sqrt(np.where(disc >= 0, disc, np.nan))
r1 = (-c1 + sq) / (2.0 * c2)
r2 = (-c1 - sq) / (2.0 * c2)
g = np.where(c1 + 2.0 * c2 * r1 < 0, r1, r2)
g = np.where(arg > 0, g, np.nan) # above harmonic cutoff
g = np.where(g >= gap_min, g, np.nan) # below mechanical minimum
return g