Skip to content

Commit d17a0ee

Browse files
authored
fix: convert unlimited TOA to expected range (#209)
* fix: convert unlimited TOA to expected range * fix: add masking in monitor correction * fix: use dim coord not 'tof' * test: add masks to expected * test: loaded monitor has correct 'tof' range
1 parent 323d88a commit d17a0ee

File tree

4 files changed

+71
-14
lines changed

4 files changed

+71
-14
lines changed

src/ess/dream/io/geant4.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,27 @@ def _to_edges(centers: sc.Variable) -> sc.Variable:
196196
)
197197

198198

199+
def _rebin_to_tofrange(da):
200+
'''Rebins a monitor TOA histogram to the range of values
201+
expected from a real instrument at ESS.
202+
That is, to the range [0, 1/14] s.
203+
204+
Strategy:
205+
1. Create a grid that alingns with the pulse times.
206+
2. Rebin TOA on that grid.
207+
3. Fold pulses into new dimension and sum over pulses.
208+
'''
209+
dim = da.dim
210+
unit = da.coords[dim].unit
211+
period = (1.0 / sc.scalar(14.0, unit='Hz')).to(unit=unit)
212+
N = sc.sum(da.coords[dim] < period).value
213+
K = (da.coords[dim].max() // period + 1).to(dtype='int')
214+
grid = sc.linspace(dim, sc.scalar(0.0, unit=unit), K * period, K * (N - 1) + 1)
215+
out = da.rebin({dim: grid}).fold(dim, sizes={'_': K, dim: N - 1}).sum('_')
216+
out.coords[dim] = sc.linspace(dim, sc.scalar(0.0, unit=unit), period, N)
217+
return out
218+
219+
199220
def load_mcstas_monitor(
200221
file_path: MonitorFilename[RunType],
201222
position: CaveMonitorPosition,
@@ -221,14 +242,14 @@ def load_mcstas_monitor(
221242
tof, counts, err = np.loadtxt(file_path, usecols=(0, 1, 2), unpack=True)
222243

223244
tof = _to_edges(sc.array(dims=["tof"], values=tof, unit="us"))
245+
data = sc.DataArray(
246+
sc.array(dims=["tof"], values=counts, variances=err**2, unit="counts"),
247+
coords={"tof": tof},
248+
)
249+
data = _rebin_to_tofrange(data)
224250
return NeXusComponent[CaveMonitor, RunType](
225251
sc.DataGroup(
226-
data=sc.DataArray(
227-
sc.array(dims=["tof"], values=counts, variances=err**2, unit="counts"),
228-
coords={
229-
"tof": tof,
230-
},
231-
),
252+
data=data,
232253
position=position,
233254
)
234255
)

src/ess/powder/correction.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,15 @@ def normalize_by_monitor_histogram(
5959
)
6060
lut = sc.lookup(norm, dim="wavelength")
6161
if detector.bins is None:
62-
result = (
63-
detector / lut[sc.midpoints(detector.coords['wavelength'], dim='dspacing')]
64-
)
62+
c = lut[sc.midpoints(detector.coords['wavelength'], dim='dspacing')]
63+
result = detector / c
64+
result.masks['monitor_intensity_is_zero'] = c == sc.scalar(0.0, unit=c.unit)
6565
else:
66-
result = detector.bins / lut
66+
c = lut(detector.bins.coords['wavelength'])
67+
result = detector / c
68+
result.bins.masks['monitor_intensity_is_zero'] = c == sc.scalar(
69+
0.0, unit=c.unit
70+
)
6771
return ScaledCountsDspacing[RunType](result)
6872

6973

tests/dream/io/geant4_test.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,19 @@
1010
import scipp.testing
1111
import scippnexus as snx
1212

13-
from ess.dream import DreamGeant4ProtonChargeWorkflow, data, load_geant4_csv
14-
from ess.powder.types import Filename, NeXusComponent, NeXusDetectorName, SampleRun
13+
from ess.dream import DreamGeant4ProtonChargeWorkflow, data, io, load_geant4_csv
14+
from ess.powder.types import (
15+
CaveMonitorPosition,
16+
Filename,
17+
NeXusComponent,
18+
NeXusDetectorName,
19+
SampleRun,
20+
)
21+
22+
23+
@pytest.fixture(scope="module")
24+
def monitor_file_path():
25+
return data.simulated_monitor_diamond_sample()
1526

1627

1728
@pytest.fixture(scope="module")
@@ -184,3 +195,15 @@ def test_geant4_in_workflow(file_path, file):
184195
expected = load_geant4_csv(file)["instrument"]["mantle"]["events"]
185196
expected.coords["detector"] = sc.scalar("mantle")
186197
sc.testing.assert_identical(detector, expected)
198+
199+
200+
def test_geant4_monitor_in_expected_range(monitor_file_path):
201+
dummy_position = CaveMonitorPosition(sc.vector([0, 0, 0.0], unit='m'))
202+
mon = io.geant4.load_mcstas_monitor(monitor_file_path, dummy_position)['data']
203+
assert (
204+
mon.coords['tof'] >= sc.scalar(0.0, unit='s').to(unit=mon.coords['tof'].unit)
205+
).all()
206+
assert (
207+
mon.coords['tof']
208+
<= sc.scalar(1 / 14.0, unit='s').to(unit=mon.coords['tof'].unit)
209+
).all()

tests/powder/correction_test.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,12 @@ def test_normalize_by_monitor_histogram_expected_results():
347347
monitor=WavelengthMonitor[SampleRun, CaveMonitor](monitor),
348348
uncertainty_broadcast_mode=UncertaintyBroadcastMode.fail,
349349
)
350-
expected = ScaledCountsDspacing[SampleRun](detector / monitor.data)
350+
# Simple way to get all false array same shape as detector data.
351+
# Is there a better way?
352+
all_false = sc.isinf(detector.bins.data)
353+
expected = ScaledCountsDspacing[SampleRun](
354+
detector / monitor.data
355+
).bins.assign_masks({'monitor_intensity_is_zero': all_false})
351356
sc.testing.assert_identical(normalized, expected)
352357

353358

@@ -362,12 +367,16 @@ def test_normalize_by_monitor_histogram_ignores_monitor_values_out_of_range():
362367
'wavelength': sc.array(dims=['wavelength'], values=[0.0, 3, 4], unit='Å')
363368
},
364369
)
370+
# Simple way to get all false array same shape as detector data.
371+
all_false = sc.isinf(detector.bins.data)
365372
normalized = normalize_by_monitor_histogram(
366373
CountsWavelength[SampleRun](detector),
367374
monitor=WavelengthMonitor[SampleRun, CaveMonitor](monitor),
368375
uncertainty_broadcast_mode=UncertaintyBroadcastMode.fail,
369376
)
370-
expected = ScaledCountsDspacing[SampleRun](detector / sc.scalar(4.0, unit='counts'))
377+
expected = ScaledCountsDspacing[SampleRun](
378+
detector / sc.scalar(4.0, unit='counts')
379+
).bins.assign_masks({'monitor_intensity_is_zero': all_false})
371380
sc.testing.assert_identical(normalized, expected)
372381

373382

0 commit comments

Comments
 (0)