2.7.1.1. pycsamt.airborne.base#

Format-neutral scientific containers for airborne EM surveys.

Three dataclasses form the common in-memory model that every technology adapter (pycsamt.airborne.mobilemt, pycsamt.airborne.ztem, pycsamt.airborne.afmag) populates instead of inventing its own survey/line/sample containers:

All three inherit CoreObject rather than MTBase: they organize and index scientific content but do not themselves perform electromagnetic arithmetic. That arithmetic lives in EMTF and TransferFunction, which these containers hold rather than duplicate.

Classes

AirborneEMDataset(name[, lines, survey, ...])

Format-neutral collection of airborne EM flight lines.

AirborneEMLine(line_id, navigation[, ...])

One airborne flight line with navigation and sparse EM records.

AirborneEMRecord(sample_id[, emtf, fields, ...])

One sample-aligned airborne EM scientific record.

class pycsamt.airborne.base.AirborneEMRecord(sample_id, emtf=None, fields=<factory>, quality=<factory>, attrs=<factory>)[source]

Bases: CoreObject

One sample-aligned airborne EM scientific record.

Parameters:
  • sample_id (str) – Identifier matching one entry of the owning line’s navigation.sample_ids. Stripped and required to be non-empty.

  • emtf (EMTF, optional) – Transfer-function payload for this sample. EMTF is reused directly rather than introduced as a parallel matrix class, so MobileMT, ZTEM, AFMAG, and future passive systems share one scientific representation instead of duplicating it.

  • fields (dict, optional) – Auxiliary decoded scalar/array fields with no stronger scientific type yet, for example a processed apparent conductivity vector. Content is technology-defined.

  • quality (dict, optional) – Sample-level quality flags or scores. Content is technology-defined.

  • attrs (dict, optional) – Free-form extension metadata.

Raises:
  • ValueError – If sample_id is empty after stripping.

  • TypeError – If emtf is supplied and is not an EMTF instance.

Examples

>>> from pycsamt.airborne import AirborneEMRecord
>>> record = AirborneEMRecord(sample_id=" S001 ")
>>> record.sample_id
'S001'
>>> record.transfer_function_names
()
sample_id: str
emtf: EMTF | None = None
fields: dict[str, Any]
quality: dict[str, Any]
attrs: dict[str, Any]
validate()[source]

Normalize identifier/dict fields and check the EMTF type.

Return type:

None

property transfer_function_names: tuple[str, ...][source]

Transfer-function names available for this sample.

class pycsamt.airborne.base.AirborneEMLine(line_id, navigation, records=<factory>, attrs=<factory>)[source]

Bases: CoreObject

One airborne flight line with navigation and sparse EM records.

Parameters:
  • line_id (str) – Flight-line identifier. Stripped and required to be non-empty.

  • navigation (NavigationTrack) – Sample-aligned navigation/attitude track defining the line’s common sample axis. Every record’s sample_id must appear in navigation.sample_ids.

  • records (dict of str to AirborneEMRecord, optional) – Records keyed by sample_id. The mapping key must equal record.sample_id for every entry.

  • attrs (dict, optional) – Free-form extension metadata.

Raises:
  • ValueError – If line_id is empty, a record key does not match record.sample_id, or a record’s sample_id is not a known navigation sample.

  • TypeError – If navigation is not a NavigationTrack, or records contains a non-AirborneEMRecord value.

Notes

Records are keyed by navigation sample_id and may be sparse. This is deliberate: a missing or rejected EM sample should not require deleting the corresponding navigation point, nor fabricating a transfer function to fill the gap.

Examples

>>> from pycsamt.airborne import AirborneEMLine, NavigationTrack
>>> nav = NavigationTrack(sample_ids=("S1", "S2"))
>>> line = AirborneEMLine(line_id="L001", navigation=nav)
>>> line.n_samples, line.n_records
(2, 0)
>>> line.missing_sample_ids
('S1', 'S2')
line_id: str
navigation: NavigationTrack
records: dict[str, AirborneEMRecord]
attrs: dict[str, Any]
validate()[source]

Normalize the identifier and re-attach incoming records.

Return type:

None

property n_samples: int[source]

Number of navigation samples on the line.

property n_records: int[source]

Number of EM records currently attached to the line.

property bbox: BBox | None[source]

Geographic bounding box when navigation coordinates exist.

property missing_sample_ids: tuple[str, ...][source]

Navigation samples that currently have no EM record.

property transfer_function_names: tuple[str, ...][source]

Sorted union of transfer-function names present on this line.

add_record(record, *, replace=False)[source]

Attach one record after verifying navigation alignment.

Parameters:
  • record (AirborneEMRecord) – Record whose sample_id must already exist on navigation.

  • replace (bool, default False) – Whether to overwrite an existing record for the same sample instead of raising.

Returns:

self, to support call chaining.

Return type:

AirborneEMLine

Raises:
add_emtf(sample_id, emtf, *, fields=None, quality=None, attrs=None, replace=False)[source]

