mt_metadata.transfer_functions.io.edi.metadata

Submodules

Classes

EMeasurement

Base class for all metadata objects with Pydantic validation.

HMeasurement

Base class for all metadata objects with Pydantic validation.

Information

Contain, read, and write info section of .edi file

DataSection

DataSection contains the small metadata block that describes which channel

DefineMeasurement

DefineMeasurement class holds information about the measurement. This

Header

A partial location class that only includes the latitude, longitude, and elevation.

Package Contents

class mt_metadata.transfer_functions.io.edi.metadata.EMeasurement(**data)

Bases: mt_metadata.base.MetadataBase

Base class for all metadata objects with Pydantic validation.

MetadataBase extends DotNotationBaseModel (which inherits from Pydantic’s BaseModel) to provide automatic validation according to metadata standards. It adds functionality beyond dictionaries, supporting JSON, XML, pandas Series, and other formats for metadata interchange.

_skip_equals

Private attribute listing fields to skip in equality comparisons

Type:

list[str]

_fields

Private attribute caching field information

Type:

dict[str, Any]

Notes

  • All field assignments are validated automatically via Pydantic

  • None values are converted to appropriate defaults (empty string or 0.0)

  • Supports nested attribute access via dot notation

  • Thread-safe for read operations after initialization

id: Annotated[float | None, Field(default=0.0, description='Channel number, could be location.channel_number.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['1']})]
chtype: Annotated[str, Field(default='', description="channel type, should start with an 'e'", alias=None, pattern='^(RR|rr|[eE])[a-zA-Z0-9_]+$', json_schema_extra={'units': None, 'required': True, 'examples': ['ex']})]
x: Annotated[float, Field(default=0.0, description='location of negative sensor relative center point in north direction', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
x2: Annotated[float, Field(default=0.0, description='location of positive sensor relative center point in north direction', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
y: Annotated[float, Field(default=0.0, description='location of negative sensor relative center point in east direction', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
y2: Annotated[float, Field(default=0.0, description='location of positive sensor relative center point in east direction', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
z: Annotated[float, Field(default=0.0, description='location of negative sensor relative center point in depth', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
z2: Annotated[float, Field(default=0.0, description='location of positive sensor relative center point in depth', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
azm: Annotated[float, Field(default=0.0, description='orientation of the sensor relative to coordinate system, clockwise positive.', alias=None, json_schema_extra={'units': 'degrees', 'required': True, 'examples': ['100.0']})]
acqchan: Annotated[str, Field(default='', description='description of acquired channel', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['100.0']})]
classmethod validate_id(value)

Ensure id is a float or None, convert if necessary

update_azimuth_from_coords()

Update azm based on coordinates after validation

property dipole_length: float

dipole length based on x, y, z coordinates

property azimuth: float

aximuth based on x, y coordinates

property channel_number: int

Extract channel number from acqchan.

write_meas_line()

write string :return: DESCRIPTION :rtype: TYPE

class mt_metadata.transfer_functions.io.edi.metadata.HMeasurement(**data)

Bases: mt_metadata.base.MetadataBase

Base class for all metadata objects with Pydantic validation.

MetadataBase extends DotNotationBaseModel (which inherits from Pydantic’s BaseModel) to provide automatic validation according to metadata standards. It adds functionality beyond dictionaries, supporting JSON, XML, pandas Series, and other formats for metadata interchange.

_skip_equals

Private attribute listing fields to skip in equality comparisons

Type:

list[str]

_fields

Private attribute caching field information

Type:

dict[str, Any]

Notes

  • All field assignments are validated automatically via Pydantic

  • None values are converted to appropriate defaults (empty string or 0.0)

  • Supports nested attribute access via dot notation

  • Thread-safe for read operations after initialization

id: Annotated[float | str | None, Field(default=0.0, description='Channel number, could be location.channel_number.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['1']})]
chtype: Annotated[str, Field(default='', description="channel type, should start with an 'h' or 'b'", alias=None, pattern='^(R|r|RR|rr|[hHbB])[a-zA-Z0-9_]+$', json_schema_extra={'units': None, 'required': True, 'examples': ['hx']})]
x: Annotated[float, Field(default=0.0, description='location of sensor relative center point in north direction', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
y: Annotated[float, Field(default=0.0, description='location of sensor relative center point in east direction', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
z: Annotated[float, Field(default=0.0, description='location of sensor relative center point in depth', alias=None, json_schema_extra={'units': 'meters', 'required': True, 'examples': ['100.0']})]
azm: Annotated[float, Field(default=0.0, description='orientation of the sensor relative to coordinate system, clockwise positive.', alias=None, json_schema_extra={'units': 'degrees', 'required': True, 'examples': ['100.0']})]
dip: Annotated[float, Field(default=0.0, description='orientation of the sensor relative to horizontal = 0', alias=None, json_schema_extra={'units': 'degrees', 'required': True, 'examples': ['100.0']})]
acqchan: Annotated[str, Field(default='', description='description of acquired channel', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['100.0']})]
classmethod validate_id(value)

Ensure id is a float or None, convert if necessary

property channel_number: int

Extract channel number from acqchan.

write_meas_line()

write string :return: DESCRIPTION :rtype: TYPE

class mt_metadata.transfer_functions.io.edi.metadata.Information(**data)

Bases: mt_metadata.base.MetadataBase

Contain, read, and write info section of .edi file

not much to really do here, but just keep it in the same format that it is read in as, except if it is in phoenix format then split the two paragraphs up so they are sequential.

info_dict: dict[str, str | list | None] = None
read_info(edi_lines)

Read information section and parse directly to info_dict.

Parameters:

edi_lines (list[str]) – List of lines from the EDI file.

write_info()

write out information

class mt_metadata.transfer_functions.io.edi.metadata.DataSection(**data)

Bases: mt_metadata.base.MetadataBase

DataSection contains the small metadata block that describes which channel is which. A typical block looks like:

>=MTSECT

    ex=1004.001
    ey=1005.001
    hx=1001.001
    hy=1002.001
    hz=1003.001
    nfreq=14
    sectid=par28ew
    nchan=None
    maxblks=None
Parameters:

fn (string) – full path to .edi file to read in.

Attributes

Description

Default

In .edi

ex

ex channel id number

None

yes

ey

ey channel id number

None

yes

hx

hx channel id number

None

yes

hy

hy channel id number

None

yes

hz

hz channel id number

None

yes

nfreq

number of frequencies

None

yes

sectid

section id, should be the same as the station name -> Header.dataid

None

yes

maxblks

maximum number of data blocks

None

yes

nchan

number of channels

None

yes

_kw_list

list of key words to put in metadata

[1]_

no

nfreq: Annotated[int, Field(default=0, description='Number of frequencies', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': [16, 1]})]
sectid: Annotated[str, Field(default='', description='ID of the station that the data is from. This is important if you have more than one station per file.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['mt001']})]
nchan: Annotated[int, Field(default=0, description='Number of channels in the transfer function', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': [7]})]
maxblocks: Annotated[int, Field(default=999, description='Maximum number of data blocks', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': [999]})]
ex: Annotated[str | None, Field(default=None, description='Measurement ID for EX', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['1']})]
ey: Annotated[str | None, Field(default=None, description='Measurement ID for EY', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['2']})]
hx: Annotated[str | None, Field(default=None, description='Measurement ID for HX', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['3']})]
hy: Annotated[str | None, Field(default=None, description='Measurement ID for HY', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['4']})]
hz: Annotated[str | None, Field(default=None, description='Measurement ID for HZ', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['5']})]
rrhx: Annotated[str | None, Field(default=None, description='Measurement ID for RRHX', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['6']})]
rrhy: Annotated[str | None, Field(default=None, description='Measurement ID for RRHY', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['7']})]
get_data(edi_lines)

Read in the data of the file, will detect if reading spectra or impedance.

read_data(edi_lines)

Read data section

write_data(data_list=None, over_dict=None)

Write the data section to a list of strings.

match_channels(ch_ids)

Match the channels in the data section with the provided channel IDs. This method updates the channel IDs based on the provided list.

class mt_metadata.transfer_functions.io.edi.metadata.DefineMeasurement(**data)

Bases: mt_metadata.base.MetadataBase

DefineMeasurement class holds information about the measurement. This includes how each channel was setup. The main block contains information on the reference location for the station. This is a bit of an archaic part and was meant for a multiple station .edi file. This section is also important if you did any forward modeling with Winglink cause it only gives the station location in this section. The other parts are how each channel was collected. An example define measurement section looks like:

>=DEFINEMEAS

    MAXCHAN=7
    MAXRUN=999
    MAXMEAS=9999
    UNITS=M
    REFTYPE=CART
    REFLAT=-30:12:49.4693
    REFLONG=139:47:50.87
    REFELEV=0

>HMEAS ID=1001.001 CHTYPE=HX X=0.0 Y=0.0 Z=0.0 AZM=0.0
>HMEAS ID=1002.001 CHTYPE=HY X=0.0 Y=0.0 Z=0.0 AZM=90.0
>HMEAS ID=1003.001 CHTYPE=HZ X=0.0 Y=0.0 Z=0.0 AZM=0.0
>EMEAS ID=1004.001 CHTYPE=EX X=0.0 Y=0.0 Z=0.0 X2=0.0 Y2=0.0
>EMEAS ID=1005.001 CHTYPE=EY X=0.0 Y=0.0 Z=0.0 X2=0.0 Y2=0.0
>HMEAS ID=1006.001 CHTYPE=HX X=0.0 Y=0.0 Z=0.0 AZM=0.0
>HMEAS ID=1007.001 CHTYPE=HY X=0.0 Y=0.0 Z=0.0 AZM=90.0
Parameters:

fn (string) – full path to .edi file to read in.

Attributes

Description

Default

In .edi

fn

Full path to edi file read in

None

no

maxchan

Maximum number of channels measured

None

yes

maxmeas

Maximum number of measurements

9999

yes

maxrun

Maximum number of measurement runs

999

yes

meas_####

HMeasurement or EMEasurment object defining the measurement made [1]__

None

yes

refelev

Reference elevation (m)

None

yes

reflat

Reference latitude [2]

None

yes

refloc

Reference location

None

yes

reflon

Reference longituted [2]__

None

yes

reftype

Reference coordinate system

‘cart’

yes

units

Units of length

m

yes

_define_meas_keys

Keys to include in define_measurment section.

[3]__

no

maxchan: Annotated[int, Field(default=999, description='maximum number of channels', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['16']})]
maxrun: Annotated[int, Field(default=999, description='maximum number of runs', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['999']})]
maxmeas: Annotated[int, Field(default=7, description='maximum number of measurements', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['999']})]
reftype: Annotated[str | None, Field(default='cartesian', description='Type of offset from reference center point.', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['cartesian', 'cart']})]
refloc: Annotated[str | None, Field(default=None, description='Description of location reference center point.', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['here']})]
reflat: Annotated[float, Field(default=0, description='Latitude of reference center point.', alias=None, json_schema_extra={'units': 'degrees', 'required': False, 'examples': ['0']})]
reflon: Annotated[float, Field(default=0, description='Longitude reference center point.', alias=None, json_schema_extra={'units': 'degrees', 'required': False, 'examples': ['0']})]
refelev: Annotated[float, Field(default=0, description='Elevation reference center point.', alias=None, json_schema_extra={'units': 'meters', 'required': False, 'examples': ['0']})]
units: Annotated[str | None, Field(default='m', description='In the EDI standards this is the elevation units.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['m']})]
measurements: Annotated[dict[str, mt_metadata.transfer_functions.io.edi.metadata.EMeasurement | mt_metadata.transfer_functions.io.edi.metadata.HMeasurement], Field(default_factory=dict, description="Dictionary of measurements with keys as channel types (e.g., 'hx', 'hy', 'ex', 'ey', etc.) and values as EMeasurement or HMeasurement objects.", alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ["{'hx': EMeasurement(...), 'hy': HMeasurement(...)}"]})]
classmethod validate_units(value)
classmethod validate_position(value, info)
property channel_ids: dict[str, str]
get_measurement_lists(edi_lines)

get measurement list including measurement setup

edi_lines

lines from the edi file to parse

Type:

str

read_measurement(edi_lines)

read the define measurment section of the edi file

should be a list with lines for:

  • maxchan

  • maxmeas

  • maxrun

  • refloc

  • refelev

  • reflat

  • reflon

  • reftype

  • units

  • dictionaries for >XMEAS with keys:

    • id

    • chtype

    • x

    • y

    • axm

    • acqchn

write_measurement(longitude_format='LON', latlon_format='degrees')

write_measurement writes the define measurement section of the edi file.

Parameters:
  • longitude_format (str, optional) – longitude format [ “LONG” | “LON” ] , by default “LON”

  • latlon_format (str, optional) – position format [ “dd” | “ degrees” ], by default “degrees” for decimal degrees If you want to write the position in degrees, use “ degrees” for the latlon_format. This will write the position in the format of HH:MM:SS.ss for the latitude and longitude. If you want to write the position in decimal degrees, use “dd” for the latlon_format.

Returns:

list of lines for the define measurement section or an empty list if no measurements are defined.

Return type:

list[str]

Raises:

ValueError – If a value cannot be converted to a float or if the longitude format is not recognized.

from_metadata(channel)

from_metadata converts a channel object into a measurement object and sets the attributes for the measurement object.

Parameters:

channel (Electric | Magnetic | Auxiliary) – The channel object to convert into a measurement object.

property channels_recorded: list[str]

Get the channels recorded

class mt_metadata.transfer_functions.io.edi.metadata.Header(**data)

Bases: mt_metadata.common.BasicLocation, mt_metadata.common.GeographicLocation

A partial location class that only includes the latitude, longitude, and elevation. This is used to avoid circular imports.

acqby: Annotated[str | None, Field(default=None, description='person, group, company, university that collected the data', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['mt experts']})]
acqdate: Annotated[mt_metadata.common.mttime.MTime | str | float | int | numpy.datetime64 | pandas.Timestamp, Field(default_factory=lambda: MTime(time_stamp=None), description='Start date the time series data were collected', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['2020-01-01']})]
coordinate_system: Annotated[mt_metadata.common.GeographicReferenceFrameEnum, Field(default='geographic', description='coordinate system the transfer function is currently in. Its preferred the transfer function be in a geographic coordinate system for archiving and sharing.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['geopgraphic']})]
dataid: Annotated[str, Field(default='', description='station ID.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['mt001']})]
enddate: Annotated[mt_metadata.common.mttime.MTime | str | float | int | numpy.datetime64 | pandas.Timestamp | None, Field(default_factory=lambda: MTime(time_stamp=None), description='End date the time series data were collected', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['2020-01-01']})]
empty: Annotated[float, Field(default=1e+32, description='null data values, usually a large number', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['1E+32']})]
fileby: Annotated[str, Field(default='', description='person, group, company, university that made the file', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['mt experts']})]
filedate: Annotated[mt_metadata.common.mttime.MTime | str | float | int | numpy.datetime64 | pandas.Timestamp, Field(default_factory=lambda: MTime(time_stamp=None), description='Date the file was made', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['2020-01-01']})]
progdate: Annotated[mt_metadata.common.mttime.MTime | str | float | int | numpy.datetime64 | pandas.Timestamp, Field(default_factory=lambda: MTime(time_stamp=None), description='Date of the most recent update of the program used to make the file', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['2020-01-01']})]
progname: Annotated[str, Field(default='mt_metadata', description='Name of the program used to make the file.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['mt_metadata']})]
progvers: Annotated[str, Field(default='0.1.6', description='Version of the program used to make the file.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['0.1.6']})]
project: Annotated[str | None, Field(default=None, description='Name of the project the data was collected for, usually a short description or acronym of the project name.', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['iMUSH']})]
prospect: Annotated[str | None, Field(default=None, description='Name of the prospect the data was collected for, usually a short description of the location', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['Benton']})]
loc: Annotated[str | None, Field(default=None, description='Usually a short description of the location', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['Benton, CA']})]
declination: Annotated[mt_metadata.common.Declination, Field(default_factory=lambda: Declination(value=0.0), description='Declination of the station in degrees', alias=None, json_schema_extra={'units': 'degrees', 'required': True, 'examples': ['Declination(10.0)']})]
stdvers: Annotated[mt_metadata.common.StdEDIversionsEnum, Field(default='SEG 1.0', description='EDI standards version SEG 1.0', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['SEG 1.0']})]
survey: Annotated[str | None, Field(default=None, description='Name of the survey', alias=None, json_schema_extra={'units': None, 'required': False, 'examples': ['CONUS']})]
units: Annotated[str | None, Field(default='milliVolt per kilometer per nanoTesla', description='In the EDI standards this is the elevation units, in newer versions this should be units of the transfer function.', alias=None, json_schema_extra={'units': None, 'required': True, 'examples': ['milliVolt per kilometer per nanoTesla']})]
classmethod validate_acqdate(field_value)
classmethod validate_units(value)
get_header_list(edi_lines)

Get the header information from the .edi file in the form of a list.

Extracts header lines from an EDI file, returning each key-value pair as a formatted string.

Parameters:

edi_lines (list of str) – List of lines from an EDI file to parse for header information.

Returns:

List of header key-value pairs in the format ‘key=value’.

Return type:

list of str

Examples

>>> header = Header()
>>> edi_lines = ['>HEAD', 'DATAID=MT001', 'LAT=45.5', '>']
>>> header.get_header_list(edi_lines)
['DATAID=MT001', 'LAT=45.5']
read_header(edi_lines)

Read and parse header information from EDI file lines.

Parses header lines from an EDI file and populates the Header object attributes with the corresponding values. Handles special cases like Phoenix-formatted EDI files and coordinate system normalization.

Parameters:

edi_lines (list of str) – List of lines from an EDI file containing header information.

Returns:

Updates the object’s attributes in place.

Return type:

None

Examples

>>> header = Header()
>>> edi_lines = ['>HEAD', 'DATAID=MT001', 'LAT=45:30:00', 'LON=-122:30:00', '>']
>>> header.read_header(edi_lines)
>>> header.dataid
'mt001'
>>> header.latitude
45.5

Notes

  • Station IDs are automatically validated and normalized to lowercase

  • Coordinate systems are normalized to ‘geographic’, ‘geomagnetic’, or ‘station’

  • Phoenix MT-Editor format is automatically detected

write_header(longitude_format='LON', latlon_format='dms', required=False)

Write header information to a list of formatted lines for EDI output.

Formats all header attributes as EDI-compliant key-value pairs. Automatically updates file metadata (filedate, progvers, progname) to current values.

Parameters:
  • longitude_format ({'LON', 'LONG'}, default 'LON') – Format for longitude field name in output.

  • latlon_format ({'dms', 'dd'}, default 'dms') – Format for latitude/longitude values. - ‘dms’: degrees:minutes:seconds (e.g., ‘45:30:00’) - ‘dd’: decimal degrees (e.g., ‘45.500000’)

  • required (bool, default False) – If True, only include required fields in output.

Returns:

List of formatted header lines starting with ‘>HEAD’ and ending with a blank line. Each data line follows the format ‘KEY=value’.

Return type:

list of str

Examples

>>> header = Header(dataid='mt001', latitude=45.5, longitude=-122.5)
>>> lines = header.write_header(latlon_format='dd')
>>> print(lines[0])
>HEAD
>>> print(lines[1])
        DATAID=mt001
>>> # Write with degrees-minutes-seconds format
>>> lines_dms = header.write_header(latlon_format='dms')
>>> # Latitude will be formatted as 45:30:00

Notes

  • filedate is automatically set to current UTC time

  • progvers is set to mt_metadata version

  • Zero declination values are omitted from output

  • None values are skipped