Skip to content

Commit c59b8a3

Browse files
committed
Simplify FocalMechanismConvention using StrEnum
1 parent 08ca92e commit c59b8a3

File tree

1 file changed

+95
-92
lines changed

1 file changed

+95
-92
lines changed

pygmt/src/_common.py

+95-92
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from collections.abc import Sequence
6+
from enum import StrEnum
67
from pathlib import Path
78
from typing import Any, ClassVar, Literal
89

@@ -43,29 +44,31 @@ def _data_geometry_is_point(data: Any, kind: str) -> bool:
4344
return False
4445

4546

46-
class _FocalMechanismConvention:
47+
class _ConventionCode(StrEnum):
48+
"""
49+
Enum to handle focal mechanism convention codes.
4750
"""
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:
5451

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:
52+
AKI_DC = "a"
53+
AKI_DEVIATORIC = "a"
54+
AKI_FULL = "a"
55+
GCMT_DC = "c"
56+
GCMT_DEVIATORIC = "c"
57+
GCMT_FULL = "c"
58+
PARTIAL_DC = "p"
59+
PARTIAL_DEVIATORIC = "p"
60+
PARTIAL_FULL = "p"
61+
MT_DC = "d"
62+
MT_DEVIATORIC = "z"
63+
MT_FULL = "m"
64+
PRINCIPAL_AXIS_DC = "y"
65+
PRINCIPAL_AXIS_DEVIATORIC = "t"
66+
PRINCIPAL_AXIS_FULL = "x"
6267

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)
6768

