essreduce 25.1.1__tar.gz → 25.2.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 (140) hide show
  1. {essreduce-25.1.1 → essreduce-25.2.1}/.copier-answers.yml +1 -1
  2. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/docs.yml +1 -1
  3. essreduce-25.2.1/.github/workflows/weekly_windows_macos.yml +42 -0
  4. {essreduce-25.1.1 → essreduce-25.2.1}/.gitignore +2 -1
  5. {essreduce-25.1.1 → essreduce-25.2.1}/.pre-commit-config.yaml +1 -1
  6. {essreduce-25.1.1/src/essreduce.egg-info → essreduce-25.2.1}/PKG-INFO +1 -1
  7. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/tof/dream.ipynb +39 -42
  8. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/tof/frame-unwrapping.ipynb +34 -155
  9. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/tof/wfm.ipynb +5 -4
  10. {essreduce-25.1.1 → essreduce-25.2.1}/pyproject.toml +1 -2
  11. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/base.txt +2 -2
  12. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/basetest.txt +5 -5
  13. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/ci.txt +2 -2
  14. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/dev.txt +3 -3
  15. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/docs.txt +14 -11
  16. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/make_base.py +9 -6
  17. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/mypy.txt +1 -1
  18. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/nightly.in +4 -1
  19. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/nightly.txt +29 -7
  20. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/static.txt +2 -2
  21. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/__init__.py +4 -4
  22. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/live/raw.py +173 -15
  23. essreduce-25.2.1/src/ess/reduce/live/roi.py +115 -0
  24. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/nexus/_nexus_loader.py +61 -22
  25. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/streaming.py +53 -3
  26. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/time_of_flight/__init__.py +4 -18
  27. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/time_of_flight/fakes.py +29 -47
  28. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/time_of_flight/simulation.py +6 -3
  29. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/time_of_flight/to_events.py +12 -7
  30. essreduce-25.2.1/src/ess/reduce/time_of_flight/toa_to_tof.py +549 -0
  31. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/time_of_flight/types.py +13 -53
  32. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/__init__.py +9 -10
  33. {essreduce-25.1.1 → essreduce-25.2.1/src/essreduce.egg-info}/PKG-INFO +1 -1
  34. {essreduce-25.1.1 → essreduce-25.2.1}/src/essreduce.egg-info/SOURCES.txt +3 -0
  35. essreduce-25.2.1/tests/live/raw_test.py +550 -0
  36. essreduce-25.2.1/tests/live/roi_test.py +184 -0
  37. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/nexus_loader_test.py +67 -1
  38. {essreduce-25.1.1 → essreduce-25.2.1}/tests/streaming_test.py +100 -0
  39. {essreduce-25.1.1 → essreduce-25.2.1}/tests/time_of_flight/to_events_test.py +40 -0
  40. {essreduce-25.1.1 → essreduce-25.2.1}/tests/time_of_flight/unwrap_test.py +116 -50
  41. essreduce-25.2.1/tests/time_of_flight/wfm_test.py +338 -0
  42. {essreduce-25.1.1 → essreduce-25.2.1}/tox.ini +4 -1
  43. essreduce-25.1.1/src/ess/reduce/time_of_flight/toa_to_tof.py +0 -541
  44. essreduce-25.1.1/tests/live/raw_test.py +0 -162
  45. essreduce-25.1.1/tests/time_of_flight/wfm_test.py +0 -433
  46. {essreduce-25.1.1 → essreduce-25.2.1}/.copier-answers.ess.yml +0 -0
  47. {essreduce-25.1.1 → essreduce-25.2.1}/.github/ISSUE_TEMPLATE/blank.md +0 -0
  48. {essreduce-25.1.1 → essreduce-25.2.1}/.github/ISSUE_TEMPLATE/high-level-requirement.yml +0 -0
  49. {essreduce-25.1.1 → essreduce-25.2.1}/.github/dependabot.yml +0 -0
  50. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/ci.yml +0 -0
  51. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/nightly_at_main.yml +0 -0
  52. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/nightly_at_release.yml +0 -0
  53. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/python-version-ci +0 -0
  54. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/release.yml +0 -0
  55. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/test.yml +0 -0
  56. {essreduce-25.1.1 → essreduce-25.2.1}/.github/workflows/unpinned.yml +0 -0
  57. {essreduce-25.1.1 → essreduce-25.2.1}/.python-version +0 -0
  58. {essreduce-25.1.1 → essreduce-25.2.1}/CODE_OF_CONDUCT.md +0 -0
  59. {essreduce-25.1.1 → essreduce-25.2.1}/CONTRIBUTING.md +0 -0
  60. {essreduce-25.1.1 → essreduce-25.2.1}/LICENSE +0 -0
  61. {essreduce-25.1.1 → essreduce-25.2.1}/MANIFEST.in +0 -0
  62. {essreduce-25.1.1 → essreduce-25.2.1}/README.md +0 -0
  63. {essreduce-25.1.1 → essreduce-25.2.1}/conda/meta.yaml +0 -0
  64. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_static/anaconda-icon.js +0 -0
  65. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_static/favicon.svg +0 -0
  66. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_static/logo-dark.svg +0 -0
  67. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_static/logo.svg +0 -0
  68. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_templates/class-template.rst +0 -0
  69. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_templates/doc_version.html +0 -0
  70. {essreduce-25.1.1 → essreduce-25.2.1}/docs/_templates/module-template.rst +0 -0
  71. {essreduce-25.1.1 → essreduce-25.2.1}/docs/about/index.md +0 -0
  72. {essreduce-25.1.1 → essreduce-25.2.1}/docs/api-reference/index.md +0 -0
  73. {essreduce-25.1.1 → essreduce-25.2.1}/docs/conf.py +0 -0
  74. {essreduce-25.1.1 → essreduce-25.2.1}/docs/developer/coding-conventions.md +0 -0
  75. {essreduce-25.1.1 → essreduce-25.2.1}/docs/developer/dependency-management.md +0 -0
  76. {essreduce-25.1.1 → essreduce-25.2.1}/docs/developer/getting-started.md +0 -0
  77. {essreduce-25.1.1 → essreduce-25.2.1}/docs/developer/gui.ipynb +0 -0
  78. {essreduce-25.1.1 → essreduce-25.2.1}/docs/developer/index.md +0 -0
  79. {essreduce-25.1.1 → essreduce-25.2.1}/docs/index.md +0 -0
  80. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/index.md +0 -0
  81. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/reduction-workflow-guidelines.md +0 -0
  82. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/tof/index.md +0 -0
  83. {essreduce-25.1.1 → essreduce-25.2.1}/docs/user-guide/widget.md +0 -0
  84. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/base.in +0 -0
  85. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/basetest.in +0 -0
  86. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/ci.in +0 -0
  87. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/dev.in +0 -0
  88. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/docs.in +0 -0
  89. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/mypy.in +0 -0
  90. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/static.in +0 -0
  91. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/test.in +0 -0
  92. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/test.txt +0 -0
  93. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/wheels.in +0 -0
  94. {essreduce-25.1.1 → essreduce-25.2.1}/requirements/wheels.txt +0 -0
  95. {essreduce-25.1.1 → essreduce-25.2.1}/resources/logo.svg +0 -0
  96. {essreduce-25.1.1 → essreduce-25.2.1}/setup.cfg +0 -0
  97. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/data.py +0 -0
  98. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/live/__init__.py +0 -0
  99. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/live/workflow.py +0 -0
  100. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/logging.py +0 -0
  101. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/nexus/__init__.py +10 -10
  102. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/nexus/json_generator.py +0 -0
  103. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/nexus/json_nexus.py +0 -0
  104. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/nexus/types.py +0 -0
  105. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/nexus/workflow.py +0 -0
  106. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/parameter.py +0 -0
  107. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/py.typed +0 -0
  108. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/scripts/grow_nexus.py +0 -0
  109. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/ui.py +0 -0
  110. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/uncertainty.py +0 -0
  111. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_base.py +0 -0
  112. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_binedges_widget.py +0 -0
  113. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_bounds_widget.py +0 -0
  114. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_config.py +0 -0
  115. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_filename_widget.py +0 -0
  116. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_linspace_widget.py +0 -0
  117. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_optional_widget.py +0 -0
  118. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_spinner.py +0 -0
  119. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_string_widget.py +0 -0
  120. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_switchable_widget.py +0 -0
  121. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/widgets/_vector_widget.py +0 -0
  122. {essreduce-25.1.1 → essreduce-25.2.1}/src/ess/reduce/workflow.py +0 -0
  123. {essreduce-25.1.1 → essreduce-25.2.1}/src/essreduce.egg-info/dependency_links.txt +0 -0
  124. {essreduce-25.1.1 → essreduce-25.2.1}/src/essreduce.egg-info/entry_points.txt +0 -0
  125. {essreduce-25.1.1 → essreduce-25.2.1}/src/essreduce.egg-info/requires.txt +0 -0
  126. {essreduce-25.1.1 → essreduce-25.2.1}/src/essreduce.egg-info/top_level.txt +0 -0
  127. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_generator_test.py +0 -0
  128. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/array_dataset.json +0 -0
  129. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/dataset.json +0 -0
  130. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/detector.json +0 -0
  131. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/entry.json +0 -0
  132. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/event_data.json +0 -0
  133. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/instrument.json +0 -0
  134. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_examples/log.json +0 -0
  135. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/json_nexus_test.py +0 -0
  136. {essreduce-25.1.1 → essreduce-25.2.1}/tests/nexus/workflow_test.py +0 -0
  137. {essreduce-25.1.1 → essreduce-25.2.1}/tests/package_test.py +0 -0
  138. {essreduce-25.1.1 → essreduce-25.2.1}/tests/scripts/test_grow_nexus.py +0 -0
  139. {essreduce-25.1.1 → essreduce-25.2.1}/tests/uncertainty_test.py +0 -0
  140. {essreduce-25.1.1 → essreduce-25.2.1}/tests/widget_test.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2
