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
@@ -2,7 +2,7 @@
2
2
  """
3
3
  Created on Wed Dec 23 21:30:36 2020
4
4
 
5
- :copyright:
5
+ :copyright:
6
6
  Jared Peacock (jpeacock@usgs.gov)
7
7
 
8
8
  :license: MIT
@@ -21,6 +21,7 @@ from . import (
21
21
  Fdsn,
22
22
  Orientation,
23
23
  Person,
24
+ provenance,
24
25
  Provenance,
25
26
  Location,
26
27
  TimePeriod,
@@ -46,8 +47,9 @@ attr_dict.add_dict(
46
47
  keys=["name", "comments", "organization"],
47
48
  )
48
49
  attr_dict.add_dict(get_schema("orientation", SCHEMA_FN_PATHS), "orientation")
50
+
49
51
  attr_dict.add_dict(
50
- Provenance()._attr_dict,
52
+ provenance.attr_dict,
51
53
  "provenance",
52
54
  )
53
55
 
@@ -3,6 +3,11 @@
3
3
  Tools to translate StationXML to MT Metadata
4
4
  """
5
5
 
6
+ try:
7
+ import obspy
8
+ except ImportError:
9
+ raise ImportError("StationXML requires obspy to be installed.")
10
+
6
11
  from .xml_network_mt_survey import XMLNetworkMTSurvey
7
12
  from .xml_equipment_mt_run import XMLEquipmentMTRun
8
13
  from .xml_station_mt_station import XMLStationMTStation
@@ -42,7 +42,7 @@ class XMLChannelMTChannel(BaseTranslator):
42
42
  "induction coil",
43
43
  "coil",
44
44
  "dipole",
45
- "electrode"
45
+ "electrode",
46
46
  ]
47
47
 
48
48
  def __init__(self):
@@ -121,8 +121,10 @@ class XMLChannelMTChannel(BaseTranslator):
121
121
  # fill channel filters
122
122
  mt_channel.filter.name = list(mt_filters.keys())
123
123
  mt_channel.filter.applied = [True] * len(list(mt_filters.keys()))
124
- if UTCDateTime(mt_channel.time_period.end) < UTCDateTime(mt_channel.time_period.start):
125
- mt_channel.time_period.end = '2200-01-01T00:00:00+00:00'
124
+ if UTCDateTime(mt_channel.time_period.end) < UTCDateTime(
125
+ mt_channel.time_period.start
126
+ ):
127
+ mt_channel.time_period.end = "2200-01-01T00:00:00+00:00"
126
128
  return mt_channel, mt_filters
127
129
 
128
130
  def mt_to_xml(self, mt_channel, filters_dict, hard_code=True):
@@ -190,12 +192,8 @@ class XMLChannelMTChannel(BaseTranslator):
190
192
  xml_channel.types = ["geophysical".upper()]
191
193
  xml_channel.sensor = self._mt_to_sensor(mt_channel)
192
194
  xml_channel.comments = self._make_xml_comments(mt_channel.comments)
193
- xml_channel.restricted_status = release_dict[
194
- xml_channel.restricted_status
195
- ]
196
- xml_channel = self._mt_to_xml_response(
197
- mt_channel, filters_dict, xml_channel
198
- )
195
+ xml_channel.restricted_status = release_dict[xml_channel.restricted_status]
196
+ xml_channel = self._mt_to_xml_response(mt_channel, filters_dict, xml_channel)
199
197
 
200
198
  for mt_key, xml_key in self.mt_translator.items():
201
199
  if xml_key is None:
@@ -211,9 +209,7 @@ class XMLChannelMTChannel(BaseTranslator):
211
209
  xml_channel.dip = mt_channel.measurement_tilt % 360
212
210
 
213
211
  else:
214
- setattr(
215
- xml_channel, xml_key, mt_channel.get_attr_from_name(mt_key)
216
- )
212
+ setattr(xml_channel, xml_key, mt_channel.get_attr_from_name(mt_key))
217
213
 
218
214
  return xml_channel
219
215
 
