From 2384354a9648eab1568241e31fd4554926b4a0b2 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Wynen Date: Fri, 13 Nov 2026 17:24:56 +0100 Subject: [PATCH] Read source position from NXmoderator --- .../dream-make-wavelength-lookup-table.ipynb | 4 +-- .../src/ess/beer/conversions.py | 6 ++-- .../src/ess/beer/mcstas/load.py | 16 ++++++--- .../essdiffraction/src/ess/dream/io/geant4.py | 5 +-- .../essdiffraction/src/ess/dream/workflows.py | 7 ++-- .../src/ess/powder/calibration.py | 3 +- .../src/ess/powder/conversion.py | 5 +-- .../essdiffraction/src/ess/powder/types.py | 1 + .../src/ess/snspowder/powgen/data.py | 9 ++--- .../tests/beer/mcstas_beamline_test.py | 4 +-- .../tests/beer/mcstas_reduction_test.py | 3 +- .../tests/dream/geant4_reduction_test.py | 6 ++-- .../docs/odin/odin-data-reduction.ipynb | 2 +- .../odin-make-wavelength-lookup-table.ipynb | 4 +-- .../tbl-make-wavelength-lookup-table.ipynb | 4 +-- .../tests/odin/data_reduction_test.py | 5 ++- packages/essnmx/src/ess/nmx/types.py | 1 + packages/essnmx/src/ess/nmx/workflows.py | 5 +-- packages/essnmx/tests/executable_test.py | 5 ++- .../user-guide/unwrap/analytical-unwrap.ipynb | 6 ++-- .../docs/user-guide/unwrap/dream.ipynb | 4 +-- .../user-guide/unwrap/frame-unwrapping.ipynb | 6 ++-- .../unwrap/lut-building-methods.ipynb | 6 ++-- .../docs/user-guide/unwrap/wfm.ipynb | 4 +-- .../essreduce/src/ess/reduce/nexus/types.py | 13 +++++-- .../src/ess/reduce/nexus/workflow.py | 33 ++++++++++++++++++ .../essreduce/src/ess/reduce/unwrap/lut.py | 12 +++++-- .../src/ess/reduce/unwrap/to_wavelength.py | 5 +-- packages/essreduce/tests/unwrap/lut_test.py | 34 +++++++++---------- .../essreduce/tests/unwrap/unwrap_test.py | 5 ++- packages/essreduce/tests/unwrap/wfm_test.py | 10 ++++-- .../essreduce/tests/unwrap/workflow_test.py | 3 +- ...create-estia-wavelength-lookup-table.ipynb | 4 +-- .../src/ess/estia/conversions.py | 5 +-- .../essreflectometry/src/ess/estia/mcstas.py | 5 +-- .../src/ess/freia/conversions.py | 9 ++--- .../essreflectometry/src/ess/freia/mcstas.py | 5 +-- .../src/ess/reflectometry/types.py | 1 + .../tests/freia/workflow_test.py | 9 ++--- .../tests/monitor_workflow_test.py | 5 ++- .../loki-make-wavelength-lookup-table.ipynb | 4 +-- packages/esssans/src/ess/isissans/general.py | 5 +-- packages/esssans/src/ess/isissans/zoom.py | 7 ++-- .../esssans/src/ess/loki/larmor_workflow.py | 5 +-- packages/esssans/src/ess/sans/conversions.py | 3 +- packages/esssans/src/ess/sans/types.py | 1 + .../esssans/tests/beam_center_finder_test.py | 5 +-- packages/esssans/tests/beam_center_test.py | 10 ++++-- packages/esssans/tests/loki/workflow_test.py | 5 +-- ...bifrost-make-wavelength-lookup-table.ipynb | 4 +-- .../src/ess/bifrost/io/nexus.py | 8 ----- .../ess/bifrost/single_crystal/conversion.py | 3 +- .../bifrost/single_crystal/time_of_flight.py | 3 +- .../ess/spectroscopy/indirect/conversion.py | 5 ++- .../src/ess/spectroscopy/indirect/ki.py | 3 +- .../src/ess/spectroscopy/types.py | 1 + 56 files changed, 213 insertions(+), 138 deletions(-) diff --git a/packages/essdiffraction/docs/user-guide/dream/dream-make-wavelength-lookup-table.ipynb b/packages/essdiffraction/docs/user-guide/dream/dream-make-wavelength-lookup-table.ipynb index c9e3590cf..cf152ff67 100644 --- a/packages/essdiffraction/docs/user-guide/dream/dream-make-wavelength-lookup-table.ipynb +++ b/packages/essdiffraction/docs/user-guide/dream/dream-make-wavelength-lookup-table.ipynb @@ -20,7 +20,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from ess.reduce import unwrap\n", - "from ess.reduce.nexus.types import AnyRun, Position\n", + "from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition\n", "from ess.dream.beamline import InstrumentConfiguration, choppers" ] }, @@ -65,7 +65,7 @@ "\n", "wf[unwrap.LtotalRange[AnyRun, snx.NXdetector]] = sc.scalar(5.0, unit=\"m\"), sc.scalar(80.0, unit=\"m\")\n", "wf[unwrap.NumberOfSimulatedNeutrons] = 200_000 # Increase this number for more reliable results\n", - "wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m')\n", + "wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m')\n", "wf[unwrap.DiskChoppers[AnyRun]] = disk_choppers\n", "wf[unwrap.DistanceResolution] = sc.scalar(0.1, unit=\"m\")\n", "wf[unwrap.TimeResolution] = sc.scalar(250.0, unit='us')\n", diff --git a/packages/essdiffraction/src/ess/beer/conversions.py b/packages/essdiffraction/src/ess/beer/conversions.py index 721153427..fce18d0db 100644 --- a/packages/essdiffraction/src/ess/beer/conversions.py +++ b/packages/essdiffraction/src/ess/beer/conversions.py @@ -8,7 +8,7 @@ from scippneutron.chopper import DiskChopper from scippneutron.conversion.tof import wavelength_from_tof -from ess.reduce.nexus.types import DiskChoppers, Position +from ess.reduce.nexus.types import DiskChoppers, NeutronSourcePosition, Position from ess.reduce.unwrap.lut import ActiveDiskChoppers, chopper_distance_along_beam from ess.reduce.unwrap.types import DetectorLtotal @@ -97,7 +97,7 @@ def _modulation_period(choppers: DiskChoppers[RunType]) -> sc.Variable: def powder_beamline_geometry_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> BeamlineGeometryGraph[RunType]: @@ -113,7 +113,7 @@ def powder_beamline_geometry_graph( def modulation_beamline_geometry_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, choppers: ActiveDiskChoppers[RunType], diff --git a/packages/essdiffraction/src/ess/beer/mcstas/load.py b/packages/essdiffraction/src/ess/beer/mcstas/load.py index 84877c0af..cb1fe25a5 100644 --- a/packages/essdiffraction/src/ess/beer/mcstas/load.py +++ b/packages/essdiffraction/src/ess/beer/mcstas/load.py @@ -7,7 +7,13 @@ import mcstastox import scipp as sc import scippnexus as snx -from ess.powder.types import CaveMonitor, EmptyDetector, RunType, WavelengthMonitor +from ess.powder.types import ( + CaveMonitor, + EmptyDetector, + NeutronSourcePosition, + RunType, + WavelengthMonitor, +) from ess.reduce.nexus.types import DiskChoppers, Position from ess.reduce.unwrap.types import DetectorLtotal @@ -273,7 +279,7 @@ def chopper_mode_from_mcstas_mode(mode: str) -> PulseShapingMode | ModulationMod def mcstas_source_position( fname: Filename[RunType], sample_position: Position[snx.NXsample, RunType] -) -> Position[snx.NXsource, RunType]: +) -> NeutronSourcePosition[RunType]: """Return the effective source position for the McStas geometry. ``sourceMantid`` is close to the choppers and its direction towards the sample @@ -314,7 +320,7 @@ def mcstas_sample_position( def mcstas_choppers( da: RawDetector[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], ) -> DiskChoppers[RunType]: """Return BEER choppers for the McStas mode in the data.""" mode = chopper_mode_from_mcstas_mode(da.coords['mode'].value) @@ -323,7 +329,7 @@ def mcstas_choppers( def _mcstas_choppers_from_file( fname: Filename[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], ) -> DiskChoppers[RunType]: """Return BEER choppers without loading detector events.""" fname = Path(fname) @@ -334,7 +340,7 @@ def _mcstas_choppers_from_file( def mcstas_detector_ltotal( detector: EmptyDetector[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], ) -> DetectorLtotal[RunType]: """Return moderator-to-detector flight path lengths for BEER McStas data.""" diff --git a/packages/essdiffraction/src/ess/dream/io/geant4.py b/packages/essdiffraction/src/ess/dream/io/geant4.py index 73b014c4e..9ab1013dc 100644 --- a/packages/essdiffraction/src/ess/dream/io/geant4.py +++ b/packages/essdiffraction/src/ess/dream/io/geant4.py @@ -16,6 +16,7 @@ Filename, MonitorFilename, MonitorType, + NeutronSourcePosition, NeXusComponent, NeXusDetectorName, Position, @@ -319,8 +320,8 @@ def assemble_monitor_data( return RawMonitor[RunType, MonitorType](monitor) -def dummy_source_position() -> Position[snx.NXsource, RunType]: - return Position[snx.NXsource, RunType]( +def dummy_source_position() -> NeutronSourcePosition[RunType]: + return NeutronSourcePosition[RunType]( sc.vector([np.nan, np.nan, np.nan], unit="mm") ) diff --git a/packages/essdiffraction/src/ess/dream/workflows.py b/packages/essdiffraction/src/ess/dream/workflows.py index f68905586..2e4f02379 100644 --- a/packages/essdiffraction/src/ess/dream/workflows.py +++ b/packages/essdiffraction/src/ess/dream/workflows.py @@ -18,6 +18,7 @@ LookupTableFilename, LookupTableRelativeErrorThreshold, Measurement, + NeutronSourcePosition, Position, ReducerSoftware, SampleRun, @@ -219,9 +220,9 @@ def DreamGeant4Workflow(*, run_norm: RunNormalization, **kwargs) -> sciline.Pipe Position[snx.NXsample, SampleRun]: sample_position, Position[snx.NXsample, VanadiumRun]: sample_position, Position[snx.NXsample, EmptyCanRun]: sample_position, - Position[snx.NXsource, SampleRun]: source_position, - Position[snx.NXsource, VanadiumRun]: source_position, - Position[snx.NXsource, EmptyCanRun]: source_position, + NeutronSourcePosition[SampleRun]: source_position, + NeutronSourcePosition[VanadiumRun]: source_position, + NeutronSourcePosition[EmptyCanRun]: source_position, AccumulatedProtonCharge[SampleRun]: charge, AccumulatedProtonCharge[VanadiumRun]: charge, AccumulatedProtonCharge[EmptyCanRun]: charge, diff --git a/packages/essdiffraction/src/ess/powder/calibration.py b/packages/essdiffraction/src/ess/powder/calibration.py index 06876e3e6..f8ef5f916 100644 --- a/packages/essdiffraction/src/ess/powder/calibration.py +++ b/packages/essdiffraction/src/ess/powder/calibration.py @@ -16,6 +16,7 @@ DetectorTwoTheta, EmptyDetector, GravityVector, + NeutronSourcePosition, Position, RunType, SampleRun, @@ -105,7 +106,7 @@ def to_cif_format(self) -> sc.DataArray: def detector_two_theta( detector: EmptyDetector[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> DetectorTwoTheta[RunType]: diff --git a/packages/essdiffraction/src/ess/powder/conversion.py b/packages/essdiffraction/src/ess/powder/conversion.py index ba66bd9c9..2f900d754 100644 --- a/packages/essdiffraction/src/ess/powder/conversion.py +++ b/packages/essdiffraction/src/ess/powder/conversion.py @@ -21,6 +21,7 @@ IntensityDspacing, IntensityTof, MonitorCoordTransformGraph, + NeutronSourcePosition, Position, QDetector, RunType, @@ -145,7 +146,7 @@ def to_dspacing_with_calibration( def powder_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> ElasticCoordTransformGraph[RunType]: @@ -256,7 +257,7 @@ def convert_reduced_to_empty_can_subtracted_tof( def powder_monitor_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> MonitorCoordTransformGraph[RunType]: diff --git a/packages/essdiffraction/src/ess/powder/types.py b/packages/essdiffraction/src/ess/powder/types.py index 3b845eb6a..018072e72 100644 --- a/packages/essdiffraction/src/ess/powder/types.py +++ b/packages/essdiffraction/src/ess/powder/types.py @@ -30,6 +30,7 @@ GravityVector = reduce_t.GravityVector RawMonitor = reduce_t.RawMonitor MonitorPositionOffset = reduce_t.MonitorPositionOffset +NeutronSourcePosition = reduce_t.NeutronSourcePosition NeXusDetectorName = reduce_t.NeXusDetectorName NeXusMonitorName = reduce_t.NeXusName NeXusComponent = reduce_t.NeXusComponent diff --git a/packages/essdiffraction/src/ess/snspowder/powgen/data.py b/packages/essdiffraction/src/ess/snspowder/powgen/data.py index c859ecc66..695e4ed14 100644 --- a/packages/essdiffraction/src/ess/snspowder/powgen/data.py +++ b/packages/essdiffraction/src/ess/snspowder/powgen/data.py @@ -19,6 +19,7 @@ GravityVector, MonitorCoordTransformGraph, MonitorType, + NeutronSourcePosition, Position, ProtonCharge, RawDataAndMetadata, @@ -242,9 +243,9 @@ def extract_accumulated_proton_charge( return AccumulatedProtonCharge[RunType](data.coords["gd_prtn_chrg"]) -def source_position(dg: RawDataAndMetadata[RunType]) -> Position[snx.NXsource, RunType]: +def source_position(dg: RawDataAndMetadata[RunType]) -> NeutronSourcePosition[RunType]: """Return the source position from a loaded data group.""" - return Position[snx.NXsource, RunType](dg["data"].coords["source_position"]) + return NeutronSourcePosition[RunType](dg["data"].coords["source_position"]) def sample_position(dg: RawDataAndMetadata[RunType]) -> Position[snx.NXsample, RunType]: @@ -268,7 +269,7 @@ def _coordinate_transformation_graph( def detector_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> ElasticCoordTransformGraph[RunType]: @@ -296,7 +297,7 @@ def detector_coordinate_transformation_graph( def monitor_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> MonitorCoordTransformGraph[RunType]: diff --git a/packages/essdiffraction/tests/beer/mcstas_beamline_test.py b/packages/essdiffraction/tests/beer/mcstas_beamline_test.py index 34fb26fdf..9e42db783 100644 --- a/packages/essdiffraction/tests/beer/mcstas_beamline_test.py +++ b/packages/essdiffraction/tests/beer/mcstas_beamline_test.py @@ -12,7 +12,7 @@ from ess.beer.types import SampleRun from ess.reduce import unwrap -from ess.reduce.nexus.types import Position +from ess.reduce.nexus.types import NeutronSourcePosition from ess.reduce.unwrap import lut @@ -24,7 +24,7 @@ def test_can_make_analytical_lookup_table_from_beer_choppers( run_types=[SampleRun], monitor_types=[], wavelength_from="analytical" ) source_position = sc.vector([0.0, 0.0, 0.0], unit="m") - wf[Position[snx.NXsource, SampleRun]] = source_position + wf[NeutronSourcePosition[SampleRun]] = source_position wf[unwrap.DiskChoppers[SampleRun]] = simulation_choppers(mode, source_position) wf[lut.LtotalRange[SampleRun, snx.NXdetector]] = ( sc.scalar(150.0, unit="m"), diff --git a/packages/essdiffraction/tests/beer/mcstas_reduction_test.py b/packages/essdiffraction/tests/beer/mcstas_reduction_test.py index 278294c37..57e9f0e80 100644 --- a/packages/essdiffraction/tests/beer/mcstas_reduction_test.py +++ b/packages/essdiffraction/tests/beer/mcstas_reduction_test.py @@ -49,6 +49,7 @@ ElasticCoordTransformGraph, EmptyDetector, KeepEvents, + NeutronSourcePosition, NormalizedDspacing, PixelMasks, QDetector, @@ -229,7 +230,7 @@ def test_beer_workflows_compute_dspacing_bins(make_workflow, mode, pulse_skippin if pulse_skipping: # Use pulse-skipping choppers with the existing detector geometry. - source_position = wf.compute(Position[snx.NXsource, SampleRun]) + source_position = wf.compute(NeutronSourcePosition[SampleRun]) chopper_mode = ModulationMode.ds0 if mode == 7 else PulseShapingMode.ds1 wf[DiskChoppers[SampleRun]] = simulation_choppers(chopper_mode, source_position) diff --git a/packages/essdiffraction/tests/dream/geant4_reduction_test.py b/packages/essdiffraction/tests/dream/geant4_reduction_test.py index 30b327b00..dd7ebfc96 100644 --- a/packages/essdiffraction/tests/dream/geant4_reduction_test.py +++ b/packages/essdiffraction/tests/dream/geant4_reduction_test.py @@ -31,6 +31,7 @@ KeepEvents, LookupTableFilename, MonitorFilename, + NeutronSourcePosition, NeXusDetectorName, OutFilename, PixelMaskFilenames, @@ -44,11 +45,10 @@ WavelengthMask, ) from scippneutron import metadata -from scippnexus import NXsource from ess.reduce import unwrap from ess.reduce import workflow as reduce_workflow -from ess.reduce.nexus.types import AnyRun, Position +from ess.reduce.nexus.types import AnyRun PARAMETRIZATION = ("detector_name", ["mantle", "endcap_backward", "endcap_forward"]) @@ -62,7 +62,7 @@ MonitorFilename[VanadiumRun]: dream.data.simulated_monitor_vanadium_sample(), MonitorFilename[EmptyCanRun]: dream.data.simulated_monitor_empty_can(), dream.InstrumentConfiguration: dream.beamline.InstrumentConfiguration.high_flux_BC215, # noqa: E501 - Position[NXsource, AnyRun]: sc.vector(value=[0, 0, -76.55], unit="m"), + NeutronSourcePosition[AnyRun]: sc.vector(value=[0, 0, -76.55], unit="m"), CalibrationFilename: None, UncertaintyBroadcastMode: UncertaintyBroadcastMode.drop, DspacingBins: sc.linspace('dspacing', 0.0, 2.3434, 201, unit='angstrom'), diff --git a/packages/essimaging/docs/odin/odin-data-reduction.ipynb b/packages/essimaging/docs/odin/odin-data-reduction.ipynb index f3244f4dc..6c53b4f5d 100644 --- a/packages/essimaging/docs/odin/odin-data-reduction.ipynb +++ b/packages/essimaging/docs/odin/odin-data-reduction.ipynb @@ -66,7 +66,7 @@ "source": [ "from ess.odin.beamline import choppers\n", "\n", - "disk_choppers = choppers(source_position=wf.compute(Position[snx.NXsource, SampleRun]))\n", + "disk_choppers = choppers(source_position=wf.compute(NeutronSourcePosition[SampleRun]))\n", "wf[unwrap.DiskChoppers[SampleRun]] = disk_choppers" ] }, diff --git a/packages/essimaging/docs/odin/odin-make-wavelength-lookup-table.ipynb b/packages/essimaging/docs/odin/odin-make-wavelength-lookup-table.ipynb index 5efc06485..b26122f9f 100644 --- a/packages/essimaging/docs/odin/odin-make-wavelength-lookup-table.ipynb +++ b/packages/essimaging/docs/odin/odin-make-wavelength-lookup-table.ipynb @@ -18,7 +18,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from ess.reduce import unwrap\n", - "from ess.reduce.nexus.types import AnyRun, Position\n", + "from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition\n", "from ess.odin.beamline import choppers" ] }, @@ -42,7 +42,7 @@ "\n", "wf = unwrap.LookupTableWorkflow()\n", "wf[unwrap.DiskChoppers[AnyRun]] = disk_choppers\n", - "wf[Position[snx.NXsource, AnyRun]] = source_position\n", + "wf[NeutronSourcePosition[AnyRun]] = source_position\n", "wf[unwrap.NumberOfSimulatedNeutrons] = 200_000 # Increase this number for more reliable results\n", "wf[unwrap.SimulationSeed] = 1234\n", "wf[unwrap.LtotalRange[AnyRun, snx.NXdetector]] = sc.scalar(5.0, unit=\"m\"), sc.scalar(65.0, unit=\"m\")\n", diff --git a/packages/essimaging/docs/tbl/tbl-make-wavelength-lookup-table.ipynb b/packages/essimaging/docs/tbl/tbl-make-wavelength-lookup-table.ipynb index af9bdd85e..1e5ca6293 100644 --- a/packages/essimaging/docs/tbl/tbl-make-wavelength-lookup-table.ipynb +++ b/packages/essimaging/docs/tbl/tbl-make-wavelength-lookup-table.ipynb @@ -18,7 +18,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from ess.reduce import unwrap\n", - "from ess.reduce.nexus.types import AnyRun, Position" + "from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition" ] }, { @@ -40,7 +40,7 @@ "\n", "wf = unwrap.LookupTableWorkflow()\n", "wf[unwrap.DiskChoppers[AnyRun]] = {}\n", - "wf[Position[snx.NXsource, AnyRun]] = source_position\n", + "wf[NeutronSourcePosition[AnyRun]] = source_position\n", "wf[unwrap.NumberOfSimulatedNeutrons] = 200_000 # Increase this number for more reliable results\n", "wf[unwrap.SimulationSeed] = 1234\n", "wf[unwrap.LtotalRange[AnyRun, snx.NXdetector]] = sc.scalar(5.0, unit=\"m\"), sc.scalar(35.0, unit=\"m\")\n", diff --git a/packages/essimaging/tests/odin/data_reduction_test.py b/packages/essimaging/tests/odin/data_reduction_test.py index c64d4b377..7d924ceb9 100644 --- a/packages/essimaging/tests/odin/data_reduction_test.py +++ b/packages/essimaging/tests/odin/data_reduction_test.py @@ -15,10 +15,9 @@ Filename, LookupTableFilename, MaskingRules, + NeutronSourcePosition, NeXusDetectorName, - NXsource, OpenBeamRun, - Position, RawDetector, SampleRun, TofDetector, @@ -40,7 +39,7 @@ def _make_workflow(wavelength_from: unwrap.WavelengthLutMode) -> sl.Pipeline: wf[LookupTableFilename] = odin.data.odin_wavelength_lookup_table() else: disk_choppers = odin_choppers( - source_position=wf.compute(Position[NXsource, SampleRun]) + source_position=wf.compute(NeutronSourcePosition[SampleRun]) ) # Shortcut to set DiskChoppers for both [SampleRun, NXdetector] and # [OpenBeamRun, NXdetector] at once diff --git a/packages/essnmx/src/ess/nmx/types.py b/packages/essnmx/src/ess/nmx/types.py index 5b38fd165..1a5c80813 100644 --- a/packages/essnmx/src/ess/nmx/types.py +++ b/packages/essnmx/src/ess/nmx/types.py @@ -15,6 +15,7 @@ RunType = nexus_types.RunType SampleRun = nexus_types.SampleRun +NeutronSourcePosition = nexus_types.NeutronSourcePosition TofDetector = unwrap_types.TofDetector diff --git a/packages/essnmx/src/ess/nmx/workflows.py b/packages/essnmx/src/ess/nmx/workflows.py index 1a50c12c0..069412ef0 100644 --- a/packages/essnmx/src/ess/nmx/workflows.py +++ b/packages/essnmx/src/ess/nmx/workflows.py @@ -10,6 +10,7 @@ from ess.reduce.nexus.types import ( EmptyDetector, Filename, + NeutronSourcePosition, NeXusComponent, NeXusTransformation, Position, @@ -86,7 +87,7 @@ def assemble_sample_metadata( def assemble_source_metadata( - source_position: Position[snx.NXsource, SampleRun], + source_position: NeutronSourcePosition[SampleRun], ) -> NMXSourceMetadata: """Assemble source metadata for NMX reduction workflow.""" return NMXSourceMetadata(position=source_position) @@ -150,7 +151,7 @@ def assemble_detector_metadata( detector_component: NeXusComponent[snx.NXdetector, SampleRun], transformation: NeXusTransformation[snx.NXdetector, SampleRun], sample_position: Position[snx.NXsample, SampleRun], - source_position: Position[snx.NXsource, SampleRun], + source_position: NeutronSourcePosition[SampleRun], empty_detector: EmptyDetector[SampleRun], ) -> NMXDetectorMetadata: """Assemble detector metadata for NMX reduction workflow.""" diff --git a/packages/essnmx/tests/executable_test.py b/packages/essnmx/tests/executable_test.py index 2ed6fb775..ef9264710 100644 --- a/packages/essnmx/tests/executable_test.py +++ b/packages/essnmx/tests/executable_test.py @@ -29,8 +29,7 @@ to_command_arguments, ) from ess.nmx.executables import reduction -from ess.nmx.types import Compression, NMXLauetof, SampleRun -from ess.reduce.nexus.types import Position +from ess.nmx.types import Compression, NeutronSourcePosition, NMXLauetof, SampleRun def _build_arg_list_from_pydantic_instance(*instances: pydantic.BaseModel) -> list[str]: @@ -430,7 +429,7 @@ def lut_file_path(tmp_path: pathlib.Path): # Simply use the default workflow for testing. workflow = initialize_nmx_workflow(config=WorkflowConfig()) - workflow[Position[snx.NXsource, SampleRun]] = sc.vector(value=[0, 0, 0], unit='m') + workflow[NeutronSourcePosition[SampleRun]] = sc.vector(value=[0, 0, 0], unit='m') lut = workflow.compute(LookupTable[SampleRun, snx.NXdetector]) # Change the tof range a bit for testing. diff --git a/packages/essreduce/docs/user-guide/unwrap/analytical-unwrap.ipynb b/packages/essreduce/docs/user-guide/unwrap/analytical-unwrap.ipynb index a5f826a03..2bdac39aa 100644 --- a/packages/essreduce/docs/user-guide/unwrap/analytical-unwrap.ipynb +++ b/packages/essreduce/docs/user-guide/unwrap/analytical-unwrap.ipynb @@ -33,7 +33,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from scippneutron.chopper import DiskChopper\n", - "from ess.reduce.nexus.types import RawDetector, SampleRun, NeXusDetectorName, Position\n", + "from ess.reduce.nexus.types import RawDetector, SampleRun, NeXusDetectorName, NeutronSourcePosition\n", "from ess.reduce.unwrap import *" ] }, @@ -311,7 +311,7 @@ "\n", "wf[RawDetector[SampleRun]] = raw_data\n", "wf[DetectorLtotal[SampleRun]] = Ltotal\n", - "wf[Position[snx.NXsource, SampleRun]] = source_position\n", + "wf[NeutronSourcePosition[SampleRun]] = source_position\n", "wf[NeXusDetectorName] = 'dream_detector'\n", "wf[DiskChoppers[SampleRun]] = disk_choppers\n", "wf[LookupTableRelativeErrorThreshold] = {'dream_detector': float(\"inf\")}\n", @@ -916,7 +916,7 @@ "outputs": [], "source": [ "wf[DiskChoppers[SampleRun]] = odin_choppers\n", - "wf[Position[snx.NXsource, SampleRun]] = source_position\n", + "wf[NeutronSourcePosition[SampleRun]] = source_position\n", "wf[DetectorLtotal[SampleRun]] = Ltotal\n", "\n", "frames = wf.compute(ChopperFrameSequence[SampleRun])\n", diff --git a/packages/essreduce/docs/user-guide/unwrap/dream.ipynb b/packages/essreduce/docs/user-guide/unwrap/dream.ipynb index 316f40c14..9a81c0d34 100644 --- a/packages/essreduce/docs/user-guide/unwrap/dream.ipynb +++ b/packages/essreduce/docs/user-guide/unwrap/dream.ipynb @@ -29,7 +29,7 @@ " RawDetector,\n", " SampleRun,\n", " NeXusDetectorName,\n", - " Position,\n", + " NeutronSourcePosition,\n", " BackgroundRun,\n", ")\n", "from ess.reduce.unwrap import *" @@ -349,7 +349,7 @@ "source": [ "lut_wf = LookupTableWorkflow(run_types=[SampleRun, BackgroundRun])\n", "lut_wf[DiskChoppers[SampleRun]] = disk_choppers\n", - "lut_wf[Position[snx.NXsource, SampleRun]] = source_position\n", + "lut_wf[NeutronSourcePosition[SampleRun]] = source_position\n", "lut_wf[LtotalRange[SampleRun, snx.NXdetector]] = (\n", " sc.scalar(25.0, unit=\"m\"),\n", " sc.scalar(80.0, unit=\"m\"),\n", diff --git a/packages/essreduce/docs/user-guide/unwrap/frame-unwrapping.ipynb b/packages/essreduce/docs/user-guide/unwrap/frame-unwrapping.ipynb index f8af0d0c2..1a852d884 100644 --- a/packages/essreduce/docs/user-guide/unwrap/frame-unwrapping.ipynb +++ b/packages/essreduce/docs/user-guide/unwrap/frame-unwrapping.ipynb @@ -32,7 +32,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from scippneutron.chopper import DiskChopper\n", - "from ess.reduce.nexus.types import RawDetector, SampleRun, NeXusDetectorName, Position\n", + "from ess.reduce.nexus.types import RawDetector, SampleRun, NeXusDetectorName, NeutronSourcePosition\n", "from ess.reduce.unwrap import *\n", "import tof\n", "\n", @@ -211,7 +211,7 @@ "outputs": [], "source": [ "lut_wf = LookupTableWorkflow(run_types=[SampleRun])\n", - "lut_wf[Position[snx.NXsource, SampleRun]] = sc.vector([0, 0, 0], unit=\"m\")\n", + "lut_wf[NeutronSourcePosition[SampleRun]] = sc.vector([0, 0, 0], unit=\"m\")\n", "lut_wf[LtotalRange[SampleRun, snx.NXdetector]] = (\n", " detectors[0].distance,\n", " detectors[-1].distance,\n", @@ -400,7 +400,7 @@ " )\n", " for ch in choppers\n", "}\n", - "lut_wf[Position[snx.NXsource, SampleRun]] = sc.vector([0, 0, 0], unit=\"m\")\n", + "lut_wf[NeutronSourcePosition[SampleRun]] = sc.vector([0, 0, 0], unit=\"m\")\n", "lut_wf[DistanceResolution] = sc.scalar(0.5, unit=\"m\")" ] }, diff --git a/packages/essreduce/docs/user-guide/unwrap/lut-building-methods.ipynb b/packages/essreduce/docs/user-guide/unwrap/lut-building-methods.ipynb index 63f3c3b37..e59fce401 100644 --- a/packages/essreduce/docs/user-guide/unwrap/lut-building-methods.ipynb +++ b/packages/essreduce/docs/user-guide/unwrap/lut-building-methods.ipynb @@ -29,7 +29,7 @@ "import scippnexus as snx\n", "from scippneutron.chopper import DiskChopper\n", "from ess.reduce import unwrap\n", - "from ess.reduce.nexus.types import AnyRun, Position" + "from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition" ] }, { @@ -153,7 +153,7 @@ "wf[unwrap.NumberOfSimulatedNeutrons] = (\n", " 200_000 # Increase this number for more reliable results\n", ")\n", - "wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, -76.55], unit=\"m\")\n", + "wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, -76.55], unit=\"m\")\n", "wf[unwrap.DiskChoppers[AnyRun]] = disk_choppers\n", "wf[unwrap.DistanceResolution] = sc.scalar(0.1, unit=\"m\")\n", "wf[unwrap.TimeResolution] = sc.scalar(250.0, unit='us')\n", @@ -284,7 +284,7 @@ " sc.scalar(50.0, unit=\"m\"),\n", " sc.scalar(60.0, unit=\"m\"),\n", ")\n", - "wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, -76.55], unit=\"m\")\n", + "wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, -76.55], unit=\"m\")\n", "wf[unwrap.DiskChoppers[AnyRun]] = disk_choppers\n", "wf[unwrap.DistanceResolution] = sc.scalar(0.1, unit=\"m\")\n", "wf[unwrap.TimeResolution] = sc.scalar(250.0, unit='us')\n", diff --git a/packages/essreduce/docs/user-guide/unwrap/wfm.ipynb b/packages/essreduce/docs/user-guide/unwrap/wfm.ipynb index 960e6277b..0d40e17b1 100644 --- a/packages/essreduce/docs/user-guide/unwrap/wfm.ipynb +++ b/packages/essreduce/docs/user-guide/unwrap/wfm.ipynb @@ -25,7 +25,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from scippneutron.chopper import DiskChopper\n", - "from ess.reduce.nexus.types import RawDetector, SampleRun, NeXusDetectorName, Position\n", + "from ess.reduce.nexus.types import RawDetector, SampleRun, NeutronSourcePosition, NeXusDetectorName\n", "from ess.reduce.unwrap import *" ] }, @@ -362,7 +362,7 @@ "source": [ "lut_wf = LookupTableWorkflow(run_types=[SampleRun])\n", "lut_wf[DiskChoppers[SampleRun]] = disk_choppers\n", - "lut_wf[Position[snx.NXsource, SampleRun]] = source_position\n", + "lut_wf[NeutronSourcePosition[SampleRun]] = source_position\n", "lut_wf[LtotalRange[SampleRun, snx.NXdetector]] = (\n", " sc.scalar(5, unit='m'),\n", " sc.scalar(35, unit='m'),\n", diff --git a/packages/essreduce/src/ess/reduce/nexus/types.py b/packages/essreduce/src/ess/reduce/nexus/types.py index 23cfcde16..f21fc5094 100644 --- a/packages/essreduce/src/ess/reduce/nexus/types.py +++ b/packages/essreduce/src/ess/reduce/nexus/types.py @@ -118,18 +118,21 @@ class TransmissionRun(Generic[ScatteringRunType]): Component = TypeVar('Component') """A beamline component in a NeXus file.""" COMPONENT_CONSTRAINTS = ( + snx.NXcrystal, snx.NXdetector, + snx.NXdisk_chopper, + snx.NXmoderator, snx.NXsample, snx.NXsource, - snx.NXdisk_chopper, - snx.NXcrystal, ) """Base constraints for the Component type variable. This list will be supplemented with monitor types when creating a pipeline. """ -UniqueComponent = TypeVar('UniqueComponent', snx.NXsample, snx.NXsource) +UniqueComponent = TypeVar( + 'UniqueComponent', snx.NXsample, snx.NXsource, snx.NXmoderator +) """Components that can be identified by their type as there will only be one. This applies for a given parent for these components, not the entire file. @@ -251,6 +254,10 @@ def __repr__(self) -> str: return f"Position(position={self._position}, time={self._time})" +class NeutronSourcePosition(sciline.Scope[RunType, sc.Variable], sc.Variable): + """Position of the neutron source, usually derived from the NXmoderator.""" + + class DetectorPositionOffset(sciline.Scope[RunType, sc.Variable], sc.Variable): """Offset for the detector position, added to base position.""" diff --git a/packages/essreduce/src/ess/reduce/nexus/workflow.py b/packages/essreduce/src/ess/reduce/nexus/workflow.py index b960a2c26..9409f87e2 100644 --- a/packages/essreduce/src/ess/reduce/nexus/workflow.py +++ b/packages/essreduce/src/ess/reduce/nexus/workflow.py @@ -34,6 +34,7 @@ Measurement, MonitorPositionOffset, MonitorType, + NeutronSourcePosition, NeXusClass, NeXusComponent, NeXusComponentLocationSpec, @@ -213,6 +214,10 @@ def nx_class_for_detector() -> NeXusClass[snx.NXdetector]: return NeXusClass[snx.NXdetector](snx.NXdetector) +def nx_class_for_moderator() -> NeXusClass[snx.NXmoderator]: + return NeXusClass[snx.NXmoderator](snx.NXmoderator) + + def nx_class_for_source() -> NeXusClass[snx.NXsource]: return NeXusClass[snx.NXsource](snx.NXsource) @@ -530,6 +535,31 @@ def compute_dynamic_position( return DynamicPosition[Component, RunType](transformation.value * origin) +def compute_neutron_source_position( + transformation: NeXusTransformation[snx.NXmoderator, RunType], +) -> NeutronSourcePosition[RunType]: + """Compute the neutron source position from the NXmoderator.""" + return compute_position(transformation) + + +def deny_source_position() -> Position[snx.NXsource, RunType]: + """Refuse to provide a source position. + + This is a safety mechanism because it is easy to accidentally request + Position[NXsource] and get correct looking, but possibly slightly incorrect data. + """ + raise RuntimeError( + "Denied access to Position[NXsource]\n" + "The workflow requested Position[scippnexus.NXsource, ...] which is almost " + "certainly an error. The source position in ESS NeXus files does not reliably " + "represent the place that neutron originate from. Instead, NXsource describes " + "neutron production (accelerator, target, etc.).\n\n" + "Use NeutronSourcePosition[RunType] or equivalently " + "Position[scippnexus.NXmoderator, RunType]. If this does not work for you, " + "insert your providder to overridethe one that produced this error." + ) + + def get_calibrated_detector( detector: NeXusComponent[snx.NXdetector, RunType], *, @@ -952,6 +982,8 @@ def load_source_metadata_from_nexus( to_multi_transformation, compute_position, compute_dynamic_position, + compute_neutron_source_position, + deny_source_position, load_nexus_data, load_nexus_component, load_proton_charge, @@ -961,6 +993,7 @@ def load_source_metadata_from_nexus( nx_class_for_crystal, nx_class_for_detector, nx_class_for_monitor, + nx_class_for_moderator, nx_class_for_source, nx_class_for_sample, nx_class_for_disk_chopper, diff --git a/packages/essreduce/src/ess/reduce/unwrap/lut.py b/packages/essreduce/src/ess/reduce/unwrap/lut.py index 79cb37908..1fbee3549 100644 --- a/packages/essreduce/src/ess/reduce/unwrap/lut.py +++ b/packages/essreduce/src/ess/reduce/unwrap/lut.py @@ -15,7 +15,13 @@ from scippneutron.chopper import DiskChopper from scippneutron.tof import chopper_cascade -from ..nexus.types import Component, DiskChoppers, MonitorType, Position, RunType +from ..nexus.types import ( + Component, + DiskChoppers, + MonitorType, + NeutronSourcePosition, + RunType, +) from .types import ( DetectorLtotal, KeepEventTimeOffset, @@ -579,7 +585,7 @@ def close_non_synced_disk_choppers( def simulate_chopper_cascade_using_tof( choppers: FrameCompatibleDiskChoppers[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], neutrons: NumberOfSimulatedNeutrons, pulse_stride: PulseStride[RunType], seed: SimulationSeed, @@ -774,7 +780,7 @@ def _estimate_wavelength_by_polygon_centers( def compute_frame_sequence( pulse_period: PulsePeriod, disk_choppers: FrameCompatibleDiskChoppers[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], source_bounds: SourceBounds, pulse_stride: PulseStride[RunType], ) -> ChopperFrameSequence[RunType]: diff --git a/packages/essreduce/src/ess/reduce/unwrap/to_wavelength.py b/packages/essreduce/src/ess/reduce/unwrap/to_wavelength.py index 57726a58c..1f65d0dc5 100644 --- a/packages/essreduce/src/ess/reduce/unwrap/to_wavelength.py +++ b/packages/essreduce/src/ess/reduce/unwrap/to_wavelength.py @@ -23,6 +23,7 @@ EmptyMonitor, GravityVector, MonitorType, + NeutronSourcePosition, NeXusName, Position, RawDetector, @@ -426,7 +427,7 @@ def _compute_wavelength_events( def detector_ltotal_from_straight_line_approximation( detector: EmptyDetector[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> DetectorLtotal[RunType]: @@ -462,7 +463,7 @@ def detector_ltotal_from_straight_line_approximation( def monitor_ltotal_from_straight_line_approximation( monitor_beamline: EmptyMonitor[RunType, MonitorType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], ) -> MonitorLtotal[RunType, MonitorType]: """ Compute Ltotal for the monitor. diff --git a/packages/essreduce/tests/unwrap/lut_test.py b/packages/essreduce/tests/unwrap/lut_test.py index 42d0eb1e6..f382075e8 100644 --- a/packages/essreduce/tests/unwrap/lut_test.py +++ b/packages/essreduce/tests/unwrap/lut_test.py @@ -10,7 +10,7 @@ from scippneutron.chopper import DiskChopper from ess.reduce import unwrap -from ess.reduce.nexus.types import AnyRun, Position +from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition from ess.reduce.unwrap import ( GenericUnwrapWorkflow, LookupTableWorkflow, @@ -37,7 +37,7 @@ def _make_workflow(wavelength_from: str = "analytical") -> sl.Pipeline: def test_lut_workflow_computes_table(detector_or_monitor, wavelength_from): wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = {} - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') wf[unwrap.PulseStride[AnyRun]] = 1 if wavelength_from == "simulation": @@ -72,7 +72,7 @@ def test_lut_workflow_computes_table(detector_or_monitor, wavelength_from): def test_lut_workflow_pulse_skipping(detector_or_monitor, wavelength_from): wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = {} - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 62 @@ -99,7 +99,7 @@ def test_lut_workflow_pulse_skipping(detector_or_monitor, wavelength_from): def test_lut_workflow_non_exact_distance_range(wavelength_from): wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = {} - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 63 @@ -209,7 +209,7 @@ def test_chopper_cascade_orders_offset_axle_by_beam_projection(): # distance, otherwise the upstream chopper is wrongly applied last (or, for a # narrow distance range, never at all). wf = _make_workflow("analytical") - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') wf[unwrap.PulseStride[AnyRun]] = 1 wf[unwrap.DiskChoppers[AnyRun]] = { 'near': _single_slit_chopper(sc.vector([0, 0.7, 6.145], unit='m')), @@ -229,7 +229,7 @@ def test_lut_workflow_computes_table_with_choppers( ): wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = _make_choppers() - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 64 @@ -267,7 +267,7 @@ def test_lut_workflow_computes_table_with_choppers( def test_lut_workflow_computes_table_with_choppers_full_beamline_range(wavelength_from): wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = _make_choppers() - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 64 @@ -319,7 +319,7 @@ def test_lut_workflow_computes_table_with_choppers_full_beamline_range(wavelengt def test_lut_workflow_raises_for_distance_before_source(wavelength_from): wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = {} - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 10], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 10], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 65 @@ -346,7 +346,7 @@ def test_lut_workflow_computes_table_using_alias(detector_or_monitor, wavelength use_simulation=(wavelength_from == "simulation"), monitor_types=[Monitor0] ) wf[unwrap.DiskChoppers[AnyRun]] = {} - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') wf[unwrap.PulseStride[AnyRun]] = 1 if wavelength_from == "simulation": @@ -433,7 +433,7 @@ def test_lut_does_not_raise_if_no_neutrons_make_it_through(wavelength_from): radius=sc.scalar(0.35, unit='m'), ) } - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, -25.0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, -25.0], unit='m') # Need to force the pulse stride so that it doesn't get set to a large value due to # the slow chopper. wf[unwrap.PulseStride[AnyRun]] = 1 @@ -481,7 +481,7 @@ def test_analytical_lut_does_not_raise_with_degenerate_polygon(): sc.scalar(15.0, unit='angstrom'), ), ) - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, -25.0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, -25.0], unit='m') wf[unwrap.PulseStride[AnyRun]] = 1 wf[unwrap.LtotalRange[AnyRun, snx.NXdetector]] = ( sc.scalar(15.0, unit='m'), @@ -504,7 +504,7 @@ def test_analytical_lut_stores_mean_and_stddev_of_flat_source_pulse(): pulse_length = sc.scalar(3.0, unit='ms') wf = _make_workflow("analytical") wf[unwrap.DiskChoppers[AnyRun]] = {} - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') wf[SourceBounds] = SourceBounds( time=(sc.scalar(0.0, unit='ms'), pulse_length), wavelength=(sc.scalar(1.0, unit='angstrom'), sc.scalar(5.0, unit='angstrom')), @@ -590,7 +590,7 @@ def test_choppers_rotate_enough_times_to_catch_slow_neutrons(): } wf = _make_workflow("analytical") wf[unwrap.DiskChoppers[AnyRun]] = choppers - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') frames = wf.compute(unwrap.ChopperFrameSequence[AnyRun]) @@ -645,7 +645,7 @@ def test_choppers_rotate_enough_times_with_slow_neutrons_to_pollute_lut(): } wf = _make_workflow("analytical") wf[unwrap.DiskChoppers[AnyRun]] = choppers - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') dist = sc.scalar(157.5, unit='m') wf[LtotalRange[AnyRun, snx.NXdetector]] = dist, dist @@ -771,7 +771,7 @@ def test_lut_workflow_drops_choppers_with_zero_frequency(wavelength_from): choppers['FOC_1'], frequency=sc.scalar(0.0, unit='Hz') ) wf[unwrap.DiskChoppers[AnyRun]] = choppers - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 77 @@ -799,7 +799,7 @@ def test_lut_workflow_treats_choppers_with_bad_frequency_as_closed(wavelength_fr choppers['FOC_1'], frequency=sc.scalar(5.0, unit='Hz') ) wf[unwrap.DiskChoppers[AnyRun]] = choppers - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 78 @@ -878,7 +878,7 @@ def test_lut_workflow_chopper_frequency_multiple_of_frame_period(wavelength_from wf = _make_workflow(wavelength_from) wf[unwrap.DiskChoppers[AnyRun]] = choppers - wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m') if wavelength_from == "simulation": wf[unwrap.NumberOfSimulatedNeutrons] = 100_000 wf[unwrap.SimulationSeed] = 79 diff --git a/packages/essreduce/tests/unwrap/unwrap_test.py b/packages/essreduce/tests/unwrap/unwrap_test.py index 82bad0814..2987601cd 100644 --- a/packages/essreduce/tests/unwrap/unwrap_test.py +++ b/packages/essreduce/tests/unwrap/unwrap_test.py @@ -6,13 +6,12 @@ import pytest import scipp as sc from scippneutron.chopper import DiskChopper -from scippnexus import NXsource from ess.reduce import unwrap from ess.reduce.nexus.types import ( + NeutronSourcePosition, NeXusDetectorName, NeXusName, - Position, RawDetector, RawMonitor, SampleRun, @@ -74,7 +73,7 @@ def _initialize_workflow(wavelength_from, distance, error_threshold, choppers): 'monitor': error_threshold, } wf[unwrap.DiskChoppers[SampleRun]] = choppers - wf[Position[NXsource, SampleRun]] = fakes.source_position() + wf[NeutronSourcePosition[SampleRun]] = fakes.source_position() return wf diff --git a/packages/essreduce/tests/unwrap/wfm_test.py b/packages/essreduce/tests/unwrap/wfm_test.py index 15989a4b8..7c732fb3e 100644 --- a/packages/essreduce/tests/unwrap/wfm_test.py +++ b/packages/essreduce/tests/unwrap/wfm_test.py @@ -5,10 +5,14 @@ import pytest import scipp as sc from scippneutron.chopper import DiskChopper -from scippnexus import NXsource from ess.reduce import unwrap -from ess.reduce.nexus.types import NeXusDetectorName, Position, RawDetector, SampleRun +from ess.reduce.nexus.types import ( + NeutronSourcePosition, + NeXusDetectorName, + RawDetector, + SampleRun, +) from ess.reduce.unwrap import ( GenericUnwrapWorkflow, fakes, @@ -150,7 +154,7 @@ def setup_workflow( wf[NeXusDetectorName] = "detector" wf[unwrap.LookupTableRelativeErrorThreshold] = {"detector": error_threshold} wf[unwrap.DiskChoppers[SampleRun]] = choppers - wf[Position[NXsource, SampleRun]] = source_position + wf[NeutronSourcePosition[SampleRun]] = source_position return wf diff --git a/packages/essreduce/tests/unwrap/workflow_test.py b/packages/essreduce/tests/unwrap/workflow_test.py index db8ad604c..19682f471 100644 --- a/packages/essreduce/tests/unwrap/workflow_test.py +++ b/packages/essreduce/tests/unwrap/workflow_test.py @@ -14,6 +14,7 @@ DiskChoppers, EmptyDetector, EmptyMonitor, + NeutronSourcePosition, NeXusData, NeXusDetectorName, NeXusName, @@ -110,7 +111,7 @@ def _make_workflow( wf[EmptyMonitor[SampleRun, Monitor0]] = monitor_geometry wf[NeXusData[Monitor0, SampleRun]] = monitor_data wf[Position[snx.NXsample, SampleRun]] = sc.vector([0, 0, 77], unit='m') - wf[Position[snx.NXsource, SampleRun]] = fakes.source_position() + wf[NeutronSourcePosition[SampleRun]] = fakes.source_position() wf[DiskChoppers[SampleRun]] = fakes.psc_choppers() wf[KeepEventTimeOffset] = keep_event_time_offset diff --git a/packages/essreflectometry/docs/user-guide/estia/create-estia-wavelength-lookup-table.ipynb b/packages/essreflectometry/docs/user-guide/estia/create-estia-wavelength-lookup-table.ipynb index 0140be937..e146cc579 100644 --- a/packages/essreflectometry/docs/user-guide/estia/create-estia-wavelength-lookup-table.ipynb +++ b/packages/essreflectometry/docs/user-guide/estia/create-estia-wavelength-lookup-table.ipynb @@ -24,7 +24,7 @@ "import scippnexus as snx\n", "import scipp.constants\n", "from ess.reduce import unwrap\n", - "from ess.reduce.nexus.types import AnyRun, Position\n", + "from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition\n", "from scippneutron.chopper import DiskChopper" ] }, @@ -80,7 +80,7 @@ "\n", "wf[unwrap.LtotalRange[AnyRun, snx.NXdetector]] = sc.scalar(35., unit=\"m\"), sc.scalar(45.0, unit=\"m\")\n", "wf[unwrap.NumberOfSimulatedNeutrons] = 1_000_000\n", - "wf[Position[snx.NXsource, AnyRun]] = sc.vector([0, 0, 0], unit='m')\n", + "wf[NeutronSourcePosition[AnyRun]] = sc.vector([0, 0, 0], unit='m')\n", "wf[unwrap.DiskChoppers[AnyRun]] = disk_choppers\n", "wf[unwrap.DistanceResolution] = sc.scalar(0.05, unit=\"m\")\n", "wf[unwrap.TimeResolution] = 1.0 / sc.scalar(14.0, unit=\"Hz\") / 200\n", diff --git a/packages/essreflectometry/src/ess/estia/conversions.py b/packages/essreflectometry/src/ess/estia/conversions.py index aeb23b3ce..1c69862d3 100644 --- a/packages/essreflectometry/src/ess/estia/conversions.py +++ b/packages/essreflectometry/src/ess/estia/conversions.py @@ -3,12 +3,13 @@ import scipp as sc from ess.reduce.nexus.types import DetectorBankSizes, Position from scippneutron.conversion import graph -from scippnexus import NXsample, NXsource +from scippnexus import NXsample from ..reflectometry import conversions as reflectometry_conversions from ..reflectometry.types import ( CoordTransformationGraph, DetectorRotation, + NeutronSourcePosition, RunType, SampleRotation, ) @@ -111,7 +112,7 @@ def divergence_angle( def coordinate_transformation_graph( - source_position: Position[NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[NXsample, RunType], sample_rotation: SampleRotation[RunType], detector_rotation: DetectorRotation[RunType], diff --git a/packages/essreflectometry/src/ess/estia/mcstas.py b/packages/essreflectometry/src/ess/estia/mcstas.py index 77bfd754f..d190f9019 100644 --- a/packages/essreflectometry/src/ess/estia/mcstas.py +++ b/packages/essreflectometry/src/ess/estia/mcstas.py @@ -6,7 +6,7 @@ import pandas as pd import scipp as sc from ess.reduce.nexus.types import Position -from scippnexus import NXsample, NXsource +from scippnexus import NXsample from ..reflectometry.load import load_h5 from ..reflectometry.types import ( @@ -17,6 +17,7 @@ DetectorLtotal, DetectorRotation, Filename, + NeutronSourcePosition, RawDetector, RunType, SampleRotation, @@ -264,7 +265,7 @@ def load_detector_rotation( def load_source_position( da: RawDetector[RunType], -) -> Position[NXsource, RunType]: +) -> NeutronSourcePosition[RunType]: """Extract source position from McStas-derived raw detector data.""" return da.coords['source_position'] diff --git a/packages/essreflectometry/src/ess/freia/conversions.py b/packages/essreflectometry/src/ess/freia/conversions.py index c73e830a0..fcdeb426a 100644 --- a/packages/essreflectometry/src/ess/freia/conversions.py +++ b/packages/essreflectometry/src/ess/freia/conversions.py @@ -3,7 +3,7 @@ import scipp as sc from ess.reduce.nexus.types import GravityVector, Position from scippneutron.conversion import graph, tof -from scippnexus import NXsample, NXsource +from scippnexus import NXsample from ..reflectometry.conversions import ( reflectometry_q, @@ -12,6 +12,7 @@ ) from ..reflectometry.types import ( CoordTransformationGraph, + NeutronSourcePosition, RunType, SampleRun, ) @@ -239,7 +240,7 @@ def incident_angle( def coordinate_transformation_graph( - source_position: Position[NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[NXsample, RunType], sample_surface_normal: SampleSurfaceNormal[RunType], gravity: GravityVector, @@ -264,7 +265,7 @@ def coordinate_transformation_graph( def sample_coordinate_transformation_graph( - source_position: Position[NXsource, SampleRun], + source_position: NeutronSourcePosition[SampleRun], sample_position: Position[NXsample, SampleRun], sample_surface_normal: SampleSurfaceNormal[SampleRun], gravity: GravityVector, @@ -360,7 +361,7 @@ def select_incident_angle( def offspecular_coordinate_transformation_graph( - source_position: Position[NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[NXsample, RunType], sample_surface_normal: SampleSurfaceNormal[RunType], gravity: GravityVector, diff --git a/packages/essreflectometry/src/ess/freia/mcstas.py b/packages/essreflectometry/src/ess/freia/mcstas.py index 98fa80d33..72db25138 100644 --- a/packages/essreflectometry/src/ess/freia/mcstas.py +++ b/packages/essreflectometry/src/ess/freia/mcstas.py @@ -14,6 +14,7 @@ DiskChoppers, EmptyDetector, Filename, + NeutronSourcePosition, NeXusDetectorName, NeXusName, Position, @@ -312,9 +313,9 @@ def _component_position(filename, component): def mcstas_source_position( filename: Filename[RunType], -) -> Position[snx.NXsource, RunType]: +) -> NeutronSourcePosition[RunType]: """Load the moderator position from the simulation geometry.""" - return Position[snx.NXsource, RunType](_component_position(filename, 'Source')) + return NeutronSourcePosition[RunType](_component_position(filename, 'Source')) def mcstas_sample_position( diff --git a/packages/essreflectometry/src/ess/reflectometry/types.py b/packages/essreflectometry/src/ess/reflectometry/types.py index 639b45121..d55843d45 100644 --- a/packages/essreflectometry/src/ess/reflectometry/types.py +++ b/packages/essreflectometry/src/ess/reflectometry/types.py @@ -18,6 +18,7 @@ DetectorPositionOffset = nexus_t.DetectorPositionOffset Filename = nexus_t.Filename Measurement = nexus_t.Measurement +NeutronSourcePosition = nexus_t.NeutronSourcePosition NeXusComponent = nexus_t.NeXusComponent NeXusDetectorName = nexus_t.NeXusDetectorName NeXusMonitorName = nexus_t.NeXusName diff --git a/packages/essreflectometry/tests/freia/workflow_test.py b/packages/essreflectometry/tests/freia/workflow_test.py index 27f2ecaeb..44743d037 100644 --- a/packages/essreflectometry/tests/freia/workflow_test.py +++ b/packages/essreflectometry/tests/freia/workflow_test.py @@ -6,7 +6,7 @@ from ess.reduce.nexus.types import GravityVector, Position from ess.reduce.unwrap.types import KeepEventTimeOffset from scipp.testing import assert_allclose, assert_identical -from scippnexus import NXsample, NXsource +from scippnexus import NXsample from ess.freia import FreiaOffspecWorkflow, FreiaWorkflow from ess.freia.corrections import RunNormalization @@ -23,6 +23,7 @@ from ess.reflectometry.types import ( BeamSize, CorrectedDetector, + NeutronSourcePosition, QBins, ReducibleData, ReferenceRun, @@ -62,7 +63,7 @@ def _make_workflow(run_norm): wf[WavelengthDetector[run]] = events wf[Position[NXsample, run]] = sc.vector([0.0, 0.0, 0.0], unit='m') # Source position must not set the angular reference frame. - wf[Position[NXsource, run]] = sc.vector([3.0, 2.0, -20.0], unit='m') + wf[NeutronSourcePosition[run]] = sc.vector([3.0, 2.0, -20.0], unit='m') wf[SampleSurfaceNormal[run]] = sc.vector( [0.0, np.cos(np.deg2rad(10.0)), -np.sin(np.deg2rad(10.0))] ) @@ -149,7 +150,7 @@ def test_offspec_workflow_supports_scalar_incidence_for_a_single_beam(method): assert wf.compute(KeepEventTimeOffset) is False wf[GravityVector] = sc.vector([0.0, 0.0, 0.0], unit='m/s^2') wf[Position[NXsample, SampleRun]] = sc.vector([0.0, 0.0, 0.0], unit='m') - wf[Position[NXsource, SampleRun]] = sc.vector([0.0, 0.0, -10.0], unit='m') + wf[NeutronSourcePosition[SampleRun]] = sc.vector([0.0, 0.0, -10.0], unit='m') # Horizontal incidence onto a sample tilted by 30 degrees. wf[SampleSurfaceNormal[SampleRun]] = sc.vector([0.0, np.sqrt(3) / 2, -0.5]) wf[UpstreamSlitCenters[SampleRun]] = {'beam': sc.vector([0.0, 0.0, -2.0], unit='m')} @@ -209,7 +210,7 @@ def test_offspec_workflow_assigns_incidence_with_independent_methods(method, exp assert wf.compute(KeepEventTimeOffset) is (method == 'shutter_logs') wf[GravityVector] = sc.vector([0.0, 0.0, 0.0], unit='m/s^2') wf[Position[NXsample, SampleRun]] = sc.vector([0.0, 0.0, 0.0], unit='m') - wf[Position[NXsource, SampleRun]] = sc.vector([0.0, 0.0, -29.0], unit='m') + wf[NeutronSourcePosition[SampleRun]] = sc.vector([0.0, 0.0, -29.0], unit='m') wf[SampleSurfaceNormal[SampleRun]] = sc.vector([0.0, 1.0, 0.0]) angles = {'a': 0.3, 'b': 1.0} wf[UpstreamSlitCenters[SampleRun]] = { diff --git a/packages/essreflectometry/tests/monitor_workflow_test.py b/packages/essreflectometry/tests/monitor_workflow_test.py index 22cba78ef..fd7640ad3 100644 --- a/packages/essreflectometry/tests/monitor_workflow_test.py +++ b/packages/essreflectometry/tests/monitor_workflow_test.py @@ -5,13 +5,12 @@ from ess.reduce.nexus.types import ( DiskChoppers, EmptyMonitor, + NeutronSourcePosition, NeXusName, - Position, RawMonitor, ) from ess.reduce.unwrap import fakes from ess.reduce.unwrap.types import LookupTableRelativeErrorThreshold, WavelengthMonitor -from scippnexus import NXsource from ess import estia, freia from ess.estia.types import BeamMonitor @@ -55,7 +54,7 @@ def test_instrument_workflow_computes_monitor_wavelength( sc.scalar(0.0), coords={'position': sc.vector([0.0, 0.0, 75.0], unit='m')}, ) - wf[Position[NXsource, SampleRun]] = fakes.source_position() + wf[NeutronSourcePosition[SampleRun]] = fakes.source_position() wf[DiskChoppers[SampleRun]] = fakes.psc_choppers() result = wf.compute(WavelengthMonitor[SampleRun, monitor_type]) diff --git a/packages/esssans/docs/user-guide/loki/loki-make-wavelength-lookup-table.ipynb b/packages/esssans/docs/user-guide/loki/loki-make-wavelength-lookup-table.ipynb index 32ff0f932..49c15fc69 100644 --- a/packages/esssans/docs/user-guide/loki/loki-make-wavelength-lookup-table.ipynb +++ b/packages/esssans/docs/user-guide/loki/loki-make-wavelength-lookup-table.ipynb @@ -18,7 +18,7 @@ "import scipp as sc\n", "import scippnexus as snx\n", "from ess.reduce import unwrap\n", - "from ess.reduce.nexus.types import AnyRun, Position" + "from ess.reduce.nexus.types import AnyRun, NeutronSourcePosition" ] }, { @@ -43,7 +43,7 @@ "\n", "wf = unwrap.LookupTableWorkflow()\n", "wf[unwrap.DiskChoppers[AnyRun]] = {}\n", - "wf[Position[snx.NXsource, AnyRun]] = source_position\n", + "wf[NeutronSourcePosition[AnyRun]] = source_position\n", "wf[unwrap.NumberOfSimulatedNeutrons] = 200_000 # Increase this number for more reliable results\n", "wf[unwrap.SimulationSeed] = 1234\n", "wf[unwrap.LtotalRange[AnyRun, snx.NXdetector]] = sc.scalar(5.0, unit=\"m\"), sc.scalar(35.0, unit=\"m\")\n", diff --git a/packages/esssans/src/ess/isissans/general.py b/packages/esssans/src/ess/isissans/general.py index 98e984d83..ceff22ea8 100644 --- a/packages/esssans/src/ess/isissans/general.py +++ b/packages/esssans/src/ess/isissans/general.py @@ -21,6 +21,7 @@ Measurement, MonitorPositionOffset, MonitorType, + NeutronSourcePosition, NeXusComponent, NeXusMonitorName, NonBackgroundWavelengthRange, @@ -95,9 +96,9 @@ def to_monitor_position_offset( def get_source_position( dg: LoadedFileContents[RunType], -) -> Position[snx.NXsource, RunType]: +) -> NeutronSourcePosition[RunType]: """Get source position from raw data.""" - return Position[snx.NXsource, RunType](dg['data'].coords['source_position']) + return NeutronSourcePosition[RunType](dg['data'].coords['source_position']) def get_sample_position( diff --git a/packages/esssans/src/ess/isissans/zoom.py b/packages/esssans/src/ess/isissans/zoom.py index 1d0785940..0336f75ac 100644 --- a/packages/esssans/src/ess/isissans/zoom.py +++ b/packages/esssans/src/ess/isissans/zoom.py @@ -10,6 +10,7 @@ Filename, Incident, MonitorType, + NeutronSourcePosition, NeXusComponent, NeXusMonitorName, RunType, @@ -17,9 +18,7 @@ Transmission, TransmissionRun, ) -from scippnexus import NXsource -from ess.reduce.nexus.types import Position from ess.reduce.workflow import register_workflow from .general import MonitorSpectrumNumber, default_parameters, get_monitor_data @@ -158,8 +157,8 @@ def ZoomTransmissionFractionWorkflow(runs: Sequence[str]) -> sciline.Pipeline: workflow[NeXusComponent[mon_type, TransmissionRun[SampleRun]]] = mapped[ NeXusComponent[mon_type, TransmissionRun[SampleRun]] ].reduce(func=_get_time_dependent_monitor) - workflow[Position[NXsource, TransmissionRun[SampleRun]]] = mapped[ - Position[NXsource, TransmissionRun[SampleRun]] + workflow[NeutronSourcePosition[TransmissionRun[SampleRun]]] = mapped[ + NeutronSourcePosition[TransmissionRun[SampleRun]] ].reduce(func=_get_unique_position) return workflow diff --git a/packages/esssans/src/ess/loki/larmor_workflow.py b/packages/esssans/src/ess/loki/larmor_workflow.py index 35af21422..29da4cf49 100644 --- a/packages/esssans/src/ess/loki/larmor_workflow.py +++ b/packages/esssans/src/ess/loki/larmor_workflow.py @@ -24,6 +24,7 @@ GravityVector, Incident, MonitorType, + NeutronSourcePosition, NeXusDetectorName, NeXusMonitorName, NonBackgroundWavelengthRange, @@ -106,7 +107,7 @@ def larmor_detector_coord_transform_graph( correct_for_gravity: CorrectForGravity, *, sample_position: Position[snx.NXsample, RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], beam_center: BeamCenter, gravity: GravityVector, ) -> ElasticCoordTransformGraph[RunType]: @@ -121,7 +122,7 @@ def larmor_detector_coord_transform_graph( def larmor_monitor_coord_transform_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], ) -> MonitorCoordTransformGraph[RunType]: """ Generate a coordinate transformation graph for SANS monitor (no scattering). diff --git a/packages/esssans/src/ess/sans/conversions.py b/packages/esssans/src/ess/sans/conversions.py index 8fed5262e..be5a44f1b 100644 --- a/packages/esssans/src/ess/sans/conversions.py +++ b/packages/esssans/src/ess/sans/conversions.py @@ -24,6 +24,7 @@ GravityVector, IofQPart, MonitorTerm, + NeutronSourcePosition, NormalizedQ, NormalizedQxQy, Numerator, @@ -132,7 +133,7 @@ def sans_elastic( correct_for_gravity: CorrectForGravity, *, sample_position: Position[snx.NXsample, RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], beam_center: BeamCenter, gravity: GravityVector, ) -> ElasticCoordTransformGraph[RunType]: diff --git a/packages/esssans/src/ess/sans/types.py b/packages/esssans/src/ess/sans/types.py index ee28d60a2..71e889dc5 100644 --- a/packages/esssans/src/ess/sans/types.py +++ b/packages/esssans/src/ess/sans/types.py @@ -25,6 +25,7 @@ GravityVector = reduce_t.GravityVector RawMonitor = reduce_t.RawMonitor MonitorPositionOffset = reduce_t.MonitorPositionOffset +NeutronSourcePosition = reduce_t.NeutronSourcePosition NeXusMonitorName = reduce_t.NeXusName NeXusComponent = reduce_t.NeXusComponent NeXusTransformation = reduce_t.NeXusTransformation diff --git a/packages/esssans/tests/beam_center_finder_test.py b/packages/esssans/tests/beam_center_finder_test.py index a664f09c6..0a8a0ad6f 100644 --- a/packages/esssans/tests/beam_center_finder_test.py +++ b/packages/esssans/tests/beam_center_finder_test.py @@ -11,6 +11,7 @@ BeamCenter, CorrectedDetector, CorrectForGravity, + NeutronSourcePosition, Numerator, Position, SampleRun, @@ -66,7 +67,7 @@ def make_workflow( workflow[ElasticCoordTransformGraph[SampleRun]] = sans_elastic( CorrectForGravity(False), sample_position=Position[snx.NXsample, SampleRun](sample_position), - source_position=Position[snx.NXsource, SampleRun](source_position), + source_position=NeutronSourcePosition[SampleRun](source_position), beam_center=BeamCenter(sc.vector([0, 0, 0], unit='m')), gravity=gravity_vector(), ) @@ -138,7 +139,7 @@ def test_center_of_mass_result_yields_zero_scattering_angle_at_the_pattern_cente graph = sans_elastic( CorrectForGravity(False), sample_position=Position[snx.NXsample, SampleRun](sample_position), - source_position=Position[snx.NXsource, SampleRun]( + source_position=NeutronSourcePosition[SampleRun]( sample_position - sc.vector([0, 0, 20.0], unit='m') ), beam_center=BeamCenter(beam_center), diff --git a/packages/esssans/tests/beam_center_test.py b/packages/esssans/tests/beam_center_test.py index c4935b291..b06ad7974 100644 --- a/packages/esssans/tests/beam_center_test.py +++ b/packages/esssans/tests/beam_center_test.py @@ -7,7 +7,13 @@ import scippnexus as snx from ess.sans.common import gravity_vector from ess.sans.conversions import sans_elastic -from ess.sans.types import BeamCenter, CorrectForGravity, Position, SampleRun +from ess.sans.types import ( + BeamCenter, + CorrectForGravity, + NeutronSourcePosition, + Position, + SampleRun, +) SAMPLE_POSITION = sc.vector([0.0, 0.0, 0.0], unit='m') # Offset of the source from the sample, i.e., the nominal beam is along Z. @@ -26,7 +32,7 @@ def make_graph( return sans_elastic( CorrectForGravity(correct_for_gravity), sample_position=Position[snx.NXsample, SampleRun](sample_position), - source_position=Position[snx.NXsource, SampleRun]( + source_position=NeutronSourcePosition[SampleRun]( sample_position + SOURCE_OFFSET ), beam_center=BeamCenter(beam_center), diff --git a/packages/esssans/tests/loki/workflow_test.py b/packages/esssans/tests/loki/workflow_test.py index a6141c213..340b64506 100644 --- a/packages/esssans/tests/loki/workflow_test.py +++ b/packages/esssans/tests/loki/workflow_test.py @@ -18,6 +18,7 @@ Filename, IntensityQ, LookupTableFilename, + NeutronSourcePosition, NeXusDetectorName, PixelMaskFilenames, QBins, @@ -31,7 +32,7 @@ from ess.reduce import workflow from ess.reduce.masking import write_pixel_masks -from ess.reduce.nexus.types import DiskChoppers, Position +from ess.reduce.nexus.types import DiskChoppers from ess.reduce.unwrap import LookupTable, LtotalRange, SourceBounds @@ -135,7 +136,7 @@ def test_loki_analytical_lookup_table_matches_simulated_source_pulse(): # choppers, 1-10 Angstrom so that frames do not overlap. See scipp/ess#770. wf = loki.LokiWorkflow(wavelength_from='analytical') wf[DiskChoppers[SampleRun]] = {} - wf[Position[snx.NXsource, SampleRun]] = sc.vector([0, 0, 0], unit='m') + wf[NeutronSourcePosition[SampleRun]] = sc.vector([0, 0, 0], unit='m') wf[LtotalRange[SampleRun, snx.NXdetector]] = ( sc.scalar(28.2, unit='m'), sc.scalar(29.2, unit='m'), diff --git a/packages/essspectroscopy/docs/user-guide/bifrost/bifrost-make-wavelength-lookup-table.ipynb b/packages/essspectroscopy/docs/user-guide/bifrost/bifrost-make-wavelength-lookup-table.ipynb index 6e23999bc..df4c0392b 100644 --- a/packages/essspectroscopy/docs/user-guide/bifrost/bifrost-make-wavelength-lookup-table.ipynb +++ b/packages/essspectroscopy/docs/user-guide/bifrost/bifrost-make-wavelength-lookup-table.ipynb @@ -57,7 +57,7 @@ "\n", "M = nexus.types.EmptyMonitor[SampleRun, NormalizationMonitor]\n", "C = RawChoppers[SampleRun]\n", - "SOURCE = Position[snx.NXsource, SampleRun]\n", + "SOURCE = NeutronSourcePosition[SampleRun]\n", "SAMPLE = Position[snx.NXsample, SampleRun]\n", "choppers, monitor, sample_position, source_position = bifrost_workflow.compute((C, M, SAMPLE, SOURCE)).values()\n", "detector = bifrost_workflow.compute(EmptyDetector[SampleRun])['arc', 0]['channel', 0]" @@ -153,7 +153,7 @@ "workflow = LookupTableWorkflow()\n", "workflow[DiskChoppers[AnyRun]] = disk_choppers\n", "workflow[LtotalRange[AnyRun, snx.NXdetector]] = (l_min, l_max)\n", - "workflow[Position[snx.NXsource, AnyRun]] = source_position\n", + "workflow[NeutronSourcePosition[AnyRun]] = source_position\n", "\n", "# Increase this number for more reliable results:\n", "workflow[NumberOfSimulatedNeutrons] = 1_000_000" diff --git a/packages/essspectroscopy/src/ess/bifrost/io/nexus.py b/packages/essspectroscopy/src/ess/bifrost/io/nexus.py index 99e3a7346..b2057262a 100644 --- a/packages/essspectroscopy/src/ess/bifrost/io/nexus.py +++ b/packages/essspectroscopy/src/ess/bifrost/io/nexus.py @@ -13,7 +13,6 @@ Analyzers, ElasticMonitor, InstrumentAngle, - NeXusClass, NeXusComponent, NeXusComponentLocationSpec, NeXusData, @@ -90,12 +89,6 @@ def extract_instrument_angle( return InstrumentAngle[RunType](angle) # type: ignore[return-value] -# See https://github.com/scipp/ess/issues/120 -def moderator_class_for_source() -> NeXusClass[snx.NXsource]: - """Select NXmoderator as the source.""" - return NeXusClass[snx.NXsource](snx.NXmoderator) - - def load_bifrost_analyzers( detector_locations: NeXusMultiComponentLocationSpec[snx.NXdetector, RunType], detector_components: NeXusMultiComponent[snx.NXdetector, RunType], @@ -352,7 +345,6 @@ def stepwise_transformation_time_filter(transform: sc.DataArray) -> sc.DataArray load_bifrost_analyzers, load_bifrost_analyzer_for_detector, load_sample_angle, - moderator_class_for_source, ) parameters = { diff --git a/packages/essspectroscopy/src/ess/bifrost/single_crystal/conversion.py b/packages/essspectroscopy/src/ess/bifrost/single_crystal/conversion.py index 386834c3d..dae63e0eb 100644 --- a/packages/essspectroscopy/src/ess/bifrost/single_crystal/conversion.py +++ b/packages/essspectroscopy/src/ess/bifrost/single_crystal/conversion.py @@ -10,6 +10,7 @@ from ess.spectroscopy.types import ( ElasticCoordTransformGraph, GravityVector, + NeutronSourcePosition, Position, QDetector, RunType, @@ -20,7 +21,7 @@ def single_crystal_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> ElasticCoordTransformGraph[RunType]: diff --git a/packages/essspectroscopy/src/ess/bifrost/single_crystal/time_of_flight.py b/packages/essspectroscopy/src/ess/bifrost/single_crystal/time_of_flight.py index 7c07f7cc8..f8a967fd2 100644 --- a/packages/essspectroscopy/src/ess/bifrost/single_crystal/time_of_flight.py +++ b/packages/essspectroscopy/src/ess/bifrost/single_crystal/time_of_flight.py @@ -9,6 +9,7 @@ EmptyDetector, ErrorLimitedLookupTable, GravityVector, + NeutronSourcePosition, Position, PulseStrideOffset, RawDetector, @@ -22,7 +23,7 @@ def detector_ltotal( sample_data: DataGroupedByRotation[RunType], - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> DetectorLtotal[RunType]: diff --git a/packages/essspectroscopy/src/ess/spectroscopy/indirect/conversion.py b/packages/essspectroscopy/src/ess/spectroscopy/indirect/conversion.py index 37fcaeae3..fb464b14a 100644 --- a/packages/essspectroscopy/src/ess/spectroscopy/indirect/conversion.py +++ b/packages/essspectroscopy/src/ess/spectroscopy/indirect/conversion.py @@ -6,7 +6,6 @@ import numpy as np import scipp as sc import scipp.constants -import scippnexus as snx from scippneutron.conversion.tof import ( energy_from_wavelength, wavevector_from_wavelength, @@ -18,8 +17,8 @@ IncidentEnergyDetector, InelasticCoordTransformGraph, MonitorCoordTransformGraph, + NeutronSourcePosition, NormalizedIncidentEnergyDetector, - Position, PrimarySpecCoordTransformGraph, RunType, SecondarySpecCoordTransformGraph, @@ -284,7 +283,7 @@ def add_spectrometer_coords( def monitor_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], ) -> MonitorCoordTransformGraph[RunType]: from scippneutron.conversion.graph import beamline, tof diff --git a/packages/essspectroscopy/src/ess/spectroscopy/indirect/ki.py b/packages/essspectroscopy/src/ess/spectroscopy/indirect/ki.py index 7d458dc18..dbe59e065 100644 --- a/packages/essspectroscopy/src/ess/spectroscopy/indirect/ki.py +++ b/packages/essspectroscopy/src/ess/spectroscopy/indirect/ki.py @@ -8,6 +8,7 @@ import scippnexus as snx from ess.spectroscopy.types import ( GravityVector, + NeutronSourcePosition, Position, PrimarySpecCoordTransformGraph, RunType, @@ -15,7 +16,7 @@ def primary_spectrometer_coordinate_transformation_graph( - source_position: Position[snx.NXsource, RunType], + source_position: NeutronSourcePosition[RunType], sample_position: Position[snx.NXsample, RunType], gravity: GravityVector, ) -> PrimarySpecCoordTransformGraph[RunType]: diff --git a/packages/essspectroscopy/src/ess/spectroscopy/types.py b/packages/essspectroscopy/src/ess/spectroscopy/types.py index 0c5b0748a..ee9cc19f0 100644 --- a/packages/essspectroscopy/src/ess/spectroscopy/types.py +++ b/packages/essspectroscopy/src/ess/spectroscopy/types.py @@ -21,6 +21,7 @@ Filename = reduce_t.Filename GravityVector = reduce_t.GravityVector Measurement = reduce_t.Measurement +NeutronSourcePosition = reduce_t.NeutronSourcePosition NeXusClass = reduce_t.NeXusClass NeXusComponentLocationSpec = reduce_t.NeXusComponentLocationSpec NeXusComponent = reduce_t.NeXusComponent