- _commit: aa5dc5e
2
+ _commit: 5c4fd02
3
3
  _src_path: gh:scipp/copier_template
4
4
  description: Common data reduction tools for the ESS facility
5
5
  max_python: '3.13'
@@ -69,7 +69,7 @@ jobs:
69
69
  name: docs_html
70
70
  path: html/
71
71
 
72
- - uses: JamesIves/github-pages-deploy-action@v4.6.9
72
+ - uses: JamesIves/github-pages-deploy-action@v4.7.2
73
73
  if: ${{ inputs.publish }}
74
74
  with:
75
75
  branch: gh-pages
@@ -0,0 +1,42 @@
1
+ name: Windows and MacOS weekly tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ - cron: '0 2 * * 1'
7
+
8
+ jobs:
9
+ pytox:
10
+ name: Python and Tox env
11
+ runs-on: 'ubuntu-24.04'
12
+ outputs:
13
+ min_python: ${{ steps.vars.outputs.min_python }}
14
+ min_tox_env: ${{ steps.vars.outputs.min_tox_env }}
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Get Python version for other CI jobs
18
+ id: vars
19
+ run: |
20
+ echo "min_python=$(cat .github/workflows/python-version-ci)" >> $GITHUB_OUTPUT
21
+ echo "min_tox_env=py$(cat .github/workflows/python-version-ci | sed 's/\.//g')" >> $GITHUB_OUTPUT
22
+ tests:
23
+ name: Tests
24
+ needs: pytox
25
+ strategy:
26
+ matrix:
27
+ os: ['macos-latest', 'windows-latest']
28
+ python:
29
+ - version: '${{needs.pytox.outputs.min_python}}'
30
+ tox-env: '${{needs.pytox.outputs.min_tox_env}}'
31
+ uses: ./.github/workflows/test.yml
32
+ with:
33
+ os-variant: ${{ matrix.os }}
34
+ python-version: ${{ matrix.python.version }}
35
+ tox-env: ${{ matrix.python.tox-env }}
36
+
37
+ docs:
38
+ needs: tests
39
+ uses: ./.github/workflows/docs.yml
40
+ with:
41
+ publish: false
42
+ branch: ${{ github.head_ref == '' && github.ref_name || github.head_ref }}
@@ -4,7 +4,8 @@ dist
4
4
  html