@@ -272,9 +268,7 @@ class XMLChannelMTChannel(BaseTranslator):
272
268
  mt_channel.negative.model = sensor.model
273
269
  mt_channel.negative.type = "electrode"
274
270
 
275
- mt_channel.dipole_length = self._parse_dipole_length(
276
- sensor.description
277
- )
271
+ mt_channel.dipole_length = self._parse_dipole_length(sensor.description)
278
272
 
279
273
  return mt_channel
280
274
 
@@ -434,7 +428,10 @@ class XMLChannelMTChannel(BaseTranslator):
434
428
  :rtype: TYPE
435
429
 
436
430
  """
431
+
437
432
  comments = []
433
+ if mt_comment is None:
434
+ return comments
438
435
  clist = mt_comment.split("run_ids:")
439
436
  for item in clist:
440
437
  if ":" in item:
@@ -479,9 +476,7 @@ class XMLChannelMTChannel(BaseTranslator):
479
476
  def _get_mt_units(self, xml_channel, mt_channel):
480
477
  """ """
481
478
  name = xml_channel.response.response_stages[-1].output_units
482
- description = xml_channel.response.response_stages[
483
- -1
484
- ].output_units_description
479
+ description = xml_channel.response.response_stages[-1].output_units_description
485
480
  if description and name:
486
481
  if len(description) > len(name):
487
482
  mt_channel.units = description
@@ -545,9 +540,9 @@ class XMLChannelMTChannel(BaseTranslator):
545
540
  return f_obj.name, False
546
541
 
547
542
  try:
548
- last = sorted(
549
- [k for k in existing_filters.keys() if mt_filter.type in k]
550
- )[-1]
543
+ last = sorted([k for k in existing_filters.keys() if mt_filter.type in k])[
544
+ -1
545
+ ]
551
546
  except IndexError:
552
547
  return f"{mt_filter.type}_{0:02}", True
553
548
  try:
@@ -582,7 +577,6 @@ class XMLChannelMTChannel(BaseTranslator):
582
577
 
583
578
  return xml_channel
584
579
 
585
-
586
580
  def _deduce_sensor_type(self, sensor):
587
581
  """
588
582
 
@@ -606,11 +600,12 @@ class XMLChannelMTChannel(BaseTranslator):
606
600
  else:
607
601
  sensor_description = copy.deepcopy(original_sensor_type)
608
602
 
609
-
610
603
  if sensor_type.lower() in self.understood_sensor_types:
611
604
  return sensor_type
612
605
  else:
613
- self.logger.warning(f" sensor {sensor} type {sensor.type} not in {self.understood_sensor_types}")
606
+ self.logger.warning(
607
+ f" sensor {sensor} type {sensor.type} not in {self.understood_sensor_types}"
608
+ )
614
609
 
615
610
  # Try handling Bartington FGM at Earthscope ... this is a place holder for handling non-standard cases
616
611
  if sensor_type.lower() == "bartington":
@@ -619,12 +614,15 @@ class XMLChannelMTChannel(BaseTranslator):
619
614
  if sensor_description == "Bartington 3-Axis Fluxgate Sensor":
620
615
  sensor_type = "magnetometer"
621
616
  if sensor_description:
622
- if ("bf-4" in sensor_description.lower()) & ("schlumberger" in sensor_description.lower()): # BSL-NCEDC
617
+ if ("bf-4" in sensor_description.lower()) & (
618
+ "schlumberger" in sensor_description.lower()
619
+ ): # BSL-NCEDC
623
620
  sensor_type = "magnetometer"
624
- elif ("electric" in sensor_description.lower()) & ("dipole" in sensor_description.lower()): # BSL-NCEDC
621
+ elif ("electric" in sensor_description.lower()) & (
622
+ "dipole" in sensor_description.lower()
623
+ ): # BSL-NCEDC
625
624
  sensor_type = "dipole"
626
625
 
627
-
628
626
  # reset sensor_type to None it it was not handled
629
627
  if not sensor_type:
630
628
  sensor_type = original_sensor_type
