mt-metadata 0.3.9__py2.py3-none-any.whl → 0.4.0__py2.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.
Potentially problematic release.
This version of mt-metadata might be problematic. Click here for more details.
- mt_metadata/__init__.py +1 -1
- mt_metadata/base/helpers.py +84 -9
- mt_metadata/base/metadata.py +137 -65
- mt_metadata/features/__init__.py +14 -0
- mt_metadata/features/coherence.py +303 -0
- mt_metadata/features/cross_powers.py +29 -0
- mt_metadata/features/fc_coherence.py +81 -0
- mt_metadata/features/feature.py +72 -0
- mt_metadata/features/feature_decimation_channel.py +26 -0
- mt_metadata/features/feature_fc.py +24 -0
- mt_metadata/{transfer_functions/processing/aurora/decimation.py → features/feature_fc_run.py} +9 -4
- mt_metadata/features/feature_ts.py +24 -0
- mt_metadata/{transfer_functions/processing/aurora/window.py → features/feature_ts_run.py} +11 -18
- mt_metadata/features/standards/__init__.py +6 -0
- mt_metadata/features/standards/base_feature.json +46 -0
- mt_metadata/features/standards/coherence.json +57 -0
- mt_metadata/features/standards/fc_coherence.json +57 -0
- mt_metadata/features/standards/feature_decimation_channel.json +68 -0
- mt_metadata/features/standards/feature_fc_run.json +35 -0
- mt_metadata/features/standards/feature_ts_run.json +35 -0
- mt_metadata/features/standards/feature_weighting_window.json +46 -0
- mt_metadata/features/standards/weight_kernel.json +46 -0
- mt_metadata/features/standards/weights.json +101 -0
- mt_metadata/features/test_helpers/channel_weight_specs_example.json +156 -0
- mt_metadata/features/weights/__init__.py +0 -0
- mt_metadata/features/weights/base.py +44 -0
- mt_metadata/features/weights/channel_weight_spec.py +209 -0
- mt_metadata/features/weights/feature_weight_spec.py +194 -0
- mt_metadata/features/weights/monotonic_weight_kernel.py +275 -0
- mt_metadata/features/weights/standards/__init__.py +6 -0
- mt_metadata/features/weights/standards/activation_monotonic_weight_kernel.json +38 -0
- mt_metadata/features/weights/standards/base.json +36 -0
- mt_metadata/features/weights/standards/channel_weight_spec.json +35 -0
- mt_metadata/features/weights/standards/composite.json +36 -0
- mt_metadata/features/weights/standards/feature_weight_spec.json +13 -0
- mt_metadata/features/weights/standards/monotonic_weight_kernel.json +49 -0
- mt_metadata/features/weights/standards/taper_monotonic_weight_kernel.json +16 -0
- mt_metadata/features/weights/taper_weight_kernel.py +60 -0
- mt_metadata/helper_functions.py +69 -0
- mt_metadata/timeseries/filters/channel_response.py +77 -37
- mt_metadata/timeseries/filters/coefficient_filter.py +6 -5
- mt_metadata/timeseries/filters/filter_base.py +11 -15
- mt_metadata/timeseries/filters/fir_filter.py +8 -1
- mt_metadata/timeseries/filters/frequency_response_table_filter.py +26 -11
- mt_metadata/timeseries/filters/helper_functions.py +0 -2
- mt_metadata/timeseries/filters/obspy_stages.py +4 -1
- mt_metadata/timeseries/filters/pole_zero_filter.py +9 -5
- mt_metadata/timeseries/filters/time_delay_filter.py +8 -1
- mt_metadata/timeseries/location.py +20 -5
- mt_metadata/timeseries/person.py +14 -7
- mt_metadata/timeseries/standards/person.json +1 -1
- mt_metadata/timeseries/standards/run.json +2 -2
- mt_metadata/timeseries/station.py +4 -2
- mt_metadata/timeseries/stationxml/__init__.py +5 -0
- mt_metadata/timeseries/stationxml/xml_channel_mt_channel.py +25 -27
- mt_metadata/timeseries/stationxml/xml_inventory_mt_experiment.py +16 -47
- mt_metadata/timeseries/stationxml/xml_station_mt_station.py +25 -24
- mt_metadata/transfer_functions/__init__.py +3 -0
- mt_metadata/transfer_functions/core.py +8 -11
- mt_metadata/transfer_functions/io/emtfxml/metadata/location.py +5 -0
- mt_metadata/transfer_functions/io/emtfxml/metadata/provenance.py +14 -3
- mt_metadata/transfer_functions/io/tools.py +2 -0
- mt_metadata/transfer_functions/io/zonge/metadata/header.py +1 -1
- mt_metadata/transfer_functions/io/zonge/metadata/standards/header.json +1 -1
- mt_metadata/transfer_functions/io/zonge/metadata/standards/job.json +2 -2
- mt_metadata/transfer_functions/io/zonge/zonge.py +19 -23
- mt_metadata/transfer_functions/processing/__init__.py +2 -1
- mt_metadata/transfer_functions/processing/aurora/__init__.py +2 -4
- mt_metadata/transfer_functions/processing/aurora/band.py +46 -125
- mt_metadata/transfer_functions/processing/aurora/channel_nomenclature.py +27 -20
- mt_metadata/transfer_functions/processing/aurora/decimation_level.py +324 -152
- mt_metadata/transfer_functions/processing/aurora/frequency_bands.py +230 -0
- mt_metadata/transfer_functions/processing/aurora/processing.py +3 -3
- mt_metadata/transfer_functions/processing/aurora/run.py +32 -7
- mt_metadata/transfer_functions/processing/aurora/standards/decimation_level.json +7 -73
- mt_metadata/transfer_functions/processing/aurora/stations.py +33 -4
- mt_metadata/transfer_functions/processing/fourier_coefficients/decimation.py +176 -178
- mt_metadata/transfer_functions/processing/fourier_coefficients/fc.py +11 -9
- mt_metadata/transfer_functions/processing/fourier_coefficients/standards/decimation.json +1 -111
- mt_metadata/transfer_functions/processing/short_time_fourier_transform.py +64 -0
- mt_metadata/transfer_functions/processing/standards/__init__.py +6 -0
- mt_metadata/transfer_functions/processing/standards/short_time_fourier_transform.json +94 -0
- mt_metadata/transfer_functions/processing/{aurora/standards/decimation.json → standards/time_series_decimation.json} +17 -6
- mt_metadata/transfer_functions/processing/{aurora/standards → standards}/window.json +13 -2
- mt_metadata/transfer_functions/processing/time_series_decimation.py +50 -0
- mt_metadata/transfer_functions/processing/window.py +118 -0
- mt_metadata/transfer_functions/tf/station.py +17 -1
- mt_metadata/utils/mttime.py +22 -3
- mt_metadata/utils/validators.py +4 -2
- {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/METADATA +39 -15
- {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/RECORD +95 -55
- {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/WHEEL +1 -1
- {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/AUTHORS.rst +0 -0
- {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/LICENSE +0 -0
- {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""
|
|
3
|
+
This module contains the DecimationLevel class.
|
|
4
|
+
TODO: Factor or rename. The decimation level class here has information about the entire processing.
|
|
5
|
+
|
|
3
6
|
Created on Thu Feb 17 14:15:20 2022
|
|
4
7
|
|
|
5
8
|
@author: jpeacock
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
6
12
|
"""
|
|
7
13
|
# =============================================================================
|
|
8
14
|
# Imports
|
|
@@ -12,18 +18,26 @@ import pandas as pd
|
|
|
12
18
|
|
|
13
19
|
from mt_metadata.base.helpers import write_lines
|
|
14
20
|
from mt_metadata.base import get_schema, Base
|
|
21
|
+
from mt_metadata.transfer_functions.processing.fourier_coefficients import (
|
|
22
|
+
Decimation as FCDecimation,
|
|
23
|
+
)
|
|
24
|
+
from typing import List, Union
|
|
15
25
|
|
|
16
26
|
from .band import Band
|
|
17
|
-
from
|
|
27
|
+
from ..time_series_decimation import TimeSeriesDecimation as Decimation
|
|
28
|
+
from ..short_time_fourier_transform import ShortTimeFourierTransform as STFT
|
|
18
29
|
from .estimator import Estimator
|
|
30
|
+
from .frequency_bands import FrequencyBands
|
|
19
31
|
from .regression import Regression
|
|
20
32
|
from .standards import SCHEMA_FN_PATHS
|
|
21
|
-
from .
|
|
33
|
+
from mt_metadata.features.weights.channel_weight_spec import ChannelWeightSpec
|
|
34
|
+
from mt_metadata.helper_functions import cast_to_class_if_dict
|
|
35
|
+
from mt_metadata.helper_functions import validate_setter_input
|
|
22
36
|
|
|
23
37
|
# =============================================================================
|
|
24
38
|
attr_dict = get_schema("decimation_level", SCHEMA_FN_PATHS)
|
|
25
|
-
attr_dict.add_dict(
|
|
26
|
-
attr_dict.add_dict(
|
|
39
|
+
attr_dict.add_dict(Decimation()._attr_dict, "decimation")
|
|
40
|
+
attr_dict.add_dict(STFT()._attr_dict, "stft")
|
|
27
41
|
attr_dict.add_dict(get_schema("regression", SCHEMA_FN_PATHS), "regression")
|
|
28
42
|
attr_dict.add_dict(get_schema("estimator", SCHEMA_FN_PATHS), "estimator")
|
|
29
43
|
|
|
@@ -31,141 +45,65 @@ attr_dict.add_dict(get_schema("estimator", SCHEMA_FN_PATHS), "estimator")
|
|
|
31
45
|
# =============================================================================
|
|
32
46
|
|
|
33
47
|
|
|
34
|
-
def df_from_bands(band_list: list) -> pd.DataFrame:
|
|
35
|
-
"""
|
|
36
|
-
Utility function that transforms a list of bands into a dataframe
|
|
37
|
-
|
|
38
|
-
Note: The decimation_level here is +1 to agree with EMTF convention.
|
|
39
|
-
Not clear this is really necessary
|
|
40
|
-
|
|
41
|
-
Parameters
|
|
42
|
-
----------
|
|
43
|
-
band_list: list
|
|
44
|
-
obtained from mt_metadata.transfer_functions.processing.aurora.decimation_level.DecimationLevel.bands
|
|
45
|
-
|
|
46
|
-
Returns
|
|
47
|
-
-------
|
|
48
|
-
out_df: pd.Dataframe
|
|
49
|
-
Same format as that generated by EMTFBandSetupFile.get_decimation_level()
|
|
50
|
-
"""
|
|
51
|
-
df_columns = [
|
|
52
|
-
"decimation_level",
|
|
53
|
-
"lower_bound_index",
|
|
54
|
-
"upper_bound_index",
|
|
55
|
-
"frequency_min",
|
|
56
|
-
"frequency_max",
|
|
57
|
-
]
|
|
58
|
-
n_rows = len(band_list)
|
|
59
|
-
df_columns_dict = {}
|
|
60
|
-
for col in df_columns:
|
|
61
|
-
df_columns_dict[col] = n_rows * [None]
|
|
62
|
-
for i_band, band in enumerate(band_list):
|
|
63
|
-
df_columns_dict["decimation_level"][i_band] = band.decimation_level + 1
|
|
64
|
-
df_columns_dict["lower_bound_index"][i_band] = band.index_min
|
|
65
|
-
df_columns_dict["upper_bound_index"][i_band] = band.index_max
|
|
66
|
-
df_columns_dict["frequency_min"][i_band] = band.frequency_min
|
|
67
|
-
df_columns_dict["frequency_max"][i_band] = band.frequency_max
|
|
68
|
-
out_df = pd.DataFrame(data=df_columns_dict)
|
|
69
|
-
out_df.sort_values(by="lower_bound_index", inplace=True)
|
|
70
|
-
out_df.reset_index(inplace=True, drop=True)
|
|
71
|
-
return out_df
|
|
72
|
-
|
|
73
|
-
def get_fft_harmonics(samples_per_window: int, sample_rate: float) -> np.ndarray:
|
|
74
|
-
"""
|
|
75
|
-
Works for odd and even number of points.
|
|
76
|
-
|
|
77
|
-
Development notes:
|
|
78
|
-
Could be modified with kwargs to support one_sided, two_sided, ignore_dc
|
|
79
|
-
ignore_nyquist, and etc. Consider taking FrequencyBands as an argument.
|
|
80
|
-
|
|
81
|
-
Parameters
|
|
82
|
-
----------
|
|
83
|
-
samples_per_window: integer
|
|
84
|
-
Number of samples in a window that will be Fourier transformed.
|
|
85
|
-
sample_rate: float
|
|
86
|
-
Inverse of time step between samples,
|
|
87
|
-
Samples per second
|
|
88
|
-
|
|
89
|
-
Returns
|
|
90
|
-
-------
|
|
91
|
-
harmonic_frequencies: numpy array
|
|
92
|
-
The frequencies that the fft will be computed.
|
|
93
|
-
These are one-sided (positive frequencies only)
|
|
94
|
-
Does not return Nyquist
|
|
95
|
-
Does return DC component
|
|
96
|
-
"""
|
|
97
|
-
n_fft_harmonics = int(samples_per_window / 2) # no bin at Nyquist,
|
|
98
|
-
delta_t = 1.0 / sample_rate
|
|
99
|
-
harmonic_frequencies = np.fft.fftfreq(samples_per_window, d=delta_t)
|
|
100
|
-
harmonic_frequencies = harmonic_frequencies[0:n_fft_harmonics]
|
|
101
|
-
return harmonic_frequencies
|
|
102
|
-
|
|
103
|
-
|
|
104
48
|
class DecimationLevel(Base):
|
|
105
49
|
__doc__ = write_lines(attr_dict)
|
|
106
50
|
|
|
107
51
|
def __init__(self, **kwargs):
|
|
108
52
|
|
|
109
|
-
self.window = Window()
|
|
110
53
|
self.decimation = Decimation()
|
|
111
54
|
self.regression = Regression()
|
|
112
55
|
self.estimator = Estimator()
|
|
56
|
+
self.stft = STFT()
|
|
113
57
|
|
|
114
58
|
self._bands = []
|
|
59
|
+
self._channel_weight_specs = []
|
|
115
60
|
|
|
116
61
|
super().__init__(attr_dict=attr_dict, **kwargs)
|
|
117
62
|
|
|
118
|
-
# if self.decimation.level == 0:
|
|
119
|
-
# self.anti_alias_filter = None
|
|
120
|
-
|
|
121
63
|
@property
|
|
122
|
-
def bands(self):
|
|
64
|
+
def bands(self) -> List[Band]:
|
|
123
65
|
"""
|
|
124
|
-
|
|
66
|
+
Return bands.
|
|
125
67
|
|
|
126
68
|
"""
|
|
127
|
-
|
|
128
|
-
for band in self._bands:
|
|
129
|
-
if isinstance(band, dict):
|
|
130
|
-
b = Band()
|
|
131
|
-
b.from_dict(band)
|
|
132
|
-
elif isinstance(band, Band):
|
|
133
|
-
b = band
|
|
134
|
-
return_list.append(b)
|
|
135
|
-
return return_list
|
|
69
|
+
return self._bands
|
|
136
70
|
|
|
137
71
|
@bands.setter
|
|
138
72
|
def bands(self, value):
|
|
139
73
|
"""
|
|
140
|
-
Set bands
|
|
74
|
+
Set bands. If any are in dict form, cast them to Band objects before setting.
|
|
141
75
|
|
|
142
76
|
:param value: list of bands
|
|
143
77
|
:type value: list, Band
|
|
144
78
|
|
|
145
79
|
"""
|
|
80
|
+
values = validate_setter_input(value, Band)
|
|
81
|
+
bands_list = [cast_to_class_if_dict(obj, Band) for obj in values]
|
|
82
|
+
self._bands = bands_list
|
|
146
83
|
|
|
147
|
-
|
|
148
|
-
|
|
84
|
+
@property
|
|
85
|
+
def channel_weight_specs(self) -> List[ChannelWeightSpec]:
|
|
86
|
+
"""
|
|
87
|
+
Return the channel weight spec objects.
|
|
149
88
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
for obj in value:
|
|
153
|
-
if not isinstance(obj, (Band, dict)):
|
|
154
|
-
raise TypeError(
|
|
155
|
-
f"List entry must be a Band object not {type(obj)}"
|
|
156
|
-
)
|
|
157
|
-
if isinstance(obj, dict):
|
|
158
|
-
band = Band()
|
|
159
|
-
band.from_dict(obj)
|
|
89
|
+
"""
|
|
90
|
+
return self._channel_weight_specs
|
|
160
91
|
|
|
161
|
-
|
|
162
|
-
|
|
92
|
+
@channel_weight_specs.setter
|
|
93
|
+
def channel_weight_specs(self, value: List[Union[dict, ChannelWeightSpec]]) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Set channel_weight_specs.
|
|
96
|
+
If any are in dict form, cast to ChannelWeightSpec before assigning.
|
|
163
97
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
98
|
+
:param value: list of ChannelWeightSpec objects
|
|
99
|
+
:type value: list, ChannelWeightSpec
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
values = validate_setter_input(value, ChannelWeightSpec)
|
|
103
|
+
cws_list = [cast_to_class_if_dict(obj, ChannelWeightSpec) for obj in values]
|
|
104
|
+
self._channel_weight_specs = cws_list
|
|
167
105
|
|
|
168
|
-
def add_band(self, band):
|
|
106
|
+
def add_band(self, band: Union[Band, dict]) -> None:
|
|
169
107
|
"""
|
|
170
108
|
add a band
|
|
171
109
|
"""
|
|
@@ -177,14 +115,13 @@ class DecimationLevel(Base):
|
|
|
177
115
|
if isinstance(band, dict):
|
|
178
116
|
obj = Band()
|
|
179
117
|
obj.from_dict(band)
|
|
180
|
-
|
|
181
118
|
else:
|
|
182
119
|
obj = band
|
|
183
120
|
|
|
184
121
|
self._bands.append(obj)
|
|
185
122
|
|
|
186
123
|
@property
|
|
187
|
-
def lower_bounds(self):
|
|
124
|
+
def lower_bounds(self) -> np.ndarray:
|
|
188
125
|
"""
|
|
189
126
|
get lower bounds index values into an array.
|
|
190
127
|
"""
|
|
@@ -192,7 +129,7 @@ class DecimationLevel(Base):
|
|
|
192
129
|
return np.array(sorted([band.index_min for band in self.bands]))
|
|
193
130
|
|
|
194
131
|
@property
|
|
195
|
-
def upper_bounds(self):
|
|
132
|
+
def upper_bounds(self) -> np.ndarray:
|
|
196
133
|
"""
|
|
197
134
|
get upper bounds index values into an array.
|
|
198
135
|
"""
|
|
@@ -200,65 +137,77 @@ class DecimationLevel(Base):
|
|
|
200
137
|
return np.array(sorted([band.index_max for band in self.bands]))
|
|
201
138
|
|
|
202
139
|
@property
|
|
203
|
-
def bands_dataframe(self):
|
|
140
|
+
def bands_dataframe(self) -> pd.DataFrame:
|
|
204
141
|
"""
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
Note: The decimation_level here is +1 to agree with EMTF convention.
|
|
208
|
-
Not clear this is really necessary
|
|
142
|
+
Utility function that transforms a list of bands into a dataframe
|
|
209
143
|
|
|
210
|
-
|
|
144
|
+
See notes in `_df_from_bands`.
|
|
211
145
|
|
|
212
146
|
Returns
|
|
213
147
|
-------
|
|
214
148
|
bands_df: pd.Dataframe
|
|
215
149
|
Same format as that generated by EMTFBandSetupFile.get_decimation_level()
|
|
216
150
|
"""
|
|
217
|
-
bands_df =
|
|
151
|
+
bands_df = _df_from_bands(self.bands)
|
|
218
152
|
return bands_df
|
|
219
153
|
|
|
220
154
|
@property
|
|
221
|
-
def frequency_sample_interval(self):
|
|
222
|
-
|
|
155
|
+
def frequency_sample_interval(self) -> float:
|
|
156
|
+
"""
|
|
157
|
+
Returns the delta_f in frequency domain df = 1 / (N * dt)
|
|
158
|
+
Here dt is the sample interval after decimation
|
|
159
|
+
"""
|
|
160
|
+
return self.decimation.sample_rate / self.stft.window.num_samples
|
|
223
161
|
|
|
224
162
|
@property
|
|
225
|
-
def band_edges(self):
|
|
163
|
+
def band_edges(self) -> np.ndarray:
|
|
164
|
+
"""
|
|
165
|
+
Returns the band edges as a numpy array
|
|
166
|
+
:return band_edges: 2D numpy array, one row per frequency band and two columns
|
|
167
|
+
:rtype band_edges: np.ndarray
|
|
168
|
+
"""
|
|
226
169
|
bands_df = self.bands_dataframe
|
|
227
170
|
band_edges = np.vstack(
|
|
228
171
|
(bands_df.frequency_min.values, bands_df.frequency_max.values)
|
|
229
172
|
).T
|
|
230
173
|
return band_edges
|
|
231
174
|
|
|
232
|
-
def frequency_bands_obj(self):
|
|
175
|
+
def frequency_bands_obj(self) -> FrequencyBands:
|
|
233
176
|
"""
|
|
234
177
|
Gets a FrequencyBands object that is used as input to processing.
|
|
235
|
-
This used to be needed because I only had
|
|
236
178
|
|
|
237
|
-
|
|
179
|
+
Used by Aurora.
|
|
180
|
+
|
|
181
|
+
TODO: consider adding .to_frequency_bands() method directly to self.bands
|
|
182
|
+
|
|
238
183
|
Returns
|
|
239
184
|
-------
|
|
185
|
+
frequency_bands: FrequencyBands
|
|
186
|
+
A FrequencyBands object that can be used as an iterator for processing.
|
|
240
187
|
|
|
241
188
|
"""
|
|
242
|
-
from mt_metadata.transfer_functions.processing.aurora.band import (
|
|
243
|
-
FrequencyBands,
|
|
244
|
-
)
|
|
245
|
-
|
|
246
189
|
frequency_bands = FrequencyBands(band_edges=self.band_edges)
|
|
247
190
|
return frequency_bands
|
|
248
191
|
|
|
249
192
|
@property
|
|
250
|
-
def fft_frequencies(self):
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
)
|
|
254
|
-
return freqs
|
|
193
|
+
def fft_frequencies(self) -> np.ndarray:
|
|
194
|
+
"""
|
|
195
|
+
Gets the harmonics of the STFT.
|
|
255
196
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
197
|
+
:return freqs: The frequencies at which the stft will be available.
|
|
198
|
+
:rtype freqs: np.ndarray
|
|
199
|
+
"""
|
|
200
|
+
freqs = self.stft.window.fft_harmonics(self.decimation.sample_rate)
|
|
201
|
+
return freqs
|
|
259
202
|
|
|
260
203
|
@property
|
|
261
|
-
def harmonic_indices(self):
|
|
204
|
+
def harmonic_indices(self) -> List[int]:
|
|
205
|
+
"""
|
|
206
|
+
Loops over all bands and returns a list of the harminic indices.
|
|
207
|
+
TODO: Distinguish the bands which are a processing construction vs harmonic indices which are FFT info.
|
|
208
|
+
:return: list of fc indices (integers)
|
|
209
|
+
:rtype: List[int]
|
|
210
|
+
"""
|
|
262
211
|
return_list = []
|
|
263
212
|
for band in self.bands:
|
|
264
213
|
fc_indices = band.harmonic_indices
|
|
@@ -270,14 +219,196 @@ class DecimationLevel(Base):
|
|
|
270
219
|
def local_channels(self):
|
|
271
220
|
return self.input_channels + self.output_channels
|
|
272
221
|
|
|
273
|
-
def
|
|
222
|
+
def is_consistent_with_archived_fc_parameters(
|
|
223
|
+
self,
|
|
224
|
+
fc_decimation: FCDecimation,
|
|
225
|
+
remote: bool
|
|
226
|
+
):
|
|
227
|
+
"""
|
|
228
|
+
Usage: For an already existing spectrogram stored in an MTH5 archive, this compares the metadata
|
|
229
|
+
within the archive (fc_decimation) with an aurora decimation level (self), and tells whether the
|
|
230
|
+
parameters are in agreement. If True, this allows aurora to skip the calculation of FCs and instead
|
|
231
|
+
read them from the archive.
|
|
232
|
+
|
|
233
|
+
TODO: Merge all checks of TimeSeriesDecimation parameters into a single check.
|
|
234
|
+
- e.g. Compress all decimation checks to: assert fc_decimation.decimation == self.decimation
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
decimation_level: FCDecimation
|
|
239
|
+
metadata describing the parameters used to compute an archived spectrogram
|
|
240
|
+
remote: bool
|
|
241
|
+
If True, we are looking for reference channels, not local channels in the FCGroup.
|
|
242
|
+
|
|
243
|
+
Iterates over FCDecimation attributes:
|
|
244
|
+
"channels_estimated": to ensure all expected channels are in the group
|
|
245
|
+
"decimation.anti_alias_filter": check that the expected AAF was applied
|
|
246
|
+
"decimation.sample_rate,
|
|
247
|
+
"decimation.method",
|
|
248
|
+
"stft.prewhitening_type",
|
|
249
|
+
"stft.recoloring",
|
|
250
|
+
"stft.pre_fft_detrend_type",
|
|
251
|
+
"stft.min_num_stft_windows",
|
|
252
|
+
"stft.window",
|
|
253
|
+
"stft.harmonic_indices",
|
|
254
|
+
Returns
|
|
255
|
+
-------
|
|
256
|
+
|
|
257
|
+
:return:
|
|
258
|
+
"""
|
|
259
|
+
# channels_estimated: Checks that the archived spectrogram has the required channels
|
|
260
|
+
if remote:
|
|
261
|
+
required_channels = self.reference_channels
|
|
262
|
+
else:
|
|
263
|
+
required_channels = self.local_channels
|
|
264
|
+
try:
|
|
265
|
+
assert set(required_channels).issubset(fc_decimation.channels_estimated)
|
|
266
|
+
except AssertionError:
|
|
267
|
+
msg = (
|
|
268
|
+
f"required_channels for processing {required_channels} not available"
|
|
269
|
+
f"-- fc channels estimated are {fc_decimation.channels_estimated}"
|
|
270
|
+
)
|
|
271
|
+
self.logger.info(msg)
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
# anti_alias_filter: Check that the data were filtered the same way
|
|
275
|
+
try:
|
|
276
|
+
assert fc_decimation.time_series_decimation.anti_alias_filter == self.decimation.anti_alias_filter
|
|
277
|
+
except AssertionError:
|
|
278
|
+
cond1 = self.time_series_decimation.anti_alias_filter == "default"
|
|
279
|
+
cond2 = fc_decimation.time_series_decimation.anti_alias_filter is None
|
|
280
|
+
if cond1 & cond2:
|
|
281
|
+
pass
|
|
282
|
+
else:
|
|
283
|
+
msg = (
|
|
284
|
+
"Antialias Filters Not Compatible -- need to add handling for "
|
|
285
|
+
f"{msg} FCdec {fc_decimation.time_series_decimation.anti_alias_filter} and "
|
|
286
|
+
f"{msg} processing config:{self.decimation.anti_alias_filter}"
|
|
287
|
+
)
|
|
288
|
+
raise NotImplementedError(msg)
|
|
289
|
+
|
|
290
|
+
# sample_rate
|
|
291
|
+
try:
|
|
292
|
+
assert (
|
|
293
|
+
fc_decimation.time_series_decimation.sample_rate
|
|
294
|
+
== self.decimation.sample_rate
|
|
295
|
+
)
|
|
296
|
+
except AssertionError:
|
|
297
|
+
msg = (
|
|
298
|
+
f"Sample rates do not agree: fc {fc_decimation.time_series_decimation.sample_rate} differs from "
|
|
299
|
+
f"processing config {self.decimation.sample_rate}"
|
|
300
|
+
)
|
|
301
|
+
self.logger.info(msg)
|
|
302
|
+
return False
|
|
303
|
+
|
|
304
|
+
# transform method (fft, wavelet, etc.)
|
|
305
|
+
try:
|
|
306
|
+
assert fc_decimation.short_time_fourier_transform.method == self.stft.method # FFT, Wavelet, etc.
|
|
307
|
+
except AssertionError:
|
|
308
|
+
msg = (
|
|
309
|
+
"Transform methods do not agree: "
|
|
310
|
+
f"fc {fc_decimation.short_time_fourier_transform.method} != processing config {self.stft.method}"
|
|
311
|
+
)
|
|
312
|
+
self.logger.info(msg)
|
|
313
|
+
return False
|
|
314
|
+
|
|
315
|
+
# prewhitening_type
|
|
316
|
+
try:
|
|
317
|
+
assert fc_decimation.stft.prewhitening_type == self.stft.prewhitening_type
|
|
318
|
+
except AssertionError:
|
|
319
|
+
msg = (
|
|
320
|
+
"prewhitening_type does not agree "
|
|
321
|
+
f"fc {fc_decimation.stft.prewhitening_type} != processing config {self.stft.prewhitening_type}"
|
|
322
|
+
)
|
|
323
|
+
self.logger.info(msg)
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
# recoloring
|
|
327
|
+
try:
|
|
328
|
+
assert fc_decimation.stft.recoloring == self.stft.recoloring
|
|
329
|
+
except AssertionError:
|
|
330
|
+
msg = (
|
|
331
|
+
"recoloring does not agree "
|
|
332
|
+
f"fc {fc_decimation.stft.recoloring} != processing config {self.stft.recoloring}"
|
|
333
|
+
)
|
|
334
|
+
self.logger.info(msg)
|
|
335
|
+
return False
|
|
336
|
+
|
|
337
|
+
# pre_fft_detrend_type
|
|
338
|
+
try:
|
|
339
|
+
assert (
|
|
340
|
+
fc_decimation.stft.pre_fft_detrend_type
|
|
341
|
+
== self.stft.pre_fft_detrend_type
|
|
342
|
+
)
|
|
343
|
+
except AssertionError:
|
|
344
|
+
msg = (
|
|
345
|
+
"pre_fft_detrend_type does not agree "
|
|
346
|
+
f"fc {fc_decimation.stft.pre_fft_detrend_type} != processing config {self.stft.pre_fft_detrend_type}"
|
|
347
|
+
)
|
|
348
|
+
self.logger.info(msg)
|
|
349
|
+
return False
|
|
350
|
+
|
|
351
|
+
# min_num_stft_windows
|
|
352
|
+
try:
|
|
353
|
+
assert (
|
|
354
|
+
fc_decimation.stft.min_num_stft_windows
|
|
355
|
+
== self.stft.min_num_stft_windows
|
|
356
|
+
)
|
|
357
|
+
except AssertionError:
|
|
358
|
+
msg = (
|
|
359
|
+
"min_num_stft_windows do not agree "
|
|
360
|
+
f"fc {fc_decimation.stft.min_num_stft_windows} != processing config {self.stft.min_num_stft_windows}"
|
|
361
|
+
)
|
|
362
|
+
self.logger.info(msg)
|
|
363
|
+
return False
|
|
364
|
+
|
|
365
|
+
# window
|
|
366
|
+
try:
|
|
367
|
+
assert fc_decimation.stft.window == self.stft.window
|
|
368
|
+
except AssertionError:
|
|
369
|
+
msg = "window does not agree: "
|
|
370
|
+
msg = f"{msg} FC Group: {fc_decimation.stft.window} "
|
|
371
|
+
msg = f"{msg} Processing Config {self.stft.window}"
|
|
372
|
+
self.logger.info(msg)
|
|
373
|
+
return False
|
|
374
|
+
|
|
375
|
+
if -1 in fc_decimation.stft.harmonic_indices:
|
|
376
|
+
# if harmonic_indices is -1, it means the archive kept all so we can skip this check.
|
|
377
|
+
pass
|
|
378
|
+
else:
|
|
379
|
+
msg = "WIP: harmonic indices in AuroraDecimationlevel are derived from processing bands -- Not robustly tested to compare with FCDecimation"
|
|
380
|
+
self.logger.debug(msg)
|
|
381
|
+
harmonic_indices_requested = self.harmonic_indices
|
|
382
|
+
fcdec_group_set = set(fc_decimation.stft.harmonic_indices)
|
|
383
|
+
processing_set = set(harmonic_indices_requested)
|
|
384
|
+
if processing_set.issubset(fcdec_group_set):
|
|
385
|
+
pass
|
|
386
|
+
else:
|
|
387
|
+
msg = (
|
|
388
|
+
f"Processing FC indices {processing_set} is not contained "
|
|
389
|
+
f"in FC indices {fcdec_group_set}"
|
|
390
|
+
)
|
|
391
|
+
self.logger.info(msg)
|
|
392
|
+
return False
|
|
393
|
+
|
|
394
|
+
# Getting here means no checks were failed. The FCDecimation supports the processing config
|
|
395
|
+
return True
|
|
396
|
+
|
|
397
|
+
def to_fc_decimation(
|
|
398
|
+
self,
|
|
399
|
+
remote: bool = False,
|
|
400
|
+
ignore_harmonic_indices: bool = True,
|
|
401
|
+
) -> FCDecimation:
|
|
274
402
|
"""
|
|
275
403
|
Generates a FC Decimation() object for use with FC Layer in mth5.
|
|
276
404
|
|
|
405
|
+
TODO: this is being tested only in aurora -- move a test to mt_metadata or move the method.
|
|
277
406
|
Ignoring for now these properties
|
|
278
407
|
"time_period.end": "1980-01-01T00:00:00+00:00",
|
|
279
408
|
"time_period.start": "1980-01-01T00:00:00+00:00",
|
|
280
409
|
|
|
410
|
+
TODO: FIXME: Assignment of TSDecimation can be done in one shot once #235 is addressed.
|
|
411
|
+
|
|
281
412
|
Parameters
|
|
282
413
|
----------
|
|
283
414
|
remote: bool
|
|
@@ -293,27 +424,68 @@ class DecimationLevel(Base):
|
|
|
293
424
|
A decimation object configured for STFT processing
|
|
294
425
|
|
|
295
426
|
"""
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
fc_dec_obj = FourierCoefficientDecimation()
|
|
300
|
-
fc_dec_obj.anti_alias_filter = self.anti_alias_filter
|
|
427
|
+
|
|
428
|
+
fc_dec_obj = FCDecimation()
|
|
429
|
+
fc_dec_obj.time_series_decimation.anti_alias_filter = self.decimation.anti_alias_filter
|
|
301
430
|
if remote:
|
|
302
431
|
fc_dec_obj.channels_estimated = self.reference_channels
|
|
303
432
|
else:
|
|
304
433
|
fc_dec_obj.channels_estimated = self.local_channels
|
|
305
|
-
fc_dec_obj.
|
|
306
|
-
fc_dec_obj.
|
|
434
|
+
fc_dec_obj.time_series_decimation.factor = self.decimation.factor
|
|
435
|
+
fc_dec_obj.time_series_decimation.level = self.decimation.level
|
|
307
436
|
if ignore_harmonic_indices:
|
|
308
437
|
pass
|
|
309
438
|
else:
|
|
310
|
-
fc_dec_obj.harmonic_indices = self.harmonic_indices()
|
|
439
|
+
fc_dec_obj.stft.harmonic_indices = self.harmonic_indices()
|
|
311
440
|
fc_dec_obj.id = f"{self.decimation.level}"
|
|
312
|
-
fc_dec_obj.method = self.method
|
|
313
|
-
fc_dec_obj.pre_fft_detrend_type = self.pre_fft_detrend_type
|
|
314
|
-
fc_dec_obj.prewhitening_type = self.prewhitening_type
|
|
315
|
-
fc_dec_obj.recoloring = self.recoloring
|
|
316
|
-
fc_dec_obj.
|
|
317
|
-
fc_dec_obj.window = self.window
|
|
441
|
+
fc_dec_obj.stft.method = self.stft.method
|
|
442
|
+
fc_dec_obj.stft.pre_fft_detrend_type = self.stft.pre_fft_detrend_type
|
|
443
|
+
fc_dec_obj.stft.prewhitening_type = self.stft.prewhitening_type
|
|
444
|
+
fc_dec_obj.stft.recoloring = self.stft.recoloring
|
|
445
|
+
fc_dec_obj.time_series_decimation.sample_rate = self.decimation.sample_rate
|
|
446
|
+
fc_dec_obj.stft.window = self.stft.window
|
|
318
447
|
|
|
319
448
|
return fc_dec_obj
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _df_from_bands(band_list: List[Union[Band, dict, None]]) -> pd.DataFrame:
|
|
452
|
+
"""
|
|
453
|
+
Utility function that transforms a list of bands into a dataframe
|
|
454
|
+
|
|
455
|
+
Note: The decimation_level here is +1 to agree with EMTF convention.
|
|
456
|
+
Not clear this is really necessary
|
|
457
|
+
TODO: Consider making this a method of FrequencyBands() class.
|
|
458
|
+
TODO: Check typehint -- should None be allowed value in the band_list?
|
|
459
|
+
TODO: Consider adding columns lower_closed, upper_closed to df
|
|
460
|
+
|
|
461
|
+
Parameters
|
|
462
|
+
----------
|
|
463
|
+
band_list: list
|
|
464
|
+
obtained from mt_metadata.transfer_functions.processing.aurora.decimation_level.DecimationLevel.bands
|
|
465
|
+
|
|
466
|
+
Returns
|
|
467
|
+
-------
|
|
468
|
+
out_df: pd.Dataframe
|
|
469
|
+
Same format as that generated by EMTFBandSetupFile.get_decimation_level()
|
|
470
|
+
"""
|
|
471
|
+
df_columns = [
|
|
472
|
+
"decimation_level",
|
|
473
|
+
"lower_bound_index",
|
|
474
|
+
"upper_bound_index",
|
|
475
|
+
"frequency_min",
|
|
476
|
+
"frequency_max",
|
|
477
|
+
]
|
|
478
|
+
n_rows = len(band_list)
|
|
479
|
+
df_columns_dict = {}
|
|
480
|
+
for col in df_columns:
|
|
481
|
+
df_columns_dict[col] = n_rows * [None]
|
|
482
|
+
for i_band, band in enumerate(band_list):
|
|
483
|
+
df_columns_dict["decimation_level"][i_band] = band.decimation_level + 1
|
|
484
|
+
df_columns_dict["lower_bound_index"][i_band] = band.index_min
|
|
485
|
+
df_columns_dict["upper_bound_index"][i_band] = band.index_max
|
|
486
|
+
df_columns_dict["frequency_min"][i_band] = band.frequency_min
|
|
487
|
+
df_columns_dict["frequency_max"][i_band] = band.frequency_max
|
|
488
|
+
out_df = pd.DataFrame(data=df_columns_dict)
|
|
489
|
+
out_df.sort_values(by="lower_bound_index", inplace=True)
|
|
490
|
+
out_df.reset_index(inplace=True, drop=True)
|
|
491
|
+
return out_df
|