5
5
  .tox
6
6
  *.egg-info
7
- uv.lock # we lock dependencies with pip-compile, not uv
7
+ # we lock dependencies with pip-compile, not uv
8
+ uv.lock
8
9
 
9
10
  *.sw?
10
11
 
@@ -23,7 +23,7 @@ repos:
23
23
  args: [ "--drop-empty-cells",
24
24
  "--extra-keys 'metadata.language_info.version cell.metadata.jp-MarkdownHeadingCollapsed cell.metadata.pycharm'" ]
25
25
  - repo: https://github.com/astral-sh/ruff-pre-commit
26
- rev: v0.6.9
26
+ rev: v0.8.0
27
27
  hooks:
28
28
  - id: ruff
29
29
  args: [ --fix ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: essreduce
3
- Version: 25.1.1
3
+ Version: 25.2.1
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License: BSD 3-Clause License
@@ -239,7 +239,7 @@
239
239
  "raw_data = ess_beamline.get_monitor(\"detector\")[0]\n",
240
240
  "\n",
241
241
  "# Visualize\n",
242
- "raw_data.hist(event_time_offset=300).sum(\"pulse\").plot()"
242
+ "raw_data.hist(event_time_offset=300).squeeze().plot()"
243
243
  ]
244
244
  },
245
245
  {
@@ -290,8 +290,10 @@
290
290
  " time_of_flight.providers(), params=time_of_flight.default_parameters()\n",
291
291
  ")\n",
292
292
  "workflow[time_of_flight.RawData] = raw_data\n",
293
- "workflow[time_of_flight.LtotalRange] = (sc.scalar(75.5, unit='m'),\n",
294
- " sc.scalar(78.0, unit='m'))\n",
293
+ "workflow[time_of_flight.LtotalRange] = (\n",
294
+ " sc.scalar(75.5, unit=\"m\"),\n",
295
+ " sc.scalar(78.0, unit=\"m\"),\n",
296
+ ")\n",
295
297
  "\n",
296
298
  "workflow.visualize(time_of_flight.TofData)"
297
299
  ]
@@ -314,8 +316,7 @@
314
316
  "outputs": [],
315
317
  "source": [
316
318
  "workflow[time_of_flight.SimulationResults] = time_of_flight.simulate_beamline(\n",
317
- " choppers=disk_choppers,\n",
318
- " neutrons=2_000_000\n",
319
+ " choppers=disk_choppers, neutrons=2_000_000\n",
319
320
  ")"
320
321
  ]
321
322
  },
@@ -343,17 +344,24 @@
343
344
  "outputs": [],
