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.

Files changed (95) hide show
  1. mt_metadata/__init__.py +1 -1
  2. mt_metadata/base/helpers.py +84 -9
  3. mt_metadata/base/metadata.py +137 -65
  4. mt_metadata/features/__init__.py +14 -0
  5. mt_metadata/features/coherence.py +303 -0
  6. mt_metadata/features/cross_powers.py +29 -0
  7. mt_metadata/features/fc_coherence.py +81 -0
  8. mt_metadata/features/feature.py +72 -0
  9. mt_metadata/features/feature_decimation_channel.py +26 -0
  10. mt_metadata/features/feature_fc.py +24 -0
  11. mt_metadata/{transfer_functions/processing/aurora/decimation.py → features/feature_fc_run.py} +9 -4
  12. mt_metadata/features/feature_ts.py +24 -0
  13. mt_metadata/{transfer_functions/processing/aurora/window.py → features/feature_ts_run.py} +11 -18
  14. mt_metadata/features/standards/__init__.py +6 -0
  15. mt_metadata/features/standards/base_feature.json +46 -0
  16. mt_metadata/features/standards/coherence.json +57 -0
  17. mt_metadata/features/standards/fc_coherence.json +57 -0
  18. mt_metadata/features/standards/feature_decimation_channel.json +68 -0
  19. mt_metadata/features/standards/feature_fc_run.json +35 -0
  20. mt_metadata/features/standards/feature_ts_run.json +35 -0
  21. mt_metadata/features/standards/feature_weighting_window.json +46 -0
  22. mt_metadata/features/standards/weight_kernel.json +46 -0
  23. mt_metadata/features/standards/weights.json +101 -0
  24. mt_metadata/features/test_helpers/channel_weight_specs_example.json +156 -0
  25. mt_metadata/features/weights/__init__.py +0 -0
  26. mt_metadata/features/weights/base.py +44 -0
  27. mt_metadata/features/weights/channel_weight_spec.py +209 -0
  28. mt_metadata/features/weights/feature_weight_spec.py +194 -0
  29. mt_metadata/features/weights/monotonic_weight_kernel.py +275 -0
  30. mt_metadata/features/weights/standards/__init__.py +6 -0
  31. mt_metadata/features/weights/standards/activation_monotonic_weight_kernel.json +38 -0
  32. mt_metadata/features/weights/standards/base.json +36 -0
  33. mt_metadata/features/weights/standards/channel_weight_spec.json +35 -0
  34. mt_metadata/features/weights/standards/composite.json +36 -0
  35. mt_metadata/features/weights/standards/feature_weight_spec.json +13 -0
  36. mt_metadata/features/weights/standards/monotonic_weight_kernel.json +49 -0
  37. mt_metadata/features/weights/standards/taper_monotonic_weight_kernel.json +16 -0
  38. mt_metadata/features/weights/taper_weight_kernel.py +60 -0
  39. mt_metadata/helper_functions.py +69 -0
  40. mt_metadata/timeseries/filters/channel_response.py +77 -37
  41. mt_metadata/timeseries/filters/coefficient_filter.py +6 -5
  42. mt_metadata/timeseries/filters/filter_base.py +11 -15
  43. mt_metadata/timeseries/filters/fir_filter.py +8 -1
  44. mt_metadata/timeseries/filters/frequency_response_table_filter.py +26 -11
  45. mt_metadata/timeseries/filters/helper_functions.py +0 -2
  46. mt_metadata/timeseries/filters/obspy_stages.py +4 -1
  47. mt_metadata/timeseries/filters/pole_zero_filter.py +9 -5
  48. mt_metadata/timeseries/filters/time_delay_filter.py +8 -1
  49. mt_metadata/timeseries/location.py +20 -5
  50. mt_metadata/timeseries/person.py +14 -7
  51. mt_metadata/timeseries/standards/person.json +1 -1
  52. mt_metadata/timeseries/standards/run.json +2 -2
  53. mt_metadata/timeseries/station.py +4 -2
  54. mt_metadata/timeseries/stationxml/__init__.py +5 -0
  55. mt_metadata/timeseries/stationxml/xml_channel_mt_channel.py +25 -27
  56. mt_metadata/timeseries/stationxml/xml_inventory_mt_experiment.py +16 -47
  57. mt_metadata/timeseries/stationxml/xml_station_mt_station.py +25 -24
  58. mt_metadata/transfer_functions/__init__.py +3 -0
  59. mt_metadata/transfer_functions/core.py +8 -11
  60. mt_metadata/transfer_functions/io/emtfxml/metadata/location.py +5 -0
  61. mt_metadata/transfer_functions/io/emtfxml/metadata/provenance.py +14 -3
  62. mt_metadata/transfer_functions/io/tools.py +2 -0
  63. mt_metadata/transfer_functions/io/zonge/metadata/header.py +1 -1
  64. mt_metadata/transfer_functions/io/zonge/metadata/standards/header.json +1 -1
  65. mt_metadata/transfer_functions/io/zonge/metadata/standards/job.json +2 -2
  66. mt_metadata/transfer_functions/io/zonge/zonge.py +19 -23
  67. mt_metadata/transfer_functions/processing/__init__.py +2 -1
  68. mt_metadata/transfer_functions/processing/aurora/__init__.py +2 -4
  69. mt_metadata/transfer_functions/processing/aurora/band.py +46 -125
  70. mt_metadata/transfer_functions/processing/aurora/channel_nomenclature.py +27 -20
  71. mt_metadata/transfer_functions/processing/aurora/decimation_level.py +324 -152
  72. mt_metadata/transfer_functions/processing/aurora/frequency_bands.py +230 -0
  73. mt_metadata/transfer_functions/processing/aurora/processing.py +3 -3
  74. mt_metadata/transfer_functions/processing/aurora/run.py +32 -7
  75. mt_metadata/transfer_functions/processing/aurora/standards/decimation_level.json +7 -73
  76. mt_metadata/transfer_functions/processing/aurora/stations.py +33 -4
  77. mt_metadata/transfer_functions/processing/fourier_coefficients/decimation.py +176 -178
  78. mt_metadata/transfer_functions/processing/fourier_coefficients/fc.py +11 -9
  79. mt_metadata/transfer_functions/processing/fourier_coefficients/standards/decimation.json +1 -111
  80. mt_metadata/transfer_functions/processing/short_time_fourier_transform.py +64 -0
  81. mt_metadata/transfer_functions/processing/standards/__init__.py +6 -0
  82. mt_metadata/transfer_functions/processing/standards/short_time_fourier_transform.json +94 -0
  83. mt_metadata/transfer_functions/processing/{aurora/standards/decimation.json → standards/time_series_decimation.json} +17 -6
  84. mt_metadata/transfer_functions/processing/{aurora/standards → standards}/window.json +13 -2
  85. mt_metadata/transfer_functions/processing/time_series_decimation.py +50 -0
  86. mt_metadata/transfer_functions/processing/window.py +118 -0
  87. mt_metadata/transfer_functions/tf/station.py +17 -1
  88. mt_metadata/utils/mttime.py +22 -3
  89. mt_metadata/utils/validators.py +4 -2
  90. {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/METADATA +39 -15
  91. {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/RECORD +95 -55
  92. {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/WHEEL +1 -1
  93. {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/AUTHORS.rst +0 -0
  94. {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/LICENSE +0 -0
  95. {mt_metadata-0.3.9.dist-info → mt_metadata-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,275 @@
1
+ """
2
+ Module for a monotonic_weight_kernel.
3
+
4
+ TODO: Ensure the standards JSON defaults are set when not specified.
5
+
6
+ """
7
+
8
+ from .base import BaseWeightKernel
9
+ from .standards import SCHEMA_FN_PATHS
10
+ from mt_metadata.base.helpers import write_lines
11
+ from mt_metadata.base import get_schema, Base
12
+
13
+
14
+ import numpy as np
15
+
16
+
17
+ # Separate schemas for each type (optional, but recommended for clarity)
18
+ TAPER_STYLES = ["rectangle", "hann", "hamming", "blackman"]
19
+ ACTIVATION_STYLES = ["sigmoid", "hard_sigmoid", "tanh", "hard_tanh"]
20
+
21
+ base_attr_dict = get_schema("monotonic_weight_kernel", SCHEMA_FN_PATHS)
22
+ taper_attr_dict = get_schema("taper_monotonic_weight_kernel", SCHEMA_FN_PATHS)
23
+ activation_attr_dict = get_schema("activation_monotonic_weight_kernel", SCHEMA_FN_PATHS)
24
+
25
+ class MonotonicWeightKernel(BaseWeightKernel):
26
+ """
27
+ MonotonicWeightKernel
28
+
29
+ Base class for monotonic weight kernels.
30
+ Handles bounds, normalization, and direction.
31
+
32
+ A weighting kernel that applies a monotonic activation/taper function between defined
33
+ lower and upper bounds, based on a given threshold direction.
34
+
35
+ There are two main types of monotonic kernels: taper and activation. The taper function
36
+ is used to smoothly transition between the lower and upper bounds over some finite interval,
37
+ while the activation style offers options that asymptote to 0 or 1, such as sigmoid or tanh.
38
+ Thus the activation style supports +/- infinity bounds, while the taper style requires finite bounds.
39
+
40
+ """
41
+ __doc__ = write_lines(base_attr_dict)
42
+
43
+ def __init__(self, attr_dict=base_attr_dict, **kwargs):
44
+ """
45
+ Constructor.
46
+ """
47
+ super().__init__(attr_dict=base_attr_dict, **kwargs)
48
+ self.weight_type = "monotonic"
49
+
50
+ @property
51
+ def _has_finite_transition_bounds(self) -> bool:
52
+ """
53
+ Check if the transition bounds are finite.
54
+
55
+ Returns
56
+ -------
57
+ bool
58
+ True if both transition_lower_bound and transition_upper_bound are finite, False otherwise.
59
+ """
60
+ lb = float(self.transition_lower_bound)
61
+ ub = float(self.transition_upper_bound)
62
+ return np.isfinite(lb) and np.isfinite(ub)
63
+
64
+ def _normalize(self, values):
65
+ """
66
+ Normalize input values to the [0, 1] interval based on finite transition bounds.
67
+
68
+ Only supports finite lower and upper bounds. Subclasses should override this method
69
+ if they wish to support infinite bounds or custom normalization.
70
+
71
+ Parameters
72
+ ----------
73
+ values : array-like
74
+ Input values to be normalized.
75
+
76
+ Returns
77
+ -------
78
+ np.ndarray
79
+ Normalized values in the range [0, 1].
80
+
81
+ Raises
82
+ ------
83
+ ValueError
84
+ If either transition bound is infinite.
85
+ """
86
+ if self._has_finite_transition_bounds:
87
+ lb = float(self.transition_lower_bound)
88
+ ub = float(self.transition_upper_bound)
89
+ values = np.asarray(values)
90
+ return (values - lb) / (ub - lb)
91
+ else:
92
+ raise ValueError("MonotonicWeightKernel only supports finite transition bounds. "
93
+ "Override _normalize in subclasses for infinite bounds.")
94
+
95
+
96
+
97
+ class TaperMonotonicWeightKernel(MonotonicWeightKernel):
98
+ """
99
+ Handles taper/window styles: rectangle, hann, hamming, blackman.
100
+ """
101
+ __doc__ = write_lines(taper_attr_dict)
102
+
103
+ def __init__(self, **kwargs):
104
+ super().__init__(attr_dict=taper_attr_dict, **kwargs)
105
+ self.weight_type = "taper_monotonic"
106
+
107
+ def _normalize(self, values):
108
+ """
109
+ Normalize input values to the [0, 1] interval based on the transition bounds and threshold direction.
110
+
111
+ This function maps the input array `values` to a normalized scale between 0 and 1, according to the
112
+ transition_lower_bound and transition_upper_bound attributes. The normalization is performed differently
113
+ depending on the threshold direction:
114
+
115
+ - If threshold is 'low cut', values below the lower bound are mapped to 0, values above the upper bound are mapped to 1,
116
+ and values in between are linearly scaled.
117
+ - If threshold is 'high cut', the mapping is reversed: values below the lower bound are mapped to 1, values above the upper bound to 0,
118
+ and values in between are linearly scaled in the opposite direction.
119
+
120
+ Parameters
121
+ ----------
122
+ values : array-like
123
+ Input values to be normalized.
124
+
125
+ Returns
126
+ -------
127
+ np.ndarray
128
+ Normalized values in the range [0, 1].
129
+
130
+ Raises
131
+ ------
132
+ ValueError
133
+ If the threshold direction is not recognized.
134
+ """
135
+ lb = float(self.transition_lower_bound)
136
+ ub = float(self.transition_upper_bound)
137
+ direction = self.threshold
138
+ transition_range = ub - lb
139
+ if direction == "low cut":
140
+ return np.clip((values - lb) / transition_range, 0, 1)
141
+ elif direction == "high cut":
142
+ return 1 - np.clip((values - lb) / transition_range, 0, 1)
143
+ else:
144
+ raise ValueError(f"Unknown threshold direction: {direction}")
145
+
146
+ def evaluate(self, values):
147
+ x = self._normalize(values)
148
+ taper = self.half_window_style
149
+ if taper == "rectangle":
150
+ if self.threshold == "low cut":
151
+ return np.where(values < self.transition_lower_bound, 0.0, 1.0)
152
+ else:
153
+ return np.where(values > self.transition_upper_bound, 0.0, 1.0)
154
+ elif taper == "hann":
155
+ return 0.5 * (1 - np.cos(np.pi * x))
156
+ elif taper == "hamming":
157
+ return 0.54 - 0.46 * np.cos(np.pi * x)
158
+ elif taper == "blackman":
159
+ return 0.42 - 0.5 * np.cos(np.pi * x) + 0.08 * np.cos(2 * np.pi * x)
160
+ else:
161
+ raise ValueError(f"Unsupported taper style: {taper}")
162
+
163
+
164
+ class ActivationMonotonicWeightKernel(MonotonicWeightKernel):
165
+ """
166
+ Handles activation styles: sigmoid, hard_sigmoid, tanh, hard_tanh.
167
+
168
+ TODO: Add more testing - this is an experimental class.
169
+
170
+ """
171
+ __doc__ = write_lines(activation_attr_dict)
172
+
173
+ def __init__(self, steepness: float = 10, **kwargs):
174
+ kwargs.pop("attr_dict", None) # Remove if present
175
+ super().__init__(attr_dict=activation_attr_dict, **kwargs)
176
+ self.weight_type = "activation_monotonic"
177
+ self.steepness = steepness
178
+
179
+ def _normalize(self, values):
180
+ """
181
+ Normalize input values to the [0, 1] interval for activation kernels, supporting infinite bounds and respecting threshold direction.
182
+
183
+ For finite bounds, applies linear normalization and reverses for 'high cut'.
184
+ For infinite bounds, subclasses should define behavior, but this implementation will map all values to 0.5.
185
+ """
186
+ lb = float(self.transition_lower_bound)
187
+ ub = float(self.transition_upper_bound)
188
+ values = np.asarray(values)
189
+ direction = getattr(self, 'threshold', 'low cut')
190
+ # Both bounds finite
191
+ if np.isfinite(lb) and np.isfinite(ub):
192
+ x = (values - lb) / (ub - lb)
193
+ if direction == 'high cut':
194
+ x = 1 - x
195
+ return np.clip(x, 0, 1)
196
+ # Infinite bounds: fallback (could be extended for custom behavior)
197
+ msg = "ActivationMonotonicWeightKernel only supports finite transition bounds. "
198
+ self.logger.warning(msg + "Returning 0.5 for all values.")
199
+ return np.full_like(values, 0.5)
200
+
201
+ def evaluate(self, values):
202
+
203
+ x = self._normalize(values)
204
+ activation_style = self.activation_style
205
+
206
+ if activation_style == "sigmoid":
207
+ y = 1 / (1 + np.exp(-float(self.steepness) * (x - 0.5)))
208
+ elif activation_style == "hard_sigmoid":
209
+ y = np.clip(0.2 * (x - 0.5) + 0.5, 0, 1)
210
+ elif activation_style == "tanh":
211
+ y = 0.5 * (np.tanh(float(self.steepness) * (x - 0.5)) + 1)
212
+ elif activation_style == "hard_tanh":
213
+ y = np.clip(x, 0, 1)
214
+ else:
215
+ raise ValueError(f"Unsupported activation style: {activation_style}")
216
+
217
+ return y
218
+
219
+
220
+ class ThresholdWeightKernel(TaperMonotonicWeightKernel):
221
+ """
222
+ ThresholdWeightKernel
223
+
224
+ A special case of MonotonicWeightKernel where the transition region is a single value,
225
+ resulting in a hard threshold (step function). This kernel outputs 0 or 1 depending on
226
+ whether the input is above or below the threshold, according to the threshold_type.
227
+
228
+ Parameters
229
+ ----------
230
+ threshold : float
231
+ The threshold value.
232
+ threshold_type : str, optional
233
+ "low cut" (default) or "high cut". Determines which side is downweighted.
234
+ **kwargs :
235
+ Additional keyword arguments passed to MonotonicWeightKernel.
236
+ """
237
+ def __init__(self, threshold, threshold_type="low cut", **kwargs):
238
+ super().__init__(
239
+ transition_lower_bound=threshold,
240
+ transition_upper_bound=threshold,
241
+ half_window_style="rectangle",
242
+ threshold=threshold_type,
243
+ **kwargs
244
+ )
245
+
246
+
247
+ # TODO: Uncomment if needed for testing or future use
248
+ # def _normalize(self, values):
249
+ # """
250
+ # Normalize input values to the [0, 1] interval, supporting infinite bounds for activation and taper kernels.
251
+ # Handles all combinations of finite and infinite transition bounds:
252
+ # - Both bounds finite: linear normalization.
253
+ # - Lower bound -inf, upper bound finite: exponential normalization.
254
+ # - Lower bound finite, upper bound +inf: exponential normalization.
255
+ # - Both bounds infinite: all values map to 0.5.
256
+ # """
257
+ # lb = float(self.transition_lower_bound)
258
+ # ub = float(self.transition_upper_bound)
259
+ # values = np.asarray(values)
260
+ # # Both bounds finite
261
+ # if np.isfinite(lb) and np.isfinite(ub):
262
+ # return np.clip((values - lb) / (ub - lb), 0, 1)
263
+ # # Lower bound -inf, upper bound finite
264
+ # elif not np.isfinite(lb) and np.isfinite(ub):
265
+ # scale = np.std(values) if np.std(values) > 0 else 1.0
266
+ # x = 1 - np.exp(-(ub - values) / scale)
267
+ # return np.clip(x, 0, 1)
268
+ # # Lower bound finite, upper bound +inf
269
+ # elif np.isfinite(lb) and not np.isfinite(ub):
270
+ # scale = np.std(values) if np.std(values) > 0 else 1.0
271
+ # x = 1 - np.exp(-(values - lb) / scale)
272
+ # return np.clip(x, 0, 1)
273
+ # # Both bounds infinite
274
+ # else:
275
+ # return np.full_like(values, 0.5)
@@ -0,0 +1,6 @@
1
+ # package file
2
+ from pathlib import Path
3
+
4
+ SCHEMA_PATH = Path(__file__).parent
5
+
6
+ SCHEMA_FN_PATHS = list(SCHEMA_PATH.glob("*.json"))
@@ -0,0 +1,38 @@
1
+ {
2
+ "threshold": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "Which side of a threshold should be downweighted.",
8
+ "options": ["low cut", "high cut"],
9
+ "alias": [],
10
+ "example": "low cut",
11
+ "default":"low cut"
12
+ },
13
+ "activation_style": {
14
+ "type": "string",
15
+ "required": true,
16
+ "style": "controlled vocabulary",
17
+ "units": null,
18
+ "description": "Tapering/activation function to use between transition bounds.",
19
+ "options": [
20
+ "sigmoid", "hard_sigmoid", "tanh", "hard_tanh"
21
+ ],
22
+ "alias": [],
23
+ "example": "tanh",
24
+ "default": "sigmoid"
25
+ },
26
+ "steepness": {
27
+ "type": "float",
28
+ "required": false,
29
+ "style": "number",
30
+ "units": null,
31
+ "options": [],
32
+ "alias": [],
33
+ "example": "10",
34
+ "default": 10,
35
+ "description": "Controls the sharpness of the activation transition."
36
+ }
37
+ }
38
+
@@ -0,0 +1,36 @@
1
+ {
2
+ "weight_type": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "Type of weighting kernel (e.g., monotonic, learned, spatial).",
8
+ "options": ["monotonic", "learned", "spatial", "custom"],
9
+ "alias": [],
10
+ "example": "monotonic",
11
+ "default": "monotonic"
12
+ },
13
+ "description": {
14
+ "type": "string",
15
+ "required": false,
16
+ "style": "free form",
17
+ "units": null,
18
+ "description": "Human-readable description of what this kernel is for.",
19
+ "options": [],
20
+ "alias": [],
21
+ "example": "This kernel smoothly transitions between 0 and 1 in a monotonic way",
22
+ "default": ""
23
+ },
24
+ "active": {
25
+ "type": "boolean",
26
+ "required": false,
27
+ "style": "free form",
28
+ "units": null,
29
+ "description": "If false, this kernel will be skipped during weighting.",
30
+ "options": [],
31
+ "alias": [],
32
+ "example": "false",
33
+ "default": true
34
+ }
35
+ }
36
+
@@ -0,0 +1,35 @@
1
+ {
2
+ "combination_style": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "How to combine multiple feature weights.",
8
+ "options": ["multiplication", "minimum", "maximum", "mean"],
9
+ "alias": [],
10
+ "example": "multiplication",
11
+ "default": "multiplication"
12
+ },
13
+ "output_channels": {
14
+ "type": "string",
15
+ "required": true,
16
+ "style": "name list",
17
+ "units": null,
18
+ "description": "list of tf ouput channels for which this weighting scheme will be applied",
19
+ "options": [],
20
+ "alias": [],
21
+ "example": "[ ex ey hz ]",
22
+ "default": []
23
+ },
24
+ "feature_weight_specs": {
25
+ "type": "integer",
26
+ "required": true,
27
+ "style": "number list",
28
+ "units": null,
29
+ "description": "List of feature weighting schemes to use for TF processing.",
30
+ "options": [],
31
+ "alias": [],
32
+ "example": "[]",
33
+ "default": null
34
+ }
35
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "composition_method": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "How to combine sub-kernels.",
8
+ "options": ["sum", "product", "min", "max", "custom"],
9
+ "alias": [],
10
+ "example": "sum",
11
+ "default": "product"
12
+ },
13
+ "kernels": {
14
+ "type": "list",
15
+ "required": true,
16
+ "style": "object list",
17
+ "units": null,
18
+ "description": "List of weight kernel instances (referenced or inline).",
19
+ "options": [],
20
+ "alias": [],
21
+ "example":[],
22
+ "default": []
23
+ },
24
+ "normalize_output": {
25
+ "type": "boolean",
26
+ "required": false,
27
+ "style": "boolean",
28
+ "units": null,
29
+ "description": "Whether to rescale combined weight back to [0,1].",
30
+ "options": [],
31
+ "alias": [],
32
+ "example": false,
33
+ "default": true
34
+ }
35
+ }
36
+
@@ -0,0 +1,13 @@
1
+ {
2
+ "feature_name": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "The name of the feature to evaluate (e.g., coherence, impedance_ratio).",
8
+ "options": ["coherence", "multiple coherence"],
9
+ "alias": [],
10
+ "example": "coherence",
11
+ "default": ""
12
+ }
13
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "threshold": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "Which side of a threshold should be downweighted.",
8
+ "options": ["low cut", "high cut"],
9
+ "alias": [],
10
+ "example": "low cut",
11
+ "default":"low cut"
12
+ },
13
+ "style": {
14
+ "type": "string",
15
+ "required": true,
16
+ "style": "controlled vocabulary",
17
+ "units": null,
18
+ "description": "Tapering/activation function to use between transition bounds.",
19
+ "options": [
20
+ "taper", "activation"
21
+ ],
22
+ "alias": [],
23
+ "example": "activation",
24
+ "default": "taper"
25
+ },
26
+ "transition_lower_bound": {
27
+ "type": "float",
28
+ "required": true,
29
+ "style": "number",
30
+ "units": null,
31
+ "description": "Start of the taper region (weight begins to change).",
32
+ "options": [],
33
+ "alias": [],
34
+ "example": "-inf",
35
+ "default": -1e9
36
+ },
37
+ "transition_upper_bound": {
38
+ "type": "float",
39
+ "required": true,
40
+ "style": "number",
41
+ "units": null,
42
+ "description": "End of the taper region (weight finishes changing).",
43
+ "options": [],
44
+ "alias": [],
45
+ "example": "+inf",
46
+ "default": 1e9
47
+ }
48
+ }
49
+
@@ -0,0 +1,16 @@
1
+ {
2
+ "half_window_style": {
3
+ "type": "string",
4
+ "required": true,
5
+ "style": "controlled vocabulary",
6
+ "units": null,
7
+ "description": "Tapering/activation function to use between transition bounds.",
8
+ "options": [
9
+ "hamming", "hann", "rectangle", "blackman"
10
+ ],
11
+ "alias": [],
12
+ "example": "hann",
13
+ "default": "rectangle"
14
+ }
15
+ }
16
+
@@ -0,0 +1,60 @@
1
+ """
2
+ Module with a compound kernel, mixing multiple monotonic kernels.
3
+ """
4
+
5
+ import numpy as np
6
+ from .monotonic_weight_kernel import TaperMonotonicWeightKernel
7
+ from .base import BaseWeightKernel
8
+ from typing import Tuple
9
+
10
+ class TaperWeightKernel(BaseWeightKernel):
11
+ """
12
+ A composite weight kernel that multiplies a low-cut and a high-cut monotonic taper kernel.
13
+
14
+ Parameters
15
+ ----------
16
+ low_cut : tuple[float, float]
17
+ (lower_bound, upper_bound) for the low-cut transition region.
18
+ high_cut : tuple[float, float]
19
+ (lower_bound, upper_bound) for the high-cut transition region.
20
+ style : str, optional
21
+ The taper style to use (default is 'hann').
22
+ **kwargs
23
+ Additional keyword arguments passed to BaseWeightKernel.
24
+ """
25
+ def __init__(
26
+ self,
27
+ low_cut: Tuple[float, float],
28
+ high_cut: Tuple[float, float],
29
+ style: str = "hann",
30
+ **kwargs
31
+ ):
32
+ super().__init__(**kwargs)
33
+ self._low_kernel = TaperMonotonicWeightKernel(
34
+ threshold="low cut",
35
+ transition_lower_bound=low_cut[0],
36
+ transition_upper_bound=low_cut[1],
37
+ half_window_style=style
38
+ )
39
+ self._high_kernel = TaperMonotonicWeightKernel(
40
+ threshold="high cut",
41
+ transition_lower_bound=high_cut[0],
42
+ transition_upper_bound=high_cut[1],
43
+ half_window_style=style
44
+ )
45
+
46
+ def evaluate(self, values: np.ndarray) -> np.ndarray:
47
+ """
48
+ Evaluate the composite taper weight kernel on the input values.
49
+
50
+ Parameters
51
+ ----------
52
+ values : np.ndarray
53
+ Input values to evaluate the kernel on.
54
+
55
+ Returns
56
+ -------
57
+ np.ndarray
58
+ The product of the low-cut and high-cut kernel evaluations.
59
+ """
60
+ return self._low_kernel.evaluate(values) * self._high_kernel.evaluate(values)
@@ -0,0 +1,69 @@
1
+ """
2
+ This module has some general helper functions that it isn't yet clear where they should live.
3
+
4
+ These may not be needed at all after the pydantic upgrade is fully integrated.
5
+ """
6
+
7
+ from typing import Dict, List, Union
8
+ from mt_metadata.base import Base
9
+
10
+
11
+ """
12
+ Here are some rather abstract functions for generalizing setters of lists,
13
+ whose elements are particular mt_metadata classes.
14
+ Example usage is decimation_level.bands
15
+
16
+ """
17
+
18
+ def validate_setter_input(value: Union[Dict, Base], expected_class: Base) -> List:
19
+ """
20
+ Takes a setter's input and makes it a list if it not.
21
+ Then asserts that every list element is of permissible type (dict or expected class)
22
+
23
+ Parameters
24
+ ----------
25
+ value: Union[Dict, Base]
26
+ The input to the setter.
27
+
28
+ expected_class: Base
29
+ Some mt_metadata class that we want the setter work with
30
+
31
+ Returns
32
+ -------
33
+ value: list
34
+ List of elements for the setter all of type expected_class or dict.
35
+ """
36
+ # Handle singleton cases
37
+ if isinstance(value, (expected_class, dict)):
38
+ value = [value, ]
39
+
40
+ if not isinstance(value, list):
41
+ raise TypeError(f"Not sure what to do with {type(value)}")
42
+
43
+ return value
44
+
45
+ def cast_to_class_if_dict(obj: Union[Dict, Base], cls: Base ) -> Base:
46
+ """
47
+
48
+ Parameters
49
+ ----------
50
+ obj: Union[Dict, Base]
51
+ Either an mt_metadata object or its dict representaiton
52
+ cls: Base
53
+ Some mt_metadata object that we want to get back
54
+
55
+ Returns
56
+ -------
57
+ either the input or the input dict cast to an mt_metadata object.
58
+ """
59
+ if not isinstance(obj, (cls, dict)):
60
+ raise TypeError(
61
+ f"List entry must be a {cls().__class__} object not {type(obj)}"
62
+ )
63
+ if isinstance(obj, dict):
64
+ mt_metadata_obj = cls()
65
+ mt_metadata_obj.from_dict(obj)
66
+ else:
67
+ mt_metadata_obj = obj
68
+
69
+ return mt_metadata_obj