Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
types
mtz_io
scaling
configurations

```
1 change: 1 addition & 0 deletions docs/user-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
maxdepth: 1
---

workflow
mcstas_workflow
mcstas_workflow_chunk
scaling_workflow
Expand Down
171 changes: 171 additions & 0 deletions docs/user-guide/workflow.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# NMX Reduction Workflow\n",
"\n",
"> NMX does not expect users to use python interface directly.<br>\n",
"This documentation is mostly for istrument data scientists or instrument scientists.<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## TL;DR"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ess.nmx.executables import reduction\n",
"from ess.nmx.data import get_small_nmx_nexus\n",
"from ess.nmx.configurations import (\n",
" ReductionConfig,\n",
" OutputConfig,\n",
" InputConfig,\n",
" WorkflowConfig,\n",
" TimeBinCoordinate,\n",
")\n",
"\n",
"# Build Configuration\n",
"config = ReductionConfig(\n",
" inputs=InputConfig(\n",
" input_file=[get_small_nmx_nexus().as_posix()],\n",
" detector_ids=[0, 1, 2], # Detector index to be reduced in alphabetical order.\n",
" ),\n",
" output=OutputConfig(\n",
" output_file=\"scipp_output.hdf\", skip_file_output=False, overwrite=True\n",
" ),\n",
" workflow=WorkflowConfig(\n",
" time_bin_coordinate=TimeBinCoordinate.time_of_flight,\n",
" nbins=10,\n",
" tof_simulation_num_neutrons=1_000_000,\n",
" tof_simulation_min_wavelength=1.8,\n",
" tof_simulation_max_wavelength=3.6,\n",
" tof_simulation_seed=42,\n",
" ),\n",
")\n",
"\n",
"# Run Reduction\n",
"reduction(config=config, display=display)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configuration\n",
"\n",
"`essnmx` provide command line data reduction tool for the reduction between `nexus` and `dials`.<br>\n",
"The `essnmx-reduce` interface will reduce `nexus` file <br>\n",
"and save the results into `NXlauetof`(not exactly but very close) format for `dials`.<br>\n",
"\n",
"Argument options could be exhaustive therefore we wrapped them into a nested pydantic model.<br>\n",
"Here is a python API you can use to build the configuration and turn it into a command line arguments.\n",
"\n",
"**Configuration object is pydantic model so it strictly check the type of the arguments.**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ess.nmx.configurations import (\n",
" ReductionConfig,\n",
" OutputConfig,\n",
" InputConfig,\n",
" WorkflowConfig,\n",
" TimeBinCoordinate,\n",
" to_command_arguments,\n",
")\n",
"\n",
"config = ReductionConfig(\n",
" inputs=InputConfig(\n",
" input_file=[\"PATH_TO_THE_NEXUS_FILE.hdf\"],\n",
" detector_ids=[0, 1, 2], # Detector index to be reduced in alphabetical order.\n",
" ),\n",
" output=OutputConfig(output_file=\"scipp_output.hdf\", skip_file_output=True),\n",
" workflow=WorkflowConfig(\n",
" time_bin_coordinate=TimeBinCoordinate.time_of_flight,\n",
" nbins=10,\n",
" tof_simulation_num_neutrons=1_000_000,\n",
" tof_simulation_min_wavelength=1.8,\n",
" tof_simulation_max_wavelength=3.6,\n",
" tof_simulation_seed=42,\n",
" ),\n",
")\n",
"\n",
"display(config)\n",
"print(to_command_arguments(config=config, one_line=True))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reduce Nexus File(s)\n",
"\n",
"`OutputConfig` has an option called `skip_file_output` if you want to reduce the file and use it only on the memory.<br>\n",
"Then you can use `save_results` function to explicitly save the results."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ess.nmx.executables import reduction\n",
"from ess.nmx.data import get_small_nmx_nexus\n",
"\n",
"config = ReductionConfig(\n",
" inputs=InputConfig(input_file=[get_small_nmx_nexus().as_posix()]),\n",
" output=OutputConfig(skip_file_output=True),\n",
")\n",
"results = reduction(config=config, display=display)\n",
"results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ess.nmx.executables import save_results\n",
"\n",
"output_config = OutputConfig(output_file=\"scipp_output.hdf\", overwrite=True)\n",
"save_results(results=results, output_config=output_config)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "nmx-dev-313",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
36 changes: 0 additions & 36 deletions src/ess/nmx/_executable_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,42 +174,6 @@ def reduction_config_from_args(args: argparse.Namespace) -> ReductionConfig:
)


def to_command_arguments(
config: ReductionConfig, one_line: bool = True
) -> list[str] | str:
"""Convert the config to a list of command line arguments.

Parameters
----------
one_line:
If True, return a single string with all arguments joined by spaces.
If False, return a list of argument strings.

"""
args = {}
for instance in config._children:
args.update(instance.model_dump(mode='python'))
args = {f"--{k.replace('_', '-')}": v for k, v in args.items() if v is not None}

arg_list = []
for k, v in args.items():
if not isinstance(v, bool):
arg_list.append(k)
if isinstance(v, list):
arg_list.extend(str(item) for item in v)
elif isinstance(v, enum.StrEnum):
arg_list.append(v.value)
else:
arg_list.append(str(v))
elif v is True:
arg_list.append(k)

if one_line:
return ' '.join(arg_list)
else:
return arg_list


def build_logger(args: argparse.Namespace | OutputConfig) -> logging.Logger:
logger = logging.getLogger(__name__)
if args.verbose:
Expand Down
55 changes: 54 additions & 1 deletion src/ess/nmx/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,22 @@ class OutputConfig(BaseModel):
default=False,
)
# File output
skip_file_output: bool = Field(
title="Skip File Output",
description="If True, the output file will not be written.",
default=False,
)
output_file: str = Field(
title="Output File",
description="Path to the output file.",
description="Path to the output file. "
"It will be overwritten if ``overwrite`` is True.",
default="scipp_output.h5",
)
overwrite: bool = Field(
title="Overwrite Output File",
description="If True, overwrite the output file if ``output_file`` exists.",
default=False,
)
compression: Compression = Field(
title="Compression",
description="Compress option of reduced output file.",
Expand All @@ -152,3 +163,45 @@ class ReductionConfig(BaseModel):
@property
def _children(self) -> list[BaseModel]:
return [self.inputs, self.workflow, self.output]


def to_command_arguments(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this interface to the configurations module so that users can use it.

*, config: ReductionConfig, one_line: bool = True, separator: str = '\\\n'
) -> list[str] | str:
"""Convert the config to a list of command line arguments.

Parameters
----------
one_line:
If True, return a single string with all arguments joined by spaces.
If False, return a list of argument strings.

"""
args = {}
for instance in config._children:
args.update(instance.model_dump(mode='python'))
args = {f"--{k.replace('_', '-')}": v for k, v in args.items() if v is not None}

arg_list = []
for k, v in args.items():
if not isinstance(v, bool):
arg_list.append(k)
if isinstance(v, list):
arg_list.extend(str(item) for item in v)
elif isinstance(v, enum.StrEnum):
arg_list.append(v.value)
else:
arg_list.append(str(v))
elif v is True:
arg_list.append(k)

if one_line:
# Default separator is backslash + newline for better readability
# Users can directly copy-paste the output in a terminal or a script.
return (
(separator + '--')
.join(" ".join(arg_list).split('--'))
.removeprefix(separator)
)
else:
return arg_list
Loading
Loading