344
345
  "source": [
345
346
  "sim = workflow.compute(time_of_flight.SimulationResults)\n",
346
- "# Compute time-of-arrival at the detector\n",
347
- "tarrival = sim.time_of_arrival + ((Ltotal - sim.distance) / sim.speed).to(unit=\"us\")\n",
348
- "# Compute time-of-flight at the detector\n",
349
- "tflight = (Ltotal / sim.speed).to(unit=\"us\")\n",
350
- "\n",
351
- "events = sc.DataArray(\n",
352
- " data=sim.weight,\n",
353
- " coords={\"wavelength\": sim.wavelength, \"toa\": tarrival, \"tof\": tflight},\n",
354
- ")\n",
355
- "fig1 = events.hist(wavelength=300, toa=300).plot(norm=\"log\")\n",
356
- "fig2 = events.hist(tof=300, toa=300).plot(norm=\"log\")\n",
347
+ "\n",
348
+ "\n",
349
+ "def to_event_time_offset(sim):\n",
350
+ " # Compute event_time_offset at the detector\n",
351
+ " eto = (\n",
352
+ " sim.time_of_arrival + ((Ltotal - sim.distance) / sim.speed).to(unit=\"us\")\n",
353
+ " ) % sc.scalar(1e6 / 14.0, unit=\"us\")\n",
354
+ " # Compute time-of-flight at the detector\n",
355
+ " tof = (Ltotal / sim.speed).to(unit=\"us\")\n",
356
+ " return sc.DataArray(\n",
357
+ " data=sim.weight,\n",
358
+ " coords={\"wavelength\": sim.wavelength, \"event_time_offset\": eto, \"tof\": tof},\n",
359
+ " )\n",
360
+ "\n",
361
+ "\n",
362
+ "events = to_event_time_offset(sim)\n",
363
+ "fig1 = events.hist(wavelength=300, event_time_offset=300).plot(norm=\"log\")\n",
364
+ "fig2 = events.hist(tof=300, event_time_offset=300).plot(norm=\"log\")\n",
357
365
  "fig1 + fig2"
358
366
  ]
359
367
  },
@@ -374,10 +382,10 @@
374
382
  "metadata": {},
375
383
  "outputs": [],
376
384
  "source": [
377
- "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable)\n",
385
+ "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable).squeeze()\n",
378
386
  "\n",
379
387
  "# Overlay mean on the figure above\n",
380
- "table[\"distance\", 13].plot(ax=fig2.ax, color=\"C1\", ls='-', marker=None)"
388
+ "table[\"distance\", 13].plot(ax=fig2.ax, color=\"C1\", ls=\"-\", marker=None)"
381
389
  ]
382
390
  },
383
391
  {
@@ -447,7 +455,8 @@
447
455
  "# Define wavelength bin edges\n",
448
456
  "wavs = sc.linspace(\"wavelength\", 0.8, 4.6, 201, unit=\"angstrom\")\n",
449
457
  "\n",
450
- "wav_wfm.hist(wavelength=wavs).sum(\"pulse\").plot()"
458
+ "histogrammed = wav_wfm.hist(wavelength=wavs).squeeze()\n",
459
+ "histogrammed.plot()"
451
460
  ]
452
461
  },
453
462
  {
@@ -473,7 +482,7 @@
473
482
  "\n",
474
483
  "pp.plot(\n",
475
484
  " {\n",
476
- " \"wfm\": wav_wfm.hist(wavelength=wavs).sum(\"pulse\"),\n",
485
+ " \"wfm\": histogrammed,\n",
477
486
  " \"ground_truth\": ground_truth.hist(wavelength=wavs),\n",
478
487
  " }\n",
479
488
  ")"
@@ -530,16 +539,12 @@
530
539
  "outputs": [],
531
540
  "source": [
532
541
  "raw_data = sc.concat(\n",
533
- " [ess_beamline.get_monitor(key)[0] for key in monitors.keys()],\n",
542
+ " [ess_beamline.get_monitor(key)[0].squeeze() for key in monitors.keys()],\n",
534
543
  " dim=\"detector_number\",\n",
535
544
  ")\n",
536
545
  "\n",
537
546
  "# Visualize\n",
538
- "pp.plot(\n",
539
- " sc.collapse(\n",
540
- " raw_data.hist(event_time_offset=300).sum(\"pulse\"), keep=\"event_time_offset\"\n",
541
- " )\n",
542
- ")"
547
+ "pp.plot(sc.collapse(raw_data.hist(event_time_offset=300), keep=\"event_time_offset\"))"
543
548
  ]
544
549
  },
