Skip to content

Commit

Permalink
Update docstrings and cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonHeybrock committed Sep 4, 2024
1 parent 56ab504 commit c152011
Showing 1 changed file with 73 additions and 95 deletions.
168 changes: 73 additions & 95 deletions src/ess/sans/normalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,41 +176,25 @@ def norm_monitor_term(
transmission_fraction: TransmissionFraction[ScatteringRunType],
) -> MonitorTerm[ScatteringRunType]:
"""
Compute the wavelength-dependent contribution to the denominator term for the I(Q)
normalization.
Keeping this as a separate function allows us to compute it once during the
iterations for finding the beam center, while the solid angle is recomputed
for each iteration.
This is basically:
``incident_monitor * transmission_fraction * direct_beam``
If the direct beam is not supplied, it is assumed to be 1.
Compute the monitor-dependent contribution to the denominator term of I(Q).
This is basically ``incident_monitor * transmission_fraction``.
Because the multiplication between the ``incident_monitor * transmission_fraction``
(pixel-independent) and the direct beam (potentially pixel-dependent) consists of a
broadcast operation which would introduce correlations, variances of the direct
beam are dropped or replaced by an upper-bound estimation, depending on the
configured mode.
Keeping the monitor term separate from the detector term allows us to compute the
the latter only once when repeatedly processing chunks of events in streamed data
processing, e.g., for live data reduction.
Parameters
----------
incident_monitor:
The incident monitor data (depends on wavelength).
transmission_fraction:
The transmission fraction (depends on wavelength).
direct_beam:
The direct beam function (depends on wavelength).
uncertainties:
The mode for broadcasting uncertainties. See
:py:class:`ess.reduce.uncertainty.UncertaintyBroadcastMode` for details.
Returns
-------
:
Wavelength-dependent term
(incident_monitor * transmission_fraction * direct_beam) to be used for
the denominator of the SANS I(Q) normalization.
Used by :py:func:`iofq_denominator`.
Monitor-dependent factor of the normalization term for the SANS I(Q).
"""
out = incident_monitor * transmission_fraction
# Convert wavelength coordinate to midpoints for future histogramming
Expand All @@ -222,92 +206,48 @@ def norm_detector_term(
solid_angle: MaskedSolidAngle[ScatteringRunType],
direct_beam: CleanDirectBeam,
uncertainties: UncertaintyBroadcastMode,
) -> CleanWavelength[ScatteringRunType, Denominator]:
# Make wavelength the inner dim
dims = list(direct_beam.dims)
dims.remove('wavelength')
dims.append('wavelength')
direct_beam = direct_beam.transpose(dims)
out = solid_angle * broadcast_uncertainties(
direct_beam, prototype=solid_angle, mode=uncertainties
)
# Convert wavelength coordinate to midpoints for future histogramming
out.coords['wavelength'] = sc.midpoints(out.coords['wavelength'])
return CleanWavelength[ScatteringRunType, Denominator](out)


