Skip to content

Commit cc554e9

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

File tree

2 files changed

+232
-200
lines changed

2 files changed

+232
-200
lines changed

pygmt/src/_common.py

+210-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,210 @@ 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+
>>> conv.params
105+
['mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf', 'exponent']
106+
107+
>>> conv = _FocalMechanismConvention("principal_axis", component="deviatoric")
108+
>>> conv.convention, conv.code
109+
('principal_axis', 't')
110+
111+
>>> conv = _FocalMechanismConvention("a")
112+
>>> conv.convention, conv.code
113+
('aki', 'a')
114+
115+
>>> conv = _FocalMechanismConvention.from_params(
116+
... ["strike", "dip", "rake", "magnitude"]
117+
... )
118+
>>> conv.convention, conv.code
119+
('aki', 'a')
120+
121+
>>> conv = _FocalMechanismConvention(convention="invalid")
122+
Traceback (most recent call last):
123+
...
124+
pygmt.exceptions.GMTInvalidInput: Invalid focal mechanism convention 'invalid'.
125+
126+
>>> conv = _FocalMechanismConvention("mt", component="invalid")
127+
Traceback (most recent call last):
128+
...
129+
pygmt.exceptions.GMTInvalidInput: Invalid component 'invalid' for ... 'mt'.
130+
131+
>>> _FocalMechanismConvention.from_params(["strike", "dip", "rake"])
132+
Traceback (most recent call last):
133+
...
134+
pygmt.exceptions.GMTInvalidInput: Fail to determine ...
135+
"""
136+
137+
# Mapping of focal mechanism conventions to their single-letter codes.
138+
_conventions: ClassVar = {
139+
"aki": "a",
140+
"gcmt": "c",
141+
"partial": "p",
142+
"mt": {"full": "m", "deviatoric": "z", "dc": "d"},
143+
"principal_axis": {"full": "x", "deviatoric": "t", "dc": "y"},
144+
}
145+
146+
# Mapping of single-letter codes to focal mechanism convention names
147+
_codes: ClassVar = {
148+
"a": "aki",
149+
"c": "gcmt",
150+
"p": "partial",
151+
"m": "mt",
152+
"z": "mt",
153+
"d": "mt",
154+
"x": "principal_axis",
155+
"t": "principal_axis",
156+
"y": "principal_axis",
157+
}
158+
159+
# Mapping of focal mechanism conventions to their parameters.
160+
_params: ClassVar = {
161+
"aki": ["strike", "dip", "rake", "magnitude"],
162+
"gcmt": [
163+
"strike1",
164+
"dip1",
165+
"rake1",
166+
"strike2",
167+
"dip2",
168+
"rake2",
169+
"mantissa",
170+
"exponent",
171+
],
172+
"partial": ["strike1", "dip1", "strike2", "fault_type", "magnitude"],
173+
"mt": ["mrr", "mtt", "mff", "mrt", "mrf", "mtf", "exponent"],
174+
"principal_axis": [
175+
"t_value",
176+
"t_azimuth",
177+
"t_plunge",
178+
"n_value",
179+
"n_azimuth",
180+
"n_plunge",
181+
"p_value",
182+
"p_azimuth",
183+
"p_plunge",
184+
"exponent",
185+
],
186+
}
187+
188+
def __init__(
189+
self,
190+
convention: Literal["aki", "gcmt", "partial", "mt", "principal_axis"],
191+
component: Literal["full", "deviatoric", "dc"] = "full",
192+
):
193+
"""
194+
Initialize the FocalMechanismConvention object.
195+
"""
196+
if convention in self._conventions:
197+
# Convention is given via 'convention' and 'component' parameters.
198+
if component not in {"full", "deviatoric", "dc"}:
199+
msg = (
200+
f"Invalid component '{component}' for focal mechanism convention "
201+
f"'{convention}'."
202+
)
203+
raise GMTInvalidInput(msg)
204+
205+
self.convention = convention
206+
self.code = self._conventions[convention]
207+
if isinstance(self.code, dict):
208+
self.code = self.code[component]
209+
elif convention in self._codes:
210+
# Convention is given as a single-letter code.
211+
self.code = convention
212+
self.convention = self._codes[convention]
213+
else:
214+
msg = f"Invalid focal mechanism convention '{convention}'."
215+
raise GMTInvalidInput(msg)
216+
self.params = self._params[self.convention]
217+
218+
@staticmethod
219+
def from_params(
220+
params: Sequence[str], component: Literal["full", "deviatoric", "dc"] = "full"
221+
) -> "_FocalMechanismConvention":
222+
"""
223+
Create a FocalMechanismConvention object from a sequence of parameters.
224+
225+
The method checks if the given parameters are a superset of a known focal
226+
mechanism convention to determine the convention. If the parameters are not
227+
sufficient to determine the convention, an exception is raised.
228+
229+
Parameters
230+
----------
231+
params
232+
Sequence of parameters to determine the focal mechanism convention. The
233+
order of the parameters does not matter.
234+
235+
Returns
236+
-------
237+
_FocalMechanismConvention
238+
The FocalMechanismConvention object.
239+
240+
Raises
241+
------
242+
GMTInvalidInput
243+
If the focal mechanism convention cannot be determined from the given
244+
parameters
245+
"""
246+
for convention, param_list in _FocalMechanismConvention._params.items():
247+
if set(param_list).issubset(set(params)):
248+
return _FocalMechanismConvention(convention, component=component)
249+
msg = "Fail to determine focal mechanism convention from the data column names."
250+
raise GMTInvalidInput(msg)

0 commit comments

Comments
 (0)