essreduce 25.5.0__tar.gz → 25.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. {essreduce-25.5.0/src/essreduce.egg-info → essreduce-25.5.1}/PKG-INFO +1 -1
  2. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/tof/dream.ipynb +3 -1
  3. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/tof/frame-unwrapping.ipynb +6 -2
  4. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/tof/wfm.ipynb +4 -4
  5. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/nexus/types.py +0 -7
  6. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/nexus/workflow.py +0 -12
  7. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/__init__.py +4 -1
  8. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/eto_to_tof.py +1 -2
  9. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/simulation.py +25 -1
  10. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/types.py +10 -0
  11. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/workflow.py +36 -3
  12. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/workflow.py +4 -2
  13. {essreduce-25.5.0 → essreduce-25.5.1/src/essreduce.egg-info}/PKG-INFO +1 -1
  14. {essreduce-25.5.0 → essreduce-25.5.1}/src/essreduce.egg-info/SOURCES.txt +2 -1
  15. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/nexus_loader_test.py +0 -41
  16. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/workflow_test.py +0 -19
  17. essreduce-25.5.1/tests/time_of_flight/workflow_test.py +136 -0
  18. {essreduce-25.5.0 → essreduce-25.5.1}/.copier-answers.ess.yml +0 -0
  19. {essreduce-25.5.0 → essreduce-25.5.1}/.copier-answers.yml +0 -0
  20. {essreduce-25.5.0 → essreduce-25.5.1}/.github/ISSUE_TEMPLATE/blank.md +0 -0
  21. {essreduce-25.5.0 → essreduce-25.5.1}/.github/ISSUE_TEMPLATE/high-level-requirement.yml +0 -0
  22. {essreduce-25.5.0 → essreduce-25.5.1}/.github/dependabot.yml +0 -0
  23. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/ci.yml +0 -0
  24. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/docs.yml +0 -0
  25. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/nightly_at_main.yml +0 -0
  26. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/nightly_at_release.yml +0 -0
  27. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/python-version-ci +0 -0
  28. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/release.yml +0 -0
  29. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/test.yml +0 -0
  30. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/unpinned.yml +0 -0
  31. {essreduce-25.5.0 → essreduce-25.5.1}/.github/workflows/weekly_windows_macos.yml +0 -0
  32. {essreduce-25.5.0 → essreduce-25.5.1}/.gitignore +0 -0
  33. {essreduce-25.5.0 → essreduce-25.5.1}/.pre-commit-config.yaml +0 -0
  34. {essreduce-25.5.0 → essreduce-25.5.1}/.python-version +0 -0
  35. {essreduce-25.5.0 → essreduce-25.5.1}/CODE_OF_CONDUCT.md +0 -0
  36. {essreduce-25.5.0 → essreduce-25.5.1}/CONTRIBUTING.md +0 -0
  37. {essreduce-25.5.0 → essreduce-25.5.1}/LICENSE +0 -0
  38. {essreduce-25.5.0 → essreduce-25.5.1}/MANIFEST.in +0 -0
  39. {essreduce-25.5.0 → essreduce-25.5.1}/README.md +0 -0
  40. {essreduce-25.5.0 → essreduce-25.5.1}/conda/meta.yaml +0 -0
  41. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_static/anaconda-icon.js +0 -0
  42. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_static/favicon.svg +0 -0
  43. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_static/logo-dark.svg +0 -0
  44. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_static/logo.svg +0 -0
  45. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_templates/class-template.rst +0 -0
  46. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_templates/doc_version.html +0 -0
  47. {essreduce-25.5.0 → essreduce-25.5.1}/docs/_templates/module-template.rst +0 -0
  48. {essreduce-25.5.0 → essreduce-25.5.1}/docs/about/index.md +0 -0
  49. {essreduce-25.5.0 → essreduce-25.5.1}/docs/api-reference/index.md +0 -0
  50. {essreduce-25.5.0 → essreduce-25.5.1}/docs/conf.py +0 -0
  51. {essreduce-25.5.0 → essreduce-25.5.1}/docs/developer/coding-conventions.md +0 -0
  52. {essreduce-25.5.0 → essreduce-25.5.1}/docs/developer/dependency-management.md +0 -0
  53. {essreduce-25.5.0 → essreduce-25.5.1}/docs/developer/getting-started.md +0 -0
  54. {essreduce-25.5.0 → essreduce-25.5.1}/docs/developer/gui.ipynb +0 -0
  55. {essreduce-25.5.0 → essreduce-25.5.1}/docs/developer/index.md +0 -0
  56. {essreduce-25.5.0 → essreduce-25.5.1}/docs/index.md +0 -0
  57. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/index.md +0 -0
  58. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/installation.md +0 -0
  59. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/reduction-workflow-guidelines.md +0 -0
  60. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/tof/index.md +0 -0
  61. {essreduce-25.5.0 → essreduce-25.5.1}/docs/user-guide/widget.md +0 -0
  62. {essreduce-25.5.0 → essreduce-25.5.1}/pyproject.toml +0 -0
  63. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/base.in +0 -0
  64. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/base.txt +0 -0
  65. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/basetest.in +0 -0
  66. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/basetest.txt +0 -0
  67. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/ci.in +0 -0
  68. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/ci.txt +0 -0
  69. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/dev.in +0 -0
  70. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/dev.txt +0 -0
  71. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/docs.in +0 -0
  72. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/docs.txt +0 -0
  73. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/make_base.py +0 -0
  74. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/mypy.in +0 -0
  75. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/mypy.txt +0 -0
  76. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/nightly.in +0 -0
  77. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/nightly.txt +0 -0
  78. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/static.in +0 -0
  79. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/static.txt +0 -0
  80. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/test.in +0 -0
  81. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/test.txt +0 -0
  82. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/wheels.in +0 -0
  83. {essreduce-25.5.0 → essreduce-25.5.1}/requirements/wheels.txt +0 -0
  84. {essreduce-25.5.0 → essreduce-25.5.1}/resources/logo.svg +0 -0
  85. {essreduce-25.5.0 → essreduce-25.5.1}/setup.cfg +0 -0
  86. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/__init__.py +0 -0
  87. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/data.py +0 -0
  88. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/live/__init__.py +0 -0
  89. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/live/raw.py +0 -0
  90. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/live/roi.py +0 -0
  91. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/live/workflow.py +0 -0
  92. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/logging.py +0 -0
  93. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/nexus/__init__.py +0 -0
  94. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/nexus/_nexus_loader.py +0 -0
  95. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/nexus/json_generator.py +0 -0
  96. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/nexus/json_nexus.py +0 -0
  97. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/parameter.py +0 -0
  98. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/py.typed +0 -0
  99. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/scripts/grow_nexus.py +0 -0
  100. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/streaming.py +0 -0
  101. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/fakes.py +0 -0
  102. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/interpolator_numba.py +0 -0
  103. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/interpolator_scipy.py +0 -0
  104. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/time_of_flight/to_events.py +0 -0
  105. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/ui.py +0 -0
  106. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/uncertainty.py +0 -0
  107. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/utils.py +0 -0
  108. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/__init__.py +0 -0
  109. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_base.py +0 -0
  110. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_binedges_widget.py +0 -0
  111. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_bounds_widget.py +0 -0
  112. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_config.py +0 -0
  113. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_filename_widget.py +0 -0
  114. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_linspace_widget.py +0 -0
  115. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_optional_widget.py +0 -0
  116. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_spinner.py +0 -0
  117. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_string_widget.py +0 -0
  118. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_switchable_widget.py +0 -0
  119. {essreduce-25.5.0 → essreduce-25.5.1}/src/ess/reduce/widgets/_vector_widget.py +0 -0
  120. {essreduce-25.5.0 → essreduce-25.5.1}/src/essreduce.egg-info/dependency_links.txt +0 -0
  121. {essreduce-25.5.0 → essreduce-25.5.1}/src/essreduce.egg-info/entry_points.txt +0 -0
  122. {essreduce-25.5.0 → essreduce-25.5.1}/src/essreduce.egg-info/requires.txt +0 -0
  123. {essreduce-25.5.0 → essreduce-25.5.1}/src/essreduce.egg-info/top_level.txt +0 -0
  124. {essreduce-25.5.0 → essreduce-25.5.1}/tests/live/raw_test.py +0 -0
  125. {essreduce-25.5.0 → essreduce-25.5.1}/tests/live/roi_test.py +0 -0
  126. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_generator_test.py +0 -0
  127. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/array_dataset.json +0 -0
  128. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/dataset.json +0 -0
  129. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/detector.json +0 -0
  130. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/entry.json +0 -0
  131. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/event_data.json +0 -0
  132. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/instrument.json +0 -0
  133. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_examples/log.json +0 -0
  134. {essreduce-25.5.0 → essreduce-25.5.1}/tests/nexus/json_nexus_test.py +0 -0
  135. {essreduce-25.5.0 → essreduce-25.5.1}/tests/package_test.py +0 -0
  136. {essreduce-25.5.0 → essreduce-25.5.1}/tests/scripts/test_grow_nexus.py +0 -0
  137. {essreduce-25.5.0 → essreduce-25.5.1}/tests/streaming_test.py +0 -0
  138. {essreduce-25.5.0 → essreduce-25.5.1}/tests/time_of_flight/interpolator_test.py +0 -0
  139. {essreduce-25.5.0 → essreduce-25.5.1}/tests/time_of_flight/to_events_test.py +0 -0
  140. {essreduce-25.5.0 → essreduce-25.5.1}/tests/time_of_flight/unwrap_test.py +0 -0
  141. {essreduce-25.5.0 → essreduce-25.5.1}/tests/time_of_flight/wfm_test.py +0 -0
  142. {essreduce-25.5.0 → essreduce-25.5.1}/tests/uncertainty_test.py +0 -0
  143. {essreduce-25.5.0 → essreduce-25.5.1}/tests/widget_test.py +0 -0
  144. {essreduce-25.5.0 → essreduce-25.5.1}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: essreduce
