pub struct MultiplicativeBaselineModel<M: FitModel> { /* private fields */ }Expand description
Bounded multiplicative polynomial baseline (issue #635):
y(E) = B(E) · T_inner(E), B(E) = b0 + b1·z + b2·z², z = ln(E / E_ref)where E_ref = √(E_min·E_max) (see baseline_reference_energy) and
T_inner is any inner FitModel — typically the bare transmission
model, or NormalizedTransmissionModel when the SAMMY additive
background is also configured (the baseline is the OUTERMOST factor).
§Placement differs on the exact resolved-count route
“Outermost” describes the TRANSMISSION routes, where the model lives in
the measured bins and B(E) corrects the measured sample/open-beam
ratio: y(E) = B(E)·[Anorm·T + additive background].
The exact separate-arm count route has two distinct axes (true-energy
quadrature vs detector-time bins), so it places B on the TRUE-ENERGY
sample arm, BEFORE the detector response, and applies the Anorm/ABC
wrapper afterwards on the measured bins:
Anorm·R[Φ·B·T]/R[Φ] + background. That ordering is deliberate — a
sample-side multiplicative correction is a property of the sample arm,
and applying it after the ratio would make it a detector-space term
instead — but it means B is NOT the outermost factor there, and a
fitted baseline / baseline_e_ref_ev is not directly comparable
across the two routes: on the transmission routes E is the measured
bin energy, on the exact count route it is the true quadrature energy.
Note b0 remains degenerate with Anorm on both (the response is
linear), which is why the free-Anorm rejection applies to both.
§INTENTIONAL DEPARTURE from SAMMY
SAMMY’s modern data-reduction path applies a SCALAR normalization plus
additive backgrounds only:
T_obs = Anorm·T + BackA + BackB/√E + BackC·√E + BackD·exp(−BackF/√E)
(cro/mnrm1.f90, subroutine Norm, applied to
every data type via the/ZeroKCrossCorrections_M.f90). SAMMY’s nearest
analogue to an energy-dependent multiplicative normalization is the
DORMANT legacy power-law Anorm = Anrm(1) + Anrm(2)·E^Anrm(3)
(acs/macs4.f90:440–450, Find_Www_Yyy), which is not reachable from the
modern reconstruction path. This low-order ln-E polynomial baseline is a
NEREIDS extension motivated by the IPTS-37432 campaign (findings A3/A5):
real VENUS sample/open-beam ratios sit a few % from unity with smooth
energy dependence, and freeing the SAMMY Anorm together with temperature
and density is degenerate on such data (observed: T → 4471 K, n +76 %,
χ²/ν 932, with no warning). The bounded multiplicative form fitted
jointly with temperature at fixed density produced χ²/ν ≈ 2–8 across the
20-run campaign.
Because b0 is exactly degenerate with Anorm, the pipeline rejects a
free Anorm alongside ANY configured baseline — including a fully
frozen one (see nereids-pipeline::validate_multiplicative_baseline).
A frozen-b0 + free-Anorm combination would be well-posed, but
supporting it buys nothing (Anorm would just play b0’s role at a
rescaled value) and splits the normalization story across two knobs;
the sanctioned combination is Anorm held fixed.
§Index invariant
The baseline indices (b0_index, b1_index, b2_index) must NOT
designate a parameter the inner model reads: the analytic Jacobian
filters the baseline indices out of the inner free set, so such a
collision cannot be detected and the column would silently omit
B(E) × ∂T_inner/∂p. Aliasing AMONG the baseline indices themselves
IS supported — the Jacobian columns accumulate.
Implementations§
Source§impl<M: FitModel> MultiplicativeBaselineModel<M>
impl<M: FitModel> MultiplicativeBaselineModel<M>
Sourcepub fn new(
inner: M,
energies: &[f64],
e_ref: f64,
b0_index: usize,
b1_index: usize,
b2_index: usize,
) -> Self
pub fn new( inner: M, energies: &[f64], e_ref: f64, b0_index: usize, b1_index: usize, b2_index: usize, ) -> Self
Create the wrapper. e_ref is normally
baseline_reference_energy(energies); it is passed explicitly so
result consumers can reconstruct B(E) with the exact same reference.
Sourcepub fn with_active_mask(self, mask: Option<&[bool]>) -> Self
pub fn with_active_mask(self, mask: Option<&[bool]>) -> Self
Scope the runtime positivity guard to the given active mask
(None = all bins active, the default). See the active_mask
field doc for why masked bins must be exempt.
Trait Implementations§
Source§impl<M: FitModel> FitModel for MultiplicativeBaselineModel<M>
impl<M: FitModel> FitModel for MultiplicativeBaselineModel<M>
Source§fn evaluate(&self, params: &[f64]) -> Result<Vec<f64>, FittingError>
fn evaluate(&self, params: &[f64]) -> Result<Vec<f64>, FittingError>
Source§fn analytical_jacobian(
&self,
params: &[f64],
free_param_indices: &[usize],
y_current: &[f64],
) -> Option<FlatMatrix>
fn analytical_jacobian( &self, params: &[f64], free_param_indices: &[usize], y_current: &[f64], ) -> Option<FlatMatrix>
Source§impl<M: FitModel> ForwardModel for MultiplicativeBaselineModel<M>
impl<M: FitModel> ForwardModel for MultiplicativeBaselineModel<M>
Source§fn predict(&self, params: &[f64]) -> Result<Vec<f64>, FittingError>
fn predict(&self, params: &[f64]) -> Result<Vec<f64>, FittingError>
Auto Trait Implementations§
impl<M> Freeze for MultiplicativeBaselineModel<M>where
M: Freeze,
impl<M> RefUnwindSafe for MultiplicativeBaselineModel<M>where
M: RefUnwindSafe,
impl<M> Send for MultiplicativeBaselineModel<M>where
M: Send,
impl<M> Sync for MultiplicativeBaselineModel<M>where
M: Sync,
impl<M> Unpin for MultiplicativeBaselineModel<M>where
M: Unpin,
impl<M> UnsafeUnpin for MultiplicativeBaselineModel<M>where
M: UnsafeUnpin,
impl<M> UnwindSafe for MultiplicativeBaselineModel<M>where
M: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more