@@ -85,9 +85,7 @@ class XMLInventoryMTExperiment:
85
85
  run_channel = deepcopy(mt_channel)
86
86
  mt_run = mt_station.get_run(run_id)
87
87
  # need to set the start and end time to the run
88
- run_channel.time_period.start = (
89
- mt_run.time_period.start
90
- )
88
+ run_channel.time_period.start = mt_run.time_period.start
91
89
  run_channel.time_period.end = mt_run.time_period.end
92
90
  mt_run.add_channel(run_channel)
93
91
  # if there are runs already try to match by start, end, sample_rate
@@ -105,16 +103,13 @@ class XMLInventoryMTExperiment:
105
103
  mt_run.time_period.start
106
104
  >= mt_channel.time_period.start
107
105
  ) and (
108
- mt_run.time_period.end
109
- <= mt_channel.time_period.end
106
+ mt_run.time_period.end <= mt_channel.time_period.end
110
107
  ):
111
108
  mt_run.channels.append(mt_channel)
112
109
  mt_run.sample_rate = mt_channel.sample_rate
113
110
  # make a new run with generic information
114
111
  else:
115
- mt_run = metadata.Run(
116
- id=f"{len(mt_station.runs)+1:03d}"
117
- )
112
+ mt_run = metadata.Run(id=f"{len(mt_station.runs)+1:03d}")
118
113
  mt_run.time_period.start = mt_channel.time_period.start
119
114
  mt_run.time_period.end = mt_channel.time_period.end
120
115
  mt_run.sample_rate = mt_channel.sample_rate
@@ -128,18 +123,14 @@ class XMLInventoryMTExperiment:
128
123
  # need to check if the network/survey already exists, the files
129
124
  # from make_mth5_from_iris have multiples of the same network
130
125
  if mt_survey.id in mt_experiment.surveys.keys():
131
- mt_experiment.surveys[mt_survey.id].stations.update(
132
- mt_survey.stations
133
- )
126
+ mt_experiment.surveys[mt_survey.id].stations.update(mt_survey.stations)
134
127
  else:
135
128
  mt_experiment.surveys.append(mt_survey)
136
129
  if mt_fn:
137
130
  mt_experiment.to_xml(fn=mt_fn)
138
131
  return mt_experiment
139
132
 
140
- def mt_to_xml(
141
- self, mt_experiment, mt_fn=None, stationxml_fn=None, ns_dict=None
142
- ):
133
+ def mt_to_xml(self, mt_experiment, mt_fn=None, stationxml_fn=None, ns_dict=None):
143
134
  """
144
135
  Convert from MT :class:`mt_metadata.timeseries.Experiment` to
145
136
  :class:`obspy.core.inventory.Inventory`
@@ -176,9 +167,7 @@ class XMLInventoryMTExperiment:
176
167
  )
177
168
  # need to sort the runs by time
178
169
  for mt_run in mt_station.runs:
179
- xml_station = self.add_run(
180
- xml_station, mt_run, mt_survey.filters
181
- )
170
+ xml_station = self.add_run(xml_station, mt_run, mt_survey.filters)
182
171
  xml_network.stations.append(xml_station)
183
172
  xml_inventory.networks.append(xml_network)
184
173
  if stationxml_fn:
@@ -204,19 +193,13 @@ class XMLInventoryMTExperiment:
204
193
  """
205
194
 
206
195
  for mt_channel in mt_run.channels:
207
- xml_channel = self.channel_translator.mt_to_xml(
208
- mt_channel, filters_dict
209
- )
210
- existing_channels = xml_station.select(
211
- channel=xml_channel.code
212
- ).channels
196
+ xml_channel = self.channel_translator.mt_to_xml(mt_channel, filters_dict)
197
+ existing_channels = xml_station.select(channel=xml_channel.code).channels
213
198
 
214
199
  if existing_channels:
215
200
  find = False
216
201
  start_list = [c.start_date for c in existing_channels]
217
- existing_channel = existing_channels[
218
- start_list.index(max(start_list))
219
- ]
202
+ existing_channel = existing_channels[start_list.index(max(start_list))]
220
203
  # should only compare the last channel
