2.16.3.5. pycsamt.geology.structural#
Structural geology — field measurements and fault traces.
Independent structural evidence to check an interpretation against, the
same role Borehole plays for lithology.
None of this is electromagnetic: positions are profile-relative metres
(x), exactly like Borehole and
StratigraphicLog – real-world
placement (lat/lon) is handled separately by pycsamt.site and
pycsamt.gis.
Two kinds of field measurement are distinguished, matching how a stereonet actually plots them:
StructuralMeasurement– a planar feature (bedding, foliation, joint, cleavage, contact, fault plane, unconformity surface), recorded as strike/dip/dip-direction.LinearMeasurement– a linear feature (fold axis, lineation, slickenline), recorded as trend/plunge.
FaultTrace is a distinct, coarser entity: where a fault crosses
the 2-D profile itself, which side is downthrown, and (if known) the
throw – the piece that plugs directly into the structural-continuity
review questions in Interpretation workflow
(“do apparent boundary offsets align with known structures?”).
StructuralModel collects all three per profile.
Angle conventions#
This is the one place the rest of pycsamt does not already agree
with itself: MT geoelectric strike (pycsamt.emtools.strike) is
axial and wrapped to (-90, 90]; pycsamt.gis/pycsamt.site
azimuth is a directed compass bearing, [0, 360); the synthetic
geometry in pycsamt.ai.geology.lenses is axial mod-180 for an
unrelated reason (ellipse symmetry). None of those fit real field data,
so this module uses the convention modern digital field-mapping tools
(FieldMove, StraboSpot) record directly from a compass-clinometer:
strike_deg,trend_deg,dip_direction_deg– compass bearings,[0, 360), degrees clockwise from north.dip_deg,plunge_deg– angle below horizontal,[0, 90].
Storing dip_direction_deg alongside strike_deg (rather than only
a right-hand-rule pair) is deliberate: the two are cross-validated against
each other in StructuralMeasurement.validate() (dip direction must
be within tolerance of strike +/- 90 degrees), which catches a transposed
field-notebook entry that a bare right-hand-rule number would not.
StructuralMeasurement.from_right_hand_rule() builds one from just
dip direction and dip, for anyone who prefers recording that way.
Classes
|
Where a fault crosses the 2-D profile. |
|
A linear structural field measurement. |
|
A planar structural field measurement. |
|
Collection of structural evidence along one survey profile. |
- class pycsamt.geology.structural.StructuralMeasurement(x, kind, strike_deg, dip_deg, dip_direction_deg, z=None, station=None, confidence=1.0, notes='', dip_direction_tolerance_deg=20.0)[source]
Bases:
PyCSAMTObjectA planar structural field measurement.
- Parameters:
x (float) – Position along the survey profile, metres.
kind (str) – Feature type, e.g.
'bedding','foliation','joint','cleavage','contact','fault_plane','unconformity'. Free text – not an enforced enumeration, matchinglithology.strike_deg (float) – Compass strike, degrees clockwise from north,
[0, 360)as measured. Normalised on construction; not reduced modulo 180, so the raw field reading is preserved.dip_deg (float) – Dip angle below horizontal, degrees,
[0, 90].dip_direction_deg (float) – Compass direction the surface dips toward,
[0, 360). Must be within dip_direction_tolerance_deg ofstrike_deg + 90orstrike_deg - 90(mod 360); raisesValueErrorotherwise, since a wider mismatch usually means one of the two readings was transposed in the field notebook.z (float, optional) – Depth (positive downward) or elevation of the measurement, metres.
Nonefor a surface outcrop reading with no associated depth.station (str, optional) – Field station or outcrop label.
confidence (float) – Subjective reading confidence,
[0, 1](default 1.0).notes (str) – Free-text field note.
dip_direction_tolerance_deg (float)
Examples
>>> m = StructuralMeasurement( ... x=500.0, kind="bedding", strike_deg=45.0, dip_deg=30.0, ... dip_direction_deg=135.0, ... ) >>> m.dip_azimuth_ok True
- x: float
- kind: str
- strike_deg: float
- dip_deg: float
- dip_direction_deg: float
- confidence: float = 1.0
- notes: str = ''
- dip_direction_tolerance_deg: float = 20.0
- validate()[source]
Re-check and re-normalise this measurement’s fields.
Called automatically by
__post_init__, and byupdate()/clone()after they set new attribute values – both go through this method rather than__post_init__(which only runs once, at construction), so aclone(dip_direction_deg=...)that breaks the strike/dip-direction consistency check is caught rather than silently accepted.- Return type:
None
- classmethod from_right_hand_rule(x, kind, dip_direction_deg, dip_deg, **kwargs)[source]
Build from a dip-direction/dip pair, deriving strike.
Strike is set to
dip_direction_deg - 90(mod 360), the right-hand-rule convention: facing along strike with the dip direction to your right.
- class pycsamt.geology.structural.LinearMeasurement(x, kind, trend_deg, plunge_deg, z=None, station=None, confidence=1.0, notes='')[source]
Bases:
PyCSAMTObjectA linear structural field measurement.
- Parameters:
x (float) – Position along the survey profile, metres.
kind (str) – Feature type, e.g.
'fold_axis','lineation','slickenline','fold_hinge','intersection_lineation'. Free text, as withStructuralMeasurement.trend_deg (float) – Compass direction the line plunges toward, degrees clockwise from north,
[0, 360).plunge_deg (float) – Angle below horizontal, degrees,
[0, 90].z (float, optional)
station (str, optional)
confidence (float)
notes (str)
Examples
>>> LinearMeasurement(x=500.0, kind="fold_axis", trend_deg=210.0, plunge_deg=15.0) LinearMeasurement(x=500.0 m, 'fold_axis', 210/15)
- x: float
- kind: str
- trend_deg: float
- plunge_deg: float
- confidence: float = 1.0
- notes: str = ''
- validate()[source]
Re-check and re-normalise this measurement’s fields.
Called by
__post_init__and byupdate/clone; seeStructuralMeasurement.validate().- Return type:
None
- class pycsamt.geology.structural.FaultTrace(x, dip_deg, downthrown_side, sense='unknown', throw_m=None, strike_deg=None, z_top=None, confidence=1.0, evidence='', notes='')[source]
Bases:
PyCSAMTObjectWhere a fault crosses the 2-D profile.
- Parameters:
x (float) – Profile position where the fault is picked, metres.
dip_deg (float) – Apparent dip of the fault plane in the section, degrees,
[0, 90](0 = flat detachment, 90 = vertical). This is the angle a 2-D EM section can actually constrain; the true 3-D dip differs unless the profile happens to run perpendicular to strike. Pass strike_deg separately when the true attitude is independently known (surface mapping, borehole).downthrown_side ({'left', 'right'}) – Which side of
x– toward decreasing or increasing profile distance – is downthrown.sense ({'normal', 'reverse', 'strike_slip', 'unknown'}) – Kinematic sense, where known (default
'unknown').throw_m (float, optional) – Vertical displacement, metres (magnitude; direction is carried by downthrown_side).
Nonewhen unknown or unmeasured.strike_deg (float, optional) – True compass strike, when independently known.
z_top (float, optional) – Depth to the top of the picked trace, metres.
Nonefor a surface trace or when unconstrained.confidence (float)
evidence (str) – Free-text source, e.g.
'resistivity offset','borehole','surface mapping'.notes (str)
Examples
>>> FaultTrace(x=500.0, dip_deg=70.0, downthrown_side="right", throw_m=12.0) FaultTrace(x=500.0 m, dip=70 deg, down=right, throw=12.0 m)
- x: float
- dip_deg: float
- downthrown_side: str
- sense: str = 'unknown'
- confidence: float = 1.0
- evidence: str = ''
- notes: str = ''
- validate()[source]
Re-check and re-normalise this trace’s fields.
Called by
__post_init__and byupdate/clone; seeStructuralMeasurement.validate().- Return type:
None
- class pycsamt.geology.structural.StructuralModel(*, planar=None, linear=None, faults=None, metadata=None)[source]
Bases:
PyCSAMTObject,MetadataMixinCollection of structural evidence along one survey profile.
- Parameters:
planar (list of StructuralMeasurement, optional)
linear (list of LinearMeasurement, optional)
faults (list of FaultTrace, optional)
metadata (dict, optional) – Free-form provenance, e.g. survey name or source file paths.
Examples
>>> model = StructuralModel( ... faults=[FaultTrace(x=500.0, dip_deg=70.0, downthrown_side="right")], ... ) >>> len(model.faults) 1
- planar: list[StructuralMeasurement]
- linear: list[LinearMeasurement]
- faults: list[FaultTrace]
- metadata: dict
- add_planar(measurement)[source]
- Parameters:
measurement (StructuralMeasurement)
- Return type:
None
- add_linear(measurement)[source]
- Parameters:
measurement (LinearMeasurement)
- Return type:
None
- add_fault(fault)[source]
- Parameters:
fault (FaultTrace)
- Return type:
None
- within(x_min, x_max)[source]
Return a new model restricted to
x_min <= x <= x_max.- Parameters:
- Return type:
- nearest(x, *, kind='faults', max_distance=None)[source]
Return the item of kind nearest to profile position x.
- Parameters:
- Return type:
StructuralMeasurement | LinearMeasurement | FaultTrace | None
- classmethod from_csv(*, planar_path=None, linear_path=None, faults_path=None, delimiter=',')[source]
Load a model from up to three CSV files, one per evidence type.
Expected columns (case-insensitive header, optional columns may be omitted):
planar_path –
x, kind, strike_deg, dip_deg, dip_direction_deg[, z, station, confidence, notes]linear_path –
x, kind, trend_deg, plunge_deg[, z, station, confidence, notes]faults_path –
x, dip_deg, downthrown_side[, sense, throw_m, strike_deg, z_top, confidence, evidence, notes]
Any path left as
Noneyields an empty list for that evidence type.