545
550
  {
@@ -655,22 +660,14 @@
655
660
  "source": [
656
661
  "# Update workflow\n",
657
662
  "workflow[time_of_flight.SimulationResults] = time_of_flight.simulate_beamline(\n",
658
- " choppers=disk_choppers,\n",
659
- " neutrons=2_000_000\n",
663
+ " choppers=disk_choppers, neutrons=2_000_000\n",
660
664
  ")\n",
661
665
  "workflow[time_of_flight.RawData] = ess_beamline.get_monitor(\"detector\")[0]\n",
662
666
  "\n",
663
667
  "sim = workflow.compute(time_of_flight.SimulationResults)\n",
664
- "# Compute time-of-arrival at the detector\n",
665
- "tarrival = sim.time_of_arrival + ((Ltotal - sim.distance) / sim.speed).to(unit=\"us\")\n",
666
- "# Compute time-of-flight at the detector\n",
667
- "tflight = (Ltotal / sim.speed).to(unit=\"us\")\n",
668
- "\n",
669
- "events = sc.DataArray(\n",
670
- " data=sim.weight,\n",
671
- " coords={\"wavelength\": sim.wavelength, \"toa\": tarrival, \"tof\": tflight},\n",
672
- ")\n",
673
- "events.hist(wavelength=300, toa=300).plot(norm=\"log\")"
668
+ "\n",
669
+ "events = to_event_time_offset(sim)\n",
670
+ "events.hist(wavelength=300, event_time_offset=300).plot(norm=\"log\")"
674
671
  ]
675
672
  },
676
673
  {
@@ -696,7 +693,7 @@
696
693
  "metadata": {},
697
694
  "outputs": [],
698
695
  "source": [
699
- "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable)\n",
696
+ "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable).squeeze()\n",
700
697
  "table.plot() / (sc.stddevs(table) / sc.values(table)).plot(norm=\"log\")"
701
698
  ]
702
699
  },
@@ -720,9 +717,9 @@
720
717
  "metadata": {},
721
718
  "outputs": [],
722
719
  "source": [
723
- "workflow[time_of_flight.LookupTableRelativeErrorThreshold] = 1.0e-2\n",
720
+ "workflow[time_of_flight.LookupTableRelativeErrorThreshold] = 0.01\n",
724
721
  "\n",
725
- "workflow.compute(time_of_flight.MaskedTimeOfFlightLookupTable).plot()"
722
+ "workflow.compute(time_of_flight.TimeOfFlightLookupTable).squeeze().plot()"
726
723
  ]
727
724
  },
728
725
  {
@@ -757,7 +754,7 @@
757
754
  "\n",
758
755
  "pp.plot(\n",
759
756
  " {\n",
760
- " \"wfm\": wav_wfm.hist(wavelength=wavs).sum(\"pulse\"),\n",
757
+ " \"wfm\": wav_wfm.hist(wavelength=wavs).squeeze(),\n",
761
758
  " \"ground_truth\": ground_truth.hist(wavelength=wavs),\n",
762
759
  " }\n",
763
760
  ")"
@@ -88,22 +88,12 @@
88
88
  "\n",
89
89
  "model = tof.Model(source=source, choppers=[chopper], detectors=detectors)\n",
90
90
  "results = model.run()\n",
91
- "pl = results.plot(cmap=\"viridis_r\")\n",
91
+ "pl = results.plot()\n",
92
92
  "\n",
93
- "for i in range(source.pulses):\n",
93
+ "for i in range(2 * source.pulses):\n",
94
94
  " pl.ax.axvline(\n",
95
95
  " i * (1.0 / source.frequency).to(unit=\"us\").value, color=\"k\", ls=\"dotted\"\n",
96
- " )\n",
97
- " x = [\n",
98
- " results[det.name].toas.data[\"visible\"][f\"pulse:{i}\"].coords[\"toa\"].min().value\n",
99
- " for det in detectors\n",
100
- " ]\n",
101
- " y = [det.distance.value for det in detectors]\n",
102
- " pl.ax.plot(x, y, \"--o\", color=\"magenta\", lw=3)\n",
103
- " if i == 0:\n",
104
- " pl.ax.text(\n",
105
- " x[2], y[2] * 1.05, \"pivot time\", va=\"bottom\", ha=\"right\", color=\"magenta\"\n",
106
- " )"
96
+ " )"
107
97
  ]
108
98
  },
109
99
  {
@@ -142,14 +132,6 @@
142
132
  " .hist(event_time_offset=200)\n",
143
133
  " .plot(title=f\"{det.name}={det.distance:c}\", color=f\"C{i}\")\n",
144
134
  " )\n",
145
- " f = subplots[i // 2, i % 2]\n",
146
- " xpiv = min(\n",
147
- " da.coords[\"toa\"].min() % (1.0 / source.frequency).to(unit=\"us\")\n",
148
- " for da in results[det.name].toas.data[\"visible\"].values()\n",
149
- " ).value\n",
150
- " f.ax.axvline(xpiv, ls=\"dashed\", color=\"magenta\", lw=2)\n",
151
- " f.ax.text(xpiv, 20, \"pivot time\", rotation=90, color=\"magenta\")\n",
152
- " f.canvas.draw()\n",
153
135
  "subplots"
154
136
  ]
155
137
  },
@@ -157,39 +139,23 @@
157
139
  "cell_type": "markdown",
158
140
  "id": "7",
159
141
  "metadata": {},