221
204
  # for existing_channel in existing_channels:
222
205
  run_list = [c.value for c in existing_channel.comments]
@@ -231,9 +214,7 @@ class XMLInventoryMTExperiment:
231
214
  f"Matched {xml_channel.code}={existing_channel.code}"
232
215
  )
233
216
  if not mt_run.id in run_list:
234
- self.logger.debug(
235
- f"adding run id {mt_run.id} to {run_list}"
236
- )
217
+ self.logger.debug(f"adding run id {mt_run.id} to {run_list}")
237
218
  existing_channel.comments.append(
238
219
  inventory.Comment(mt_run.id, subject="mt.run.id")
239
220
  )
@@ -250,22 +231,16 @@ class XMLInventoryMTExperiment:
250
231
  )
251
232
  run_list = [c.value for c in xml_channel.comments]
252
233
  if not mt_run.id in run_list:
253
- self.logger.debug(
254
- f"adding run id {mt_run.id} to {run_list}"
255
- )
234
+ self.logger.debug(f"adding run id {mt_run.id} to {run_list}")
256
235
  xml_channel.comments.append(
257
236
  inventory.Comment(mt_run.id, subject="mt.run.id")
258
237
  )
259
238
  xml_station.channels.append(xml_channel)
260
239
  else:
261
- self.logger.debug(
262
- f"no existing channels for {xml_channel.code}"
263
- )
240
+ self.logger.debug(f"no existing channels for {xml_channel.code}")
264
241
  run_list = [c.value for c in xml_channel.comments]
265
242
  if not mt_run.id in run_list:
266
- self.logger.debug(
267
- f"adding run id {mt_run.id} to {run_list}"
268
- )
243
+ self.logger.debug(f"adding run id {mt_run.id} to {run_list}")
269
244
  xml_channel.comments.append(
270
245
  inventory.Comment(mt_run.id, subject="mt.run.id")
271
246
  )
@@ -294,20 +269,14 @@ class XMLInventoryMTExperiment:
294
269
  )
295
270
  return False
296
271
  if xml_channel_01.sensor != xml_channel_02.sensor:
297
- self.logger.debug(
298
- f"{xml_channel_01.sensor} != {xml_channel_02.sensor}"
299
- )
272
+ self.logger.debug(f"{xml_channel_01.sensor} != {xml_channel_02.sensor}")
300
273
  return False
301
- if round(xml_channel_01.latitude, 3) != round(
302
- xml_channel_02.latitude, 3
303
- ):
274
+ if round(xml_channel_01.latitude, 3) != round(xml_channel_02.latitude, 3):
304
275
  self.logger.debug(
305
276
  f"{round(xml_channel_01.latitude, 3)} != {round(xml_channel_02.latitude, 3)}"
306
277
  )
307
278
  return False
308
- if round(xml_channel_01.longitude, 3) != round(
309
- xml_channel_02.longitude, 3
310
- ):
279
+ if round(xml_channel_01.longitude, 3) != round(xml_channel_02.longitude, 3):
311
280
  self.logger.debug(
312
281
  f"{round(xml_channel_01.longitude, 3)} != {round(xml_channel_02.longitude, 3)}"
313
282
  )
