Skip to content

Commit af5ba53

Browse files
committed
Add private _FocalMechanismConvention class and refactor Figure.meca
1 parent 914c1c6 commit af5ba53

File tree

2 files changed

+233
-200
lines changed

2 files changed

+233
-200
lines changed

pygmt/src/_common.py

+211-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
Common functions used in multiple PyGMT functions/methods.
33
"""
44

5+
from collections.abc import Sequence
56
from pathlib import Path
6-
from typing import Any
7+
from typing import Any, ClassVar, Literal
78

9+
from pygmt.exceptions import GMTInvalidInput
810
from pygmt.src.which import which
911

1012

@@ -39,3 +41,211 @@ def _data_geometry_is_point(data: Any, kind: str) -> bool:
3941
except FileNotFoundError:
4042
pass
4143
return False
44+
45+
46+
class _FocalMechanismConvention:
47+
"""
48+
Class to handle focal mechanism convention, code, and associated parameters.
49+
50+
Parameters
51+
----------
52+
convention
53+
The focal mechanism convention. Valid values are:
54+
55+
- ``"aki"``: Aki and Richards convention.
56+
- ``"gcmt"``: Global CMT (Centroid Moment Tensor) convention.
57+
- ``"partial"``: Partial focal mechanism convention.
58+
- ``"mt"``: Moment Tensor convention.
59+
- ``"principal_axis"``: Principal axis convention.
60+
component
61+
The component of the seismic moment tensor to plot. Valid values are:
62+
63+
- ``"full"``: the full tensor seismic moment tensor
64+
- ``"dc"``: the closest double coupe defined from the moment tensor (zero trace
65+
and zero determinant)
66+
- ``"deviatoric"``: deviatoric part of the moment tensor (zero trace)
67+
68+
Only valid for conventions ``"mt"`` and ``"principal_axis"``.
69+
70+
Attributes
71+
----------
72+
convention
73+
The focal mechanism convention.
74+
params
75+
List of parameters associated with the focal mechanism convention.
76+
code
77+
The single-letter code that can be used in meca/coupe's -S option.
78+
79+
Examples
80+
--------
81+
>>> from pygmt.src._common import _FocalMechanismConvention
82+
83+
>>> conv = _FocalMechanismConvention("aki")
84+
>>> conv.convention, conv.code
85+
('aki', 'a')
86+
>>> conv.params
87+
['strike', 'dip', 'rake', 'magnitude']
88+
89+
>>> conv = _FocalMechanismConvention("gcmt")
90+
>>> conv.convention, conv.code
91+
('gcmt', 'c')
92+
>>> conv.params
93+
['strike1', 'dip1', 'rake1', 'strike2', 'dip2', 'rake2', 'mantissa', 'exponent']
94+
95+
>>> conv = _FocalMechanismConvention("partial")
96+
>>> conv.convention, conv.code
97+
('partial', 'p')
98+
>>> conv.params
99+
['strike1', 'dip1', 'strike2', 'fault_type', 'magnitude']
100+
101+
>>> conv = _FocalMechanismConvention("mt", component="dc")
102+
>>> conv.convention, conv.code
103+
('mt', 'd')
104+
['mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf', 'exponent']
105+
106+
>>> conv = _FocalMechanismConvention("principal_axis", component="deviatoric")
107+
>>> conv.convention, conv.code
108+
('principal_axis', 't')
109+
110+
>>> conv = _FocalMechanismConvention("a")
111+
>>> conv.convention, conv.code
112+
('aki', 'a')
113+
>>> conv.params
114+
['strike', 'dip', 'rake', 'magnitude']
115+
116+
>>> conv = _FocalMechanismConvention.from_params(
117+
... ["strike", "dip", "rake", "magnitude"]
118+
... )
119+
>>> conv.convention, conv.code
120+
('aki', 'a')
121+
122+
>>> conv = _FocalMechanismConvention(convention="invalid")
123+
Traceback (most recent call last):
124+
...
125+
GMTInvalidInput: Invalid focal mechanism convention 'invalid'.
126+
127+
>>> conv = _FocalMechanismConvention("mt", component="invalid")
128+
Traceback (most recent call last):
129+
...
130+
GMTInvalidInput: Invalid component 'invalid' for focal mechanism convention 'mt'.
131+
132+
>>> _FocalMechanismConvention.from_params(["strike", "dip", "rake"])
133+
Traceback (most recent call last):
134+
...
135+
GMTInvalidInput: Fail to determine focal mechanism convention from ...
136+
"""
137+
138+
# Mapping of focal mechanism conventions to their single-letter codes.
139+
_conventions: ClassVar = {
140+
"aki": "a",
141+
"gcmt": "c",
142+
"partial": "p",
143+
"mt": {"full": "m", "deviatoric": "z", "dc": "d"},
144+
"principal_axis": {"full": "x", "deviatoric": "t", "dc": "y"},
145+
}
146+
147+
# Mapping of single-letter codes to focal mechanism convention names
148+
_codes: ClassVar = {
149+
"a": "aki",
150+
"c": "gcmt",
151+
"p": "partial",
152+
"m": "mt",
153+
"z": "mt",
154+
"d": "mt",
155+
"x": "principal_axis",
156+
"t": "principal_axis",
157+
"y": "principal_axis",
158+
}
159+
160+
# Mapping of focal mechanism conventions to their parameters.
161+
_params: ClassVar = {
162+
"aki": ["strike", "dip", "rake", "magnitude"],
163+
"gcmt": [
164+
"strike1",
165+
"dip1",
166+
"rake1",
167+
"strike2",
168+
"dip2",
169+
"rake2",
170+
"mantissa",
171+
"exponent",
172+
],
173+
"partial": ["strike1", "dip1", "strike2", "fault_type", "magnitude"],
174+
"mt": ["mrr", "mtt", "mff", "mrt", "mrf", "mtf", "exponent"],
175+
"principal_axis": [
176+
"t_value",
177+
"t_azimuth",
178+
"t_plunge",
179+
"n_value",
180+
"n_azimuth",
181+
"n_plunge",
182+
"p_value",
183+
"p_azimuth",
184+
"p_plunge",
185+
"exponent",
186+
],
187+
}
188+
189+
def __init__(
190+
self,
191+
convention: Literal["aki", "gcmt", "partial", "mt", "principal_axis"],
192+
component: Literal["full", "deviatoric", "dc"] = "full",
193+
):
194+
"""
195+
Initialize the FocalMechanismConvention object.
196+
"""
197+
if convention in self._conventions:
198+
# Convention is given via 'convention' and 'component' parameters.
199+
if component not in {"full", "deviatoric", "dc"}:
200+
msg = (
201+
f"Invalid component '{component}' for focal mechanism convention "
202+
f"'{convention}'."
203+
)
204+
raise GMTInvalidInput(msg)
205+
206+
self.convention = convention
207+
self.code = self._conventions[convention]
208+
if isinstance(self.code, dict):
209+
self.code = self.code[component]
210+
elif convention in self._codes:
211+
# Convention is given as a single-letter code.
212+
self.code = convention
213+
self.convention = self._codes[convention]
214+
else:
215+
msg = f"Invalid focal mechanism convention '{convention}'."
216+
raise GMTInvalidInput(msg)
217+
self.params = self._params[self.convention]
218+
219+
@staticmethod
220+
def from_params(
221+
params: Sequence[str], component: Literal["full", "deviatoric", "dc"] = "full"
222+
) -> "_FocalMechanismConvention":
223+
"""
224+
Create a FocalMechanismConvention object from a sequence of parameters.
225+
226+
The method checks if the given parameters are a superset of a known focal
227+
mechanism convention to determine the convention. If the parameters are not
228+
sufficient to determine the convention, an exception is raised.
229+
230+
Parameters
231+
----------
232+
params
233+
Sequence of parameters to determine the focal mechanism convention. The
234+
order of the parameters does not matter.
235+
236+
Returns
237+
-------
238+
_FocalMechanismConvention
239+
The FocalMechanismConvention object.
240+
241+
Raises
242+
------
243+
GMTInvalidInput
244+
If the focal mechanism convention cannot be determined from the given
245+
parameters
246+
"""
247+
for convention, param_list in _FocalMechanismConvention._params.items():
248+
if set(param_list).issubset(set(params)):
249+
return _FocalMechanismConvention(convention, component=component)
250+
msg = "Fail to determine focal mechanism convention from the data column names."
251+
raise GMTInvalidInput(msg)

0 commit comments

Comments
 (0)