3
- Version: 25.5.0
3
+ Version: 25.5.1
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License: BSD 3-Clause License
@@ -305,7 +305,9 @@
305
305
  "metadata": {},
306
306
  "outputs": [],
307
307
  "source": [
308
- "wf = time_of_flight.GenericTofWorkflow()\n",
308
+ "wf = time_of_flight.GenericTofWorkflow(\n",
309
+ " tof_lut_provider=time_of_flight.TofLutProvider.TOF\n",
310
+ ")\n",
309
311
  "wf[DetectorData[SampleRun]] = raw_data\n",
310
312
  "wf[time_of_flight.DetectorLtotal[SampleRun]] = Ltotal\n",
311
313
  "wf[time_of_flight.LtotalRange] = (\n",
@@ -159,7 +159,9 @@
159
159
  "metadata": {},
160
160
  "outputs": [],
161
161
  "source": [
162
- "wf = time_of_flight.GenericTofWorkflow()\n",
162
+ "wf = time_of_flight.GenericTofWorkflow(\n",
163
+ " tof_lut_provider=time_of_flight.TofLutProvider.TOF\n",
164
+ ")\n",
163
165
  "\n",
164
166
  "wf[DetectorData[SampleRun]] = nxevent_data\n",
165
167
  "wf[time_of_flight.DetectorLtotal[SampleRun]] = nxevent_data.coords[\"Ltotal\"]\n",
@@ -368,7 +370,9 @@
368
370
  "metadata": {},
369
371
  "outputs": [],
370
372
  "source": [
371
- "wf = time_of_flight.GenericTofWorkflow()\n",
373
+ "wf = time_of_flight.GenericTofWorkflow(\n",
374
+ " tof_lut_provider=time_of_flight.TofLutProvider.TOF\n",
375
+ ")\n",
372
376
  "\n",
373
377
  "nxevent_data = results.to_nxevent_data()\n",
374
378
  "\n",
@@ -326,7 +326,9 @@
326
326
  "metadata": {},
327
327
  "outputs": [],
328
328
  "source": [
329
- "wf = time_of_flight.GenericTofWorkflow()\n",
329
+ "wf = time_of_flight.GenericTofWorkflow(\n",
330
+ " tof_lut_provider=time_of_flight.TofLutProvider.TOF\n",
331
+ ")\n",
330
332
  "wf[DetectorData[SampleRun]] = raw_data\n",
331
333
  "wf[time_of_flight.DetectorLtotal[SampleRun]] = Ltotal\n",
332
334
  "wf[time_of_flight.LtotalRange] = Ltotal, Ltotal\n",
@@ -353,9 +355,7 @@
353
355
  "outputs": [],
354
356
  "source": [
355
357
  "wf[time_of_flight.SimulationResults] = time_of_flight.simulate_beamline(\n",
356
- " choppers=disk_choppers,\n",
357
- " source_position=source_position,\n",
358
- " neutrons=3_000_000\n",
358
+ " choppers=disk_choppers, source_position=source_position, neutrons=3_000_000\n",
359
359
  ")"
360
360
  ]
361
361
  },
@@ -341,10 +341,3 @@ class Choppers(
341
341
  sc.DataGroup[sc.DataGroup[Any]],
342
342
  ):
343
343
  """All choppers in a NeXus file."""
344
-
345
-
346
- class Analyzers(
347
- sciline.Scope[RunType, sc.DataGroup[sc.DataGroup[Any]]],
348
- sc.DataGroup[sc.DataGroup[Any]],
349
- ):
350
- """All analyzers in a NeXus file."""
@@ -19,7 +19,6 @@ from ..utils import prune_type_vars
19
19
  from . import _nexus_loader as nexus
20
20
  from .types import (
21
21
  AllNeXusComponents,
22
- Analyzers,
23
22
  Beamline,
24
23
  CalibratedBeamline,
25
24
  CalibratedDetector,
@@ -525,13 +524,6 @@ def parse_disk_choppers(
525
524
  )
526
525
 
527
526
 
528
- def parse_analyzers(
529
- analyzers: AllNeXusComponents[snx.NXcrystal, RunType],
530
- ) -> Analyzers[RunType]:
531
- """Convert the NeXus representation of an analyzer to ours."""
532
- return Analyzers[RunType](analyzers.apply(nexus.compute_component_position))
533
-
534
-
535
527
  def _drop(
536
528
  children: dict[str, snx.Field | snx.Group], classes: tuple[snx.NXobject, ...]
537
529
  ) -> dict[str, snx.Field | snx.Group]:
@@ -628,7 +620,6 @@ _common_providers = (
628
620
  nx_class_for_source,
629
621
  nx_class_for_sample,
630
622
  nx_class_for_disk_chopper,
631
- nx_class_for_crystal,
632
623
  )
633
624
 
634
625
  _monitor_providers = (
@@ -647,8 +638,6 @@ _detector_providers = (
647
638
 
648
639
  _chopper_providers = (parse_disk_choppers,)
649
640
 
650
- _analyzer_providers = (parse_analyzers,)
651
-
652
641
  _metadata_providers = (
653
642
  load_beamline_metadata_from_nexus,
654
643
  load_measurement_metadata_from_nexus,
@@ -711,7 +700,6 @@ def GenericNeXusWorkflow(
711
700
  *_monitor_providers,
712
701
  *_detector_providers,
713
702
  *_chopper_providers,
714
- *_analyzer_providers,
715
703
  *_metadata_providers,
716
704
  )
717
705
  )
@@ -29,9 +29,10 @@ from .types import (
29
29
  ResampledMonitorTofData,
30
30
  SimulationResults,
31
31
  TimeOfFlightLookupTable,
32
+ TimeOfFlightLookupTableFilename,
32
33
  TimeResolution,
33
34
  )
34
- from .workflow import GenericTofWorkflow
35
+ from .workflow import GenericTofWorkflow, TofLutProvider
35
36
 
36
37
  __all__ = [
37
38
  "DetectorLtotal",
@@ -49,7 +50,9 @@ __all__ = [
49
50
  "ResampledMonitorTofData",
50
51
  "SimulationResults",
51
52
  "TimeOfFlightLookupTable",
53
+ "TimeOfFlightLookupTableFilename",
52
54
  "TimeResolution",
55
+ "TofLutProvider",
53
56
  "default_parameters",
54
57
  "providers",
55
58
  "resample_detector_time_of_flight_data",
@@ -12,7 +12,6 @@ from collections.abc import Callable
12
12
  import numpy as np
13
13
  import scipp as sc
14
14
  import scippneutron as scn
15
- from scipp._scipp.core import _bins_no_validate
16
15
  from scippneutron._utils import elem_unit
17
16
 
18
17
  try:
@@ -532,7 +531,7 @@ def _time_of_flight_data_events(
532
531
 
533
532
  parts = da.bins.constituents
534
533
  parts["data"] = tofs
535
- return da.bins.assign_coords(tof=_bins_no_validate(**parts))
534
+ return da.bins.assign_coords(tof=sc.bins(**parts, validate_indices=False))
536
535
 
537
536
 
538
537
  def detector_ltotal_from_straight_line_approximation(
@@ -3,9 +3,11 @@
3
3
  from collections.abc import Mapping
4
4
 
5
5
  import scipp as sc
6
+ import scippnexus as snx
6
7
  from scippneutron.chopper import DiskChopper
7
8
 
8
- from .types import SimulationResults
9
+ from ..nexus.types import Choppers, Position, SampleRun
10
+ from .types import NumberOfSimulatedNeutrons, SimulationResults
9
11
 
10
12
 
11
13
  def simulate_beamline(
@@ -82,3 +84,25 @@ def simulate_beamline(
82
84
  weight=events.data,
83
85
  distance=furthest_chopper.distance,
84
86
  )
87
+
88
+
89
+ def simulate_chopper_cascade_using_tof(
90
+ choppers: Choppers[SampleRun],
91
+ neutrons: NumberOfSimulatedNeutrons,
92
+ source_position: Position[snx.NXsource, SampleRun],
93
+ ) -> SimulationResults:
94
+ """
95
+ Simulate neutrons traveling through the chopper cascade using the ``tof`` package.
96
+
97
+ Parameters
98
+ ----------
99
+ choppers:
100
+ Chopper settings.
101
+ neutrons:
102
+ Number of neutrons to simulate.
103
+ source_position:
104
+ Position of the source.
105
+ """
106
+ return simulate_beamline(
107
+ choppers=choppers, neutrons=neutrons, source_position=source_position
108
+ )
@@ -46,6 +46,12 @@ class SimulationResults:
46
46
  distance: sc.Variable
47
47
 
48
48
 
49
+ NumberOfSimulatedNeutrons = NewType("NumberOfSimulatedNeutrons", int)
50
+ """
51
+ Number of neutrons simulated in the simulation that is used to create the lookup table.
52
+ This is typically a large number, e.g., 1e6 or 1e7.
53
+ """
54
+
49
55
  LtotalRange = NewType("LtotalRange", tuple[sc.Variable, sc.Variable])
50
56
  """
51
57
  Range (min, max) of the total length of the flight path from the source to the detector.
@@ -78,6 +84,10 @@ resolution in the lookup table will be at least the supplied value here, but may
78
84
  smaller if the pulse period is not an integer multiple of the time resolution.
79
85
  """
80
86
 
87
+ TimeOfFlightLookupTableFilename = NewType("TimeOfFlightLookupTableFilename", str)
88
+ """Filename of the time-of-flight lookup table."""
89
+
90
+
81
91
  TimeOfFlightLookupTable = NewType("TimeOfFlightLookupTable", sc.DataArray)
82
92
  """
83
93
  Lookup table giving time-of-flight as a function of distance and time of arrival.
@@ -1,18 +1,36 @@
1
1
  # SPDX-License-Identifier: BSD-3-Clause
2
2
  # Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
3
3
  from collections.abc import Iterable
4
+ from enum import Enum, auto
4
5
 
5
6
  import sciline
7
+ import scipp as sc
6
8
 
7
9
  from ..nexus import GenericNeXusWorkflow
8
10
  from ..utils import prune_type_vars
9
- from .eto_to_tof import default_parameters, providers
11
+ from . import eto_to_tof, simulation
12
+ from .types import TimeOfFlightLookupTable, TimeOfFlightLookupTableFilename
13
+
14
+
15
+ class TofLutProvider(Enum):
16
+ """Provider for the time-of-flight lookup table."""
17
+
18
+ FILE = auto() # From file
19
+ TOF = auto() # Computed with 'tof' package from chopper settings
20
+ MCSTAS = auto() # McStas simulation (not implemented yet)
21
+
22
+
23
+ def load_tof_lookup_table(
24
+ filename: TimeOfFlightLookupTableFilename,
25
+ ) -> TimeOfFlightLookupTable:
26
+ return TimeOfFlightLookupTable(sc.io.load_hdf5(filename))
10
27
 
11
28
 
12
29
  def GenericTofWorkflow(
13
30
  *,
14
31
  run_types: Iterable[sciline.typing.Key] | None = None,
15
32
  monitor_types: Iterable[sciline.typing.Key] | None = None,
33
+ tof_lut_provider: TofLutProvider = TofLutProvider.FILE,
16
34
  ) -> sciline.Pipeline:
17
35
  """
18
36
  Generic workflow for computing the neutron time-of-flight for detector and monitor
@@ -42,6 +60,11 @@ def GenericTofWorkflow(
42
60
  List of monitor types to include in the workflow. If not provided, all monitor
43
61
  types are included.
44
62
  Must be a possible value of :class:`ess.reduce.nexus.types.MonitorType`.
63
+ tof_lut_provider:
64
+ Specifies how the time-of-flight lookup table is provided:
65
+ - FILE: Read from a file
66
+ - TOF: Computed from chopper settings using the 'tof' package
67
+ - MCSTAS: From McStas simulation (not implemented yet)
45
68
 
46
69
  Returns
47
70
  -------
@@ -50,9 +73,19 @@ def GenericTofWorkflow(
50
73
  """
51
74
  wf = GenericNeXusWorkflow(run_types=run_types, monitor_types=monitor_types)
52
75
 
53
- for provider in providers():
76
+ for provider in eto_to_tof.providers():
54
77
  wf.insert(provider)
55
- for key, value in default_parameters().items():
78
+
79
+ if tof_lut_provider == TofLutProvider.FILE:
80
+ wf.insert(load_tof_lookup_table)
81
+ else:
82
+ wf.insert(eto_to_tof.compute_tof_lookup_table)
83
+ if tof_lut_provider == TofLutProvider.TOF:
84
+ wf.insert(simulation.simulate_chopper_cascade_using_tof)
85
+ if tof_lut_provider == TofLutProvider.MCSTAS:
86
+ raise NotImplementedError("McStas simulation not implemented yet")
87
+
88
+ for key, value in eto_to_tof.default_parameters().items():
56
89
  wf[key] = value
57
90
 
58
91
  if run_types is not None or monitor_types is not None:
@@ -53,12 +53,14 @@ def get_typical_outputs(pipeline: Pipeline) -> tuple[Key, ...]:
53
53
  if (typical_outputs := getattr(pipeline, "typical_outputs", None)) is None:
54
54
  graph = pipeline.underlying_graph
55
55
  sink_nodes = [node for node, degree in graph.out_degree if degree == 0]
56
- return sorted(_with_pretty_names(sink_nodes))
56
+ return sorted(_with_pretty_names(sink_nodes), key=lambda x: x[0])
57
57
  return _with_pretty_names(typical_outputs)
58
58
 
59
59
 
60
60
  def get_possible_outputs(pipeline: Pipeline) -> tuple[Key, ...]:
61
- return sorted(_with_pretty_names(tuple(pipeline.underlying_graph.nodes)))
61
+ return sorted(
62
+ _with_pretty_names(tuple(pipeline.underlying_graph.nodes)), key=lambda x: x[0]
63
+ )
62
64
 
63
65
 
64
66
  def _with_pretty_names(outputs: Sequence[Key]) -> tuple[tuple[str, Key], ...]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: essreduce
3
- Version: 25.5.0
3
+ Version: 25.5.1
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License: BSD 3-Clause License
@@ -138,4 +138,5 @@ tests/scripts/test_grow_nexus.py
138
138
  tests/time_of_flight/interpolator_test.py
139
139
  tests/time_of_flight/to_events_test.py
140
140
  tests/time_of_flight/unwrap_test.py
141
- tests/time_of_flight/wfm_test.py
141
+ tests/time_of_flight/wfm_test.py
142
+ tests/time_of_flight/workflow_test.py
@@ -107,27 +107,6 @@ def _choppers_data() -> sc.DataGroup[sc.DataGroup[Any]]:
107
107
  )
108
108
 
109
109
 
110
- def _analyzers_data() -> sc.DataGroup[sc.DataGroup[Any]]:
111
- return sc.DataGroup[sc.DataGroup[Any]](
112
- {
113
- 'analyzer_B': sc.DataGroup[Any](
114
- {
115
- 'd_spacing': sc.scalar(3.355, unit='angstrom'),
116
- 'usage': 'Bragg',
117
- 'nexus_component_name': 'analyzer_B',
118
- }
119
- ),
120
- 'analyzer_A': sc.DataGroup[Any](
121
- {
122
- 'd_spacing': sc.scalar(3.104, unit='angstrom'),
123
- 'usage': 'Bragg',
124
- 'nexus_component_name': 'analyzer_A',
125
- }
126
- ),
127
- }
128
- )
129
-
130
-
131
110
  def _write_transformation(group: snx.Group, offset: sc.Variable) -> None:
132
111
  group.create_field('depends_on', sc.scalar('transformations/t1'))
133
112
  transformations = group.create_class('transformations', snx.NXtransformations)
@@ -184,11 +163,6 @@ def _write_nexus_data(store: Path | BytesIO) -> None:
184
163
  chop.create_field('slit_height', chopper['slit_height'])
185
164
  chop.create_field('slits', chopper['slits'])
186
165
 
187
- for name, analyzer in _analyzers_data().items():
188
- ana = instrument.create_class(name, snx.NXcrystal)
189
- ana.create_field('d_spacing', analyzer['d_spacing'])
190
- ana.create_field('usage', analyzer['usage'])
191
-
192
166
 
193
167
  @contextmanager
194
168
  def _file_store(request: pytest.FixtureRequest):
@@ -273,11 +247,6 @@ def expected_choppers() -> sc.DataGroup[sc.DataGroup[Any]]:
273
247
  return _choppers_data()
274
248
 
275
249
 
276
- @pytest.fixture
277
- def expected_analyzers() -> sc.DataGroup[sc.DataGroup[Any]]:
278
- return _analyzers_data()
279
-
280
-
281
250
  def test_load_data_loads_expected_event_data(nexus_file, expected_bank12):
282
251
  events = nexus.load_data(
283
252
  nexus_file,
@@ -536,16 +505,6 @@ def test_load_disk_choppers(nexus_file, expected_choppers, entry_name):
536
505
  sc.testing.assert_identical(choppers, expected_choppers)
537
506
 
538
507
 
539
- @pytest.mark.parametrize('entry_name', [None, nexus.types.NeXusEntryName('entry-001')])
540
- def test_load_analyzers(nexus_file, expected_analyzers, entry_name):
541
- loc = NeXusLocationSpec(
542
- filename=nexus_file,
543
- entry_name=entry_name,
544
- )
545
- analyzers = nexus.load_all_components(loc, nx_class=snx.NXcrystal)
546
- sc.testing.assert_identical(analyzers, expected_analyzers)
547
-
548
-
549
508
  def test_extract_detector_data():
550
509
  detector = sc.DataGroup(
551
510
  {
@@ -10,7 +10,6 @@ from scipp.testing import assert_identical
10
10
  from ess.reduce import data
11
11
  from ess.reduce.nexus import compute_component_position, workflow
12
12
  from ess.reduce.nexus.types import (
13
- Analyzers,
14
13
  BackgroundRun,
15
14
  Beamline,
16
15
  Choppers,
@@ -573,18 +572,6 @@ def test_generic_nexus_workflow_load_choppers() -> None:
573
572
  assert chopper['slit_edges'].shape == (2,)
574
573
 
575
574
 
576
- def test_generic_nexus_workflow_load_analyzers() -> None:
577
- wf = GenericNeXusWorkflow()
578
- wf[Filename[SampleRun]] = data.bifrost_simulated_elastic()
579
- analyzers = wf.compute(Analyzers[SampleRun])
580
-
581
- assert len(analyzers) == 45
582
- analyzer = analyzers['144_channel_2_1_monochromator']
583
- assert 'position' in analyzer
584
- assert analyzer['d_spacing'].ndim == 0
585
- assert analyzer['usage'] == 'Bragg'
586
-
587
-
588
575
  def test_generic_nexus_workflow_load_beamline_metadata() -> None:
589
576
  wf = GenericNeXusWorkflow()
590
577
  wf[Filename[SampleRun]] = data.bifrost_simulated_elastic()
@@ -626,8 +613,6 @@ def test_generic_nexus_workflow_includes_only_given_run_and_monitor_types() -> N
626
613
  assert MonitorData[BackgroundRun, Monitor3] not in graph
627
614
  assert Choppers[SampleRun] in graph
628
615
  assert Choppers[BackgroundRun] not in graph
629
- assert Analyzers[SampleRun] in graph
630
- assert Analyzers[BackgroundRun] not in graph
631
616
 
632
617
  assert NeXusComponentLocationSpec[Monitor1, SampleRun] in graph
633
618
  assert NeXusComponentLocationSpec[Monitor2, SampleRun] not in graph
@@ -664,8 +649,6 @@ def test_generic_nexus_workflow_includes_only_given_run_types() -> None:
664
649
  assert MonitorData[SampleRun, Monitor3] not in graph
665
650
  assert Choppers[EmptyBeamRun] in graph
666
651
  assert Choppers[SampleRun] not in graph
667
- assert Analyzers[EmptyBeamRun] in graph
668
- assert Analyzers[SampleRun] not in graph
669
652
 
670
653
  excluded_run_types = set(RunType.__constraints__) - {EmptyBeamRun}
671
654
  for node in graph:
@@ -689,8 +672,6 @@ def test_generic_nexus_workflow_includes_only_given_monitor_types() -> None:
689
672
  assert MonitorData[BackgroundRun, Monitor3] not in graph
690
673
  assert Choppers[SampleRun] in graph
691
674
  assert Choppers[BackgroundRun] in graph
692
- assert Analyzers[SampleRun] in graph
693
- assert Analyzers[BackgroundRun] in graph
694
675
 
695
676
  excluded_monitor_types = set(MonitorType.__constraints__) - {
696
677
  Monitor1,
@@ -0,0 +1,136 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause
2
+ # Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
3
+ import numpy as np
4
+ import pytest
5
+ import sciline
6
+ import scipp as sc
7
+ import scippnexus as snx
8
+ from scipp.testing import assert_identical
9
+
10
+ from ess.reduce import time_of_flight
11
+ from ess.reduce.nexus.types import (
12
+ CalibratedBeamline,
13
+ Choppers,
14
+ DetectorData,
15
+ NeXusData,
16
+ Position,
17
+ SampleRun,
18
+ )
19
+ from ess.reduce.time_of_flight import GenericTofWorkflow, TofLutProvider, fakes
20
+
21
+ sl = pytest.importorskip("sciline")
22
+
23
+
24
+ @pytest.fixture
25
+ def calibrated_beamline():
26
+ return sc.DataArray(
27
+ data=sc.ones(dims=["detector_number"], shape=[10]),
28
+ coords={
29
+ "Ltotal": sc.scalar(80.0, unit="m"),
30
+ "detector_number": sc.array(
31
+ dims=["detector_number"], values=np.arange(10), unit=None
32
+ ),
33
+ },
34
+ )
35
+
36
+
37
+ @pytest.fixture
38
+ def nexus_data():
39
+ events = sc.DataArray(
40
+ data=sc.ones(dims=["event"], shape=[1000]),
41
+ coords={
42
+ "event_time_offset": sc.linspace(
43
+ "event", 0.0, 1000.0 / 14, num=1000, unit="ms"
44
+ ).to(unit="ns"),
45
+ "event_id": sc.array(
46
+ dims=["event"], values=np.arange(1000) % 10, unit=None
47
+ ),
48
+ },
49
+ )
50
+ return sc.DataArray(
51
+ sc.bins(
52
+ begin=sc.array(dims=["pulse"], values=[0], unit=None),
53
+ data=events,
54
+ dim="event",
55
+ )
56
+ )
57
+
58
+
59
+ def test_GenericTofWorkflow_can_compute_tof_lut_without_nexus_file_or_detector_info():
60
+ wf = GenericTofWorkflow(tof_lut_provider=TofLutProvider.TOF)
61
+ wf[Choppers[SampleRun]] = fakes.psc_choppers()
62
+ wf[time_of_flight.types.NumberOfSimulatedNeutrons] = 10_000
63
+ wf[time_of_flight.types.LtotalRange] = (
64
+ sc.scalar(0.0, unit="m"),
65
+ sc.scalar(100.0, unit="m"),
66
+ )
67
+ wf[Position[snx.NXsource, SampleRun]] = fakes.source_position()
68
+ lut = wf.compute(time_of_flight.TimeOfFlightLookupTable)
69
+ assert isinstance(lut, sc.DataArray)
70
+
71
+
72
+ def test_GenericTofWorkflow_with_tof_lut_from_tof_simulation(
73
+ calibrated_beamline: sc.DataArray, nexus_data: sc.DataArray
74
+ ):
75
+ wf = GenericTofWorkflow(tof_lut_provider=TofLutProvider.TOF)
76
+ wf[CalibratedBeamline[SampleRun]] = calibrated_beamline
77
+ wf[NeXusData[snx.NXdetector, SampleRun]] = nexus_data
78
+
79
+ # Should be able to compute DetectorData without chopper and simulation params
80
+ # This contains event_time_offset (time-of-arrival).
81
+ _ = wf.compute(DetectorData[SampleRun])
82
+ # LUT and Tof data cannot be computed without chopper and simulation params
83
+ with pytest.raises(sciline.UnsatisfiedRequirement):
84
+ _ = wf.compute(time_of_flight.TimeOfFlightLookupTable)
85
+ with pytest.raises(sciline.UnsatisfiedRequirement):
86
+ _ = wf.compute(time_of_flight.DetectorTofData[SampleRun])
87
+
88
+ wf[Choppers[SampleRun]] = fakes.psc_choppers()
89
+ wf[time_of_flight.types.NumberOfSimulatedNeutrons] = 10_000
90
+ wf[time_of_flight.types.LtotalRange] = (
91
+ sc.scalar(0.0, unit="m"),
92
+ sc.scalar(100.0, unit="m"),
93
+ )
94
+ wf[Position[snx.NXsource, SampleRun]] = fakes.source_position()
95
+
96
+ # Should be able to compute DetectorData with chopper and simulation params
97
+ _ = wf.compute(time_of_flight.TimeOfFlightLookupTable)
98
+ detector = wf.compute(time_of_flight.DetectorTofData[SampleRun])
99
+ assert 'tof' in detector.bins.coords
100
+
101
+
102
+ def test_GenericTofWorkflow_with_tof_lut_from_mcstas_simulation():
103
+ with pytest.raises(
104
+ NotImplementedError, match="McStas simulation not implemented yet"
105
+ ):
106
+ GenericTofWorkflow(tof_lut_provider=TofLutProvider.MCSTAS)
107
+
108
+
109
+ def test_GenericTofWorkflow_with_tof_lut_from_file(
110
+ calibrated_beamline: sc.DataArray,
111
+ nexus_data: sc.DataArray,
112
+ tmp_path: pytest.TempPathFactory,
113
+ ):
114
+ make_lut_wf = GenericTofWorkflow(tof_lut_provider=TofLutProvider.TOF)
115
+ make_lut_wf[Choppers[SampleRun]] = fakes.psc_choppers()
116
+ make_lut_wf[time_of_flight.types.NumberOfSimulatedNeutrons] = 10_000
117
+ make_lut_wf[time_of_flight.types.LtotalRange] = (
118
+ sc.scalar(0.0, unit="m"),
119
+ sc.scalar(100.0, unit="m"),
120
+ )
121
+ make_lut_wf[Position[snx.NXsource, SampleRun]] = fakes.source_position()
122
+ lut = make_lut_wf.compute(time_of_flight.TimeOfFlightLookupTable)
123
+ lut.save_hdf5(filename=tmp_path / "lut.h5")
124
+
125
+ wf = GenericTofWorkflow(tof_lut_provider=TofLutProvider.FILE)
126
+ wf[CalibratedBeamline[SampleRun]] = calibrated_beamline
127
+ wf[NeXusData[snx.NXdetector, SampleRun]] = nexus_data
128
+ wf[time_of_flight.TimeOfFlightLookupTableFilename] = (
129
+ tmp_path / "lut.h5"
130
+ ).as_posix()
131
+
132
+ loaded_lut = wf.compute(time_of_flight.TimeOfFlightLookupTable)
133
+ assert_identical(lut, loaded_lut)
134
+
135
+ detector = wf.compute(time_of_flight.DetectorTofData[SampleRun])
136
+ assert 'tof' in detector.bins.coords
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes