pytesprocess 0.1.1__tar.gz → 0.1.2__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.
- {pytesprocess-0.1.1/pytesprocess.egg-info → pytesprocess-0.1.2}/PKG-INFO +3 -1
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/README.md +1 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pyproject.toml +1 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/_version.py +1 -1
- pytesprocess-0.1.2/pytesprocess/cli/__init__.py +16 -0
- pytesprocess-0.1.2/pytesprocess/cli/commands/__init__.py +5 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/cli/commands/event.py +11 -0
- pytesprocess-0.1.2/pytesprocess/cli/commands/feature.py +144 -0
- pytesprocess-0.1.2/pytesprocess/cli/commands/randoms.py +88 -0
- pytesprocess-0.1.2/pytesprocess/cli/commands/trigger.py +118 -0
- pytesprocess-0.1.2/pytesprocess/cli/commands/vibration.py +18 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/cli/main.py +27 -14
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/manager.py +20 -9
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/resolvers/__init__.py +3 -1
- pytesprocess-0.1.2/pytesprocess/config/resolvers/common.py +127 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/resolvers/feature.py +57 -32
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/resolvers/salting.py +5 -34
- pytesprocess-0.1.2/pytesprocess/config/resolvers/trigger.py +63 -0
- pytesprocess-0.1.2/pytesprocess/config/resolvers/vibration.py +230 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/selectors.py +30 -42
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/validation.py +11 -2
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/algorithms.py +281 -39
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/eventbuilder.py +143 -42
- pytesprocess-0.1.2/pytesprocess/core/filter_metadata.py +166 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/filterbuilder.py +4 -3
- pytesprocess-0.1.2/pytesprocess/core/filterdata.py +1628 -0
- pytesprocess-0.1.2/pytesprocess/core/ivsweep.py +1675 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/noise.py +274 -352
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/noisemodel.py +331 -584
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/oftrigger.py +105 -57
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/template.py +101 -30
- pytesprocess-0.1.2/pytesprocess/core/trigger_utils.py +18 -0
- pytesprocess-0.1.2/pytesprocess/process/data_source.py +419 -0
- pytesprocess-0.1.2/pytesprocess/process/feature_errors.py +113 -0
- pytesprocess-0.1.2/pytesprocess/process/feature_memory.py +40 -0
- pytesprocess-0.1.2/pytesprocess/process/feature_plan.py +300 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/feature_resources.py +49 -34
- pytesprocess-0.1.2/pytesprocess/process/feature_schema.py +80 -0
- pytesprocess-0.1.2/pytesprocess/process/feature_windows.py +26 -0
- pytesprocess-0.1.2/pytesprocess/process/feature_work.py +279 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/features.py +442 -144
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/filterprocess.py +7 -1
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/processing_data.py +147 -84
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/randoms.py +223 -36
- pytesprocess-0.1.2/pytesprocess/process/randoms_work.py +110 -0
- pytesprocess-0.1.2/pytesprocess/process/trigger_partitions.py +152 -0
- pytesprocess-0.1.2/pytesprocess/process/trigger_work.py +108 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/triggers.py +366 -84
- pytesprocess-0.1.2/pytesprocess/process/work_plan.py +217 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/utils/__init__.py +2 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/utils/arg_utils.py +23 -12
- pytesprocess-0.1.2/pytesprocess/utils/channels.py +186 -0
- pytesprocess-0.1.2/pytesprocess/utils/dataframe_output.py +270 -0
- pytesprocess-0.1.2/pytesprocess/utils/downsampling.py +170 -0
- pytesprocess-0.1.2/pytesprocess/utils/event_metadata.py +155 -0
- pytesprocess-0.1.2/pytesprocess/utils/filter_hdf5.py +539 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/utils/utils.py +31 -127
- pytesprocess-0.1.2/pytesprocess/vibration/__init__.py +7 -0
- pytesprocess-0.1.2/pytesprocess/vibration/analyzer.py +407 -0
- pytesprocess-0.1.2/pytesprocess/vibration/estimators.py +342 -0
- pytesprocess-0.1.2/pytesprocess/vibration/plotting.py +315 -0
- pytesprocess-0.1.2/pytesprocess/vibration/processing.py +581 -0
- pytesprocess-0.1.2/pytesprocess/workflows/__init__.py +3 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/workflows/processing.py +5 -6
- {pytesprocess-0.1.1 → pytesprocess-0.1.2/pytesprocess.egg-info}/PKG-INFO +3 -1
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess.egg-info/SOURCES.txt +60 -1
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess.egg-info/requires.txt +1 -0
- pytesprocess-0.1.2/tests/test_channel_expressions.py +187 -0
- pytesprocess-0.1.2/tests/test_cli_exit_status.py +61 -0
- pytesprocess-0.1.2/tests/test_config_resolvers.py +204 -0
- pytesprocess-0.1.2/tests/test_dataframe_output.py +136 -0
- pytesprocess-0.1.2/tests/test_downsampled_filters.py +144 -0
- pytesprocess-0.1.2/tests/test_downsampling.py +132 -0
- pytesprocess-0.1.2/tests/test_empty_and_selected_features.py +226 -0
- pytesprocess-0.1.2/tests/test_eventbuilder_coincidence.py +115 -0
- pytesprocess-0.1.2/tests/test_feature_downsampling.py +156 -0
- pytesprocess-0.1.2/tests/test_feature_memory.py +176 -0
- pytesprocess-0.1.2/tests/test_feature_on_error.py +201 -0
- pytesprocess-0.1.2/tests/test_feature_schema.py +62 -0
- pytesprocess-0.1.2/tests/test_feature_tasks.py +89 -0
- pytesprocess-0.1.2/tests/test_feature_unit_execution.py +229 -0
- pytesprocess-0.1.2/tests/test_feature_window_errors.py +271 -0
- pytesprocess-0.1.2/tests/test_feature_work_units.py +368 -0
- pytesprocess-0.1.2/tests/test_fft_bin.py +280 -0
- pytesprocess-0.1.2/tests/test_filter_compatibility.py +143 -0
- pytesprocess-0.1.2/tests/test_filter_hdf5.py +193 -0
- pytesprocess-0.1.2/tests/test_filter_metadata.py +185 -0
- pytesprocess-0.1.2/tests/test_memory_limit_per_worker.py +92 -0
- pytesprocess-0.1.2/tests/test_metadata_roundtrip.py +275 -0
- pytesprocess-0.1.2/tests/test_native_features.py +195 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/tests/test_processing_data_v15.py +2 -1
- pytesprocess-0.1.2/tests/test_processing_guards.py +160 -0
- pytesprocess-0.1.2/tests/test_randoms_work_units.py +130 -0
- pytesprocess-0.1.2/tests/test_restricted_processing.py +159 -0
- pytesprocess-0.1.2/tests/test_signed_feature_windows.py +293 -0
- pytesprocess-0.1.2/tests/test_template_metadata.py +82 -0
- pytesprocess-0.1.2/tests/test_trigger_alignment.py +120 -0
- pytesprocess-0.1.2/tests/test_trigger_downsampling.py +311 -0
- pytesprocess-0.1.2/tests/test_trigger_filter_reuse.py +113 -0
- pytesprocess-0.1.2/tests/test_trigger_partition_groups.py +293 -0
- pytesprocess-0.1.2/tests/test_trigger_work_units.py +138 -0
- pytesprocess-0.1.2/tests/test_vibration_estimators.py +155 -0
- pytesprocess-0.1.2/tests/test_vibration_processing.py +319 -0
- pytesprocess-0.1.1/pytesprocess/cli/__init__.py +0 -1
- pytesprocess-0.1.1/pytesprocess/cli/commands/__init__.py +0 -5
- pytesprocess-0.1.1/pytesprocess/config/resolvers/common.py +0 -56
- pytesprocess-0.1.1/pytesprocess/config/resolvers/trigger.py +0 -84
- pytesprocess-0.1.1/pytesprocess/core/filterdata.py +0 -1849
- pytesprocess-0.1.1/pytesprocess/core/ivsweep.py +0 -2072
- pytesprocess-0.1.1/pytesprocess/process/data_source.py +0 -185
- pytesprocess-0.1.1/pytesprocess/process/feature_plan.py +0 -186
- pytesprocess-0.1.1/pytesprocess/utils/dataframe_output.py +0 -120
- pytesprocess-0.1.1/pytesprocess/utils/filter_hdf5.py +0 -594
- pytesprocess-0.1.1/pytesprocess/workflows/__init__.py +0 -3
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/LICENSE +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/__init__.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/cli/commands/filter.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/cli/commands/ivsweep.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/cli/common.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/__init__.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/loader.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/config/warnings.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/__init__.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/core/didv.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/__init__.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/event_context.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/process/ivprocess.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/salting/__init__.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/salting/generator.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/salting/injector.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/salting/sampling.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess/workflows/salting.py +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess.egg-info/dependency_links.txt +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess.egg-info/entry_points.txt +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/pytesprocess.egg-info/top_level.txt +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/setup.cfg +0 -0
- {pytesprocess-0.1.1 → pytesprocess-0.1.2}/tests/test_cli_steps_v16.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytesprocess
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Detector data processing for pytesdaqx acquisitions
|
|
5
5
|
Author-email: Bruno Serfass <serfass@berkeley.edu>, Samuel Watkins <samwatkins@berkeley.edu>
|
|
6
6
|
License: MIT License
|
|
@@ -37,6 +37,7 @@ Requires-Dist: matplotlib
|
|
|
37
37
|
Requires-Dist: PyYAML
|
|
38
38
|
Requires-Dist: qetpy>=1.8.6
|
|
39
39
|
Requires-Dist: pandas
|
|
40
|
+
Requires-Dist: tables
|
|
40
41
|
Requires-Dist: pytesdaqx
|
|
41
42
|
Requires-Dist: humanfriendly
|
|
42
43
|
Requires-Dist: vaex
|
|
@@ -106,6 +107,7 @@ The separate detector-characterization/filter workflows remain explicit:
|
|
|
106
107
|
```bash
|
|
107
108
|
pytesprocess filter ACQUISITION --config processing.yaml
|
|
108
109
|
pytesprocess ivsweep ACQUISITION
|
|
110
|
+
pytesprocess vibration ACQUISITION --config vibration.yaml
|
|
109
111
|
```
|
|
110
112
|
|
|
111
113
|
Example:
|
|
@@ -56,6 +56,7 @@ The separate detector-characterization/filter workflows remain explicit:
|
|
|
56
56
|
```bash
|
|
57
57
|
pytesprocess filter ACQUISITION --config processing.yaml
|
|
58
58
|
pytesprocess ivsweep ACQUISITION
|
|
59
|
+
pytesprocess vibration ACQUISITION --config vibration.yaml
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
Example:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# special file for defining the current version of the package
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.2"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Command-line interface for pytesprocess.
|
|
2
|
+
|
|
3
|
+
``main`` is re-exported lazily: ``pytesprocess.workflows`` imports helpers from
|
|
4
|
+
``pytesprocess.cli.common``, so eagerly pulling in the command modules here
|
|
5
|
+
would make that a circular import.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__all__ = ["main"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __getattr__(name):
|
|
12
|
+
if name == "main":
|
|
13
|
+
from .main import main
|
|
14
|
+
|
|
15
|
+
return main
|
|
16
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -51,6 +51,17 @@ def configure(parser):
|
|
|
51
51
|
default=10.0,
|
|
52
52
|
dest="partition_duration",
|
|
53
53
|
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--memory-limit",
|
|
56
|
+
default="2GB",
|
|
57
|
+
help=(
|
|
58
|
+
"Memory safety limit per worker, not shared between them. For "
|
|
59
|
+
"continuous triggering it bounds the retained waveforms of one "
|
|
60
|
+
"connected pileup/coincidence group, whose size is set by the "
|
|
61
|
+
"data rather than by how much is processed at a time; raise it "
|
|
62
|
+
"if triggering reports the buffer limit."
|
|
63
|
+
),
|
|
64
|
+
)
|
|
54
65
|
|
|
55
66
|
# Random-generation options. They are validated only when randoms is
|
|
56
67
|
# requested, so the same top-level command works for other step sets.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Feature work-unit planning and execution.
|
|
2
|
+
|
|
3
|
+
Two commands that between them let feature processing be divided across any
|
|
4
|
+
number of workers: ``plan`` writes the division once, ``run`` executes one
|
|
5
|
+
piece of it. A unit is a plain command line, so the same division can be
|
|
6
|
+
driven by a shell loop, a job array, or anything else.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from pytesprocess.cli.common import add_common_acquisition_args, output_base
|
|
12
|
+
from pytesprocess.process import FeatureProcessing
|
|
13
|
+
from pytesprocess.process.feature_work import load_plan
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# This command builds its own sub-parsers, so the shared acquisition
|
|
17
|
+
# arguments are added per action rather than once at the top level.
|
|
18
|
+
COMMON_ARGS = False
|
|
19
|
+
|
|
20
|
+
_MEASUREMENT_TYPES = ["background", "calibration"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _add_shared(parser):
|
|
24
|
+
add_common_acquisition_args(parser, config=True, ncores=False)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--measurement-type",
|
|
27
|
+
default="background",
|
|
28
|
+
choices=_MEASUREMENT_TYPES,
|
|
29
|
+
help="Raw measurement type of the data behind the trigger dataframe.",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument("--external-features")
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--salting-dataframe",
|
|
34
|
+
help="Existing salting dataframe to inject during feature processing.",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def configure(parser):
|
|
39
|
+
actions = parser.add_subparsers(dest="action", required=True)
|
|
40
|
+
|
|
41
|
+
plan = actions.add_parser(
|
|
42
|
+
"plan",
|
|
43
|
+
help="divide a trigger dataframe into feature work units",
|
|
44
|
+
)
|
|
45
|
+
_add_shared(plan)
|
|
46
|
+
plan.add_argument(
|
|
47
|
+
"--trigger-dataframe",
|
|
48
|
+
required=True,
|
|
49
|
+
help="Trigger dataframe group or shard(s) to divide.",
|
|
50
|
+
)
|
|
51
|
+
size = plan.add_mutually_exclusive_group()
|
|
52
|
+
size.add_argument(
|
|
53
|
+
"--units",
|
|
54
|
+
type=int,
|
|
55
|
+
help="Number of work units. Sizes then differ by at most one row.",
|
|
56
|
+
)
|
|
57
|
+
size.add_argument(
|
|
58
|
+
"--rows-per-unit",
|
|
59
|
+
type=int,
|
|
60
|
+
help="Fixed rows per unit, which bounds the memory one worker needs.",
|
|
61
|
+
)
|
|
62
|
+
plan.add_argument(
|
|
63
|
+
"--plan-file",
|
|
64
|
+
help="Where to write the plan. Default: plan.json in the new group.",
|
|
65
|
+
)
|
|
66
|
+
plan.add_argument("--campaign", help="Campaign name, recorded for provenance.")
|
|
67
|
+
|
|
68
|
+
run = actions.add_parser(
|
|
69
|
+
"run",
|
|
70
|
+
help="process exactly one work unit of a plan",
|
|
71
|
+
)
|
|
72
|
+
_add_shared(run)
|
|
73
|
+
run.add_argument("--plan", required=True, help="Plan file to read.")
|
|
74
|
+
run.add_argument(
|
|
75
|
+
"--unit", type=int, required=True, help="1-based unit index to process."
|
|
76
|
+
)
|
|
77
|
+
run.add_argument(
|
|
78
|
+
"--trigger-dataframe",
|
|
79
|
+
help="Trigger dataframe group. Default: derived from the plan itself.",
|
|
80
|
+
)
|
|
81
|
+
run.add_argument(
|
|
82
|
+
"--product-group",
|
|
83
|
+
help="Override the product group recorded in the plan.",
|
|
84
|
+
)
|
|
85
|
+
run.add_argument("--memory-limit", default="2GB")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def run(args):
|
|
89
|
+
if args.action == "plan":
|
|
90
|
+
return _plan(args)
|
|
91
|
+
return _run_unit(args)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _processor(args, trigger_dataframe):
|
|
95
|
+
return FeatureProcessing(
|
|
96
|
+
args.acquisition,
|
|
97
|
+
args.config,
|
|
98
|
+
streams=args.streams,
|
|
99
|
+
trigger_dataframe_path=trigger_dataframe,
|
|
100
|
+
external_file=args.external_features,
|
|
101
|
+
processing_label=args.processing_label,
|
|
102
|
+
restricted=args.restricted,
|
|
103
|
+
data_type=args.measurement_type,
|
|
104
|
+
salting_dataframe=args.salting_dataframe,
|
|
105
|
+
verbose=not args.quiet,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _plan(args):
|
|
110
|
+
proc = _processor(args, args.trigger_dataframe)
|
|
111
|
+
return proc.plan(
|
|
112
|
+
n_units=args.units,
|
|
113
|
+
rows_per_unit=args.rows_per_unit,
|
|
114
|
+
save_path=output_base(args),
|
|
115
|
+
plan_path=args.plan_file,
|
|
116
|
+
campaign=args.campaign,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _run_unit(args):
|
|
121
|
+
plan = load_plan(args.plan)
|
|
122
|
+
trigger_dataframe = args.trigger_dataframe or _trigger_path_from_plan(plan)
|
|
123
|
+
proc = _processor(args, trigger_dataframe)
|
|
124
|
+
return proc.process_unit(
|
|
125
|
+
plan,
|
|
126
|
+
args.unit,
|
|
127
|
+
product_group=args.product_group,
|
|
128
|
+
memory_limit=args.memory_limit,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _trigger_path_from_plan(plan):
|
|
133
|
+
"""Locate the input trigger dataframe from the plan's own segments.
|
|
134
|
+
|
|
135
|
+
A unit job is told a plan and an index; it should not also have to be
|
|
136
|
+
told where the input lives, since the plan already says.
|
|
137
|
+
"""
|
|
138
|
+
for unit in plan.get("units", []):
|
|
139
|
+
for segment in unit.get("selector", {}).get("segments", []):
|
|
140
|
+
return str(Path(segment["file"]).parent)
|
|
141
|
+
raise ValueError(
|
|
142
|
+
"ERROR: This plan has no row segments, so the input trigger "
|
|
143
|
+
"dataframe cannot be located. Pass --trigger-dataframe explicitly."
|
|
144
|
+
)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Random-trigger work-unit planning and execution.
|
|
2
|
+
|
|
3
|
+
``plan`` describes the selection as one unit and records the seed; ``run``
|
|
4
|
+
generates it. The step is one unit by design: randoms are drawn across the
|
|
5
|
+
whole selection at once, so dividing it would change the product rather than
|
|
6
|
+
distribute the work.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pytesprocess.cli.common import add_common_acquisition_args, output_base
|
|
10
|
+
from pytesprocess.process import Randoms
|
|
11
|
+
from pytesprocess.process.work_plan import load_plan
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# This command builds its own sub-parsers, so the shared acquisition
|
|
15
|
+
# arguments are added per action rather than once at the top level.
|
|
16
|
+
COMMON_ARGS = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _add_shared(parser):
|
|
20
|
+
add_common_acquisition_args(parser, config=False, ncores=False)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def configure(parser):
|
|
24
|
+
actions = parser.add_subparsers(dest="action", required=True)
|
|
25
|
+
|
|
26
|
+
plan = actions.add_parser(
|
|
27
|
+
"plan", help="describe the random selection as a work unit"
|
|
28
|
+
)
|
|
29
|
+
_add_shared(plan)
|
|
30
|
+
count = plan.add_mutually_exclusive_group(required=True)
|
|
31
|
+
count.add_argument("--nrandoms", type=int, help="Exact number to generate.")
|
|
32
|
+
count.add_argument(
|
|
33
|
+
"--random-rate", type=float,
|
|
34
|
+
help="Average rate in Hz; the count follows from the live time.",
|
|
35
|
+
)
|
|
36
|
+
plan.add_argument("--min-separation-msec", type=float, default=0.0)
|
|
37
|
+
plan.add_argument("--edge-exclusion-msec", type=float, default=0.0)
|
|
38
|
+
plan.add_argument("--partition-duration", type=float, dest="partition_duration")
|
|
39
|
+
plan.add_argument(
|
|
40
|
+
"--random-seed", type=int,
|
|
41
|
+
help="Seed to record. One is generated and recorded if omitted.",
|
|
42
|
+
)
|
|
43
|
+
plan.add_argument("--plan-file")
|
|
44
|
+
plan.add_argument("--campaign")
|
|
45
|
+
|
|
46
|
+
run = actions.add_parser("run", help="generate the randoms of a plan")
|
|
47
|
+
_add_shared(run)
|
|
48
|
+
run.add_argument("--plan", required=True)
|
|
49
|
+
run.add_argument("--unit", type=int, default=1)
|
|
50
|
+
run.add_argument("--product-group")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def run(args):
|
|
54
|
+
if args.action == "plan":
|
|
55
|
+
return _plan(args)
|
|
56
|
+
return _run_unit(args)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _processor(args):
|
|
60
|
+
return Randoms(
|
|
61
|
+
args.acquisition,
|
|
62
|
+
streams=args.streams,
|
|
63
|
+
processing_label=args.processing_label,
|
|
64
|
+
data_type="background",
|
|
65
|
+
restricted=args.restricted,
|
|
66
|
+
verbose=not args.quiet,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _plan(args):
|
|
71
|
+
return _processor(args).plan(
|
|
72
|
+
nrandoms=args.nrandoms,
|
|
73
|
+
random_rate=args.random_rate,
|
|
74
|
+
min_separation_msec=args.min_separation_msec,
|
|
75
|
+
edge_exclusion_msec=args.edge_exclusion_msec,
|
|
76
|
+
partition_target_duration_s=args.partition_duration,
|
|
77
|
+
random_seed=args.random_seed,
|
|
78
|
+
save_path=output_base(args),
|
|
79
|
+
plan_path=args.plan_file,
|
|
80
|
+
campaign=args.campaign,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _run_unit(args):
|
|
85
|
+
plan = load_plan(args.plan, step="randoms")
|
|
86
|
+
return _processor(args).process_unit(
|
|
87
|
+
plan, args.unit, product_group=args.product_group
|
|
88
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Trigger work-unit planning and execution.
|
|
2
|
+
|
|
3
|
+
``plan`` divides the selected streams into units, one per stream, and records
|
|
4
|
+
the live time and edge exclusion once so that every unit stamps the same
|
|
5
|
+
values. ``run`` executes one unit of that plan.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pytesprocess.cli.common import (
|
|
9
|
+
add_common_acquisition_args,
|
|
10
|
+
output_base,
|
|
11
|
+
processing_config,
|
|
12
|
+
trigger_edge_parameters,
|
|
13
|
+
)
|
|
14
|
+
from pytesprocess.process import TriggerProcessing
|
|
15
|
+
from pytesprocess.process.work_plan import load_plan
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# This command builds its own sub-parsers, so the shared acquisition
|
|
19
|
+
# arguments are added per action rather than once at the top level.
|
|
20
|
+
COMMON_ARGS = False
|
|
21
|
+
|
|
22
|
+
_MEASUREMENT_TYPES = ["background", "calibration"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _add_shared(parser):
|
|
26
|
+
add_common_acquisition_args(parser, config=True, ncores=False)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--measurement-type",
|
|
29
|
+
default="background",
|
|
30
|
+
choices=_MEASUREMENT_TYPES,
|
|
31
|
+
)
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--salting-dataframe",
|
|
34
|
+
help="Existing salting dataframe to inject while triggering.",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--partition-duration", type=float, default=10.0,
|
|
38
|
+
dest="partition_duration",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def configure(parser):
|
|
43
|
+
actions = parser.add_subparsers(dest="action", required=True)
|
|
44
|
+
|
|
45
|
+
plan = actions.add_parser(
|
|
46
|
+
"plan", help="divide the selected streams into trigger work units"
|
|
47
|
+
)
|
|
48
|
+
_add_shared(plan)
|
|
49
|
+
plan.add_argument(
|
|
50
|
+
"--plan-file",
|
|
51
|
+
help="Where to write the plan. Default: plan.json in the new group.",
|
|
52
|
+
)
|
|
53
|
+
plan.add_argument("--campaign", help="Campaign name, recorded for provenance.")
|
|
54
|
+
|
|
55
|
+
run = actions.add_parser("run", help="process exactly one work unit of a plan")
|
|
56
|
+
_add_shared(run)
|
|
57
|
+
run.add_argument("--plan", required=True, help="Plan file to read.")
|
|
58
|
+
run.add_argument(
|
|
59
|
+
"--unit", type=int, required=True, help="1-based unit index to process."
|
|
60
|
+
)
|
|
61
|
+
run.add_argument(
|
|
62
|
+
"--product-group", help="Override the product group recorded in the plan."
|
|
63
|
+
)
|
|
64
|
+
run.add_argument("--ntriggers", type=int, default=-1)
|
|
65
|
+
run.add_argument("--memory-limit", default="2GB")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def run(args):
|
|
69
|
+
if args.action == "plan":
|
|
70
|
+
return _plan(args)
|
|
71
|
+
return _run_unit(args)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _processor(args, config):
|
|
75
|
+
return TriggerProcessing(
|
|
76
|
+
args.acquisition,
|
|
77
|
+
config,
|
|
78
|
+
streams=args.streams,
|
|
79
|
+
processing_label=args.processing_label,
|
|
80
|
+
restricted=args.restricted,
|
|
81
|
+
data_type=args.measurement_type,
|
|
82
|
+
salting_dataframe=args.salting_dataframe,
|
|
83
|
+
verbose=not args.quiet,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _plan(args):
|
|
88
|
+
config, catalog = processing_config(
|
|
89
|
+
args, workflows=["trigger"], measurement_types=args.measurement_type
|
|
90
|
+
)
|
|
91
|
+
# Computed once, over the whole selection, and carried in the plan: live
|
|
92
|
+
# time is not a property any single unit can work out for itself.
|
|
93
|
+
edge_msec, livetime = trigger_edge_parameters(config, catalog)
|
|
94
|
+
|
|
95
|
+
proc = _processor(args, config)
|
|
96
|
+
return proc.plan(
|
|
97
|
+
save_path=output_base(args),
|
|
98
|
+
plan_path=args.plan_file,
|
|
99
|
+
campaign=args.campaign,
|
|
100
|
+
edge_exclusion_msec=edge_msec,
|
|
101
|
+
livetime=livetime,
|
|
102
|
+
partition_target_duration_s=args.partition_duration,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _run_unit(args):
|
|
107
|
+
plan = load_plan(args.plan, step="trigger")
|
|
108
|
+
config, _ = processing_config(
|
|
109
|
+
args, workflows=["trigger"], measurement_types=args.measurement_type
|
|
110
|
+
)
|
|
111
|
+
proc = _processor(args, config)
|
|
112
|
+
return proc.process_unit(
|
|
113
|
+
plan,
|
|
114
|
+
args.unit,
|
|
115
|
+
product_group=args.product_group,
|
|
116
|
+
ntriggers=args.ntriggers,
|
|
117
|
+
memory_limit=args.memory_limit,
|
|
118
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pytesprocess.vibration import VibrationProcessing
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def configure(parser):
|
|
5
|
+
parser.add_argument("--config", help="Processing YAML file with a 'vibration' section")
|
|
6
|
+
parser.add_argument("--channels", nargs="+")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run(args):
|
|
10
|
+
proc = VibrationProcessing(
|
|
11
|
+
args.acquisition, config_file=args.config, streams=args.streams,
|
|
12
|
+
restricted=args.restricted, verbose=not args.quiet,
|
|
13
|
+
)
|
|
14
|
+
proc.process(
|
|
15
|
+
channels=args.channels, ncores=args.ncores,
|
|
16
|
+
lgc_output=False, lgc_save=True,
|
|
17
|
+
save_path=args.output, processing_label=args.processing_label,
|
|
18
|
+
)
|
|
@@ -7,11 +7,18 @@ import multiprocessing
|
|
|
7
7
|
import sys
|
|
8
8
|
|
|
9
9
|
from .common import add_common_acquisition_args
|
|
10
|
-
from .commands import
|
|
10
|
+
from .commands import (
|
|
11
|
+
event, feature, filter as filter_command, ivsweep, randoms, trigger,
|
|
12
|
+
vibration,
|
|
13
|
+
)
|
|
11
14
|
|
|
12
15
|
_SECONDARY_COMMANDS = {
|
|
16
|
+
"feature": feature,
|
|
13
17
|
"filter": filter_command,
|
|
14
18
|
"ivsweep": ivsweep,
|
|
19
|
+
"randoms": randoms,
|
|
20
|
+
"trigger": trigger,
|
|
21
|
+
"vibration": vibration,
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
|
|
@@ -20,9 +27,9 @@ def _event_parser():
|
|
|
20
27
|
prog="pytesprocess",
|
|
21
28
|
description=(
|
|
22
29
|
"TES event-processing workflow. Provide an acquisition and the "
|
|
23
|
-
"requested --steps. Separate filter
|
|
24
|
-
"available as 'pytesprocess filter ...'
|
|
25
|
-
"'pytesprocess ivsweep ...'."
|
|
30
|
+
"requested --steps. Separate filter, ivsweep and vibration "
|
|
31
|
+
"workflows are available as 'pytesprocess filter ...', "
|
|
32
|
+
"'pytesprocess ivsweep ...' and 'pytesprocess vibration ...'."
|
|
26
33
|
),
|
|
27
34
|
)
|
|
28
35
|
add_common_acquisition_args(parser, config=True, ncores=True)
|
|
@@ -35,11 +42,14 @@ def _secondary_parser(name, module):
|
|
|
35
42
|
prog=f"pytesprocess {name}",
|
|
36
43
|
description=f"pytesprocess {name} workflow",
|
|
37
44
|
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
# A command with its own sub-actions adds the shared acquisition
|
|
46
|
+
# arguments to each of them instead of once here.
|
|
47
|
+
if getattr(module, "COMMON_ARGS", True):
|
|
48
|
+
add_common_acquisition_args(
|
|
49
|
+
parser,
|
|
50
|
+
config=(name == "filter"),
|
|
51
|
+
ncores=True,
|
|
52
|
+
)
|
|
43
53
|
module.configure(parser)
|
|
44
54
|
return parser
|
|
45
55
|
|
|
@@ -47,8 +57,8 @@ def _secondary_parser(name, module):
|
|
|
47
57
|
def build_parser(command=None):
|
|
48
58
|
"""Build one CLI parser.
|
|
49
59
|
|
|
50
|
-
``command=None`` builds the default event-processing parser. ``filter
|
|
51
|
-
and ``
|
|
60
|
+
``command=None`` builds the default event-processing parser. ``filter``,
|
|
61
|
+
``ivsweep`` and ``vibration`` build the explicit secondary workflow parsers.
|
|
52
62
|
"""
|
|
53
63
|
if command is None:
|
|
54
64
|
return _event_parser()
|
|
@@ -58,6 +68,7 @@ def build_parser(command=None):
|
|
|
58
68
|
|
|
59
69
|
|
|
60
70
|
def main(argv=None):
|
|
71
|
+
"""Return zero after a successful workflow; processing errors propagate."""
|
|
61
72
|
try:
|
|
62
73
|
multiprocessing.set_start_method("spawn")
|
|
63
74
|
except RuntimeError:
|
|
@@ -70,12 +81,14 @@ def main(argv=None):
|
|
|
70
81
|
module = _SECONDARY_COMMANDS[command]
|
|
71
82
|
parser = build_parser(command)
|
|
72
83
|
args = parser.parse_args(args_in)
|
|
73
|
-
|
|
84
|
+
module.run(args)
|
|
85
|
+
return 0
|
|
74
86
|
|
|
75
87
|
parser = build_parser()
|
|
76
88
|
args = parser.parse_args(args_in)
|
|
77
|
-
|
|
89
|
+
event.run(args)
|
|
90
|
+
return 0
|
|
78
91
|
|
|
79
92
|
|
|
80
93
|
if __name__ == "__main__":
|
|
81
|
-
main()
|
|
94
|
+
sys.exit(main())
|
|
@@ -5,7 +5,10 @@ import warnings
|
|
|
5
5
|
|
|
6
6
|
from .loader import load_processing_yaml
|
|
7
7
|
from .warnings import ConfigDeprecationWarning
|
|
8
|
-
from .resolvers import
|
|
8
|
+
from .resolvers import (
|
|
9
|
+
FeatureConfigResolver, TriggerConfigResolver, SaltingConfigResolver,
|
|
10
|
+
VibrationConfigResolver,
|
|
11
|
+
)
|
|
9
12
|
from .resolvers.common import rename_obsolete_keys
|
|
10
13
|
from .validation import (
|
|
11
14
|
normalize_workflows, validate_resolved_workflow,
|
|
@@ -20,7 +23,8 @@ _LEGACY_SALTING_OVERALL = {
|
|
|
20
23
|
}
|
|
21
24
|
_RESERVED_TOP_LEVEL = {
|
|
22
25
|
'config_version', 'resources', 'trigger', 'feature', 'salting', 'filter',
|
|
23
|
-
'ivsweep', 'didv', 'noise', 'template', '
|
|
26
|
+
'ivsweep', 'didv', 'noise', 'template', 'vibration', 'global', 'filter_file',
|
|
27
|
+
'didv_file'
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
|
|
@@ -73,13 +77,13 @@ class ProcessingConfig:
|
|
|
73
77
|
Returns
|
|
74
78
|
-------
|
|
75
79
|
dict
|
|
76
|
-
Validation summary
|
|
80
|
+
Validation summary (also displayed at CLI startup).
|
|
77
81
|
|
|
78
82
|
Notes
|
|
79
83
|
-----
|
|
80
|
-
This method performs no processing
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
This method performs no processing. The CLI workflows call it to
|
|
85
|
+
validate all requested workflows before starting the first
|
|
86
|
+
processing stage.
|
|
83
87
|
"""
|
|
84
88
|
names = normalize_workflows(workflows)
|
|
85
89
|
if names is None:
|
|
@@ -103,7 +107,7 @@ class ProcessingConfig:
|
|
|
103
107
|
def _configured_workflows(self):
|
|
104
108
|
names = []
|
|
105
109
|
for name in ('trigger', 'feature', 'salting', 'filter', 'ivsweep',
|
|
106
|
-
'didv', 'noise', 'template'):
|
|
110
|
+
'didv', 'noise', 'template', 'vibration'):
|
|
107
111
|
section = self._canonical.get(name, {})
|
|
108
112
|
if not isinstance(section, dict):
|
|
109
113
|
continue
|
|
@@ -125,7 +129,7 @@ class ProcessingConfig:
|
|
|
125
129
|
'global': copy.deepcopy(self._canonical['resources'])
|
|
126
130
|
}
|
|
127
131
|
for name in ('trigger', 'feature', 'salting', 'filter', 'ivsweep',
|
|
128
|
-
'didv', 'noise', 'template'):
|
|
132
|
+
'didv', 'noise', 'template', 'vibration'):
|
|
129
133
|
out[name] = self.get_config(name)
|
|
130
134
|
return out
|
|
131
135
|
|
|
@@ -151,6 +155,10 @@ class ProcessingConfig:
|
|
|
151
155
|
self._canonical['salting'], self._canonical['resources'],
|
|
152
156
|
self._available_channels, version=self._version,
|
|
153
157
|
).resolve()
|
|
158
|
+
elif name == 'vibration':
|
|
159
|
+
resolved = VibrationConfigResolver(
|
|
160
|
+
self._canonical['vibration'], self._available_channels,
|
|
161
|
+
).resolve()
|
|
154
162
|
elif name in {'filter', 'ivsweep'}:
|
|
155
163
|
resolved = copy.deepcopy(self._canonical.get(name, {}))
|
|
156
164
|
elif name in {'didv', 'noise', 'template'}:
|
|
@@ -169,7 +177,7 @@ class ProcessingConfig:
|
|
|
169
177
|
def _normalize_v2(self, raw):
|
|
170
178
|
allowed = {
|
|
171
179
|
'config_version', 'resources', 'trigger', 'feature', 'salting',
|
|
172
|
-
'filter', 'ivsweep', 'didv', 'noise', 'template'
|
|
180
|
+
'filter', 'ivsweep', 'didv', 'noise', 'template', 'vibration'
|
|
173
181
|
}
|
|
174
182
|
unknown = set(raw) - allowed
|
|
175
183
|
if unknown:
|
|
@@ -190,6 +198,7 @@ class ProcessingConfig:
|
|
|
190
198
|
'didv': copy.deepcopy(raw.get('didv', {})),
|
|
191
199
|
'noise': copy.deepcopy(raw.get('noise', {})),
|
|
192
200
|
'template': copy.deepcopy(raw.get('template', {})),
|
|
201
|
+
'vibration': copy.deepcopy(raw.get('vibration', {})),
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
def _normalize_v2_workflow(self, section, name):
|
|
@@ -230,6 +239,7 @@ class ProcessingConfig:
|
|
|
230
239
|
didv_cfg = work.pop('didv', {}) or {}
|
|
231
240
|
noise_cfg = work.pop('noise', {}) or {}
|
|
232
241
|
template_cfg = work.pop('template', {}) or {}
|
|
242
|
+
vibration_cfg = work.pop('vibration', {}) or {}
|
|
233
243
|
global_feature = work.pop('global', {}) or {}
|
|
234
244
|
work.pop('config_version', None)
|
|
235
245
|
work.pop('resources', None)
|
|
@@ -262,6 +272,7 @@ class ProcessingConfig:
|
|
|
262
272
|
'didv': didv_cfg,
|
|
263
273
|
'noise': noise_cfg,
|
|
264
274
|
'template': template_cfg,
|
|
275
|
+
'vibration': vibration_cfg,
|
|
265
276
|
}
|
|
266
277
|
|
|
267
278
|
@staticmethod
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from .feature import FeatureConfigResolver
|
|
2
2
|
from .trigger import TriggerConfigResolver
|
|
3
3
|
from .salting import SaltingConfigResolver
|
|
4
|
+
from .vibration import VibrationConfigResolver
|
|
4
5
|
|
|
5
|
-
__all__ = ['FeatureConfigResolver', 'TriggerConfigResolver', 'SaltingConfigResolver'
|
|
6
|
+
__all__ = ['FeatureConfigResolver', 'TriggerConfigResolver', 'SaltingConfigResolver',
|
|
7
|
+
'VibrationConfigResolver']
|