essreduce 26.1.0__tar.gz → 26.1.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.
- {essreduce-26.1.0 → essreduce-26.1.1}/PKG-INFO +1 -1
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/streaming.py +15 -1
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/lut.py +4 -7
- {essreduce-26.1.0 → essreduce-26.1.1}/src/essreduce.egg-info/PKG-INFO +1 -1
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/streaming_test.py +129 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.copier-answers.ess.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.copier-answers.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/ISSUE_TEMPLATE/high-level-requirement.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/dependabot.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/ci.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/docs.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/nightly_at_main.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/nightly_at_main_lower_bound.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/nightly_at_release.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/python-version-ci +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/release.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/test.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/unpinned.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.github/workflows/weekly_windows_macos.yml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.gitignore +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.pre-commit-config.yaml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/.python-version +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/CODE_OF_CONDUCT.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/CONTRIBUTING.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/LICENSE +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/MANIFEST.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/README.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_static/anaconda-icon.js +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_static/favicon.svg +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_static/logo-dark.svg +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_static/logo.svg +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_templates/class-template.rst +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_templates/doc_version.html +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/_templates/module-template.rst +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/about/index.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/api-reference/index.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/conf.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/developer/coding-conventions.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/developer/dependency-management.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/developer/getting-started.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/developer/gui.ipynb +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/developer/index.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/index.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/index.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/installation.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/reduction-workflow-guidelines.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/tof/dream.ipynb +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/tof/frame-unwrapping.ipynb +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/tof/index.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/tof/wfm.ipynb +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/docs/user-guide/widget.md +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/pyproject.toml +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/base.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/base.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/basetest.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/basetest.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/ci.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/ci.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/dev.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/dev.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/docs.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/docs.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/make_base.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/mypy.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/mypy.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/nightly.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/nightly.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/static.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/static.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/test.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/test.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/wheels.in +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/requirements/wheels.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/resources/logo.svg +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/setup.cfg +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/__init__.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/data/__init__.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/data/_registry.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/live/__init__.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/live/raw.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/live/roi.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/live/workflow.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/logging.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/nexus/__init__.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/nexus/_nexus_loader.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/nexus/json_generator.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/nexus/json_nexus.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/nexus/types.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/nexus/workflow.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/normalization.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/parameter.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/py.typed +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/scripts/grow_nexus.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/__init__.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/eto_to_tof.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/fakes.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/interpolator_numba.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/interpolator_scipy.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/resample.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/types.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/time_of_flight/workflow.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/ui.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/uncertainty.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/__init__.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_base.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_binedges_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_bounds_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_config.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_filename_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_linspace_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_optional_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_spinner.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_string_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_switchable_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/widgets/_vector_widget.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/ess/reduce/workflow.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/essreduce.egg-info/SOURCES.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/essreduce.egg-info/dependency_links.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/essreduce.egg-info/entry_points.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/essreduce.egg-info/requires.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/src/essreduce.egg-info/top_level.txt +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/accumulators_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/conftest.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/live/raw_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/live/roi_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_generator_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/array_dataset.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/dataset.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/detector.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/entry.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/event_data.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/instrument.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_examples/log.json +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/json_nexus_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/nexus_loader_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/nexus/workflow_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/normalization_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/package_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/scripts/test_grow_nexus.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/time_of_flight/interpolator_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/time_of_flight/lut_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/time_of_flight/resample_tests.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/time_of_flight/unwrap_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/time_of_flight/wfm_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/time_of_flight/workflow_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/uncertainty_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tests/widget_test.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tools/shrink_nexus.py +0 -0
- {essreduce-26.1.0 → essreduce-26.1.1}/tox.ini +0 -0
|
@@ -108,6 +108,14 @@ class Accumulator(ABC, Generic[T]):
|
|
|
108
108
|
Clear the accumulator, resetting it to its initial state.
|
|
109
109
|
"""
|
|
110
110
|
|
|
111
|
+
def on_finalize(self) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Called after finalize retrieves value.
|
|
114
|
+
|
|
115
|
+
Override this method to perform custom cleanup after each finalize cycle.
|
|
116
|
+
The default implementation does nothing.
|
|
117
|
+
"""
|
|
118
|
+
|
|
111
119
|
|
|
112
120
|
class EternalAccumulator(Accumulator[T]):
|
|
113
121
|
"""
|
|
@@ -422,6 +430,9 @@ class StreamProcessor:
|
|
|
422
430
|
needs_recompute |= self._context_key_to_cached_context_nodes_map[key]
|
|
423
431
|
for key, value in context.items():
|
|
424
432
|
self._context_workflow[key] = value
|
|
433
|
+
# Propagate context values to finalize workflow so providers that depend
|
|
434
|
+
# on context keys receive the updated values during finalize().
|
|
435
|
+
self._finalize_workflow[key] = value
|
|
425
436
|
results = self._context_workflow.compute(needs_recompute)
|
|
426
437
|
for key, value in results.items():
|
|
427
438
|
if key in self._target_keys:
|
|
@@ -505,7 +516,10 @@ class StreamProcessor:
|
|
|
505
516
|
"""
|
|
506
517
|
for key in self._accumulators:
|
|
507
518
|
self._finalize_workflow[key] = self._accumulators[key].value
|
|
508
|
-
|
|
519
|
+
result = self._finalize_workflow.compute(self._target_keys)
|
|
520
|
+
for acc in self._accumulators.values():
|
|
521
|
+
acc.on_finalize()
|
|
522
|
+
return result
|
|
509
523
|
|
|
510
524
|
def clear(self) -> None:
|
|
511
525
|
"""
|
|
@@ -30,9 +30,6 @@ class SimulationResults:
|
|
|
30
30
|
time_of_arrival:
|
|
31
31
|
Time of arrival of the neutrons at the position where the events were recorded
|
|
32
32
|
(1d array of size N).
|
|
33
|
-
speed:
|
|
34
|
-
Speed of the neutrons, typically derived from the wavelength of the neutrons
|
|
35
|
-
(1d array of size N).
|
|
36
33
|
wavelength:
|
|
37
34
|
Wavelength of the neutrons (1d array of size N).
|
|
38
35
|
weight:
|
|
@@ -48,12 +45,14 @@ class SimulationResults:
|
|
|
48
45
|
"""
|
|
49
46
|
|
|
50
47
|
time_of_arrival: sc.Variable
|
|
51
|
-
speed: sc.Variable
|
|
52
48
|
wavelength: sc.Variable
|
|
53
49
|
weight: sc.Variable
|
|
54
50
|
distance: sc.Variable
|
|
55
51
|
choppers: DiskChoppers[AnyRun] | None = None
|
|
56
52
|
|
|
53
|
+
def __post_init__(self):
|
|
54
|
+
self.speed = (sc.constants.h / sc.constants.m_n) / self.wavelength
|
|
55
|
+
|
|
57
56
|
|
|
58
57
|
NumberOfSimulatedNeutrons = NewType("NumberOfSimulatedNeutrons", int)
|
|
59
58
|
"""
|
|
@@ -239,7 +238,7 @@ def make_tof_lookup_table(
|
|
|
239
238
|
----------
|
|
240
239
|
simulation:
|
|
241
240
|
Results of a time-of-flight simulation used to create a lookup table.
|
|
242
|
-
The results should be a flat table with columns for time-of-arrival,
|
|
241
|
+
The results should be a flat table with columns for time-of-arrival,
|
|
243
242
|
wavelength, and weight.
|
|
244
243
|
ltotal_range:
|
|
245
244
|
Range of total flight path lengths from the source to the detector.
|
|
@@ -436,7 +435,6 @@ def simulate_chopper_cascade_using_tof(
|
|
|
436
435
|
events = source.data.squeeze().flatten(to='event')
|
|
437
436
|
return SimulationResults(
|
|
438
437
|
time_of_arrival=events.coords["birth_time"],
|
|
439
|
-
speed=events.coords["speed"],
|
|
440
438
|
wavelength=events.coords["wavelength"],
|
|
441
439
|
weight=events.data,
|
|
442
440
|
distance=0.0 * sc.units.m,
|
|
@@ -451,7 +449,6 @@ def simulate_chopper_cascade_using_tof(
|
|
|
451
449
|
]
|
|
452
450
|
return SimulationResults(
|
|
453
451
|
time_of_arrival=events.coords["toa"],
|
|
454
|
-
speed=events.coords["speed"],
|
|
455
452
|
wavelength=events.coords["wavelength"],
|
|
456
453
|
weight=events.data,
|
|
457
454
|
distance=furthest_chopper.distance,
|
|
@@ -908,3 +908,132 @@ def test_StreamProcessor_rejects_context_keys_depending_on_dynamic_keys() -> Non
|
|
|
908
908
|
target_keys=(Output,),
|
|
909
909
|
accumulators=(Output,),
|
|
910
910
|
)
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
def test_StreamProcessor_calls_on_finalize_after_finalize() -> None:
|
|
914
|
+
"""Test that on_finalize hook is called on accumulators after finalize."""
|
|
915
|
+
|
|
916
|
+
class TrackingAccumulator(streaming.EternalAccumulator[sc.Variable]):
|
|
917
|
+
def __init__(self) -> None:
|
|
918
|
+
super().__init__()
|
|
919
|
+
self.finalize_count = 0
|
|
920
|
+
|
|
921
|
+
def on_finalize(self) -> None:
|
|
922
|
+
self.finalize_count += 1
|
|
923
|
+
|
|
924
|
+
base_workflow = sciline.Pipeline(
|
|
925
|
+
(make_static_a, make_accum_a, make_accum_b, make_target)
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
accum_a = TrackingAccumulator()
|
|
929
|
+
accum_b = TrackingAccumulator()
|
|
930
|
+
|
|
931
|
+
streaming_wf = streaming.StreamProcessor(
|
|
932
|
+
base_workflow=base_workflow,
|
|
933
|
+
dynamic_keys=(DynamicA, DynamicB),
|
|
934
|
+
target_keys=(Target,),
|
|
935
|
+
accumulators={AccumA: accum_a, AccumB: accum_b},
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
assert accum_a.finalize_count == 0
|
|
939
|
+
assert accum_b.finalize_count == 0
|
|
940
|
+
|
|
941
|
+
streaming_wf.accumulate({DynamicA: sc.scalar(1), DynamicB: sc.scalar(4)})
|
|
942
|
+
result = streaming_wf.finalize()
|
|
943
|
+
assert sc.identical(result[Target], sc.scalar(2 * 1.0 / 4.0))
|
|
944
|
+
|
|
945
|
+
assert accum_a.finalize_count == 1
|
|
946
|
+
assert accum_b.finalize_count == 1
|
|
947
|
+
|
|
948
|
+
streaming_wf.accumulate({DynamicA: sc.scalar(2), DynamicB: sc.scalar(5)})
|
|
949
|
+
result = streaming_wf.finalize()
|
|
950
|
+
assert sc.identical(result[Target], sc.scalar(2 * 3.0 / 9.0))
|
|
951
|
+
|
|
952
|
+
assert accum_a.finalize_count == 2
|
|
953
|
+
assert accum_b.finalize_count == 2
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def test_on_finalize_can_clear_accumulator_for_window_behavior() -> None:
|
|
957
|
+
"""Test that on_finalize can be used to create windowed accumulators."""
|
|
958
|
+
|
|
959
|
+
class WindowAccumulator(streaming.EternalAccumulator[sc.Variable]):
|
|
960
|
+
"""Accumulator that clears after each finalize cycle."""
|
|
961
|
+
|
|
962
|
+
def on_finalize(self) -> None:
|
|
963
|
+
self.clear()
|
|
964
|
+
|
|
965
|
+
base_workflow = sciline.Pipeline(
|
|
966
|
+
(make_static_a, make_accum_a, make_accum_b, make_target)
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
# Use windowed accumulator for AccumA, eternal for AccumB
|
|
970
|
+
streaming_wf = streaming.StreamProcessor(
|
|
971
|
+
base_workflow=base_workflow,
|
|
972
|
+
dynamic_keys=(DynamicA, DynamicB),
|
|
973
|
+
target_keys=(Target,),
|
|
974
|
+
accumulators={
|
|
975
|
+
AccumA: WindowAccumulator(),
|
|
976
|
+
AccumB: streaming.EternalAccumulator(),
|
|
977
|
+
},
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
streaming_wf.accumulate({DynamicA: sc.scalar(1), DynamicB: sc.scalar(4)})
|
|
981
|
+
result = streaming_wf.finalize()
|
|
982
|
+
# AccumA = 2*1 = 2, AccumB = 4
|
|
983
|
+
assert sc.identical(result[Target], sc.scalar(2 * 1.0 / 4.0))
|
|
984
|
+
|
|
985
|
+
streaming_wf.accumulate({DynamicA: sc.scalar(2), DynamicB: sc.scalar(5)})
|
|
986
|
+
result = streaming_wf.finalize()
|
|
987
|
+
# AccumA was cleared, now 2*2 = 4, AccumB accumulated to 4+5 = 9
|
|
988
|
+
assert sc.identical(result[Target], sc.scalar(2 * 2.0 / 9.0))
|
|
989
|
+
|
|
990
|
+
streaming_wf.accumulate({DynamicA: sc.scalar(3), DynamicB: sc.scalar(6)})
|
|
991
|
+
result = streaming_wf.finalize()
|
|
992
|
+
# AccumA was cleared, now 2*3 = 6, AccumB accumulated to 9+6 = 15
|
|
993
|
+
assert sc.identical(result[Target], sc.scalar(2 * 3.0 / 15.0))
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def test_StreamProcessor_finalize_provider_uses_context_directly() -> None:
|
|
997
|
+
"""Test that finalize-time providers can access context keys directly.
|
|
998
|
+
|
|
999
|
+
This covers the case where a provider that runs during finalize() (i.e., one
|
|
1000
|
+
that depends on accumulated values) also needs access to a context key.
|
|
1001
|
+
The context value must be propagated to the finalize workflow.
|
|
1002
|
+
"""
|
|
1003
|
+
Streamed = NewType('Streamed', int)
|
|
1004
|
+
Context = NewType('Context', int)
|
|
1005
|
+
Accumulated = NewType('Accumulated', int)
|
|
1006
|
+
Output = NewType('Output', int)
|
|
1007
|
+
|
|
1008
|
+
def accumulate(streamed: Streamed) -> Accumulated:
|
|
1009
|
+
return Accumulated(streamed)
|
|
1010
|
+
|
|
1011
|
+
def make_output(accumulated: Accumulated, context: Context) -> Output:
|
|
1012
|
+
# This provider runs at finalize time (depends on Accumulated)
|
|
1013
|
+
# and needs direct access to Context
|
|
1014
|
+
return Output(accumulated + context)
|
|
1015
|
+
|
|
1016
|
+
wf = sciline.Pipeline((accumulate, make_output))
|
|
1017
|
+
streaming_wf = streaming.StreamProcessor(
|
|
1018
|
+
base_workflow=wf,
|
|
1019
|
+
dynamic_keys=(Streamed,),
|
|
1020
|
+
context_keys=(Context,),
|
|
1021
|
+
target_keys=(Output,),
|
|
1022
|
+
accumulators=(Accumulated,),
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
streaming_wf.set_context({Context: sc.scalar(100)})
|
|
1026
|
+
streaming_wf.accumulate({Streamed: sc.scalar(1)})
|
|
1027
|
+
streaming_wf.accumulate({Streamed: sc.scalar(2)})
|
|
1028
|
+
result = streaming_wf.finalize()
|
|
1029
|
+
|
|
1030
|
+
# Accumulated = 1 + 2 = 3, Context = 100, Output = 3 + 100 = 103
|
|
1031
|
+
assert sc.identical(result[Output], sc.scalar(103))
|
|
1032
|
+
|
|
1033
|
+
# Update context and verify finalize sees the new value
|
|
1034
|
+
streaming_wf.set_context({Context: sc.scalar(200)})
|
|
1035
|
+
streaming_wf.accumulate({Streamed: sc.scalar(4)})
|
|
1036
|
+
result = streaming_wf.finalize()
|
|
1037
|
+
|
|
1038
|
+
# Accumulated = 3 + 4 = 7, Context = 200, Output = 7 + 200 = 207
|
|
1039
|
+
assert sc.identical(result[Output], sc.scalar(207))
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|