@@ -2,7 +2,7 @@
2
2
  """
3
3
  Created on Thu Feb 18 12:49:13 2021
4
4
 
5
- :copyright:
5
+ :copyright:
6
6
  Jared Peacock (jpeacock@usgs.gov)
7
7
 
8
8
  :license: MIT
@@ -142,9 +142,7 @@ class XMLStationMTStation(BaseTranslator):
142
142
  mt_station.id = mt_station.fdsn.id
143
143
 
144
144
  # read in equipment information
145
- mt_station = self._equipments_to_runs(
146
- xml_station.equipments, mt_station
147
- )
145
+ mt_station = self._equipments_to_runs(xml_station.equipments, mt_station)
148
146
  mt_station = self._add_run_comments(run_comments, mt_station)
149
147
 
150
148
  return mt_station
@@ -175,37 +173,46 @@ class XMLStationMTStation(BaseTranslator):
175
173
  mt_station.fdsn.id = mt_station.id
176
174
 
177
175
  xml_station = inventory.Station(
178
- code,
176
+ code.upper(),
179
177
  mt_station.location.latitude,
180
178
  mt_station.location.longitude,
181
179
  mt_station.location.elevation,
182
180
  )
183
181
 
184
182
  for xml_key, mt_key in self.xml_translator.items():
185
- if xml_key in ["alternate_code"]:
186
- xml_station.alternate_code = mt_station.id
183
+ # need to skip code because we just set it above and it needs to be upper.
184
+
187
185
  if mt_key is None:
188
- msg = "cannot currently map mt_key.station to inventory.station.{0}".format(
189
- xml_key
186
+ self.logger.debug(
187
+ f"Cannot currently map mt_key.station to inventory.station.{xml_key}"
190
188
  )
191
- self.logger.debug(msg)
192
189
  continue
193
190
 
194
- if xml_key == "operators":
191
+ if xml_key in ["code"]:
192
+ continue
193
+ elif xml_key in ["alternate_code"]:
194
+ xml_station.alternate_code = mt_station.id.upper()
195
+
196
+ elif xml_key == "operators":
195
197
  if mt_station.acquired_by.name:
196
198
  if mt_station.acquired_by.organization is None:
197
199
  mt_station.acquired_by.organization = " "
198
200
  operator = inventory.Operator(
199
201
  agency=mt_station.acquired_by.organization
200
202
  )
201
- person = inventory.Person(
202
- names=[mt_station.acquired_by.name]
203
- )
203
+ person = inventory.Person(names=[mt_station.acquired_by.name])
204
204
  operator.contacts = [person]
205
205
  xml_station.operators = [operator]
206
206
 
207
207
  elif xml_key == "site":
208
- xml_station.site.name = mt_station.geographic_name
208
+ if mt_station.geographic_name is None:
209
+ xml_station.site.name = mt_station.id.upper()
210
+ self.logger.warning(
211
+ f"Station.geographic_name is None, using Station.id = {mt_station.id}."
212
+ "Check StationXML site.name."
213
+ )
214
+ else:
215
+ xml_station.site.name = mt_station.geographic_name
209
216
 
210
217
  elif xml_key == "comments":
211
218
  if mt_station.comments is not None:
@@ -216,9 +223,7 @@ class XMLStationMTStation(BaseTranslator):
216
223
  xml_station.restricted_status
217
224
  ]
218
225
  else:
219
- setattr(
220
- xml_station, xml_key, mt_station.get_attr_from_name(mt_key)
221
- )
226
+ setattr(xml_station, xml_key, mt_station.get_attr_from_name(mt_key))
222
227
 
223
228
  # add mt comments
224
229
  xml_station.comments = self.make_mt_comments(mt_station, "mt.station")
@@ -281,9 +286,7 @@ class XMLStationMTStation(BaseTranslator):
281
286
  if run_attr == "comments":
282
287
  value = f"{ckey}: {cvalue}"
283
288
  try:
284
- station_obj.runs[
285
- run_index
286
- ].comments += f", {value}"
289
+ station_obj.runs[run_index].comments += f", {value}"
287
290
  except TypeError:
288
291
  station_obj.runs[run_index].comments = value
289
292
  else:
@@ -293,8 +296,6 @@ class XMLStationMTStation(BaseTranslator):
293
296
  c_attr, cvalue
294
297
  )
295
298
  else:
296
- station_obj.runs[run_index].set_attr_from_name(
297
- ckey, cvalue
298
- )
299
+ station_obj.runs[run_index].set_attr_from_name(ckey, cvalue)
299
300
 
300
301
  return station_obj
@@ -9,6 +9,7 @@ STANDARD_OUTPUT_CHANNELS = [
9
9
  "hz",
10
10
  ]
11
11
 
12
+ # channel nomenclature mappings
12
13
  CHANNEL_MAPS = {
13
14
  "default": {"hx": "hx", "hy": "hy", "hz": "hz", "ex": "ex", "ey": "ey"},
14
15
  "lemi12": {"hx": "bx", "hy": "by", "hz": "bz", "ex": "e1", "ey": "e2"},
@@ -16,6 +17,8 @@ CHANNEL_MAPS = {
16
17
  "phoenix123": {"hx": "h1", "hy": "h2", "hz": "h3", "ex": "e1", "ey": "e2"},
17
18
  "musgraves": {"hx": "bx", "hy": "by", "hz": "bz", "ex": "ex", "ey": "ey"},
18
19
  }
20
+ CHANNEL_MAPS["nims"] = CHANNEL_MAPS["default"] # Alias NIMS system to use same config as default
21
+
19
22
 
20
23
 
21
24
  def get_allowed_channel_names(standard_names):
@@ -37,7 +37,7 @@ from mt_metadata.transfer_functions.io.zfiles.metadata import (
37
37
  from mt_metadata.base.helpers import validate_name
38
38
  from mt_metadata.utils.list_dict import ListDict
39
39
  from mt_metadata import DEFAULT_CHANNEL_NOMENCLATURE
40
-
40
+ from typing import Optional
41
41
  # =============================================================================
42
42
 
43
43
 
@@ -179,12 +179,12 @@ class TF:
179
179
 
180
180
  return f"TF( {(', ').join(lines)} )"
181
181
 
182
- def __eq__(self, other):
182
+ def __eq__(self, other, ignore_station_metadata_keys: Optional[list] = None):
183
183
  is_equal = True
184
184
  if not isinstance(other, TF):
185
185
  self.logger.info(f"Comparing object is not TF, type {type(other)}")
186
186
  is_equal = False
187
- if self.station_metadata != other.station_metadata:
187
+ if not self.station_metadata.__eq__(other.station_metadata, ignore_keys=ignore_station_metadata_keys):
188
188
  self.logger.info("Station metadata is not equal")
189
189
  is_equal = False
190
190
  if self.survey_metadata != other.survey_metadata:
@@ -375,9 +375,7 @@ class TF:
375
375
  """
