Skip to content

Commit df8db90

Browse files
authored
Merge pull request #166 from scipp/isolate-mcstas
Isolate mcstas specific implementation.
2 parents bf3edaa + 88c5963 commit df8db90

23 files changed

+1056
-469
lines changed

docs/api-reference/index.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@
3131
3232
data
3333
mcstas
34-
reduction
35-
nexus
36-
streaming
3734
types
3835
mtz_io
3936
scaling

docs/user-guide/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
maxdepth: 1
66
---
77
8-
workflow
9-
workflow_chunk
8+
mcstas_workflow
9+
mcstas_workflow_chunk
1010
scaling_workflow
1111
installation
1212
```
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"# Workflow\n",
7+
"# McStas Workflow\n",
88
"In this example, we will use McStas 3 simulation file.\n",
99
"\n",
1010
"## Build Pipeline (Collect Parameters and Providers)\n",
@@ -19,14 +19,14 @@
1919
"metadata": {},
2020
"outputs": [],
2121
"source": [
22-
"from ess.nmx.mcstas import McStasWorkflow\n",
22+
"from ess.nmx.mcstas import NMXMcStasWorkflow\n",
2323
"from ess.nmx.data import get_small_mcstas\n",
2424
"\n",
25-
"from ess.nmx.types import *\n",
26-
"from ess.nmx.reduction import merge_panels\n",
27-
"from ess.nmx.nexus import export_as_nexus\n",
25+
"from ess.nmx.mcstas.types import *\n",
26+
"from ess.nmx.mcstas.reduction import merge_panels\n",
27+
"from ess.nmx.mcstas.nexus import export_as_nexus\n",
2828
"\n",
29-
"wf = McStasWorkflow()\n",
29+
"wf = NMXMcStasWorkflow()\n",
3030
"# Replace with the path to your own file\n",
3131
"wf[FilePath] = get_small_mcstas()\n",
3232
"wf[MaximumCounts] = 10000\n",

docs/user-guide/workflow_chunk.ipynb renamed to docs/user-guide/mcstas_workflow_chunk.ipynb

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"# Workflow - Chunk by Chunk\n",
7+
"# McStas Workflow - Chunk by Chunk\n",
88
"In this example, we will process McStas events chunk by chunk, panel by panel."
99
]
1010
},
@@ -21,11 +21,11 @@
2121
"metadata": {},
2222
"outputs": [],
2323
"source": [
24-
"from ess.nmx.mcstas import McStasWorkflow\n",
24+
"from ess.nmx.mcstas import NMXMcStasWorkflow\n",
2525
"from ess.nmx.data import get_small_mcstas\n",
26-
"from ess.nmx.types import *\n",
26+
"from ess.nmx.mcstas.types import *\n",
2727
"\n",
28-
"wf = McStasWorkflow()\n",
28+
"wf = NMXMcStasWorkflow()\n",
2929
"# Replace with the path to your own file\n",
3030
"wf[FilePath] = get_small_mcstas()\n",
3131
"wf[MaximumCounts] = 10_000\n",
@@ -92,12 +92,11 @@
9292
"metadata": {},
9393
"outputs": [],
9494
"source": [
95-
"from ess.nmx.types import DetectorName\n",
9695
"from ess.nmx.mcstas.load import (\n",
9796
" raw_event_data_chunk_generator,\n",
9897
" mcstas_weight_to_probability_scalefactor,\n",
9998
")\n",
100-
"from ess.nmx.streaming import calculate_number_of_chunks\n",
99+
"from ess.nmx.mcstas.streaming import calculate_number_of_chunks\n",
101100
"from ipywidgets import IntProgress\n",
102101
"\n",
103102
"CHUNK_SIZE = 10 # Number of event rows to process at once\n",
@@ -194,7 +193,7 @@
194193
"metadata": {},
195194
"outputs": [],
196195
"source": [
197-
"from ess.nmx.nexus import NXLauetofWriter\n",
196+
"from ess.nmx.mcstas.nexus import NXLauetofWriter\n",
198197
"\n",
199198
"\n",
200199
"def temp_generator(file_path, detector_name):\n",

src/ess/nmx/__init__.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@
1111

1212
del importlib
1313

14-
from .reduction import NMXReducedDataGroup
15-
from .types import MaximumCounts, NMXRawEventCountsDataGroup
14+
from .mcstas import NMXMcStasWorkflow
1615

17-
default_parameters = {MaximumCounts: 10000}
18-
19-
del MaximumCounts
20-
21-
__all__ = [
22-
"NMXRawEventCountsDataGroup",
23-
"NMXReducedDataGroup",
24-
"default_parameters",
25-
]
16+
__all__ = ["NMXMcStasWorkflow"]

src/ess/nmx/_executable_helper.py

Lines changed: 115 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -140,21 +140,6 @@ def add_args_from_pydantic_model(
140140
return parser
141141

142142

143-
T = TypeVar('T', bound=BaseModel)
144-
145-
146-
def from_args(cls: type[T], args: argparse.Namespace) -> T:
147-
"""Create an instance of the pydantic model from the argparse namespace.
148-
149-
It ignores any extra arguments in the namespace that are not part of the model.
150-
"""
151-
kwargs = {
152-
field_name: _retrieve_field_value(field_name, field_info, args)
153-
for field_name, field_info in cls.model_fields.items()
154-
}
155-
return cls(**kwargs)
156-
157-
158143
class InputConfig(BaseModel):
159144
# Add title of the basemodel
160145
model_config = {"title": "Input Configuration"}
@@ -201,42 +186,66 @@ class InputConfig(BaseModel):
201186
)
202187

203188

204-
class TOAUnit(enum.StrEnum):
189+
class TimeBinUnit(enum.StrEnum):
205190
ms = 'ms'
206191
us = 'us'
207192
ns = 'ns'
208193

209194

195+
class TimeBinCoordinate(enum.StrEnum):
196+
event_time_offset = 'event_time_offset'
197+
time_of_flight = 'time_of_flight'
198+
199+
210200
class WorkflowConfig(BaseModel):
211201
# Add title of the basemodel
212202
model_config = {"title": "Workflow Configuration"}
203+
time_bin_coordinate: TimeBinCoordinate = Field(
204+
title="Time Bin Coordinate",
205+
description="Coordinate to bin the time data.",
206+
default=TimeBinCoordinate.event_time_offset,
207+
)
213208
nbins: int = Field(
214-
title="Number of TOF Bins",
215-
description="Number of TOF bins",
209+
title="Number of Time Bins",
210+
description="Number of Time bins",
216211
default=50,
217212
)
218-
min_toa: int = Field(
219-
title="Minimum Time of Arrival",
220-
description="Minimum time of arrival (TOA) in [toa_unit].",
221-
default=0,
213+
min_time_bin: int | None = Field(
214+
title="Minimum Time Bin",
215+
description="Minimum time edge of [time_bin_coordinate] in [time_bin_unit].",
216+
default=None,
222217
)
223-
max_toa: int = Field(
224-
title="Maximum Time of Arrival",
225-
description="Maximum time of arrival (TOA) in [toa_unit].",
226-
default=int((1 / 14) * 1_000),
218+
max_time_bin: int | None = Field(
219+
title="Maximum Time Bin",
220+
description="Maximum time edge of [time_bin_coordinate] in [time_bin_unit].",
221+
default=None,
227222
)
228-
toa_unit: TOAUnit = Field(
229-
title="Unit of TOA",
230-
description="Unit of TOA.",
231-
default=TOAUnit.ms,
223+
time_bin_unit: TimeBinUnit = Field(
224+
title="Unit of Time Bins",
225+
description="Unit of time bins.",
226+
default=TimeBinUnit.ms,
232227
)
233-
fast_axis: Literal['x', 'y'] | None = Field(
234-
title="Fast Axis",
235-
description="Specify the fast axis of the detector. "
236-
"If None, it will be determined "
237-
"automatically based on the pixel offsets.",
228+
tof_lookup_table_file_path: str | None = Field(
229+
title="TOF Lookup Table File Path",
230+
description="Path to the TOF lookup table file. "
231+
"If None, the lookup table will be computed on-the-fly.",
238232
default=None,
239233
)
234+
tof_simulation_min_wavelength: float = Field(
235+
title="TOF Simulation Minimum Wavelength",
236+
description="Minimum wavelength for TOF simulation in Angstrom.",
237+
default=1.8,
238+
)
239+
tof_simulation_max_wavelength: float = Field(
240+
title="TOF Simulation Maximum Wavelength",
241+
description="Maximum wavelength for TOF simulation in Angstrom.",
242+
default=3.6,
243+
)
244+
tof_simulation_seed: int = Field(
245+
title="TOF Simulation Seed",
246+
description="Random seed for TOF simulation.",
247+
default=42, # No reason.
248+
)
240249

241250

242251
class OutputConfig(BaseModel):
@@ -265,64 +274,82 @@ class ReductionConfig(BaseModel):
265274
"""Container for all reduction configurations."""
266275

267276
inputs: InputConfig
268-
workflow: WorkflowConfig
269-
output: OutputConfig
270-
271-
@classmethod
272-
def build_argument_parser(cls) -> argparse.ArgumentParser:
273-
parser = argparse.ArgumentParser(
274-
description="Command line arguments for the ESS NMX reduction. "
275-
"It assumes 14 Hz pulse speed."
276-
)
277-
parser = add_args_from_pydantic_model(model_cls=InputConfig, parser=parser)
278-
parser = add_args_from_pydantic_model(model_cls=WorkflowConfig, parser=parser)
279-
parser = add_args_from_pydantic_model(model_cls=OutputConfig, parser=parser)
280-
return parser
281-
282-
@classmethod
283-
def from_args(cls, args: argparse.Namespace) -> "ReductionConfig":
284-
return cls(
285-
inputs=from_args(InputConfig, args),
286-
workflow=from_args(WorkflowConfig, args),
287-
output=from_args(OutputConfig, args),
288-
)
277+
workflow: WorkflowConfig = Field(default_factory=WorkflowConfig)
278+
output: OutputConfig = Field(default_factory=OutputConfig)
289279

290280
@property
291281
def _children(self) -> list[BaseModel]:
292282
return [self.inputs, self.workflow, self.output]
293283

294-
def to_command_arguments(self, one_line: bool = True) -> list[str] | str:
295-
"""Convert the config to a list of command line arguments.
296-
297-
Parameters
298-
----------
299-
one_line:
300-
If True, return a single string with all arguments joined by spaces.
301-
If False, return a list of argument strings.
302-
303-
"""
304-
args = {}
305-
for instance in self._children:
306-
args.update(instance.model_dump(mode='python'))
307-
args = {f"--{k.replace('_', '-')}": v for k, v in args.items()}
308-
309-
arg_list = []
310-
for k, v in args.items():
311-
if not isinstance(v, bool):
312-
arg_list.append(k)
313-
if isinstance(v, list):
314-
arg_list.extend(str(item) for item in v)
315-
elif isinstance(v, enum.StrEnum):
316-
arg_list.append(v.value)
317-
else:
318-
arg_list.append(str(v))
319-
elif v is True:
320-
arg_list.append(k)
321-
322-
if one_line:
323-
return ' '.join(arg_list)
324-
else:
325-
return arg_list
284+
285+
T = TypeVar('T', bound=BaseModel)
286+
287+
288+
def from_args(cls: type[T], args: argparse.Namespace) -> T:
289+
"""Create an instance of the pydantic model from the argparse namespace.
290+
291+
It ignores any extra arguments in the namespace that are not part of the model.
292+
"""
293+
kwargs = {
294+
field_name: _retrieve_field_value(field_name, field_info, args)
295+
for field_name, field_info in cls.model_fields.items()
296+
}
297+
return cls(**kwargs)
298+
299+
300+
def build_reduction_argument_parser() -> argparse.ArgumentParser:
301+
parser = argparse.ArgumentParser(
302+
description="Command line arguments for the ESS NMX reduction. "
303+
"It assumes 14 Hz pulse speed."
304+
)
305+
parser = add_args_from_pydantic_model(model_cls=InputConfig, parser=parser)
306+
parser = add_args_from_pydantic_model(model_cls=WorkflowConfig, parser=parser)
307+
parser = add_args_from_pydantic_model(model_cls=OutputConfig, parser=parser)
308+
return parser
309+
310+
311+
def reduction_config_from_args(args: argparse.Namespace) -> ReductionConfig:
312+
return ReductionConfig(
313+
inputs=from_args(InputConfig, args),
314+
workflow=from_args(WorkflowConfig, args),
315+
output=from_args(OutputConfig, args),
316+
)
317+
318+
319+
def to_command_arguments(
320+
config: ReductionConfig, one_line: bool = True
321+
) -> list[str] | str:
322+
"""Convert the config to a list of command line arguments.
323+
324+
Parameters
325+
----------
326+
one_line:
327+
If True, return a single string with all arguments joined by spaces.
328+
If False, return a list of argument strings.
329+
330+
"""
331+
args = {}
332+
for instance in config._children:
333+
args.update(instance.model_dump(mode='python'))
334+
args = {f"--{k.replace('_', '-')}": v for k, v in args.items() if v is not None}
335+
336+
arg_list = []
337+
for k, v in args.items():
338+
if not isinstance(v, bool):
339+
arg_list.append(k)
340+
if isinstance(v, list):
341+
arg_list.extend(str(item) for item in v)
342+
elif isinstance(v, enum.StrEnum):
343+
arg_list.append(v.value)
344+
else:
345+
arg_list.append(str(v))
346+
elif v is True:
347+
arg_list.append(k)
348+
349+
if one_line:
350+
return ' '.join(arg_list)
351+
else:
352+
return arg_list
326353

327354

328355
def build_logger(args: argparse.Namespace | OutputConfig) -> logging.Logger:

0 commit comments

Comments
 (0)