Add intensity_map_predict_gap with corrected 3-parameter fit
CI for csaxs_bec / test (push) Successful in 1m33s
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:
@@ -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
|
||||
Reference in New Issue
Block a user