376
376
 
377
377
  if station_metadata is not None:
378
- station_metadata = self._validate_station_metadata(
379
- station_metadata
380
- )
378
+ station_metadata = self._validate_station_metadata(station_metadata)
381
379
 
382
380
  runs = ListDict()
383
381
  if self.run_metadata.id not in ["0", 0, None]:
@@ -1943,7 +1941,7 @@ class TF:
1943
1941
  "residual_covariance": "residual_covariance",
1944
1942
  "transfer_function_error": "tf_err",
1945
1943
  "survey_metadata": "survey_metadata",
1946
- "station_metadata": "station_metadata",
1944
+ # "station_metadata": "station_metadata",
1947
1945
  "_rotation_angle": "rotation_angle",
1948
1946
  }
1949
1947
  )
@@ -1956,7 +1954,7 @@ class TF:
1956
1954
  "tipper": "t",
1957
1955
  "tipper_error": "t_err",
1958
1956
  "survey_metadata": "survey_metadata",
1959
- "station_metadata": "station_metadata",
1957
+ # "station_metadata": "station_metadata",
1960
1958
  "_rotation_angle": "rotation_angle",
1961
1959
  }
1962
1960
  )
@@ -2054,7 +2052,7 @@ class TF:
2054
2052
  f"Input must be a EMTFXML object not {type(emtfxml_obj)}"
2055
2053
  )
2056
2054
  self.survey_metadata = emtfxml_obj.survey_metadata
2057
- self.station_metadata = emtfxml_obj.station_metadata
2055
+ self.station_metadata = self.survey_metadata.stations[0]
2058
2056
 
2059
2057
  self.period = emtfxml_obj.data.period
2060
2058
  self.impedance = emtfxml_obj.data.z
@@ -2116,7 +2114,7 @@ class TF:
2116
2114
  "tipper": "t",