160
- "source": [
161
- "### Pivot time\n",
162
- "\n",
163
- "To compute the time-of-flight for a neutron, we need to identify which source pulse it originated from.\n",
164
- "\n",
165
- "In the first figure, the pink lines represent the earliest recorded arrival time at each detector:\n",
166
- "we know that within a given frame at a selected detector,\n",
167
- "any neutron recorded at a time earlier than this 'pivot' time must from from a previous pulse.\n",
168
- "\n",
169
- "The position of the pink lines is repeated in the second figure (above).\n",
170
- "We can use this knowledge to unwrap the frames and compute the absolute time-of-arrival of the neutrons at the detectors."
171
- ]
172
- },
173
- {
174
- "cell_type": "markdown",
175
- "id": "8",
176
- "metadata": {},
177
142
  "source": [
178
143
  "### Computing time-of-flight\n",
179
144
  "\n",
180
- "The pivot time and the resulting offsets can be computed from the properties of the source pulse and the chopper cascade.\n",
181
- "\n",
182
145
  "We describe in this section the workflow that computes time-of-flight,\n",
183
146
  "given `event_time_zero` and `event_time_offset` for neutron events,\n",
184
147
  "as well as the properties of the source pulse and the choppers in the beamline.\n",
185
148
  "\n",
149
+ "In short, we use a lookup table which can predict the wavelength (or time-of-flight) of the neutrons,\n",
150
+ "according to their `event_time_offset`.\n",
151
+ "\n",
186
152
  "The workflow can be visualized as follows:"
187
153
  ]
188
154
  },
