From 187f299df010d5f1ab01af227a0cbd630efe2b37 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 29 Oct 2025 10:55:56 +0100 Subject: [PATCH 01/13] feat: pulse shaping --- src/ess/beer/io.py | 97 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/src/ess/beer/io.py b/src/ess/beer/io.py index e800e659..37c7062c 100644 --- a/src/ess/beer/io.py +++ b/src/ess/beer/io.py @@ -4,6 +4,7 @@ import h5py import scipp as sc +import scipp.constants from .types import ( Filename, @@ -44,20 +45,25 @@ def _unique_child_group_h5( def _load_beer_mcstas(f, bank=1): - for key in f['/entry1/instrument/components']: - if 'sampleMantid' in key: - sample_position_path = f'/entry1/instrument/components/{key}/Position' - break - else: - raise ValueError('Sample position entry not found in file.') - data, events, params, sample_pos, chopper_pos = _load_h5( - f, - f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t', - f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t/events', - 'NXentry/simulation/Param', - sample_position_path, - '/entry1/instrument/components/0017_cMCA/Position', + positions = { + key: f'/entry1/instrument/components/{key}/Position' + for key in f['/entry1/instrument/components'] + } + data, events, params, sample_pos, psc1_pos, psc2_pos, psc3_pos, mca_pos, mcc_pos = ( + _load_h5( + f, + f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t', + f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t/events', + 'NXentry/simulation/Param', + positions['sampleMantid'], + positions['PSC1'], + positions['PSC2'], + positions['PSC3'], + positions['MCA'], + positions['MCC'], + ) ) + events = events[()] da = sc.DataArray( sc.array(dims=['events'], values=events[:, 0], variances=events[:, 0] ** 2), @@ -75,21 +81,70 @@ def _load_beer_mcstas(f, bank=1): v = v[0] if isinstance(v, bytes): v = v.decode() - if k in ('mode', 'sample_filename'): + if k in ('mode', 'sample_filename', 'lambda'): da.coords[k] = sc.scalar(v) + if da.coords['lambda'].value == '0': + if da.coords['mode'].value in [ + '0', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '15', + '16', + ]: + da.coords['lambda'] = sc.scalar(2.1, unit='angstrom') + elif da.coords['mode'].value in ['1', '2']: + da.coords['lambda'] = sc.scalar(3.1, unit='angstrom') + elif da.coords['mode'].value == '11': + da.coords['lambda'] = sc.scalar(3.0, unit='angstrom') + elif da.coords['mode'].value == '12': + da.coords['lambda'] = sc.scalar(3.5, unit='angstrom') + elif da.coords['mode'].value == '13': + da.coords['lambda'] = sc.scalar(6.0, unit='angstrom') + elif da.coords['mode'].value == '14': + da.coords['lambda'] = sc.scalar(4.0, unit='angstrom') + else: + da.coords['lambda'] = sc.scalar( + float(da.coords['lambda'].value), unit='angstrom' + ) + da.coords['sample_position'] = sc.vector(sample_pos[:], unit='m') da.coords['detector_position'] = sc.vector( list(map(float, da.coords.pop('position').value.split(' '))), unit='m' ) - da.coords['chopper_position'] = sc.vector(chopper_pos[:], unit='m') + + if da.coords['mode'].value in ['0', '1', '2', '11', '16']: + da.coords['chopper_position'] = sc.vector([0.0, 0.0, 0.0], unit='m') + elif da.coords['mode'].value in ['3', '4', '12', '13', '15']: + da.coords['chopper_position'] = sc.vector( + 0.5 * (psc1_pos[:] + psc3_pos[:]), unit='m' + ) + elif da.coords['mode'].value in ['5', '6']: + da.coords['chopper_position'] = sc.vector( + 0.5 * (psc1_pos[:] + psc2_pos[:]), unit='m' + ) + elif da.coords['mode'].value in ['7', '8', '9', '10']: + da.coords['chopper_position'] = sc.vector(0.5 * mca_pos[:], unit='m') + elif da.coords['mode'].value == '14': + da.coords['chopper_position'] = sc.vector(mcc_pos[:], unit='m') + else: + raise ValueError(f'Unkonwn chopper mode {da.coords["mode"].value}.') + da.coords['x'].unit = 'm' da.coords['y'].unit = 'm' da.coords['t'].unit = 's' z = sc.norm(da.coords['detector_position'] - da.coords['sample_position']) + # z = da.coords['position'].fields.z - da.coords['sample_position'].fields.z L1 = sc.norm(da.coords['sample_position'] - da.coords['chopper_position']) L2 = sc.sqrt(da.coords['x'] ** 2 + da.coords['y'] ** 2 + z**2) + # Source is assumed to be at the origin da.coords['L0'] = L1 + L2 + sc.norm(da.coords['chopper_position']) da.coords['Ltotal'] = L1 + L2 @@ -102,7 +157,17 @@ def _load_beer_mcstas(f, bank=1): da.coords.pop('y') da.coords.pop('n') - da.coords['event_time_offset'] = da.coords.pop('t') + # expression of temporal offset delta_t checked for pulse shaping mode: 4,5,6 + delta_t = ( + sc.constants.m_n + / sc.constants.h + * da.coords['lambda'] + * sc.norm(da.coords['chopper_position']).to(unit='angstrom') + ).to(unit='s') + + t = da.coords.pop('t') + da.coords['event_time_offset'] = t % sc.scalar(1 / 14, unit=t.unit) + da.coords["approximate_tof"] = da.coords['event_time_offset'] - delta_t return da From ed8ba37d6afa2d0959593594600f9e5523cc0997 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 29 Oct 2025 16:39:42 +0100 Subject: [PATCH 02/13] fix --- src/ess/beer/io.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ess/beer/io.py b/src/ess/beer/io.py index 37c7062c..bdf00888 100644 --- a/src/ess/beer/io.py +++ b/src/ess/beer/io.py @@ -46,8 +46,10 @@ def _unique_child_group_h5( def _load_beer_mcstas(f, bank=1): positions = { - key: f'/entry1/instrument/components/{key}/Position' + name: f'/entry1/instrument/components/{key}/Position' for key in f['/entry1/instrument/components'] + for name in ['sampleMantid', 'PSC1', 'PSC2', 'PSC3', 'MCA', 'MCC'] + if name in key } data, events, params, sample_pos, psc1_pos, psc2_pos, psc3_pos, mca_pos, mcc_pos = ( _load_h5( From 5aed46fe999898a29541fceb3462cf94a5ad19c5 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Mon, 3 Nov 2025 16:11:10 +0100 Subject: [PATCH 03/13] fix: load chopper settings in all modes - truncate time to limits of event_time_offset --- src/ess/beer/__init__.py | 2 + src/ess/beer/clustering.py | 14 ++++-- src/ess/beer/conversions.py | 76 +++++++++++++++++++++++++------- src/ess/beer/io.py | 88 +++++++++++++++++++++---------------- src/ess/beer/workflow.py | 11 ++++- 5 files changed, 133 insertions(+), 58 deletions(-) diff --git a/src/ess/beer/__init__.py b/src/ess/beer/__init__.py index fee0b817..829dab28 100644 --- a/src/ess/beer/__init__.py +++ b/src/ess/beer/__init__.py @@ -9,6 +9,7 @@ from .io import load_beer_mcstas from .workflow import ( + BeerMcStasWorkflowPulseShaping, BeerModMcStasWorkflow, BeerModMcStasWorkflowKnownPeaks, default_parameters, @@ -22,6 +23,7 @@ del importlib __all__ = [ + 'BeerMcStasWorkflowPulseShaping', 'BeerModMcStasWorkflow', 'BeerModMcStasWorkflowKnownPeaks', '__version__', diff --git a/src/ess/beer/clustering.py b/src/ess/beer/clustering.py index 59f6aeae..2ad5526d 100644 --- a/src/ess/beer/clustering.py +++ b/src/ess/beer/clustering.py @@ -2,6 +2,7 @@ from scippneutron.conversion.tof import dspacing_from_tof from scipy.signal import find_peaks, medfilt +from .conversions import _t0_estimate, _time_of_arrival from .types import RawDetector, RunType, StreakClusteredData @@ -10,12 +11,17 @@ def cluster_events_by_streak(da: RawDetector[RunType]) -> StreakClusteredData[Ru return sc.DataGroup({k: cluster_events_by_streak(v) for k, v in da.items()}) da = da.copy(deep=False) - # TODO: approximate t0 should depend on chopper information - approximate_t0 = sc.scalar(6e-3, unit='s') + t = _time_of_arrival( + da.coords['event_time_offset'], + da.coords['tc'].to(unit=da.coords['event_time_offset'].unit), + ) + approximate_t0 = _t0_estimate( + da.coords['wavelength_estimate'], da.coords['L0'], da.coords['Ltotal'] + ).to(unit=t.unit) da.coords['coarse_d'] = dspacing_from_tof( - tof=da.coords['event_time_offset'] - approximate_t0, - Ltotal=da.coords['L0'], + tof=t - approximate_t0, + Ltotal=da.coords['Ltotal'], two_theta=da.coords['two_theta'], ).to(unit='angstrom') diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index be08c8b6..c403434d 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -38,7 +38,10 @@ def compute_tof_in_each_cluster( max_distance_from_streak_line = mod_period / 3 sin_theta_L = sc.sin(da.bins.coords['two_theta'] / 2) * da.bins.coords['Ltotal'] - t = da.bins.coords['event_time_offset'] + t = _time_of_arrival( + da.bins.coords['event_time_offset'], + da.coords['tc'].to(unit=da.bins.coords['event_time_offset'].unit), + ) for _ in range(15): s, t0 = _linear_regression_by_bin(sin_theta_L, t, da.data) @@ -77,7 +80,7 @@ def _linear_regression_by_bin( def _compute_d( - event_time_offset: sc.Variable, + time_of_arrival: sc.Variable, theta: sc.Variable, dhkl_list: sc.Variable, pulse_length: sc.Variable, @@ -87,7 +90,7 @@ def _compute_d( given a list of known peaks.""" # Source: https://www2.mcstas.org/download/components/3.4/contrib/NPI_tof_dhkl_detector.comp sinth = sc.sin(theta) - t = event_time_offset + t = time_of_arrival d = sc.empty(dims=sinth.dims, shape=sinth.shape, unit=dhkl_list[0].unit) d[:] = sc.scalar(float('nan'), unit=dhkl_list[0].unit) @@ -95,7 +98,7 @@ def _compute_d( dtfound[:] = sc.scalar(float('nan'), unit=t.unit) const = (2 * sinth * L0 / (scipp.constants.h / scipp.constants.m_n)).to( - unit=f'{event_time_offset.unit}/angstrom' + unit=f'{time_of_arrival.unit}/angstrom' ) for dhkl in dhkl_list: @@ -112,8 +115,18 @@ def _compute_d( return d -def _tof_from_dhkl( +def _time_of_arrival( event_time_offset: sc.Variable, + tc: sc.Variable, +): + _eto = event_time_offset + T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit) + tc = tc.to(unit=_eto.unit) + return sc.where(_eto >= tc % T, _eto, _eto + T) + + +def _tof_from_dhkl( + time_of_arrival: sc.Variable, theta: sc.Variable, coarse_dhkl: sc.Variable, Ltotal: sc.Variable, @@ -123,17 +136,17 @@ def _tof_from_dhkl( '''Computes tof for BEER given the dhkl peak that the event belongs to''' # Source: https://www2.mcstas.org/download/components/3.4/contrib/NPI_tof_dhkl_detector.comp # tref = 2 * d_hkl * sin(theta) / hm * Ltotal - # tc = event_time_zero - time0 - tref + # tc = time_of_arrival - time0 - tref # dt = floor(tc / mod_period + 0.5) * mod_period + time0 - # tof = event_time_offset - dt + # tof = time_of_arrival - dt c = (-2 * 1.0 / (scipp.constants.h / scipp.constants.m_n)).to( - unit=f'{event_time_offset.unit}/m/angstrom' + unit=f'{time_of_arrival.unit}/m/angstrom' ) out = sc.sin(theta) out *= c out *= coarse_dhkl out *= Ltotal - out += event_time_offset + out += time_of_arrival out -= time0 out /= mod_period out += 0.5 @@ -141,7 +154,7 @@ def _tof_from_dhkl( out *= mod_period out += time0 out *= -1 - out += event_time_offset + out += time_of_arrival return out @@ -152,10 +165,10 @@ def tof_from_known_dhkl_graph( dhkl_list: DHKLList, ) -> TofCoordTransformGraph: def _compute_coarse_dspacing( - event_time_offset, + time_of_arrival: sc.Variable, theta: sc.Variable, pulse_length: sc.Variable, - L0, + L0: sc.Variable, ): '''To capture dhkl_list, otherwise it causes an error when ``.transform_coords`` is called unless it is called with @@ -164,7 +177,7 @@ def _compute_coarse_dspacing( with dimensions not present on the data. ''' return _compute_d( - event_time_offset=event_time_offset, + time_of_arrival=time_of_arrival, theta=theta, pulse_length=pulse_length, L0=L0, @@ -176,12 +189,41 @@ def _compute_coarse_dspacing( 'mod_period': lambda: mod_period, 'time0': lambda: time0, 'tof': _tof_from_dhkl, + 'time_of_arrival': _time_of_arrival, 'coarse_dhkl': _compute_coarse_dspacing, 'theta': lambda two_theta: two_theta / 2, } -def compute_tof_from_known_peaks( +def _t0_estimate( + wavelength_estimate: sc.Variable, + L0: sc.Variable, + Ltotal: sc.Variable, +) -> sc.Variable: + return ( + sc.constants.m_n + / sc.constants.h + * wavelength_estimate + * (L0 - Ltotal).to(unit=wavelength_estimate.unit) + ).to(unit='s') + + +def _tof_from_t0_estimate( + time_of_arrival: sc.Variable, + t0_estimate: sc.Variable, +) -> sc.Variable: + return time_of_arrival - t0_estimate + + +def tof_from_t0_estimate() -> TofCoordTransformGraph: + return { + 't0_estimate': _t0_estimate, + 'tof': _tof_from_t0_estimate, + 'time_of_arrival': _time_of_arrival, + } + + +def compute_tof( da: RawDetector[RunType], graph: TofCoordTransformGraph ) -> TofDetector[RunType]: return da.transform_coords(('tof',), graph=graph) @@ -189,6 +231,10 @@ def compute_tof_from_known_peaks( convert_from_known_peaks_providers = ( tof_from_known_dhkl_graph, - compute_tof_from_known_peaks, + compute_tof, +) +convert_pulse_shaping = ( + tof_from_t0_estimate, + compute_tof, ) providers = (compute_tof_in_each_cluster,) diff --git a/src/ess/beer/io.py b/src/ess/beer/io.py index bdf00888..73d147ca 100644 --- a/src/ess/beer/io.py +++ b/src/ess/beer/io.py @@ -48,22 +48,32 @@ def _load_beer_mcstas(f, bank=1): positions = { name: f'/entry1/instrument/components/{key}/Position' for key in f['/entry1/instrument/components'] - for name in ['sampleMantid', 'PSC1', 'PSC2', 'PSC3', 'MCA', 'MCC'] + for name in ['sampleMantid', 'PSC1', 'PSC2', 'PSC3', 'MCA', 'MCB', 'MCC'] if name in key } - data, events, params, sample_pos, psc1_pos, psc2_pos, psc3_pos, mca_pos, mcc_pos = ( - _load_h5( - f, - f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t', - f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t/events', - 'NXentry/simulation/Param', - positions['sampleMantid'], - positions['PSC1'], - positions['PSC2'], - positions['PSC3'], - positions['MCA'], - positions['MCC'], - ) + ( + data, + events, + params, + sample_pos, + psc1_pos, + psc2_pos, + psc3_pos, + mca_pos, + mcb_pos, + mcc_pos, + ) = _load_h5( + f, + f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t', + f'NXentry/NXdetector/bank{bank:02}_events_dat_list_p_x_y_n_id_t/events', + 'NXentry/simulation/Param', + positions['sampleMantid'], + positions['PSC1'], + positions['PSC2'], + positions['PSC3'], + positions['MCA'], + positions['MCB'], + positions['MCC'], ) events = events[()] @@ -86,7 +96,10 @@ def _load_beer_mcstas(f, bank=1): if k in ('mode', 'sample_filename', 'lambda'): da.coords[k] = sc.scalar(v) - if da.coords['lambda'].value == '0': + if 'lambda' in da.coords: + da.coords['wavelength_estimate'] = da.coords.pop('lambda') + + if da.coords['wavelength_estimate'].value == '0': if da.coords['mode'].value in [ '0', '3', @@ -100,20 +113,20 @@ def _load_beer_mcstas(f, bank=1): '15', '16', ]: - da.coords['lambda'] = sc.scalar(2.1, unit='angstrom') + da.coords['wavelength_estimate'] = sc.scalar(2.1, unit='angstrom') elif da.coords['mode'].value in ['1', '2']: - da.coords['lambda'] = sc.scalar(3.1, unit='angstrom') + da.coords['wavelength_estimate'] = sc.scalar(3.1, unit='angstrom') elif da.coords['mode'].value == '11': - da.coords['lambda'] = sc.scalar(3.0, unit='angstrom') + da.coords['wavelength_estimate'] = sc.scalar(3.0, unit='angstrom') elif da.coords['mode'].value == '12': - da.coords['lambda'] = sc.scalar(3.5, unit='angstrom') + da.coords['wavelength_estimate'] = sc.scalar(3.5, unit='angstrom') elif da.coords['mode'].value == '13': - da.coords['lambda'] = sc.scalar(6.0, unit='angstrom') + da.coords['wavelength_estimate'] = sc.scalar(6.0, unit='angstrom') elif da.coords['mode'].value == '14': - da.coords['lambda'] = sc.scalar(4.0, unit='angstrom') + da.coords['wavelength_estimate'] = sc.scalar(4.0, unit='angstrom') else: - da.coords['lambda'] = sc.scalar( - float(da.coords['lambda'].value), unit='angstrom' + da.coords['wavelength_estimate'] = sc.scalar( + float(da.coords['wavelength_estimate'].value), unit='angstrom' ) da.coords['sample_position'] = sc.vector(sample_pos[:], unit='m') @@ -121,7 +134,7 @@ def _load_beer_mcstas(f, bank=1): list(map(float, da.coords.pop('position').value.split(' '))), unit='m' ) - if da.coords['mode'].value in ['0', '1', '2', '11', '16']: + if da.coords['mode'].value in ['0', '1', '2', '11']: da.coords['chopper_position'] = sc.vector([0.0, 0.0, 0.0], unit='m') elif da.coords['mode'].value in ['3', '4', '12', '13', '15']: da.coords['chopper_position'] = sc.vector( @@ -132,9 +145,13 @@ def _load_beer_mcstas(f, bank=1): 0.5 * (psc1_pos[:] + psc2_pos[:]), unit='m' ) elif da.coords['mode'].value in ['7', '8', '9', '10']: - da.coords['chopper_position'] = sc.vector(0.5 * mca_pos[:], unit='m') + da.coords['chopper_position'] = sc.vector(mca_pos[:], unit='m') elif da.coords['mode'].value == '14': da.coords['chopper_position'] = sc.vector(mcc_pos[:], unit='m') + elif da.coords['mode'].value == '16': + da.coords['chopper_position'] = sc.vector( + 0.5 * (mca_pos[:] + mcb_pos[:]), unit='m' + ) else: raise ValueError(f'Unkonwn chopper mode {da.coords["mode"].value}.') @@ -143,7 +160,6 @@ def _load_beer_mcstas(f, bank=1): da.coords['t'].unit = 's' z = sc.norm(da.coords['detector_position'] - da.coords['sample_position']) - # z = da.coords['position'].fields.z - da.coords['sample_position'].fields.z L1 = sc.norm(da.coords['sample_position'] - da.coords['chopper_position']) L2 = sc.sqrt(da.coords['x'] ** 2 + da.coords['y'] ** 2 + z**2) @@ -159,17 +175,15 @@ def _load_beer_mcstas(f, bank=1): da.coords.pop('y') da.coords.pop('n') - # expression of temporal offset delta_t checked for pulse shaping mode: 4,5,6 - delta_t = ( + t = da.coords.pop('t') + da.coords['event_time_offset'] = t % sc.scalar(1 / 14, unit='s').to(unit=t.unit) + da.coords["tc"] = ( sc.constants.m_n / sc.constants.h - * da.coords['lambda'] - * sc.norm(da.coords['chopper_position']).to(unit='angstrom') - ).to(unit='s') + * da.coords['wavelength_estimate'] + * da.coords['L0'].min().to(unit='angstrom') + ).to(unit='s') - sc.scalar(1 / 14, unit='s') / 2 - t = da.coords.pop('t') - da.coords['event_time_offset'] = t % sc.scalar(1 / 14, unit=t.unit) - da.coords["approximate_tof"] = da.coords['event_time_offset'] - delta_t return da @@ -218,10 +232,8 @@ def mcstas_chopper_delay_from_mode( da: RawDetector[SampleRun], ) -> WavelengthDefinitionChopperDelay: mode = next(iter(d.coords['mode'] for d in da.values())).value - if mode in ('7', '8'): - return sc.scalar(0.00245635, unit='s') - if mode in ('9', '10'): - return sc.scalar(0.0033730158730158727, unit='s') + if mode in ('7', '8', '9', '10'): + return sc.scalar(0.0024730158730158727, unit='s') if mode == '16': return sc.scalar(0.000876984126984127, unit='s') raise ValueError(f'Mode {mode} is not known.') diff --git a/src/ess/beer/workflow.py b/src/ess/beer/workflow.py index f47fdcb0..d96ed2e5 100644 --- a/src/ess/beer/workflow.py +++ b/src/ess/beer/workflow.py @@ -4,7 +4,7 @@ import scipp as sc from .clustering import providers as clustering_providers -from .conversions import convert_from_known_peaks_providers +from .conversions import convert_from_known_peaks_providers, convert_pulse_shaping from .conversions import providers as conversion_providers from .io import mcstas_providers from .types import ( @@ -41,3 +41,12 @@ def BeerModMcStasWorkflowKnownPeaks(): params=default_parameters, constraints={RunType: (SampleRun,)}, ) + + +def BeerMcStasWorkflowPulseShaping(): + '''Workflow to process BEER (pulse shaping modes) McStas files''' + return sl.Pipeline( + (*mcstas_providers, *convert_pulse_shaping), + params=default_parameters, + constraints={RunType: (SampleRun,)}, + ) From 3cd12afa89aa9c338ab10b8892f79932a75e33c2 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Tue, 4 Nov 2025 15:28:08 +0100 Subject: [PATCH 04/13] data: add updated simulation files to pooch, and add provider for parameters --- src/ess/beer/data.py | 17 +++++++++++++++++ src/ess/beer/io.py | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/ess/beer/data.py b/src/ess/beer/data.py index 70bd4391..ff09b427 100644 --- a/src/ess/beer/data.py +++ b/src/ess/beer/data.py @@ -28,6 +28,15 @@ "silicon-dhkl.tab": "md5:59ee9ed57a7c039ce416c8df886da9cc", "duplex-dhkl.tab": "md5:b4c6c2fcd66466ad291f306b2d6b346e", "dhkl_quartz_nc.tab": "md5:40887d736e3acf859e44488bfd9a9213", + # Simulations from new model with corrected(?) L0. + # For correct reduction you need to use + # beer.io.mcstas_chopper_delay_from_mode_new_simulations + # to obtain the correct WavelengthDefinitionChopperDelay for these files. + "silicon-mode10-new-model.h5": "md5:98500830f27700fc719634e1acd49944", + "silicon-mode16-new-model.h5": "md5:393f9287e7d3f97ceedbe64343918413", + "silicon-mode7-new-model.h5": "md5:d2070d3132722bb551d99b243c62752f", + "silicon-mode8-new-model.h5": "md5:d6dfdf7e87eccedf4f83c67ec552ca22", + "silicon-mode9-new-model.h5": "md5:694a17fb616b7f1c20e94d9da113d201", }, ) @@ -54,6 +63,14 @@ def mcstas_silicon_medium_resolution() -> Path: return _registry.get_path('silicon-mode09.h5') +def mcstas_silicon_new_model(mode: int) -> Path: + """ + Simulated intensity from silicon sample with + medium resolution chopper configuration. + """ + return _registry.get_path(f'silicon-mode{mode}-new-model.h5') + + def duplex_peaks() -> Path: return _registry.get_path('duplex-dhkl.tab') diff --git a/src/ess/beer/io.py b/src/ess/beer/io.py index 73d147ca..03d1929b 100644 --- a/src/ess/beer/io.py +++ b/src/ess/beer/io.py @@ -231,6 +231,10 @@ def load_beer_mcstas_provider( def mcstas_chopper_delay_from_mode( da: RawDetector[SampleRun], ) -> WavelengthDefinitionChopperDelay: + '''These settings are good for the set of McStas runs that we + use in the docs currently. + Eventually we will want to determine this from the chopper information + in the files, but that information is not in the simulation output.''' mode = next(iter(d.coords['mode'] for d in da.values())).value if mode in ('7', '8', '9', '10'): return sc.scalar(0.0024730158730158727, unit='s') @@ -239,6 +243,25 @@ def mcstas_chopper_delay_from_mode( raise ValueError(f'Mode {mode} is not known.') +def mcstas_chopper_delay_from_mode_new_simulations( + da: RawDetector[SampleRun], +) -> WavelengthDefinitionChopperDelay: + '''Celine has a new simulation with some changes to the chopper placement(?). + For those simulations we need to adapt the chopper delay values.''' + mode = next(iter(d.coords['mode'] for d in da.values())).value + if mode == '7': + return sc.scalar(0.001370158730158727, unit='s') + if mode == '8': + return sc.scalar(0.001370158730158727, unit='s') + if mode == '9': + return sc.scalar(0.0022630158730158727, unit='s') + if mode == '10': + return sc.scalar(0.0022630158730158727, unit='s') + if mode == '16': + return sc.scalar(0.000476984126984127, unit='s') + raise ValueError(f'Mode {mode} is not known.') + + def mcstas_modulation_period_from_mode(da: RawDetector[SampleRun]) -> ModulationPeriod: mode = next(iter(d.coords['mode'] for d in da.values())).value if mode in ('7', '8'): From 402382eb8c2de622fb92c72723053e57214510ca Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Tue, 4 Nov 2025 15:43:38 +0100 Subject: [PATCH 05/13] docs --- src/ess/beer/data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ess/beer/data.py b/src/ess/beer/data.py index ff09b427..e9e7e15e 100644 --- a/src/ess/beer/data.py +++ b/src/ess/beer/data.py @@ -65,8 +65,7 @@ def mcstas_silicon_medium_resolution() -> Path: def mcstas_silicon_new_model(mode: int) -> Path: """ - Simulated intensity from silicon sample with - medium resolution chopper configuration. + Simulated intensity from duplex sample with ``mode`` chopper configuration. """ return _registry.get_path(f'silicon-mode{mode}-new-model.h5') From 7e7329d2934cb8d81d7538947be2a27eb2b3b004 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 5 Nov 2025 09:11:15 +0100 Subject: [PATCH 06/13] fix: make functions public --- src/ess/beer/clustering.py | 6 +++--- src/ess/beer/conversions.py | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ess/beer/clustering.py b/src/ess/beer/clustering.py index 2ad5526d..40c848a4 100644 --- a/src/ess/beer/clustering.py +++ b/src/ess/beer/clustering.py @@ -2,7 +2,7 @@ from scippneutron.conversion.tof import dspacing_from_tof from scipy.signal import find_peaks, medfilt -from .conversions import _t0_estimate, _time_of_arrival +from .conversions import t0_estimate, time_of_arrival from .types import RawDetector, RunType, StreakClusteredData @@ -11,11 +11,11 @@ def cluster_events_by_streak(da: RawDetector[RunType]) -> StreakClusteredData[Ru return sc.DataGroup({k: cluster_events_by_streak(v) for k, v in da.items()}) da = da.copy(deep=False) - t = _time_of_arrival( + t = time_of_arrival( da.coords['event_time_offset'], da.coords['tc'].to(unit=da.coords['event_time_offset'].unit), ) - approximate_t0 = _t0_estimate( + approximate_t0 = t0_estimate( da.coords['wavelength_estimate'], da.coords['L0'], da.coords['Ltotal'] ).to(unit=t.unit) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index c403434d..b2a4640b 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -38,7 +38,7 @@ def compute_tof_in_each_cluster( max_distance_from_streak_line = mod_period / 3 sin_theta_L = sc.sin(da.bins.coords['two_theta'] / 2) * da.bins.coords['Ltotal'] - t = _time_of_arrival( + t = time_of_arrival( da.bins.coords['event_time_offset'], da.coords['tc'].to(unit=da.bins.coords['event_time_offset'].unit), ) @@ -115,7 +115,7 @@ def _compute_d( return d -def _time_of_arrival( +def time_of_arrival( event_time_offset: sc.Variable, tc: sc.Variable, ): @@ -189,13 +189,13 @@ def _compute_coarse_dspacing( 'mod_period': lambda: mod_period, 'time0': lambda: time0, 'tof': _tof_from_dhkl, - 'time_of_arrival': _time_of_arrival, + 'time_of_arrival': time_of_arrival, 'coarse_dhkl': _compute_coarse_dspacing, 'theta': lambda two_theta: two_theta / 2, } -def _t0_estimate( +def t0_estimate( wavelength_estimate: sc.Variable, L0: sc.Variable, Ltotal: sc.Variable, @@ -208,18 +208,18 @@ def _t0_estimate( ).to(unit='s') -def _tof_from_t0_estimate( +def _tof_from_t0( time_of_arrival: sc.Variable, - t0_estimate: sc.Variable, + t0: sc.Variable, ) -> sc.Variable: - return time_of_arrival - t0_estimate + return time_of_arrival - t0 def tof_from_t0_estimate() -> TofCoordTransformGraph: return { - 't0_estimate': _t0_estimate, - 'tof': _tof_from_t0_estimate, - 'time_of_arrival': _time_of_arrival, + 't0': t0_estimate, + 'tof': _tof_from_t0, + 'time_of_arrival': time_of_arrival, } From 2ed96877e6cdf92bd2b3dcc9d3632ab91e3a1e25 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 5 Nov 2025 09:16:52 +0100 Subject: [PATCH 07/13] docs --- src/ess/beer/conversions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index b2a4640b..1eac3b4f 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -119,6 +119,8 @@ def time_of_arrival( event_time_offset: sc.Variable, tc: sc.Variable, ): + '''Does frame unwrapping based on the cutoff time `tc` and `event_time_ffset`. + Events before `tc` are assumed to come from the previous pulse.''' _eto = event_time_offset T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit) tc = tc.to(unit=_eto.unit) @@ -200,6 +202,7 @@ def t0_estimate( L0: sc.Variable, Ltotal: sc.Variable, ) -> sc.Variable: + '''Estimates the time-at-chopper by assuming the wavelength.''' return ( sc.constants.m_n / sc.constants.h @@ -212,6 +215,7 @@ def _tof_from_t0( time_of_arrival: sc.Variable, t0: sc.Variable, ) -> sc.Variable: + '''Computes time-of-flight by subtracting a start time.''' return time_of_arrival - t0 From a405435e387ed004fbcf3f6253a6cb204183943f Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 5 Nov 2025 09:36:15 +0100 Subject: [PATCH 08/13] refactor: extract mode param mapping --- src/ess/beer/io.py | 124 ++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 53 deletions(-) diff --git a/src/ess/beer/io.py b/src/ess/beer/io.py index 03d1929b..96a9bed0 100644 --- a/src/ess/beer/io.py +++ b/src/ess/beer/io.py @@ -44,6 +44,63 @@ def _unique_child_group_h5( return out +def _wavelength_estimate_from_mode(mode, value_in_file): + if value_in_file != '0': + return sc.scalar(float(value_in_file), unit='angstrom') + if mode in [ + '0', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '15', + '16', + ]: + return sc.scalar(2.1, unit='angstrom') + elif mode in ['1', '2']: + return sc.scalar(3.1, unit='angstrom') + elif mode == '11': + return sc.scalar(3.0, unit='angstrom') + elif mode == '12': + return sc.scalar(3.5, unit='angstrom') + elif mode == '13': + return sc.scalar(6.0, unit='angstrom') + elif mode == '14': + return sc.scalar(4.0, unit='angstrom') + else: + raise ValueError(f'Unkonwn chopper mode {mode}.') + + +def _chopper_position_from_mode( + mode, + *, + psc1_pos, + psc2_pos, + psc3_pos, + mca_pos, + mcb_pos, + mcc_pos, +): + if mode in ['0', '1', '2', '11']: + return sc.vector([0.0, 0.0, 0.0], unit='m') + elif mode in ['3', '4', '12', '13', '15']: + return sc.vector(0.5 * (psc1_pos + psc3_pos), unit='m') + elif mode in ['5', '6']: + return sc.vector(0.5 * (psc1_pos + psc2_pos), unit='m') + elif mode in ['7', '8', '9', '10']: + return sc.vector(mca_pos, unit='m') + elif mode == '14': + return sc.vector(mcc_pos, unit='m') + elif mode == '16': + return sc.vector(0.5 * (mca_pos + mcb_pos), unit='m') + else: + raise ValueError(f'Unkonwn chopper mode {mode}.') + + def _load_beer_mcstas(f, bank=1): positions = { name: f'/entry1/instrument/components/{key}/Position' @@ -96,65 +153,26 @@ def _load_beer_mcstas(f, bank=1): if k in ('mode', 'sample_filename', 'lambda'): da.coords[k] = sc.scalar(v) - if 'lambda' in da.coords: - da.coords['wavelength_estimate'] = da.coords.pop('lambda') - - if da.coords['wavelength_estimate'].value == '0': - if da.coords['mode'].value in [ - '0', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '15', - '16', - ]: - da.coords['wavelength_estimate'] = sc.scalar(2.1, unit='angstrom') - elif da.coords['mode'].value in ['1', '2']: - da.coords['wavelength_estimate'] = sc.scalar(3.1, unit='angstrom') - elif da.coords['mode'].value == '11': - da.coords['wavelength_estimate'] = sc.scalar(3.0, unit='angstrom') - elif da.coords['mode'].value == '12': - da.coords['wavelength_estimate'] = sc.scalar(3.5, unit='angstrom') - elif da.coords['mode'].value == '13': - da.coords['wavelength_estimate'] = sc.scalar(6.0, unit='angstrom') - elif da.coords['mode'].value == '14': - da.coords['wavelength_estimate'] = sc.scalar(4.0, unit='angstrom') - else: - da.coords['wavelength_estimate'] = sc.scalar( - float(da.coords['wavelength_estimate'].value), unit='angstrom' - ) + da.coords['wavelength_estimate'] = _wavelength_estimate_from_mode( + da.coords['mode'].value, + da.coords.pop('lambda').value if 'lambda' in da.coords else '0', + ) + + da.coords['chopper_position'] = _chopper_position_from_mode( + da.coords['mode'].value, + psc1_pos=psc1_pos[:], + psc2_pos=psc2_pos[:], + psc3_pos=psc3_pos[:], + mca_pos=mca_pos[:], + mcb_pos=mcb_pos[:], + mcc_pos=mcc_pos[:], + ) da.coords['sample_position'] = sc.vector(sample_pos[:], unit='m') da.coords['detector_position'] = sc.vector( list(map(float, da.coords.pop('position').value.split(' '))), unit='m' ) - if da.coords['mode'].value in ['0', '1', '2', '11']: - da.coords['chopper_position'] = sc.vector([0.0, 0.0, 0.0], unit='m') - elif da.coords['mode'].value in ['3', '4', '12', '13', '15']: - da.coords['chopper_position'] = sc.vector( - 0.5 * (psc1_pos[:] + psc3_pos[:]), unit='m' - ) - elif da.coords['mode'].value in ['5', '6']: - da.coords['chopper_position'] = sc.vector( - 0.5 * (psc1_pos[:] + psc2_pos[:]), unit='m' - ) - elif da.coords['mode'].value in ['7', '8', '9', '10']: - da.coords['chopper_position'] = sc.vector(mca_pos[:], unit='m') - elif da.coords['mode'].value == '14': - da.coords['chopper_position'] = sc.vector(mcc_pos[:], unit='m') - elif da.coords['mode'].value == '16': - da.coords['chopper_position'] = sc.vector( - 0.5 * (mca_pos[:] + mcb_pos[:]), unit='m' - ) - else: - raise ValueError(f'Unkonwn chopper mode {da.coords["mode"].value}.') - da.coords['x'].unit = 'm' da.coords['y'].unit = 'm' da.coords['t'].unit = 's' From ae279d46bfbfdd85ff6a4a657f179511aea91ab6 Mon Sep 17 00:00:00 2001 From: jokasimr Date: Wed, 5 Nov 2025 10:50:06 +0100 Subject: [PATCH 09/13] Update src/ess/beer/conversions.py Co-authored-by: Jan-Lukas Wynen --- src/ess/beer/conversions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index 1eac3b4f..c225665f 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -119,7 +119,7 @@ def time_of_arrival( event_time_offset: sc.Variable, tc: sc.Variable, ): - '''Does frame unwrapping based on the cutoff time `tc` and `event_time_ffset`. + '''Does frame unwrapping based on the cutoff time ``tc`` and ``event_time_offset``. Events before `tc` are assumed to come from the previous pulse.''' _eto = event_time_offset T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit) From 96467a76326c2501341851d58c82485149f06b1c Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 5 Nov 2025 10:55:35 +0100 Subject: [PATCH 10/13] fix: unnecessary mod --- src/ess/beer/conversions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index 1eac3b4f..00dc5d63 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -124,7 +124,7 @@ def time_of_arrival( _eto = event_time_offset T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit) tc = tc.to(unit=_eto.unit) - return sc.where(_eto >= tc % T, _eto, _eto + T) + return sc.where(_eto >= tc, _eto, _eto + T) def _tof_from_dhkl( From 36544a5a8d9566661c68288d622a12dff4b9688b Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Wed, 5 Nov 2025 13:32:03 +0100 Subject: [PATCH 11/13] docs --- src/ess/beer/conversions.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index 360e6fd8..b730ac65 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -119,8 +119,9 @@ def time_of_arrival( event_time_offset: sc.Variable, tc: sc.Variable, ): - '''Does frame unwrapping based on the cutoff time ``tc`` and ``event_time_offset``. - Events before `tc` are assumed to come from the previous pulse.''' + '''Does frame unwrapping for pulse shaping chopper modes. + + Events before the "cutoff time" `tc` are assumed to come from the previous pulse.''' _eto = event_time_offset T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit) tc = tc.to(unit=_eto.unit) @@ -166,6 +167,9 @@ def tof_from_known_dhkl_graph( time0: WavelengthDefinitionChopperDelay, dhkl_list: DHKLList, ) -> TofCoordTransformGraph: + '''Graph computing ``tof`` in modulation chopper modes using + list of peak positions.''' + def _compute_coarse_dspacing( time_of_arrival: sc.Variable, theta: sc.Variable, @@ -220,6 +224,7 @@ def _tof_from_t0( def tof_from_t0_estimate() -> TofCoordTransformGraph: + '''Graph for computing ``tof`` in pulse shaping chopper modes.''' return { 't0': t0_estimate, 'tof': _tof_from_t0, @@ -230,6 +235,7 @@ def tof_from_t0_estimate() -> TofCoordTransformGraph: def compute_tof( da: RawDetector[RunType], graph: TofCoordTransformGraph ) -> TofDetector[RunType]: + '''Uses the transformation graph to compute ``tof``.''' return da.transform_coords(('tof',), graph=graph) From d94cc3779e3edea8f24f513d8f0049c75c4dd347 Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Thu, 6 Nov 2025 09:31:58 +0100 Subject: [PATCH 12/13] refactor: change name to signal it makes a graph --- src/ess/beer/conversions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index b730ac65..535160e5 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -223,7 +223,7 @@ def _tof_from_t0( return time_of_arrival - t0 -def tof_from_t0_estimate() -> TofCoordTransformGraph: +def tof_from_t0_estimate_graph() -> TofCoordTransformGraph: '''Graph for computing ``tof`` in pulse shaping chopper modes.''' return { 't0': t0_estimate, @@ -244,7 +244,7 @@ def compute_tof( compute_tof, ) convert_pulse_shaping = ( - tof_from_t0_estimate, + tof_from_t0_estimate_graph, compute_tof, ) providers = (compute_tof_in_each_cluster,) From f68032a59afa850d99bba7b4182e610a433569cb Mon Sep 17 00:00:00 2001 From: Johannes Kasimir Date: Thu, 6 Nov 2025 09:42:10 +0100 Subject: [PATCH 13/13] docs: change quotes to double --- src/ess/beer/conversions.py | 30 +++++++++++++++--------------- src/ess/beer/types.py | 10 +++++----- src/ess/beer/workflow.py | 10 +++++----- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/ess/beer/conversions.py b/src/ess/beer/conversions.py index 535160e5..0475b101 100644 --- a/src/ess/beer/conversions.py +++ b/src/ess/beer/conversions.py @@ -18,7 +18,7 @@ def compute_tof_in_each_cluster( da: StreakClusteredData[RunType], mod_period: ModulationPeriod, ) -> TofDetector[RunType]: - '''Fits a line through each cluster, the intercept of the line is t0. + """Fits a line through each cluster, the intercept of the line is t0. The line is fitted using linear regression with an outlier removal procedure. The algorithm is: @@ -30,7 +30,7 @@ def compute_tof_in_each_cluster( of the points in the cluster, and probably should belong to another cluster or are part of the background. 3. Go back to 1) and iterate until convergence. A few iterations should be enough. - ''' + """ if isinstance(da, sc.DataGroup): return sc.DataGroup( {k: compute_tof_in_each_cluster(v, mod_period) for k, v in da.items()} @@ -60,10 +60,10 @@ def compute_tof_in_each_cluster( def _linear_regression_by_bin( x: sc.Variable, y: sc.Variable, w: sc.Variable ) -> tuple[sc.Variable, sc.Variable]: - '''Performs a weighted linear regression of the points + """Performs a weighted linear regression of the points in the binned variables ``x`` and ``y`` weighted by ``w``. Returns ``b1`` and ``b0`` such that ``y = b1 * x + b0``. - ''' + """ w = sc.values(w) tot_w = w.bins.sum() @@ -119,9 +119,9 @@ def time_of_arrival( event_time_offset: sc.Variable, tc: sc.Variable, ): - '''Does frame unwrapping for pulse shaping chopper modes. + """Does frame unwrapping for pulse shaping chopper modes. - Events before the "cutoff time" `tc` are assumed to come from the previous pulse.''' + Events before the "cutoff time" `tc` are assumed to come from the previous pulse.""" _eto = event_time_offset T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit) tc = tc.to(unit=_eto.unit) @@ -136,7 +136,7 @@ def _tof_from_dhkl( mod_period: sc.Variable, time0: sc.Variable, ) -> sc.Variable: - '''Computes tof for BEER given the dhkl peak that the event belongs to''' + """Computes tof for BEER given the dhkl peak that the event belongs to""" # Source: https://www2.mcstas.org/download/components/3.4/contrib/NPI_tof_dhkl_detector.comp # tref = 2 * d_hkl * sin(theta) / hm * Ltotal # tc = time_of_arrival - time0 - tref @@ -167,8 +167,8 @@ def tof_from_known_dhkl_graph( time0: WavelengthDefinitionChopperDelay, dhkl_list: DHKLList, ) -> TofCoordTransformGraph: - '''Graph computing ``tof`` in modulation chopper modes using - list of peak positions.''' + """Graph computing ``tof`` in modulation chopper modes using + list of peak positions.""" def _compute_coarse_dspacing( time_of_arrival: sc.Variable, @@ -176,12 +176,12 @@ def _compute_coarse_dspacing( pulse_length: sc.Variable, L0: sc.Variable, ): - '''To capture dhkl_list, otherwise it causes an error when + """To capture dhkl_list, otherwise it causes an error when ``.transform_coords`` is called unless it is called with ``keep_indermediates=False``. The error happens because data arrays do not allow coordinates with dimensions not present on the data. - ''' + """ return _compute_d( time_of_arrival=time_of_arrival, theta=theta, @@ -206,7 +206,7 @@ def t0_estimate( L0: sc.Variable, Ltotal: sc.Variable, ) -> sc.Variable: - '''Estimates the time-at-chopper by assuming the wavelength.''' + """Estimates the time-at-chopper by assuming the wavelength.""" return ( sc.constants.m_n / sc.constants.h @@ -219,12 +219,12 @@ def _tof_from_t0( time_of_arrival: sc.Variable, t0: sc.Variable, ) -> sc.Variable: - '''Computes time-of-flight by subtracting a start time.''' + """Computes time-of-flight by subtracting a start time.""" return time_of_arrival - t0 def tof_from_t0_estimate_graph() -> TofCoordTransformGraph: - '''Graph for computing ``tof`` in pulse shaping chopper modes.''' + """Graph for computing ``tof`` in pulse shaping chopper modes.""" return { 't0': t0_estimate, 'tof': _tof_from_t0, @@ -235,7 +235,7 @@ def tof_from_t0_estimate_graph() -> TofCoordTransformGraph: def compute_tof( da: RawDetector[RunType], graph: TofCoordTransformGraph ) -> TofDetector[RunType]: - '''Uses the transformation graph to compute ``tof``.''' + """Uses the transformation graph to compute ``tof``.""" return da.transform_coords(('tof',), graph=graph) diff --git a/src/ess/beer/types.py b/src/ess/beer/types.py index dd90f586..d0caafb1 100644 --- a/src/ess/beer/types.py +++ b/src/ess/beer/types.py @@ -31,17 +31,17 @@ class StreakClusteredData(sciline.Scope[RunType, sc.DataArray], sc.DataArray): TofCoordTransformGraph = NewType("TofCoordTransformGraph", dict) PulseLength = NewType('PulseLength', sc.Variable) -'''Length of the neutron source pulse in time.''' +"""Length of the neutron source pulse in time.""" ModulationPeriod = NewType('ModulationPeriod', sc.Variable) -'''The effective period of the modulating chopper: +"""The effective period of the modulating chopper: ``1 / (K * F)`` where ``K`` is the number of chopper openings and -``F`` is the chopper frequency.''' +``F`` is the chopper frequency.""" WavelengthDefinitionChopperDelay = NewType( 'WavelengthDefinitionChopperDelay', sc.Variable ) -'''Wavelength definition chopper time delay relative to source pulse.''' +"""Wavelength definition chopper time delay relative to source pulse.""" DHKLList = NewType('DHKLList', sc.Variable) -'''List of peak position estimates.''' +"""List of peak position estimates.""" diff --git a/src/ess/beer/workflow.py b/src/ess/beer/workflow.py index d96ed2e5..6545267b 100644 --- a/src/ess/beer/workflow.py +++ b/src/ess/beer/workflow.py @@ -24,8 +24,8 @@ def BeerModMcStasWorkflow(): - '''Workflow to process BEER (modulation regime) McStas files without a list - of estimated peak positions.''' + """Workflow to process BEER (modulation regime) McStas files without a list + of estimated peak positions.""" return sl.Pipeline( (*mcstas_providers, *clustering_providers, *conversion_providers), params=default_parameters, @@ -34,8 +34,8 @@ def BeerModMcStasWorkflow(): def BeerModMcStasWorkflowKnownPeaks(): - '''Workflow to process BEER (modulation regime) McStas files using a list - of estimated peak positions.''' + """Workflow to process BEER (modulation regime) McStas files using a list + of estimated peak positions.""" return sl.Pipeline( (*mcstas_providers, *convert_from_known_peaks_providers), params=default_parameters, @@ -44,7 +44,7 @@ def BeerModMcStasWorkflowKnownPeaks(): def BeerMcStasWorkflowPulseShaping(): - '''Workflow to process BEER (pulse shaping modes) McStas files''' + """Workflow to process BEER (pulse shaping modes) McStas files""" return sl.Pipeline( (*mcstas_providers, *convert_pulse_shaping), params=default_parameters,