68-
Only valid for conventions ``"mt"`` and ``"principal_axis"``.
69+
class _FocalMechanismConvention:
70+
"""
71+
Class to handle focal mechanism convention, code, and associated parameters.
6972
7073
Attributes
7174
----------
@@ -81,81 +84,51 @@ class _FocalMechanismConvention:
8184
>>> from pygmt.src._common import _FocalMechanismConvention
8285
8386
>>> conv = _FocalMechanismConvention("aki")
84-
>>> conv.convention, conv.code
85-
('aki', 'a')
87+
>>> conv.code
88+
<_ConventionCode.AKI_DC: 'a'>
8689
>>> conv.params
8790
['strike', 'dip', 'rake', 'magnitude']
8891
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')
92+
>>> conv = _FocalMechanismConvention("mt")
93+
>>> conv.code
94+
<_ConventionCode.MT_FULL: 'm'>
9895
>>> conv.params
99-
['strike1', 'dip1', 'strike2', 'fault_type', 'magnitude']
96+
['mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf', 'exponent']
10097
10198
>>> conv = _FocalMechanismConvention("mt", component="dc")
102-
>>> conv.convention, conv.code
103-
('mt', 'd')
99+
>>> conv.code
100+
<_ConventionCode.MT_DC: 'd'>
104101
>>> conv.params
105102
['mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf', 'exponent']
106103
107-
>>> conv = _FocalMechanismConvention("principal_axis", component="deviatoric")
108-
>>> conv.convention, conv.code
109-
('principal_axis', 't')
110-
111104
>>> conv = _FocalMechanismConvention("a")
112-
>>> conv.convention, conv.code
113-
('aki', 'a')
105+
>>> conv.code
106+
<_ConventionCode.AKI_DC: 'a'>
107+
>>> conv.params
108+
['strike', 'dip', 'rake', 'magnitude']
114109
115110
>>> conv = _FocalMechanismConvention.from_params(
116111
... ["strike", "dip", "rake", "magnitude"]
117112
... )
118-
>>> conv.convention, conv.code
119-
('aki', 'a')
113+
>>> conv.code
114+
<_ConventionCode.AKI_DC: 'a'>
120115
121116
>>> conv = _FocalMechanismConvention(convention="invalid")
122117
Traceback (most recent call last):
123118
...
124-
pygmt.exceptions.GMTInvalidInput: Invalid focal mechanism convention 'invalid'.
119+
pygmt.exceptions.GMTInvalidInput: Invalid focal mechanism ...'.
125120
126121
>>> conv = _FocalMechanismConvention("mt", component="invalid")
127122
Traceback (most recent call last):
128123
...
129-
pygmt.exceptions.GMTInvalidInput: Invalid component 'invalid' for ... 'mt'.
124+
pygmt.exceptions.GMTInvalidInput: Invalid focal mechanism ...'.
130125
131126
>>> _FocalMechanismConvention.from_params(["strike", "dip", "rake"])
132127
Traceback (most recent call last):
133128
...
134-
pygmt.exceptions.GMTInvalidInput: Fail to determine ...
129+
pygmt.exceptions.GMTInvalidInput: Fail to determine focal mechanism convention...
135130
"""
136131

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-
159132
# Mapping of focal mechanism conventions to their parameters.
160133
_params: ClassVar = {
161134
"aki": ["strike", "dip", "rake", "magnitude"],
@@ -191,36 +164,63 @@ def __init__(
191164
component: Literal["full", "deviatoric", "dc"] = "full",
192165
):
193166
"""
194-
Initialize the FocalMechanismConvention object.
167+
Initialize the FocalMechanismConvention object from convention and component.
168+
169+
If the convention is specified via a single-letter code, the convention and
170+
component are determined from the code.
171+
172+
Parameters
173+
----------
174+
convention
175+
The focal mechanism convention. Valid values are:
176+
177+
- ``"aki"``: Aki and Richards convention.
178+
- ``"gcmt"``: Global CMT (Centroid Moment Tensor) convention.
179+
- ``"partial"``: Partial focal mechanism convention.
180+
- ``"mt"``: Moment Tensor convention.
181+
- ``"principal_axis"``: Principal axis convention.
182+
component
183+
The component of the seismic moment tensor to plot. Valid values are:
184+
185+
- ``"full"``: the full tensor seismic moment tensor
186+
- ``"dc"``: the closest double coupe defined from the moment tensor (zero
187+
trace and zero determinant)
188+
- ``"deviatoric"``: deviatoric part of the moment tensor (zero trace)
189+
190+
Doesn't apply to conventions ``"aki"``, ``"gcmt"``, and ``"partial"``.
195191
"""
196-
if convention in self._conventions:
197-
# Convention is given via 'convention' and 'component' parameters.
198-
if component not in {"full", "deviatoric", "dc"}:
192+
if convention in _ConventionCode.__members__.values():
193+
# 'convention' is specified via the actual single-letter convention code.
194+
self.code = _ConventionCode(convention)
195+
# Parse the convention from the convention code name.
196+
self._convention = "_".join(self.code.name.split("_")[:-1]).lower()
197+
else:
198+
# Convention is specified via 'convention' and 'component'.
199+
name = f"{convention.upper()}_{component.upper()}" # e.g., "AKI_DC"
200+
if name not in _ConventionCode.__members__:
199201
msg = (
200-
f"Invalid component '{component}' for focal mechanism convention "
201-
f"'{convention}'."
202+
"Invalid focal mechanism convention with "
203+
f"convention='{convention}' and component='{component}'."
202204
)
203205
raise GMTInvalidInput(msg)
206+
self.code = _ConventionCode[name]
207+
self._convention = convention
204208

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]
209+
@property
210+
def params(self):
211+
"""
212+
The parameters associated with the focal mechanism convention.
213+
"""
214+
return self._params[self._convention]
217215

218-
@staticmethod
216+
@classmethod
219217
def from_params(
220-
params: Sequence[str], component: Literal["full", "deviatoric", "dc"] = "full"
218+
cls,
219+
params: Sequence[str],
220+
component: Literal["full", "deviatoric", "dc"] = "full",
221221
) -> "_FocalMechanismConvention":
222222
"""
223-
Create a FocalMechanismConvention object from a sequence of parameters.
223+
Create a _FocalMechanismConvention object from a sequence of parameters.
224224
225225
The method checks if the given parameters are a superset of a known focal
226226
mechanism convention to determine the convention. If the parameters are not
@@ -235,16 +235,19 @@ def from_params(
235235
Returns
236236
-------
237237
_FocalMechanismConvention
238-
The FocalMechanismConvention object.
238+
The _FocalMechanismConvention object.
239239
240240
Raises
241241
------
242242
GMTInvalidInput
243243
If the focal mechanism convention cannot be determined from the given
244-
parameters
244+
parameters.
245245
"""
246-
for convention, param_list in _FocalMechanismConvention._params.items():
246+
for convention, param_list in cls._params.items():
247247
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."
248+
return cls(convention, component=component)
249+
msg = (
250+
"Fail to determine focal mechanism convention from the given parameters: "
251+
f"{', '.join(params)}."
252+
)
250253
raise GMTInvalidInput(msg)

0 commit comments

Comments
 (0)