189
155
  {
190
156
  "cell_type": "code",
191
157
  "execution_count": null,
192
- "id": "9",
158
+ "id": "8",
193
159
  "metadata": {},
194
160
  "outputs": [],
195
161
  "source": [
@@ -221,95 +187,16 @@
221
187
  },
222
188
  {
223
189
  "cell_type": "markdown",
224
- "id": "10",
225
- "metadata": {},
226
- "source": [
227
- "#### Unwrapped neutron time-of-arrival\n",
228
- "\n",
229
- "The first step that is computed in the workflow is the unwrapped detector arrival time of each neutron.\n",
230
- "This is essentially just `event_time_offset + event_time_zero`."
231
- ]
232
- },
233
- {
234
- "cell_type": "code",
235
- "execution_count": null,
236
- "id": "11",
237
- "metadata": {},
238
- "outputs": [],
239
- "source": [
240
- "da = workflow.compute(time_of_flight.UnwrappedTimeOfArrival)[\n",
241
- " \"detector_number\", 2\n",
242
- "] # Look at a single detector\n",
243
- "da.bins.concat().value.hist(time_of_arrival=300).plot()"
244
- ]
245
- },
246
- {
247
- "cell_type": "markdown",
248
- "id": "12",
249
- "metadata": {},
250
- "source": [
251
- "#### Unwrapped neutron time-of-arrival minus pivot time\n",
252
- "\n",
253
- "The next step is to subtract the pivot time to the unwrapped arrival times,\n",
254
- "to align the times so that they start at zero.\n",
255
- "\n",
256
- "This allows us to perform a computationally cheap modulo operation on the times below."
257
- ]
258
- },
259
- {
260
- "cell_type": "code",
261
- "execution_count": null,
262
- "id": "13",
263
- "metadata": {},
264
- "outputs": [],
265
- "source": [
266
- "da = workflow.compute(time_of_flight.UnwrappedTimeOfArrivalMinusPivotTime)[\n",
267
- " \"detector_number\", 2\n",
268
- "]\n",
269
- "f = da.bins.concat().value.hist(time_of_arrival=300).plot()\n",
270
- "for i in range(source.pulses):\n",
271
- " f.ax.axvline(\n",
272
- " i * (1.0 / source.frequency).to(unit=\"us\").value, color=\"k\", ls=\"dotted\"\n",
273
- " )\n",
274
- "f"
275
- ]
276
- },
277
- {
278
- "cell_type": "markdown",
279
- "id": "14",
280
- "metadata": {},
281
- "source": [
282
- "The vertical dotted lines here represent the frame period.\n",
283
- "\n",
284
- "#### Unwrapped neutron time-of-arrival modulo the frame period\n",
285
- "\n",
286
- "We now wrap the arrival times with the frame period to obtain well formed (unbroken) set of events.\n",
287
- "\n",
288
- "We also re-add the pivot time offset we had subtracted earlier (to enable to modulo operation)."
289
- ]
290
- },
291
- {
292
- "cell_type": "code",
293
- "execution_count": null,
294
- "id": "15",
295
- "metadata": {},
296
- "outputs": [],
297
- "source": [
298
- "da = workflow.compute(time_of_flight.FrameFoldedTimeOfArrival)[\"detector_number\", 2]\n",
299
- "da.bins.concat().value.hist(time_of_arrival=200).plot()"
300
- ]
301
- },
302
- {
303
- "cell_type": "markdown",
304
- "id": "16",
190
+ "id": "9",
305
191
  "metadata": {},
306
192
  "source": [
307
- "#### Create a lookup table\n",
193
+ "#### Create the lookup table\n",
308
194
  "\n",
309
- "The chopper information is next used to construct a lookup table that provides an estimate of the real time-of-flight as a function of time-of-arrival.\n",
195
+ "The chopper information is used to construct a lookup table that provides an estimate of the real time-of-flight as a function of time-of-arrival.\n",
310
196
  "\n",
311
197
  "The `tof` module can be used to propagate a pulse of neutrons through the chopper system to the detectors,\n",
312
198
  "and predict the most likely neutron wavelength for a given time-of-arrival.\n",
199
+ "More advanced programs such as McStas can of course also be used for even better results.\n",
313
200
  "\n",
314
201
  "We typically have hundreds of thousands of pixels in an instrument,\n",
315
202
  "but it is actually not necessary to propagate the neutrons to 10<sup>5</sup> detectors.\n",
@@ -332,17 +219,17 @@
332
219
  {
333
220
  "cell_type": "code",
334
221
  "execution_count": null,
335
- "id": "17",
222
+ "id": "10",
336
223
  "metadata": {},
337
224
  "outputs": [],
338
225
  "source": [
339
- "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable)\n",
226
+ "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable).squeeze()\n",
340
227
  "table.plot()"
341
228
  ]
342
229
  },
343
230
  {
344
231
  "cell_type": "markdown",
345
- "id": "18",
232
+ "id": "11",
346
233
  "metadata": {},
347
234
  "source": [
348
235
  "#### Computing time-of-flight from the lookup\n",
@@ -353,19 +240,19 @@
353
240
  {
354
241
  "cell_type": "code",
355
242
  "execution_count": null,
356
- "id": "19",
243
+ "id": "12",
357
244
  "metadata": {},
358
245
  "outputs": [],
359
246
  "source": [
360
247
  "tofs = workflow.compute(time_of_flight.TofData)\n",
361
248
  "\n",
362
- "tof_hist = tofs.bins.concat(\"pulse\").hist(tof=sc.scalar(500.0, unit=\"us\"))\n",
249
+ "tof_hist = tofs.hist(tof=sc.scalar(500.0, unit=\"us\"))\n",
363
250
  "pp.plot({det.name: tof_hist[\"detector_number\", i] for i, det in enumerate(detectors)})"
364
251
  ]
365
252
  },
366
253
  {
367
254
  "cell_type": "markdown",
368
- "id": "20",
255
+ "id": "13",
369
256
  "metadata": {},
370
257
  "source": [
371
258
  "### Converting to wavelength\n",
@@ -379,7 +266,7 @@
379
266
  {
380
267
  "cell_type": "code",
381
268
  "execution_count": null,
382
- "id": "21",
269
+ "id": "14",
383
270
  "metadata": {},
384
271
  "outputs": [],
385
272
  "source": [
@@ -393,11 +280,7 @@
393
280
  "bins = sc.linspace(\"wavelength\", 6.0, 9.0, 101, unit=\"angstrom\")\n",
394
281
  "\n",
395
282
  "# Compute wavelengths\n",
396
- "wav_hist = (\n",
397
- " tofs.transform_coords(\"wavelength\", graph=graph)\n",
398
- " .bins.concat(\"pulse\")\n",
399
- " .hist(wavelength=bins)\n",
400
- ")\n",
283
+ "wav_hist = tofs.transform_coords(\"wavelength\", graph=graph).hist(wavelength=bins)\n",
401
284
  "wavs = {det.name: wav_hist[\"detector_number\", i] for i, det in enumerate(detectors)}\n",
402
285
  "\n",
403
286
  "ground_truth = results[\"detector\"].data.flatten(to=\"event\")\n",
@@ -411,7 +294,7 @@
411
294
  },
412
295
  {
413
296
  "cell_type": "markdown",
414
- "id": "22",
297
+ "id": "15",
415
298
  "metadata": {},
416
299
  "source": [
417
300
  "We see that all detectors agree on the wavelength spectrum,\n",
@@ -432,7 +315,7 @@
432
315
  {
433
316
  "cell_type": "code",
434
317
  "execution_count": null,
435
- "id": "23",
318
+ "id": "16",
436
319
  "metadata": {},
437
320
  "outputs": [],
438
321
  "source": [
@@ -467,7 +350,7 @@
467
350
  },
468
351
  {
469
352
  "cell_type": "markdown",
470
- "id": "24",
353
+ "id": "17",
471
354
  "metadata": {},
472
355
  "source": [
473
356
  "### Computing time-of-flight\n",
@@ -481,7 +364,7 @@
481
364
  {
482
365
  "cell_type": "code",
483
366
  "execution_count": null,
484
- "id": "25",
367
+ "id": "18",
485
368
  "metadata": {},
486
369
  "outputs": [],
487
370
  "source": [
@@ -517,27 +400,27 @@
517
400
  },
518
401
  {
519
402
  "cell_type": "markdown",
520
- "id": "26",
403
+ "id": "19",
521
404
  "metadata": {},
522
405
  "source": [
523
- "If we inspect the time-of-flight lookup table,\n",
524
- "we can see that the time-of-arrival (toa) dimension now spans longer than the pulse period of 71 ms."
406
+ "The time-of-flight lookup table now has an extra `pulse` dimension for each pulse in the stride:"
525
407
  ]
526
408
  },
527
409
  {
528
410
  "cell_type": "code",
529
411
  "execution_count": null,
530
- "id": "27",
412
+ "id": "20",
531
413
  "metadata": {},
532
414
  "outputs": [],
533
415
  "source": [
534
416
  "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable)\n",
535
- "table.plot()"
417
+ "\n",
418
+ "table[\"pulse\", 0].plot(title=\"Pulse-0\") + table[\"pulse\", 1].plot(title=\"Pulse-1\")"
536
419
  ]
537
420
  },
538
421
  {
539
422
  "cell_type": "markdown",
540
- "id": "28",
423
+ "id": "21",
541
424
  "metadata": {},
542
425
  "source": [
543
426
  "The time-of-flight profiles are then:"
@@ -546,19 +429,19 @@
546
429
  {
547
430
  "cell_type": "code",
548
431
  "execution_count": null,
549
- "id": "29",
432
+ "id": "22",
550
433
  "metadata": {},
551
434
  "outputs": [],
552
435
  "source": [
553
436
  "tofs = workflow.compute(time_of_flight.TofData)\n",
554
437
  "\n",
555
- "tof_hist = tofs.bins.concat(\"pulse\").hist(tof=sc.scalar(500.0, unit=\"us\"))\n",
438
+ "tof_hist = tofs.hist(tof=sc.scalar(500.0, unit=\"us\"))\n",
556
439
  "pp.plot({det.name: tof_hist[\"detector_number\", i] for i, det in enumerate(detectors)})"
557
440
  ]
558
441
  },
559
442
  {
560
443
  "cell_type": "markdown",
561
- "id": "30",
444
+ "id": "23",
562
445
  "metadata": {},
563
446
  "source": [
564
447
  "### Conversion to wavelength\n",
@@ -569,7 +452,7 @@
569
452
  {
570
453
  "cell_type": "code",
571
454
  "execution_count": null,
572
- "id": "31",
455
+ "id": "24",
573
456
  "metadata": {},
574
457
  "outputs": [],
575
458
  "source": [
@@ -577,11 +460,7 @@
577
460
  "bins = sc.linspace(\"wavelength\", 1.0, 8.0, 401, unit=\"angstrom\")\n",
578
461
  "\n",
579
462
  "# Compute wavelengths\n",
580
- "wav_hist = (\n",
581
- " tofs.transform_coords(\"wavelength\", graph=graph)\n",
582
- " .bins.concat(\"pulse\")\n",
583
- " .hist(wavelength=bins)\n",
584
- ")\n",
463
+ "wav_hist = tofs.transform_coords(\"wavelength\", graph=graph).hist(wavelength=bins)\n",
585
464
  "wavs = {det.name: wav_hist[\"detector_number\", i] for i, det in enumerate(detectors)}\n",
586
465
  "\n",
587
466
  "ground_truth = results[\"detector\"].data.flatten(to=\"event\")\n",
@@ -259,7 +259,7 @@
259
259
  "raw_data = ess_beamline.get_monitor(\"detector\")[0]\n",
260
260
  "\n",
261
261
  "# Visualize\n",
262
- "raw_data.hist(event_time_offset=300).sum(\"pulse\").plot()"
262
+ "raw_data.hist(event_time_offset=300).squeeze().plot()"
263
263
  ]
264
264
  },
265
265
  {
@@ -394,7 +394,7 @@
394
394
  "metadata": {},
395
395
  "outputs": [],
396
396
  "source": [
397
- "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable)\n",
397
+ "table = workflow.compute(time_of_flight.TimeOfFlightLookupTable).squeeze()\n",
398
398
  "\n",
399
399
  "# Overlay mean on the figure above\n",
400
400
  "table[\"distance\", 1].plot(ax=fig2.ax, color=\"C1\", ls=\"-\", marker=None)\n",
@@ -477,7 +477,8 @@
477
477
  "# Define wavelength bin edges\n",
478
478
  "wavs = sc.linspace(\"wavelength\", 2, 10, 301, unit=\"angstrom\")\n",
479
479
  "\n",
480
- "wav_wfm.hist(wavelength=wavs).sum(\"pulse\").plot()"
480
+ "histogrammed = wav_wfm.hist(wavelength=wavs).squeeze()\n",
481
+ "histogrammed.plot()"
481
482
  ]
482
483
  },
483
484
  {
@@ -503,7 +504,7 @@
503
504
  "\n",
504
505
  "pp.plot(\n",
505
506
  " {\n",
506
- " \"wfm\": wav_wfm.hist(wavelength=wavs).sum(\"pulse\"),\n",
507
+ " \"wfm\": histogrammed,\n",
507
508
  " \"ground_truth\": ground_truth.hist(wavelength=wavs),\n",
508
509
  " }\n",
509
510
  ")"