pytesprocess 0.1.1__py3-none-any.whl
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/__init__.py +9 -0
- pytesprocess/_version.py +2 -0
- pytesprocess/cli/__init__.py +1 -0
- pytesprocess/cli/commands/__init__.py +5 -0
- pytesprocess/cli/commands/event.py +66 -0
- pytesprocess/cli/commands/filter.py +17 -0
- pytesprocess/cli/commands/ivsweep.py +29 -0
- pytesprocess/cli/common.py +86 -0
- pytesprocess/cli/main.py +81 -0
- pytesprocess/config/__init__.py +4 -0
- pytesprocess/config/loader.py +94 -0
- pytesprocess/config/manager.py +297 -0
- pytesprocess/config/resolvers/__init__.py +5 -0
- pytesprocess/config/resolvers/common.py +56 -0
- pytesprocess/config/resolvers/feature.py +293 -0
- pytesprocess/config/resolvers/salting.py +86 -0
- pytesprocess/config/resolvers/trigger.py +84 -0
- pytesprocess/config/selectors.py +108 -0
- pytesprocess/config/validation.py +314 -0
- pytesprocess/config/warnings.py +2 -0
- pytesprocess/core/__init__.py +10 -0
- pytesprocess/core/algorithms.py +1455 -0
- pytesprocess/core/didv.py +1648 -0
- pytesprocess/core/eventbuilder.py +495 -0
- pytesprocess/core/filterbuilder.py +81 -0
- pytesprocess/core/filterdata.py +1849 -0
- pytesprocess/core/ivsweep.py +2072 -0
- pytesprocess/core/noise.py +923 -0
- pytesprocess/core/noisemodel.py +1408 -0
- pytesprocess/core/oftrigger.py +1035 -0
- pytesprocess/core/template.py +450 -0
- pytesprocess/process/__init__.py +6 -0
- pytesprocess/process/data_source.py +185 -0
- pytesprocess/process/event_context.py +35 -0
- pytesprocess/process/feature_plan.py +186 -0
- pytesprocess/process/feature_resources.py +267 -0
- pytesprocess/process/features.py +1024 -0
- pytesprocess/process/filterprocess.py +1176 -0
- pytesprocess/process/ivprocess.py +1380 -0
- pytesprocess/process/processing_data.py +967 -0
- pytesprocess/process/randoms.py +921 -0
- pytesprocess/process/triggers.py +1011 -0
- pytesprocess/salting/__init__.py +7 -0
- pytesprocess/salting/generator.py +364 -0
- pytesprocess/salting/injector.py +329 -0
- pytesprocess/salting/sampling.py +84 -0
- pytesprocess/utils/__init__.py +5 -0
- pytesprocess/utils/arg_utils.py +122 -0
- pytesprocess/utils/dataframe_output.py +120 -0
- pytesprocess/utils/filter_hdf5.py +594 -0
- pytesprocess/utils/utils.py +701 -0
- pytesprocess/workflows/__init__.py +3 -0
- pytesprocess/workflows/processing.py +317 -0
- pytesprocess/workflows/salting.py +133 -0
- pytesprocess-0.1.1.dist-info/METADATA +211 -0
- pytesprocess-0.1.1.dist-info/RECORD +60 -0
- pytesprocess-0.1.1.dist-info/WHEEL +5 -0
- pytesprocess-0.1.1.dist-info/entry_points.txt +2 -0
- pytesprocess-0.1.1.dist-info/licenses/LICENSE +21 -0
- pytesprocess-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Mapping
|
|
5
|
+
|
|
6
|
+
from pytesprocess.utils import utils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
'TraceSpec',
|
|
11
|
+
'OFModelSpec',
|
|
12
|
+
'FeatureAlgorithmPlan',
|
|
13
|
+
'FeaturePlan',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Keep the resource declaration explicit. Algorithms not listed here are
|
|
18
|
+
# ordinary trace algorithms (including user/external extractors).
|
|
19
|
+
_FFT_ALGORITHMS = {'psd_amp', 'psd_peaks', 'phase'}
|
|
20
|
+
_OF_PREFIXES = ('of1x1', 'of1x2x2', 'of1x3x3', 'ofnxm', 'ofnxmx2')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, order=True)
|
|
24
|
+
class TraceSpec:
|
|
25
|
+
"""Raw event-window definition used by one or more feature algorithms."""
|
|
26
|
+
|
|
27
|
+
nb_samples: int
|
|
28
|
+
nb_pretrigger_samples: int
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_config(cls, config: Mapping[str, Any]):
|
|
32
|
+
nb_samples = config.get('nb_samples')
|
|
33
|
+
nb_pre = config.get('nb_pretrigger_samples')
|
|
34
|
+
if nb_samples is None and nb_pre is None:
|
|
35
|
+
return None
|
|
36
|
+
if nb_samples is None or nb_pre is None:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
'ERROR: Feature trace length and pretrigger length must both be defined.'
|
|
39
|
+
)
|
|
40
|
+
return cls(int(nb_samples), int(nb_pre))
|
|
41
|
+
|
|
42
|
+
def as_tuple(self):
|
|
43
|
+
return (self.nb_samples, self.nb_pretrigger_samples)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class OFModelSpec:
|
|
48
|
+
"""Identity of static QETpy OF state.
|
|
49
|
+
|
|
50
|
+
Event pretrigger is intentionally absent: it selects a raw event window but
|
|
51
|
+
does not change the template/noise optimal-filter model.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
nb_samples: int
|
|
55
|
+
csd_tag: str = 'default'
|
|
56
|
+
coupling: str = 'AC'
|
|
57
|
+
ignored_frequency_peaks: tuple = ()
|
|
58
|
+
ignore_harmonics: bool = False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class FeatureAlgorithmPlan:
|
|
63
|
+
channel: str
|
|
64
|
+
feature_channel: str
|
|
65
|
+
name: str
|
|
66
|
+
base_algorithm: str
|
|
67
|
+
resource_kind: str
|
|
68
|
+
trace_spec: TraceSpec | None
|
|
69
|
+
of_model_spec: OFModelSpec | None
|
|
70
|
+
params: Mapping[str, Any]
|
|
71
|
+
physical_channels: tuple[str, ...]
|
|
72
|
+
of_signal_channels: tuple[str, ...]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class FeaturePlan:
|
|
76
|
+
"""Compile normalized feature YAML into an execution/resource plan once."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, processing_config, available_channels, sample_rate_hz):
|
|
79
|
+
self._processing_config = processing_config
|
|
80
|
+
self._available_channels = list(available_channels or [])
|
|
81
|
+
self._sample_rate_hz = float(sample_rate_hz)
|
|
82
|
+
self._algorithms = []
|
|
83
|
+
self._trace_channels = {}
|
|
84
|
+
self._compile()
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def algorithms(self):
|
|
88
|
+
return tuple(self._algorithms)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def of_algorithms(self):
|
|
92
|
+
return tuple(item for item in self._algorithms
|
|
93
|
+
if item.resource_kind == 'optimal_filter')
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def traces_config(self):
|
|
97
|
+
if not self._trace_channels:
|
|
98
|
+
return None
|
|
99
|
+
return {
|
|
100
|
+
spec.as_tuple(): list(channels)
|
|
101
|
+
for spec, channels in self._trace_channels.items()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def _resource_kind(base_algorithm):
|
|
106
|
+
if base_algorithm in _FFT_ALGORITHMS:
|
|
107
|
+
return 'fft'
|
|
108
|
+
if base_algorithm.startswith(_OF_PREFIXES):
|
|
109
|
+
return 'optimal_filter'
|
|
110
|
+
return 'trace'
|
|
111
|
+
|
|
112
|
+
def _compile(self):
|
|
113
|
+
for channel, channel_cfg in self._processing_config.items():
|
|
114
|
+
if not isinstance(channel_cfg, dict):
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
feature_channel = channel_cfg.get('feature_channel', channel)
|
|
118
|
+
physical_channels, _ = utils.split_channel_name(
|
|
119
|
+
channel, available_channels=self._available_channels
|
|
120
|
+
)
|
|
121
|
+
physical_channels = tuple(physical_channels)
|
|
122
|
+
|
|
123
|
+
for algorithm, raw_params in channel_cfg.items():
|
|
124
|
+
if not isinstance(raw_params, dict):
|
|
125
|
+
continue
|
|
126
|
+
if not raw_params.get('run', True):
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
params = dict(raw_params)
|
|
130
|
+
base_algorithm = params.get('base_algorithm', algorithm)
|
|
131
|
+
resource_kind = self._resource_kind(base_algorithm)
|
|
132
|
+
trace_spec = TraceSpec.from_config(params)
|
|
133
|
+
|
|
134
|
+
if resource_kind in ('fft', 'optimal_filter') and trace_spec is None:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f'ERROR: Algorithm "{algorithm}" for channel "{channel}" '
|
|
137
|
+
'requires an explicit trace length/pretrigger.'
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
of_spec = None
|
|
141
|
+
if resource_kind == 'optimal_filter':
|
|
142
|
+
ignored = params.get('ignored_frequency_peaks', ())
|
|
143
|
+
if ignored is None:
|
|
144
|
+
ignored = ()
|
|
145
|
+
elif not isinstance(ignored, (list, tuple)):
|
|
146
|
+
ignored = (ignored,)
|
|
147
|
+
else:
|
|
148
|
+
ignored = tuple(ignored)
|
|
149
|
+
of_spec = OFModelSpec(
|
|
150
|
+
nb_samples=trace_spec.nb_samples,
|
|
151
|
+
csd_tag=str(params.get('csd_tag', 'default')),
|
|
152
|
+
coupling=str(params.get('coupling', 'AC')),
|
|
153
|
+
ignored_frequency_peaks=tuple(ignored),
|
|
154
|
+
ignore_harmonics=bool(params.get('ignore_harmonics', False)),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
of_signal_channels = physical_channels
|
|
158
|
+
if resource_kind == 'optimal_filter':
|
|
159
|
+
# Preserve the established OF convention: ``|`` means
|
|
160
|
+
# multiple OF channels, while derived ``+``/``-`` names
|
|
161
|
+
# are one composite OF signal stored under that expression.
|
|
162
|
+
of_signal_channels, _ = utils.split_channel_name(
|
|
163
|
+
channel, available_channels=self._available_channels,
|
|
164
|
+
separator='|'
|
|
165
|
+
)
|
|
166
|
+
of_signal_channels = tuple(of_signal_channels)
|
|
167
|
+
|
|
168
|
+
plan = FeatureAlgorithmPlan(
|
|
169
|
+
channel=channel,
|
|
170
|
+
feature_channel=feature_channel,
|
|
171
|
+
name=algorithm,
|
|
172
|
+
base_algorithm=base_algorithm,
|
|
173
|
+
resource_kind=resource_kind,
|
|
174
|
+
trace_spec=trace_spec,
|
|
175
|
+
of_model_spec=of_spec,
|
|
176
|
+
params=params,
|
|
177
|
+
physical_channels=physical_channels,
|
|
178
|
+
of_signal_channels=of_signal_channels,
|
|
179
|
+
)
|
|
180
|
+
self._algorithms.append(plan)
|
|
181
|
+
|
|
182
|
+
if trace_spec is not None:
|
|
183
|
+
existing = self._trace_channels.setdefault(trace_spec, [])
|
|
184
|
+
for chan in physical_channels:
|
|
185
|
+
if chan not in existing:
|
|
186
|
+
existing.append(chan)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import qetpy as qp
|
|
7
|
+
|
|
8
|
+
from .feature_plan import TraceSpec, OFModelSpec, FeatureAlgorithmPlan, FeaturePlan
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'EventTraceCache',
|
|
13
|
+
'EventFFTData',
|
|
14
|
+
'FeatureResources',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EventFFTData:
|
|
19
|
+
"""Small signal-FFT view used by non-OF frequency-domain features.
|
|
20
|
+
|
|
21
|
+
It intentionally exposes the subset of the old OFBase signal API used by
|
|
22
|
+
psd_amp/psd_peaks/phase, without constructing a QETpy OFBase.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, channel, trace, sample_rate_hz):
|
|
26
|
+
self._channel = channel
|
|
27
|
+
self._fs = float(sample_rate_hz)
|
|
28
|
+
self._nbins = None
|
|
29
|
+
self._freqs = None
|
|
30
|
+
self._fft = None
|
|
31
|
+
if trace is not None:
|
|
32
|
+
self._nbins = int(trace.shape[-1])
|
|
33
|
+
freqs, fft = qp.utils.fft(np.asarray(trace), self._fs, axis=-1)
|
|
34
|
+
self._freqs = np.asarray(freqs)
|
|
35
|
+
# Match QETpy OFBase.update_signal() normalization.
|
|
36
|
+
self._fft = np.asarray(fft) / self._nbins
|
|
37
|
+
|
|
38
|
+
def is_signal_stored(self, channel):
|
|
39
|
+
return channel == self._channel and self._fft is not None
|
|
40
|
+
|
|
41
|
+
def signal_fft(self, channel, squeeze_array=False):
|
|
42
|
+
if not self.is_signal_stored(channel):
|
|
43
|
+
raise ValueError(f'ERROR: FFT data are not stored for channel {channel}.')
|
|
44
|
+
out = self._fft
|
|
45
|
+
return np.squeeze(out) if squeeze_array else np.atleast_2d(out)
|
|
46
|
+
|
|
47
|
+
def fft_freqs(self):
|
|
48
|
+
return self._freqs
|
|
49
|
+
|
|
50
|
+
def nb_samples(self):
|
|
51
|
+
return self._nbins
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class EventTraceCache:
|
|
55
|
+
"""Per-event lazy cache for trace windows and their FFTs."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, processing_data, sample_rate_hz, weights=None):
|
|
58
|
+
self._processing_data = processing_data
|
|
59
|
+
self._sample_rate_hz = float(sample_rate_hz)
|
|
60
|
+
self._weights = weights or {}
|
|
61
|
+
self._traces = {}
|
|
62
|
+
self._ffts = {}
|
|
63
|
+
|
|
64
|
+
def _weights_for(self, channel):
|
|
65
|
+
return self._weights.get(channel)
|
|
66
|
+
|
|
67
|
+
def get_trace(self, channel, trace_spec: TraceSpec | None):
|
|
68
|
+
key = (channel, trace_spec)
|
|
69
|
+
if key not in self._traces:
|
|
70
|
+
if trace_spec is None:
|
|
71
|
+
trace = self._processing_data.get_channel_trace(
|
|
72
|
+
channel, weights=self._weights_for(channel)
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
trace = self._processing_data.get_channel_trace(
|
|
76
|
+
channel,
|
|
77
|
+
nb_samples=trace_spec.nb_samples,
|
|
78
|
+
nb_pretrigger_samples=trace_spec.nb_pretrigger_samples,
|
|
79
|
+
weights=self._weights_for(channel),
|
|
80
|
+
)
|
|
81
|
+
self._traces[key] = trace
|
|
82
|
+
return self._traces[key]
|
|
83
|
+
|
|
84
|
+
def get_fft_data(self, channel, trace_spec: TraceSpec):
|
|
85
|
+
key = (channel, trace_spec)
|
|
86
|
+
if key not in self._ffts:
|
|
87
|
+
trace = self.get_trace(channel, trace_spec)
|
|
88
|
+
self._ffts[key] = EventFFTData(
|
|
89
|
+
channel, trace, self._sample_rate_hz
|
|
90
|
+
)
|
|
91
|
+
return self._ffts[key]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class _OFModelState:
|
|
96
|
+
of_base: object
|
|
97
|
+
template_definitions: dict = field(default_factory=dict)
|
|
98
|
+
current_trace_spec: TraceSpec | None = None
|
|
99
|
+
loaded_channels: set = field(default_factory=set)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class FeatureResources:
|
|
103
|
+
"""Static feature resources plus lazy per-event QETpy signal loading."""
|
|
104
|
+
|
|
105
|
+
def __init__(self, feature_plan: FeaturePlan, filter_data,
|
|
106
|
+
sample_rate_hz, available_channels=None, verbose=True):
|
|
107
|
+
self._plan = feature_plan
|
|
108
|
+
self._filter_data = filter_data
|
|
109
|
+
self._sample_rate_hz = float(sample_rate_hz)
|
|
110
|
+
self._available_channels = list(available_channels or [])
|
|
111
|
+
self._verbose = verbose
|
|
112
|
+
self._of_models = {}
|
|
113
|
+
self._prepare()
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def number_of_models(self):
|
|
117
|
+
return len(self._of_models)
|
|
118
|
+
|
|
119
|
+
def begin_event(self):
|
|
120
|
+
# Dynamic signal state is cleared once per event. Static templates,
|
|
121
|
+
# CSDs, phis, weights and p-matrices remain in the OFBase.
|
|
122
|
+
for state in self._of_models.values():
|
|
123
|
+
state.of_base.clear_signal()
|
|
124
|
+
state.current_trace_spec = None
|
|
125
|
+
state.loaded_channels.clear()
|
|
126
|
+
|
|
127
|
+
def _get_state(self, spec: OFModelSpec):
|
|
128
|
+
state = self._of_models.get(spec)
|
|
129
|
+
if state is None:
|
|
130
|
+
state = _OFModelState(
|
|
131
|
+
of_base=qp.OFBase(self._sample_rate_hz, verbose=self._verbose)
|
|
132
|
+
)
|
|
133
|
+
self._of_models[spec] = state
|
|
134
|
+
return state
|
|
135
|
+
|
|
136
|
+
def _prepare(self):
|
|
137
|
+
if not self._plan.of_algorithms:
|
|
138
|
+
return
|
|
139
|
+
if self._filter_data is None:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
'ERROR: Feature optimal-filter algorithms require a filter_file.'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
for plan in self._plan.of_algorithms:
|
|
145
|
+
self._prepare_algorithm(plan)
|
|
146
|
+
|
|
147
|
+
def _prepare_algorithm(self, plan: FeatureAlgorithmPlan):
|
|
148
|
+
spec = plan.of_model_spec
|
|
149
|
+
state = self._get_state(spec)
|
|
150
|
+
of = state.of_base
|
|
151
|
+
params = plan.params
|
|
152
|
+
channel = plan.channel
|
|
153
|
+
|
|
154
|
+
# Static noise model.
|
|
155
|
+
if of.csd(channel) is None:
|
|
156
|
+
csd, _freqs, metadata = self._filter_data.get_csd(
|
|
157
|
+
channel, tag=spec.csd_tag, fold=False, return_metadata=True
|
|
158
|
+
)
|
|
159
|
+
fs_filter = metadata.get('sample_rate_hz', metadata.get('sample_rate'))
|
|
160
|
+
if fs_filter is not None and float(fs_filter) != self._sample_rate_hz:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f'ERROR: Sample rate is inconsistent between raw data '
|
|
163
|
+
f'({self._sample_rate_hz}) and CSD ({fs_filter}) for '
|
|
164
|
+
f'channel {channel}.'
|
|
165
|
+
)
|
|
166
|
+
if int(csd.shape[-1]) != spec.nb_samples:
|
|
167
|
+
raise ValueError(
|
|
168
|
+
f'ERROR: Number of samples is inconsistent between raw data '
|
|
169
|
+
f'({spec.nb_samples}) and CSD ({csd.shape[-1]}) for '
|
|
170
|
+
f'channel {channel}, algorithm {plan.name}.'
|
|
171
|
+
)
|
|
172
|
+
ignored = list(spec.ignored_frequency_peaks) or None
|
|
173
|
+
of.set_csd(
|
|
174
|
+
channel, csd,
|
|
175
|
+
coupling=spec.coupling,
|
|
176
|
+
ignored_frequency_peaks=ignored,
|
|
177
|
+
ignore_harmonics=spec.ignore_harmonics,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
template_tag = params.get('template_tag')
|
|
181
|
+
if template_tag is None:
|
|
182
|
+
raise ValueError(
|
|
183
|
+
f'ERROR: a "template_tag" in yaml file is required for '
|
|
184
|
+
f'channel {channel}, algorithm "{plan.name}"!'
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
template, _time, metadata = self._filter_data.get_template(
|
|
188
|
+
channel, tag=template_tag, return_metadata=True
|
|
189
|
+
)
|
|
190
|
+
if int(template.shape[-1]) != spec.nb_samples:
|
|
191
|
+
raise ValueError(
|
|
192
|
+
f'ERROR: Number of samples is inconsistent between raw data '
|
|
193
|
+
f'({spec.nb_samples}) and template ("{template_tag}") for '
|
|
194
|
+
f'channel {channel}, algorithm {plan.name}.'
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
template_pre = metadata.get('nb_pretrigger_samples')
|
|
198
|
+
if template_pre is None:
|
|
199
|
+
template_pre = plan.trace_spec.nb_pretrigger_samples
|
|
200
|
+
template_pre = int(template_pre)
|
|
201
|
+
integralnorm = bool(params.get('integralnorm', False))
|
|
202
|
+
template_key = (channel, str(template_tag))
|
|
203
|
+
definition = (integralnorm, template_pre)
|
|
204
|
+
old_definition = state.template_definitions.get(template_key)
|
|
205
|
+
if old_definition is not None and old_definition != definition:
|
|
206
|
+
raise ValueError(
|
|
207
|
+
f'ERROR: Conflicting definitions for template tag "{template_tag}" '
|
|
208
|
+
f'on channel {channel}: {old_definition} vs {definition}. '
|
|
209
|
+
'Use a distinct template tag for different normalization/pretrigger.'
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if old_definition is None:
|
|
213
|
+
of.add_template(
|
|
214
|
+
channel, template,
|
|
215
|
+
template_tag=template_tag,
|
|
216
|
+
pretrigger_samples=template_pre,
|
|
217
|
+
integralnorm=integralnorm,
|
|
218
|
+
overwrite=False,
|
|
219
|
+
)
|
|
220
|
+
state.template_definitions[template_key] = definition
|
|
221
|
+
|
|
222
|
+
if of.phi(channel, template_tag) is None:
|
|
223
|
+
of.calc_phi(channel, template_tag)
|
|
224
|
+
|
|
225
|
+
if plan.base_algorithm == 'ofnxmx2':
|
|
226
|
+
if ('template_group_ids' not in params or 'fit_window' not in params):
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f'ERROR: "template_group_ids" and "fit_window" required '
|
|
229
|
+
f'in yaml file for channel {channel}, algorithm {plan.name}!'
|
|
230
|
+
)
|
|
231
|
+
of.set_time_constraints(
|
|
232
|
+
channel,
|
|
233
|
+
template_group_ids=np.asarray(params['template_group_ids']),
|
|
234
|
+
fit_window=np.asarray(params['fit_window']),
|
|
235
|
+
restrict_time_flag=bool(params.get('restrict_time_flag', True)),
|
|
236
|
+
time_constraints_tag=plan.name,
|
|
237
|
+
)
|
|
238
|
+
of.calc_p_matrix(
|
|
239
|
+
channel,
|
|
240
|
+
template_tag=template_tag,
|
|
241
|
+
time_constraints_tag=plan.name,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def get_of_base(self, plan: FeatureAlgorithmPlan,
|
|
245
|
+
event_cache: EventTraceCache):
|
|
246
|
+
if plan.resource_kind != 'optimal_filter':
|
|
247
|
+
return None
|
|
248
|
+
state = self._of_models[plan.of_model_spec]
|
|
249
|
+
|
|
250
|
+
# One static OF model can be reused for several event pretriggers.
|
|
251
|
+
# Switching TraceSpec only replaces per-event signal state.
|
|
252
|
+
if state.current_trace_spec is None:
|
|
253
|
+
state.current_trace_spec = plan.trace_spec
|
|
254
|
+
elif state.current_trace_spec != plan.trace_spec:
|
|
255
|
+
state.of_base.clear_signal()
|
|
256
|
+
state.loaded_channels.clear()
|
|
257
|
+
state.current_trace_spec = plan.trace_spec
|
|
258
|
+
|
|
259
|
+
for channel in plan.of_signal_channels:
|
|
260
|
+
if channel in state.loaded_channels:
|
|
261
|
+
continue
|
|
262
|
+
trace = event_cache.get_trace(channel, plan.trace_spec)
|
|
263
|
+
if trace is not None:
|
|
264
|
+
state.of_base.update_signal(channel, trace, calc_fft=True)
|
|
265
|
+
state.loaded_channels.add(channel)
|
|
266
|
+
|
|
267
|
+
return state.of_base
|