def iofq_denominator(
wavelength_term: MonitorTerm[ScatteringRunType],
solid_angle: MaskedSolidAngle[ScatteringRunType],
uncertainties: UncertaintyBroadcastMode,
) -> CleanWavelength[ScatteringRunType, Denominator]:
"""
Compute the denominator term for the I(Q) normalization.
In a SANS experiment, the scattering cross section :math:`I(Q)` is defined as
(`Heenan et al. 1997 <https://doi.org/10.1107/S0021889897002173>`_):
.. math::
I(Q) = \\frac{\\partial\\Sigma{Q}}{\\partial\\Omega} = \\frac{A_{H} \\Sigma_{R,\\lambda\\subset Q} C(R, \\lambda)}{A_{M} t \\Sigma_{R,\\lambda\\subset Q}M(\\lambda)T(\\lambda)D(\\lambda)\\Omega(R)}
where :math:`A_{H}` is the area of a mask (which avoids saturating the detector)
placed between the monitor of area :math:`A_{M}` and the main detector.
:math:`\\Omega` is the detector solid angle, and :math:`C` is the count rate on the
main detector, which depends on the position :math:`R` and the wavelength.
:math:`t` is the sample thickness, :math:`M` represents the incident monitor count
rate for the sample run, and :math:`T` is known as the transmission fraction.
Note that the incident monitor used to compute the transmission fraction is not
necessarily the same as :math:`M`, as the transmission fraction is usually computed
from a separate 'transmission' run (in the 'sample' run, the transmission monitor is
commonly moved out of the beam path, to avoid polluting the sample detector signal).
Finally, :math:`D` is the 'direct beam function', and is defined as
Compute the detector-dependent contribution to the denominator term of I(Q).
.. math::
D(\\lambda) = \\frac{\\eta(\\lambda)}{\\eta_{M}(\\lambda)} \\frac{A_{H}}{A_{M}}
where :math:`\\eta` and :math:`\\eta_{M}` are the detector and monitor
efficiencies, respectively.
Hence, in order to normalize the main detector counts :math:`C`, we need compute the
transmission fraction :math:`T(\\lambda)`, the direct beam function
:math:`D(\\lambda)` and the solid angle :math:`\\Omega(R)`.
This is basically ``solid_angle * direct_beam``.
If the direct beam is not supplied, it is assumed to be 1.
The denominator is then simply:
:math:`M_{\\lambda} T_{\\lambda} D_{\\lambda} \\Omega_{R}`,
which is equivalent to ``wavelength_term * solid_angle``.
The ``wavelength_term`` includes all but the ``solid_angle`` and is computed by
:py:func:`iofq_norm_wavelength_term_sample` or
:py:func:`iofq_norm_wavelength_term_background`.
Keeping the monitor term separate from the detector term allows us to compute the
the latter only once when repeatedly processing chunks of events in streamed data
processing, e.g., for live data reduction.
Because the multiplication between the wavelength dependent terms
and the pixel dependent term (solid angle) consists of a broadcast operation which
would introduce correlations, variances are dropped or replaced by an upper-bound
estimation, depending on the configured mode.
Because the multiplication between the pixel-dependent solid angle and the
(potentially) pixel-independent direct beam constitutes a broadcast operation which
would introduce correlations, variances of the direct beam are dropped or replaced
by an upper-bound estimation, depending on the configured mode.
Parameters
----------
wavelength_term:
The term that depends on wavelength, computed by
:py:func:`iofq_norm_wavelength_term`.
solid_angle:
The solid angle of the detector pixels, as viewed from the sample position.
direct_beam:
The direct beam function (depends on wavelength).
uncertainties:
The mode for broadcasting uncertainties. See
:py:class:`ess.reduce.uncertainty.UncertaintyBroadcastMode` for details.
Returns
-------
:
The denominator for the SANS I(Q) normalization.
""" # noqa: E501
denominator = solid_angle * broadcast_uncertainties(
wavelength_term, prototype=solid_angle, mode=uncertainties
Detector-dependent factor of the normalization term for the SANS I(Q).
"""
# Make wavelength the inner dim
dims = list(direct_beam.dims)
dims.remove('wavelength')
dims.append('wavelength')
direct_beam = direct_beam.transpose(dims)
out = solid_angle * broadcast_uncertainties(
direct_beam, prototype=solid_angle, mode=uncertainties
)
return CleanWavelength[ScatteringRunType, Denominator](denominator)
# Convert wavelength coordinate to midpoints for future histogramming
out.coords['wavelength'] = sc.midpoints(out.coords['wavelength'])
return CleanWavelength[ScatteringRunType, Denominator](out)


def process_wavelength_bands(
Expand Down Expand Up @@ -353,10 +293,48 @@ def _normalize(
uncertainties: UncertaintyBroadcastMode,
) -> sc.DataArray:
"""
Perform normalization of counts as a function of Q.
If the numerator contains events, we use the sc.lookup function to perform the
Perform normalization of counts as a function of Q to obtain I(Q) or I(Qx, Qy).
division.
In a SANS experiment, the scattering cross section :math:`I(Q)` is defined as
(`Heenan et al. 1997 <https://doi.org/10.1107/S0021889897002173>`_):
.. math::
I(Q) = \\frac{\\partial\\Sigma{Q}}{\\partial\\Omega} = \\frac{A_{H} \\Sigma_{R,\\lambda\\subset Q} C(R, \\lambda)}{A_{M} t \\Sigma_{R,\\lambda\\subset Q}M(\\lambda)T(\\lambda)D(\\lambda)\\Omega(R)}
where :math:`A_{H}` is the area of a mask (which avoids saturating the detector)
placed between the monitor of area :math:`A_{M}` and the main detector.
:math:`\\Omega` is the detector solid angle, and :math:`C` is the count rate on the
main detector, which depends on the position :math:`R` and the wavelength.
:math:`t` is the sample thickness, :math:`M` represents the incident monitor count
rate for the sample run, and :math:`T` is known as the transmission fraction.
Note that the incident monitor used to compute the transmission fraction is not
necessarily the same as :math:`M`, as the transmission fraction is usually computed
from a separate 'transmission' run (in the 'sample' run, the transmission monitor is
commonly moved out of the beam path, to avoid polluting the sample detector signal).
Finally, :math:`D` is the 'direct beam function', and is defined as
.. math::
D(\\lambda) = \\frac{\\eta(\\lambda)}{\\eta_{M}(\\lambda)} \\frac{A_{H}}{A_{M}}
where :math:`\\eta` and :math:`\\eta_{M}` are the detector and monitor
efficiencies, respectively.
Hence, in order to normalize the main detector counts :math:`C`, we need compute the
transmission fraction :math:`T(\\lambda)`, the direct beam function
:math:`D(\\lambda)` and the solid angle :math:`\\Omega(R)`.
The denominator is then simply:
:math:`M_{\\lambda} T_{\\lambda} D_{\\lambda} \\Omega_{R}`,
which is equivalent to ``wavelength_term * solid_angle``.
The ``wavelength_term`` includes all but the ``solid_angle`` and is computed by
:py:func:`iofq_norm_wavelength_term_sample` or
:py:func:`iofq_norm_wavelength_term_background`.
Parameters
----------
numerator:
Expand All @@ -374,8 +352,8 @@ def _normalize(
Returns
-------
:
The input data normalized by the supplied denominator.
"""
Normalized I(Q) or I(Qx, Qy).
""" # noqa: E501
if return_events and numerator.bins is not None:
# Naive event-mode normalization is not correct if norm-term has variances.
# See https://doi.org/10.3233/JNR-220049 for context.
Expand Down

0 comments on commit c152011

Please sign in to comment.