Workflow#

In this example, we will use McStas 3 simulation file.

Build Pipeline (Collect Parameters and Providers)#

Import the providers from load_mcstas_nexus to use the McStas simulation data workflow. MaximumProbability can be manually provided to derive more realistic number of events. It is because weights are given as probability, not number of events in a McStas file.

[1]:
from ess.nmx.mcstas import McStasWorkflow
from ess.nmx.data import small_mcstas_3_sample

from ess.nmx.types import *
from ess.nmx.reduction import NMXData, NMXReducedData, merge_panels
from ess.nmx.nexus import export_as_nexus

wf = McStasWorkflow()
# Replace with the path to your own file
wf[FilePath] = small_mcstas_3_sample()
wf[MaximumProbability] = 10000
wf[TimeBinSteps] = 50
Downloading file 'small_mcstas_3_sample.h5' from 'https://public.esss.dk/groups/scipp/ess/nmx/small_mcstas_3_sample.h5' to '/home/runner/.cache/essnmx/0'.

To see what the workflow can produce, display it:

[2]:
wf
[2]:
Name Value Source
CrystalRotation
load_crystal_rotation ess.nmx.mcstas.load.load_crystal_rotation
DetectorBankPrefix
load_event_data_bank_name ess.nmx.mcstas.load.load_event_data_bank_name
DetectorIndex
DetectorName
detector_name_from_index ess.nmx.mcstas.load.detector_name_from_index
EventData
event_weights_from_probability ess.nmx.mcstas.load.event_weights_from_probability
FilePath
/home/runner/.cache/essnmx/0/s... /home/runner/.cache/essnmx/0/small_mcstas_3_sample.h5
MaximumProbability 10000
McStasInstrument
read_mcstas_geometry_xml ess.nmx.mcstas.xml.read_mcstas_geometry_xml
NMXData
load_mcstas ess.nmx.mcstas.load.load_mcstas
NMXReducedData
bin_time_of_arrival ess.nmx.reduction.bin_time_of_arrival
ProtonCharge
proton_charge_from_event_data ess.nmx.mcstas.load.proton_charge_from_event_data
RawEventData
load_raw_event_data ess.nmx.mcstas.load.load_raw_event_data
TimeBinSteps 50

We want to reduce all three panels, so we map the relevant part of the workflow over a list of the three panels:

[3]:
# DetectorIndex selects what detector panels to include in the run
# in this case we select all three panels.
wf[NMXReducedData] = (
    wf[NMXReducedData]
    .map({DetectorIndex: sc.arange('panel', 3, unit=None)})
    .reduce(index="panel", func=merge_panels)
)

Build Workflow#

[4]:
wf.visualize(NMXReducedData, graph_attr={"rankdir": "TD"}, compact=True)
[4]:
../_images/examples_workflow_7_0.svg

Compute Desired Types#

[5]:
from cyclebane.graph import NodeName, IndexValues

# Event data grouped by pixel id for each of the selected detectors
targets = [NodeName(NMXData, IndexValues(("panel",), (i,))) for i in range(3)]
dg = merge_panels(*wf.compute(targets).values())
dg
[5]:
  • weights
    scipp
    DataArray
    (panel: 3, id: 1638400)
    DataArrayView
    binned data [len=0, len=0, ..., len=0, len=0]
  • proton_charge
    scipp
    Variable
    (panel: 3)
    float64
    0.006, 0.005, 0.005
  • crystal_rotation
    scipp
    Variable
    ()
    vector3
    deg
    [0. 0. 0.]
  • fast_axis
    scipp
    Variable
    (panel: 3)
    vector3
    𝟙
    [ 0.999986 0. -0.00529148], [-0.00531614 0. -0.99998587], [0.00531614 0. 0.99998587]
  • slow_axis
    scipp
    Variable
    (panel: 3)
    vector3
    𝟙
    [0. 1. 0.], [0. 1. 0.], [0. 1. 0.]
  • origin_position
    scipp
    Variable
    (panel: 3)
    vector3
    m
    [-0.248454 -0.25 0.292 ], [-0.288666 -0.25 0.252 ], [ 0.288667 -0.25 -0.251 ]
  • sample_position
    scipp
    Variable
    ()
    vector3
    m
    [0. 0. 0.]
  • source_position
    scipp
    Variable
    ()
    vector3
    m
    [ -0.53123 0. -157.405 ]
  • sample_name
    scipp
    Variable
    ()
    string
    sampleMantid
  • position
    scipp
    Variable
    (panel: 3, id: 1638400)
    vector3
    m
    [-0.248454 -0.25 0.292 ], [-0.24805401 -0.25 0.29199788], ..., [0.29138461 0.2616 0.26019278], [0.29138674 0.2616 0.26059277]
[6]:
# Data from all selected detectors binned by panel, pixel and timeslice
binned_dg = wf.compute(NMXReducedData)
binned_dg
[6]:
  • counts
    scipp
    DataArray
    (panel: 3, id: 1638400, t: 50)
    float64
    counts
    0.0, 0.0, ..., 0.0, 0.0
  • proton_charge
    scipp
    Variable
    (panel: 3)
    float64
    0.006, 0.005, 0.005
  • crystal_rotation
    scipp
    Variable
    ()
    vector3
    deg
    [0. 0. 0.]
  • fast_axis
    scipp
    Variable
    (panel: 3)
    vector3
    𝟙
    [ 0.999986 0. -0.00529148], [-0.00531614 0. -0.99998587], [0.00531614 0. 0.99998587]
  • slow_axis
    scipp
    Variable
    (panel: 3)
    vector3
    𝟙
    [0. 1. 0.], [0. 1. 0.], [0. 1. 0.]
  • origin_position
    scipp
    Variable
    (panel: 3)
    vector3
    m
    [-0.248454 -0.25 0.292 ], [-0.288666 -0.25 0.252 ], [ 0.288667 -0.25 -0.251 ]
  • sample_position
    scipp
    Variable
    ()
    vector3
    m
    [0. 0. 0.]
  • source_position
    scipp
    Variable
    ()
    vector3
    m
    [ -0.53123 0. -157.405 ]
  • sample_name
    scipp
    Variable
    ()
    string
    sampleMantid
  • position
    scipp
    Variable
    (panel: 3, id: 1638400)
    vector3
    m
    [-0.248454 -0.25 0.292 ], [-0.24805401 -0.25 0.29199788], ..., [0.29138461 0.2616 0.26019278], [0.29138674 0.2616 0.26059277]

Export Results#

NMXReducedData object has a method to export the data into nexus or h5 file.

You can save the result as test.nxs, for example:

[7]:
export_as_nexus(binned_dg, "test.nxs")

Instrument View#

Pixel positions are not used for later steps, but it is included in the coordinates for instrument view.

All pixel positions are relative to the sample position, therefore the sample is at (0, 0, 0).

It might be very slow or not work in the ``VS Code`` jupyter notebook editor.

[8]:
import scippneutron as scn

da = dg["weights"]
da.coords["position"] = dg["position"]
# Plot one out of 100 pixels to reduce size of docs output
view = scn.instrument_view(da["id", ::100].hist(), pixel_size=0.0075)
view
[8]: