From be891e0a8c02e0ced5796781ed5f03c52894dce4 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Thu, 19 Oct 2023 16:05:53 +0200 Subject: [PATCH] Continue adapting amor to sciline --- docs/examples/amor.ipynb | 26 +++++++---- src/essreflectometry/amor/calibrations.py | 29 ++++++------ src/essreflectometry/amor/normalize.py | 45 +++++++++++-------- src/essreflectometry/amor/resolution.py | 36 +++++++++++++++ .../reflectometry/conversions.py | 21 +++++---- .../reflectometry/corrections.py | 19 ++++---- src/essreflectometry/reflectometry/types.py | 28 +++++++++--- 7 files changed, 142 insertions(+), 62 deletions(-) diff --git a/docs/examples/amor.ipynb b/docs/examples/amor.ipynb index 1df5392..e01f8c4 100644 --- a/docs/examples/amor.ipynb +++ b/docs/examples/amor.ipynb @@ -13,26 +13,34 @@ "from essreflectometry.amor.load import load\n", "from essreflectometry.amor.beamline import make_beamline\n", "from essreflectometry.amor.conversions import specular_reflection\n", + "from essreflectometry.amor.resolution import add_resolutions, compute_resolution\n", + "from essreflectometry.amor.normalize import normalize_by_supermirror\n", + "from essreflectometry.amor.calibrations import supermirror_calibration\n", + "from essreflectometry.reflectometry.corrections import footprint_correction, normalize_by_counts\n", "from essreflectometry.reflectometry.conversions import providers\n", "from essreflectometry.reflectometry.types import (\n", - " ThetaBins, WavelengthBins, Sample, SampleRotation, Filename,\n", - " ThetaData, WavelengthData,\n", + " ThetaBins, WavelengthBins, Sample, Reference, Sample, SampleRotation, Filename,\n", + " ThetaData, WavelengthData, HistogrammedByQ, QDataWithResolutions, QData, QBins, NormalizedIOverQ\n", ")\n", "\n", "\n", - "\n", - "\n", "pipeline = sciline.Pipeline(\n", - " [load, make_beamline, specular_reflection] + providers,\n", + " [load, make_beamline, specular_reflection, footprint_correction,\n", + " add_resolutions, compute_resolution, normalize_by_counts, supermirror_calibration, normalize_by_supermirror]\n", + " + providers,\n", " params={\n", - " ThetaBins: sc.linspace(dim='theta', start=0, stop=np.pi/2, num=200, unit='rad'),\n", + " ThetaBins: sc.linspace(dim='theta', start=0, stop=np.pi/2, num=2, unit='rad'),\n", " WavelengthBins: sc.array(dims=['wavelength'], values=[2.4, 16.0], unit='angstrom'),\n", + " QBins: sc.geomspace(dim='Q', start=0.008, stop=0.075, num=200, unit='1/angstrom'),\n", + "\n", " SampleRotation[Sample]: sc.scalar(0.7989, unit='deg'),\n", " Filename[Sample]: \"sample.nxs\",\n", + " SampleRotation[Reference]: sc.scalar(0.8389, unit='deg'),\n", + " Filename[Reference]: \"reference.nxs\",\n", " }\n", ")\n", "\n", - "pipeline.visualize(ThetaData[Sample])" + "pipeline.visualize(NormalizedIOverQ)" ] }, { @@ -41,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "pipeline.compute(WavelengthData[Sample])" + "pipeline.compute(HistogrammedByQ[QData[Reference]])" ] }, { @@ -50,7 +58,7 @@ "metadata": {}, "outputs": [], "source": [ - "(pipeline.compute(WavelengthData[Sample])\n", + "(pipeline.compute(WithResolution)\n", " .bins.concat('detector_number').hist(wavelength=200)\n", " .plot())" ] diff --git a/src/essreflectometry/amor/calibrations.py b/src/essreflectometry/amor/calibrations.py index fd3d149..3d308b6 100644 --- a/src/essreflectometry/amor/calibrations.py +++ b/src/essreflectometry/amor/calibrations.py @@ -2,15 +2,17 @@ # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) import scipp as sc -from ..reflectometry import orso +# from ..reflectometry import orso +from ..reflectometry.types import CalibratedReference, HistogrammedByQ, QData, Reference def supermirror_calibration( - data_array: sc.DataArray, - m_value: sc.Variable = None, - critical_edge: sc.Variable = None, - alpha: sc.Variable = None, -) -> sc.Variable: + data_array: HistogrammedByQ[QData[Reference]], +) -> CalibratedReference: + # TODO + m_value: sc.Variable = None + critical_edge: sc.Variable = None + alpha: sc.Variable = None """ Calibrate supermirror measurements @@ -38,13 +40,14 @@ def supermirror_calibration( alpha = sc.scalar(0.25 / 0.088, unit=sc.units.angstrom) calibration = calibration_factor(data_array, m_value, critical_edge, alpha) data_array_cal = data_array * calibration - try: - data_array_cal.attrs['orso'].value.reduction.corrections += [ - 'supermirror calibration' - ] - except KeyError: - orso.not_found_warning() - return data_array_cal + # TODO + # try: + # data_array_cal.attrs['orso'].value.reduction.corrections += [ + # 'supermirror calibration' + # ] + # except KeyError: + # orso.not_found_warning() + return CalibratedReference(data_array_cal) def calibration_factor( diff --git a/src/essreflectometry/amor/normalize.py b/src/essreflectometry/amor/normalize.py index ceead34..fbb626e 100644 --- a/src/essreflectometry/amor/normalize.py +++ b/src/essreflectometry/amor/normalize.py @@ -2,12 +2,20 @@ # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) import scipp as sc -from ..reflectometry import orso +# from ..reflectometry import orso +from ..reflectometry.types import ( + CalibratedReference, + HistogrammedByQ, + NormalizedData, + NormalizedIOverQ, + QDataWithResolutions, +) def normalize_by_supermirror( - sample: sc.DataArray, supermirror: sc.DataArray -) -> sc.DataArray: + sample: NormalizedData[HistogrammedByQ[QDataWithResolutions]], + supermirror: NormalizedData[CalibratedReference], +) -> NormalizedIOverQ: """ Normalize the sample measurement by the (ideally calibrated) supermirror. @@ -26,19 +34,20 @@ def normalize_by_supermirror( """ normalized = sample / supermirror normalized.masks['no_reference_neutrons'] = (supermirror == sc.scalar(0)).data - try: - normalized.attrs['orso'] = sample.attrs['orso'] - normalized.attrs['orso'].value.reduction.corrections = list( - set( - sample.attrs['orso'].value.reduction.corrections - + supermirror.attrs['orso'].value.reduction.corrections - ) - ) - normalized.attrs[ - 'orso' - ].value.data_source.measurement.reference = supermirror.attrs[ - 'orso' - ].value.data_source.measurement.data_files - except KeyError: - orso.not_found_warning() + # TODO + # try: + # normalized.attrs['orso'] = sample.attrs['orso'] + # normalized.attrs['orso'].value.reduction.corrections = list( + # set( + # sample.attrs['orso'].value.reduction.corrections + # + supermirror.attrs['orso'].value.reduction.corrections + # ) + # ) + # normalized.attrs[ + # 'orso' + # ].value.data_source.measurement.reference = supermirror.attrs[ + # 'orso' + # ].value.data_source.measurement.data_files + # except KeyError: + # orso.not_found_warning() return normalized diff --git a/src/essreflectometry/amor/resolution.py b/src/essreflectometry/amor/resolution.py index 7591a8e..d663852 100644 --- a/src/essreflectometry/amor/resolution.py +++ b/src/essreflectometry/amor/resolution.py @@ -2,9 +2,45 @@ # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) import scipp as sc +from ..reflectometry.types import ( + FootprintCorrected, + QData, + QDataWithResolutions, + Resolutions, + Sample, +) from .tools import fwhm_to_std +def compute_resolution(da: FootprintCorrected[Sample]) -> Resolutions: + return Resolutions( + { + 'wavelength_resolution': wavelength_resolution( + chopper_1_position=da.coords['source_chopper_1'].value['position'], + chopper_2_position=da.coords['source_chopper_2'].value['position'], + pixel_position=da.coords['position'], + ), + 'angular_resolution': angular_resolution( + pixel_position=da.coords['position'], + theta=da.bins.coords['theta'], + detector_spatial_resolution=da.coords['detector_spatial_resolution'], + ), + 'sample_size_resolution': sample_size_resolution( + pixel_position=da.coords['position'], + sample_size=da.coords['sample_size'], + ), + } + ) + + +def add_resolutions( + da: QData[Sample], resolutions: Resolutions +) -> QDataWithResolutions: + for coord, value in resolutions.items(): + da.coords[coord] = value + return QDataWithResolutions(da) + + def wavelength_resolution( chopper_1_position: sc.Variable, chopper_2_position: sc.Variable, diff --git a/src/essreflectometry/reflectometry/conversions.py b/src/essreflectometry/reflectometry/conversions.py index d3ad917..acd58a3 100644 --- a/src/essreflectometry/reflectometry/conversions.py +++ b/src/essreflectometry/reflectometry/conversions.py @@ -7,6 +7,11 @@ # from . import orso from .types import ( + CorrectedQData, + FootprintCorrected, + HistogrammedByQ, + QBins, + QData, Raw, Run, SpecularReflectionCoordTransformGraph, @@ -213,8 +218,10 @@ def wavelength_to_theta( def theta_to_q( - data_array: sc.DataArray, q_edges: sc.Variable = None, graph: dict = None -) -> sc.DataArray: + data_array: FootprintCorrected[Run], + q_edges: QBins, + graph: SpecularReflectionCoordTransformGraph, +) -> QData[Run]: """ Convert from theta to Q and if necessary bin in Q. @@ -233,14 +240,12 @@ def theta_to_q( : New data array with theta coordinate. """ - graph = graph if graph is not None else specular_reflection() data_array_q = data_array.transform_coords(["Q"], graph=graph) - if q_edges is not None: - data_array_q = data_array_q.bin({q_edges.dim: q_edges}) - return data_array_q + data_array_q = data_array_q.bin({q_edges.dim: q_edges}) + return QData[Run](data_array_q) -def sum_bins(data_array: sc.DataArray): +def sum_bins(data_array: CorrectedQData) -> HistogrammedByQ[CorrectedQData]: """ Sum the event bins and propagate the maximum resolution, where available. @@ -262,4 +267,4 @@ def sum_bins(data_array: sc.DataArray): return data_array_summed -providers = [tof_to_wavelength, wavelength_to_theta] +providers = [tof_to_wavelength, wavelength_to_theta, theta_to_q, sum_bins] diff --git a/src/essreflectometry/reflectometry/corrections.py b/src/essreflectometry/reflectometry/corrections.py index a8833c5..22de22b 100644 --- a/src/essreflectometry/reflectometry/corrections.py +++ b/src/essreflectometry/reflectometry/corrections.py @@ -5,9 +5,10 @@ from ..amor.tools import fwhm_to_std from . import orso +from .types import FootprintCorrected, Normalizable, NormalizedData, Run, ThetaData -def footprint_correction(data_array: sc.DataArray) -> sc.DataArray: +def footprint_correction(data_array: ThetaData[Run]) -> FootprintCorrected[Run]: """ Perform the footprint correction on the data array that has a :code:`beam_size` and binned :code:`theta` values. @@ -29,16 +30,16 @@ def footprint_correction(data_array: sc.DataArray) -> sc.DataArray: fwhm_to_std(data_array.coords['sample_size'] / size_of_beam_on_sample) ) data_array_fp_correction = data_array / footprint_scale.squeeze() - try: - data_array_fp_correction.attrs['orso'].value.reduction.corrections += [ - 'footprint correction' - ] - except KeyError: - orso.not_found_warning() - return data_array_fp_correction + # try: + # data_array_fp_correction.attrs['orso'].value.reduction.corrections += [ + # 'footprint correction' + # ] + # except KeyError: + # orso.not_found_warning() + return FootprintCorrected[Run](data_array_fp_correction) -def normalize_by_counts(data_array: sc.DataArray) -> sc.DataArray: +def normalize_by_counts(data_array: Normalizable) -> NormalizedData[Normalizable]: """ Normalize the bin-summed data by the total number of counts. If the data has variances, a check is performed to ensure that the counts in each diff --git a/src/essreflectometry/reflectometry/types.py b/src/essreflectometry/reflectometry/types.py index ff9e797..b5ea793 100644 --- a/src/essreflectometry/reflectometry/types.py +++ b/src/essreflectometry/reflectometry/types.py @@ -24,9 +24,8 @@ class ThetaData(sciline.Scope[Run, sc.DataArray], sc.DataArray): """Wavelength data transformed to theta""" -class Experiment(sciline.Scope[Run, sc.DataArray], sc.DataArray): - """Experiment data with added coordinates: - wavelength, incidence angle, and momentum transfer""" +class QData(sciline.Scope[Run, sc.DataArray], sc.DataArray): + """Theta data transformed to momentum transfer""" class FootprintCorrected(sciline.Scope[Run, sc.DataArray], sc.DataArray): @@ -34,12 +33,12 @@ class FootprintCorrected(sciline.Scope[Run, sc.DataArray], sc.DataArray): CalibratedReference = NewType('CalibratedReference', sc.DataArray) -WithQResolution = NewType('WithQResolution', sc.DataArray) +Resolutions = NewType('Resolutions', dict) Normalized = NewType('Normalized', sc.DataArray) CountsByMomentumTransfer = NewType('CountsByMomentumTransfer', sc.DataArray) ''' Parameters for the workflow ''' -MomentumTransferBins = NewType('MomentumTransferBins', sc.Variable) +QBins = NewType('QBins', sc.Variable) WavelengthBins = NewType('WavelengthBins', sc.Variable) ThetaBins = NewType('ThetaBins', sc.Variable) @@ -55,3 +54,22 @@ class BeamlineParams(sciline.Scope[Run, dict], dict): SpecularReflectionCoordTransformGraph = NewType( 'SpecularReflectionCoordTransformGraph', dict ) + +QDataWithResolutions = NewType('QDataWithResolutions', sc.DataArray) +CorrectedQData = TypeVar('CorrectedQData', QData[Reference], QDataWithResolutions) + + +class HistogrammedByQ(sciline.Scope[CorrectedQData, sc.DataArray], sc.DataArray): + """Histogrammmed by Q. Either reference data or sample data with resolutions.""" + + +Normalizable = TypeVar( + 'Normalizable', HistogrammedByQ[QDataWithResolutions], CalibratedReference +) + + +class NormalizedData(sciline.Scope[Normalizable, sc.DataArray], sc.DataArray): + """Normalized histogramm by Q.""" + + +NormalizedIOverQ = NewType('NormalizedIOverQ', sc.DataArray)