2117
2115
  "tipper_error": "t_err",
2118
2116
  "survey_metadata": "survey_metadata",
2119
- "station_metadata": "station_metadata",
2117
+ # "station_metadata": "station_metadata",
2120
2118
  }
2121
2119
  )
2122
2120
 
@@ -2377,7 +2375,6 @@ class TF:
2377
2375
  if not isinstance(avg_obj, ZongeMTAvg):
2378
2376
  raise TypeError(f"Input must be a ZMM object not {type(avg_obj)}")
2379
2377
  self.survey_metadata = avg_obj.survey_metadata
2380
- self.station_metadata = avg_obj.station_metadata
2381
2378
 
2382
2379
  self.period = 1.0 / avg_obj.frequency
2383
2380
  self.impedance = avg_obj.z
@@ -24,6 +24,8 @@ from mt_metadata.transfer_functions.io.emtfxml.metadata import helpers
24
24
  # =============================================================================
25
25
  attr_dict = get_schema("location", SCHEMA_FN_PATHS)
26
26
  attr_dict.add_dict(Declination()._attr_dict, "declination")
27
+
28
+
27
29
  # =============================================================================
28
30
  class Location(Base):
29
31
  __doc__ = write_lines(attr_dict)
@@ -31,6 +33,9 @@ class Location(Base):
31
33
  def __init__(self, **kwargs):
32
34
 
33
35
  self.declination = Declination()
36
+ self._latitude = 0.0
37
+ self._longitude = 0.0
38
+ self._elevation = 0.0
34
39
 
35
40
  super().__init__(attr_dict=attr_dict, **kwargs)
36
41
 
@@ -25,9 +25,20 @@ from mt_metadata import __version__
25
25
 
26
26
  # =============================================================================
27
27
  attr_dict = get_schema("provenance", SCHEMA_FN_PATHS)
28
- person_dict = get_schema("person", SCHEMA_FN_PATHS)
29
- attr_dict.add_dict(person_dict, "creator")
30
- attr_dict.add_dict(person_dict, "submitter")
28
+ person_dict = get_schema(
29
+ "person",
30
+ SCHEMA_FN_PATHS,
31
+ )
32
+ attr_dict.add_dict(
33
+ person_dict,
34
+ "creator",
35
+ keys=["name", "comments", "url", "org", "org_url", "email"],
36
+ )
37
+ attr_dict.add_dict(
38
+ person_dict,
39
+ "submitter",
40
+ keys=["name", "comments", "url", "org", "org_url", "email"],
41
+ )
31
42
  # =============================================================================
32
43
 
33
44
 
@@ -198,7 +198,9 @@ def get_nm_elev(latitude, longitude):
198
198
 
199
199
  # read the xml response and convert to a float
200
200
  try:
201
+ # unpack the response to a dictionary
201
202
  info = json.loads(response.read())
203
+
202
204
  except json.JSONDecodeError:
203
205
  logger.error(
204
206
  f"Input values (latitude={latitude}, longitude={longitude}) "
@@ -68,7 +68,7 @@ class Header(Base):
68
68
  self.stn = STN()
69
69
  self.line = Line()
70
70
  self.unit = Unit()
71
- self.job = Job
71
+ self.job = Job()
72
72
  self._elevation = 0.0
73
73
  super().__init__(attr_dict=attr_dict, **kwargs)
74
74
 
@@ -8,6 +8,6 @@
8
8
  "options": [],
9
9
  "alias": [],
10
10
  "example": "null",
11
- "default": null
11
+ "default": null
12
12
  }
13
13
  }
@@ -8,7 +8,7 @@
8
8
  "options": [],
9
9
  "alias": [],
10
10
  "example": "yellowstone",
11
- "default": null
11
+ "default": null
12
12
  },
13
13
  "for": {
14
14
  "type": "string",
@@ -19,7 +19,7 @@
19
19
  "options": [],
20
20
  "alias": [],
21
21
  "example": "NSF",
22
- "default": null
22
+ "default": null
23
23
  }
24
24
 
25
25
  }