Build and attach one AirborneEMRecord from an EMTF.

Convenience wrapper around add_record() for the common case of attaching a decoded EMTF response without constructing the record explicitly.

Parameters:
  • sample_id (str) – Navigation sample identifier for the new record.

  • emtf (EMTF) – Transfer-function payload for the sample.

  • fields (dict, optional) – Forwarded to AirborneEMRecord.

  • quality (dict, optional) – Forwarded to AirborneEMRecord.

  • attrs (dict, optional) – Forwarded to AirborneEMRecord.

  • replace (bool, default False) – Forwarded to add_record().

Returns:

The record that was attached.

Return type:

AirborneEMRecord

get_record(sample_id)[source]

Return a record by sample identifier, or None when absent.

Raises:

KeyError – If sample_id is not a known navigation sample.

Parameters:

sample_id (str)

Return type:

AirborneEMRecord | None

record_at(index)[source]

Return the record aligned with navigation index index.

Parameters:

index (int)

Return type:

AirborneEMRecord | None

iter_records()[source]

Iterate records in navigation order, skipping missing samples.

Return type:

Iterator[AirborneEMRecord]

class pycsamt.airborne.base.AirborneEMDataset(name, lines=<factory>, survey=None, instrument=None, method='AEM', attrs=<factory>)[source]

Bases: CoreObject

Format-neutral collection of airborne EM flight lines.

Parameters:
  • name (str) – Survey/dataset name. Stripped and required to be non-empty.

  • lines (dict of str to AirborneEMLine, optional) – Flight lines keyed by line_id. The mapping key must equal line.line_id for every entry.

  • survey (SurveyMeta, optional) – Survey-level metadata.

  • instrument (InstrumentMeta, optional) – System/instrument metadata.

  • method (str, default "AEM") – Survey method label, upper-cased on construction (for example "AEM").

  • attrs (dict, optional) – Free-form extension metadata.

Raises:
  • ValueError – If name or method is empty, or a line mapping key does not match line.line_id.

  • TypeError – If survey, instrument, or an entry of lines has the wrong type.

Notes

The dataset is intentionally an organisational layer above EMTF. It does not define a MobileMT, ZTEM, or AFMAG file schema, and it inherits CoreObject rather than MTBase: aggregating flight lines is not itself electromagnetic arithmetic, so this class should not carry MTBase’s numeric EM utilities (those belong to the EMTF/ TransferFunction objects it holds). Technology adapters populate this object rather than introducing separate transfer-function mathematics.

Examples

>>> from pycsamt.airborne import AirborneEMDataset
>>> dataset = AirborneEMDataset(name="survey-001")
>>> dataset.method, dataset.n_lines
('AEM', 0)
name: str
lines: dict[str, AirborneEMLine]
survey: SurveyMeta | None = None
instrument: InstrumentMeta | None = None
method: str = 'AEM'
attrs: dict[str, Any]
validate()[source]

Normalize identifier/method fields and re-attach lines.

Return type:

None

property line_ids: tuple[str, ...][source]

Flight-line identifiers in insertion order.

property n_lines: int[source]

Number of flight lines.

property n_samples: int[source]

Total number of navigation samples across all lines.

property n_records: int[source]

Total number of attached EM records across all lines.

property transfer_function_names: tuple[str, ...][source]

Sorted union of transfer-function types in the dataset.

property bbox: BBox | None[source]

Geographic bounding box over all lines with finite coordinates.

add_line(line, *, replace=False)[source]

Attach one flight line.

Parameters:
  • line (AirborneEMLine) – Flight line to attach.

  • replace (bool, default False) – Whether to overwrite an existing line with the same line_id instead of raising.

Returns:

self, to support call chaining.

Return type:

AirborneEMDataset

Raises:
get_line(line_id)[source]

Return a line by identifier, or None when absent.

Parameters:

line_id (str)

Return type:

AirborneEMLine | None

iter_lines()[source]

Iterate flight lines in insertion order.

Return type:

Iterator[AirborneEMLine]

iter_records()[source]

Iterate (line_id, record) pairs in navigation order.

Return type:

Iterator[tuple[str, AirborneEMRecord]]

emtf_records()[source]

Return all non-empty EMTF records keyed by line/sample ID.

Returns:

Mapping from (line_id, sample_id) to the attached EMTF. Records with no EMTF payload are omitted rather than represented with a placeholder.

Return type:

dict of (str, str) to EMTF

inspect()[source]

Return the common airborne inspection summary lazily.

Returns:

Compact scientific inventory; see pycsamt.airborne.qc.inspect_airborne().

Return type:

AirborneInspection

Notes

The import is deferred to avoid a hard import-time dependency between pycsamt.airborne.base and pycsamt.airborne.qc, which itself imports this module.

qc()[source]

Return the common structural airborne QC report lazily.

Returns:

Structural/metadata completeness report; see pycsamt.airborne.qc.assess_airborne_qc().

Return type:

AirborneQCReport