Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cc554e9
Add private _FocalMechanismConvention class and refactor Figure.meca
seisman Oct 2, 2024
08ca92e
Merge branch 'main' into refactor/meca-convention
seisman Dec 4, 2024
c59b8a3
Simplify FocalMechanismConvention using StrEnum
seisman Dec 20, 2024
88d7353
Merge branch 'main' into refactor/meca-convention
seisman Dec 20, 2024
a7cbca0
Update docstrings
seisman Dec 20, 2024
9f6361a
Remove unnecessary docstrings
seisman Dec 20, 2024
cf5840e
Merge branch 'main' into refactor/meca-convention
seisman Dec 20, 2024
06652e6
Rename _ConventionCode to _FocalMechanismConventionCode
seisman Dec 21, 2024
3f93b8b
Merge branch 'main' into refactor/meca-convention
seisman Jan 4, 2025
495b5c6
Use the TODO comment
seisman Jan 4, 2025
ebe7f38
Fix typos
seisman Jan 7, 2025
97e70a7
Merge branch 'main' into refactor/meca-convention
seisman Feb 3, 2025
44ba2a6
Merge branch 'main' into refactor/meca-convention
seisman Feb 5, 2025
27e302f
Merge branch 'main' into refactor/meca-convention
seisman Feb 7, 2025
d4f14f1
Merge branch 'main' into refactor/meca-convention
seisman Feb 8, 2025
2d08324
Merge branch 'main' into refactor/meca-convention
seisman Feb 10, 2025
3656cc2
Merge branch 'main' into refactor/meca-convention
seisman Feb 11, 2025
2f0f7fd
Merge branch 'main' into refactor/meca-convention
seisman Feb 12, 2025
3fe03a7
Merge branch 'main' into refactor/meca-convention
seisman Feb 18, 2025
8ada405
Apply suggestions from code review
seisman Feb 18, 2025
28c71ff
Fix styling
seisman Feb 18, 2025
edfed02
Fix styling
seisman Feb 18, 2025
fa38aba
Merge branch 'main' into refactor/meca-convention
seisman Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 210 additions & 1 deletion pygmt/src/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
Common functions used in multiple PyGMT functions/methods.
"""

from collections.abc import Sequence
from pathlib import Path
from typing import Any
from typing import Any, ClassVar, Literal

from pygmt.exceptions import GMTInvalidInput
from pygmt.src.which import which


Expand Down Expand Up @@ -39,3 +41,210 @@ def _data_geometry_is_point(data: Any, kind: str) -> bool:
except FileNotFoundError:
pass
return False


class _FocalMechanismConvention:
"""
Class to handle focal mechanism convention, code, and associated parameters.

Parameters
----------
convention
The focal mechanism convention. Valid values are:

- ``"aki"``: Aki and Richards convention.
- ``"gcmt"``: Global CMT (Centroid Moment Tensor) convention.
- ``"partial"``: Partial focal mechanism convention.
- ``"mt"``: Moment Tensor convention.
- ``"principal_axis"``: Principal axis convention.
component
The component of the seismic moment tensor to plot. Valid values are:

- ``"full"``: the full tensor seismic moment tensor
- ``"dc"``: the closest double coupe defined from the moment tensor (zero trace
and zero determinant)
- ``"deviatoric"``: deviatoric part of the moment tensor (zero trace)

Only valid for conventions ``"mt"`` and ``"principal_axis"``.

Attributes
----------
convention
The focal mechanism convention.
params
List of parameters associated with the focal mechanism convention.
code
The single-letter code that can be used in meca/coupe's -S option.

Examples
--------
>>> from pygmt.src._common import _FocalMechanismConvention

>>> conv = _FocalMechanismConvention("aki")
>>> conv.convention, conv.code
('aki', 'a')
>>> conv.params
['strike', 'dip', 'rake', 'magnitude']

>>> conv = _FocalMechanismConvention("gcmt")
>>> conv.convention, conv.code
('gcmt', 'c')
>>> conv.params
['strike1', 'dip1', 'rake1', 'strike2', 'dip2', 'rake2', 'mantissa', 'exponent']

>>> conv = _FocalMechanismConvention("partial")
>>> conv.convention, conv.code
('partial', 'p')
>>> conv.params
['strike1', 'dip1', 'strike2', 'fault_type', 'magnitude']

>>> conv = _FocalMechanismConvention("mt", component="dc")
>>> conv.convention, conv.code
('mt', 'd')
>>> conv.params
['mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf', 'exponent']

>>> conv = _FocalMechanismConvention("principal_axis", component="deviatoric")
>>> conv.convention, conv.code
('principal_axis', 't')

>>> conv = _FocalMechanismConvention("a")
>>> conv.convention, conv.code
('aki', 'a')

>>> conv = _FocalMechanismConvention.from_params(
... ["strike", "dip", "rake", "magnitude"]
... )
>>> conv.convention, conv.code
('aki', 'a')

>>> conv = _FocalMechanismConvention(convention="invalid")
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Invalid focal mechanism convention 'invalid'.

>>> conv = _FocalMechanismConvention("mt", component="invalid")
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Invalid component 'invalid' for ... 'mt'.

>>> _FocalMechanismConvention.from_params(["strike", "dip", "rake"])
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Fail to determine ...
"""

# Mapping of focal mechanism conventions to their single-letter codes.
_conventions: ClassVar = {
"aki": "a",
"gcmt": "c",
"partial": "p",
"mt": {"full": "m", "deviatoric": "z", "dc": "d"},
"principal_axis": {"full": "x", "deviatoric": "t", "dc": "y"},
}

# Mapping of single-letter codes to focal mechanism convention names
_codes: ClassVar = {
"a": "aki",
"c": "gcmt",
"p": "partial",
"m": "mt",
"z": "mt",
"d": "mt",
"x": "principal_axis",
"t": "principal_axis",
"y": "principal_axis",
}

# Mapping of focal mechanism conventions to their parameters.
_params: ClassVar = {
"aki": ["strike", "dip", "rake", "magnitude"],
"gcmt": [
"strike1",
"dip1",
"rake1",
"strike2",
"dip2",
"rake2",
"mantissa",
"exponent",
],
"partial": ["strike1", "dip1", "strike2", "fault_type", "magnitude"],
"mt": ["mrr", "mtt", "mff", "mrt", "mrf", "mtf", "exponent"],
"principal_axis": [
"t_value",
"t_azimuth",
"t_plunge",
"n_value",
"n_azimuth",
"n_plunge",
"p_value",
"p_azimuth",
"p_plunge",
"exponent",
],
}

def __init__(
self,
convention: Literal["aki", "gcmt", "partial", "mt", "principal_axis"],
component: Literal["full", "deviatoric", "dc"] = "full",
):
"""
Initialize the FocalMechanismConvention object.
"""
if convention in self._conventions:
# Convention is given via 'convention' and 'component' parameters.
if component not in {"full", "deviatoric", "dc"}:
msg = (
f"Invalid component '{component}' for focal mechanism convention "
f"'{convention}'."
)
raise GMTInvalidInput(msg)

self.convention = convention
self.code = self._conventions[convention]
if isinstance(self.code, dict):
self.code = self.code[component]
elif convention in self._codes:
# Convention is given as a single-letter code.
self.code = convention
self.convention = self._codes[convention]
else:
msg = f"Invalid focal mechanism convention '{convention}'."
raise GMTInvalidInput(msg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if some of the pattern matching on 'convention' long names (e.g. akl) or single-letter aliases (e.g. a) could be simplified using StrEnum, but not sure how to handle the extra logic around 'component's. Haven't looked into it too closely, but there's supposedly ways to extend Enums with extra methods - https://realpython.com/python-enum/#adding-and-tweaking-member-methods.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, using StrEnum can simplify the code logic. The _FocalMechanismConvention class has been refactored in c59b8a3.

self.params = self._params[self.convention]

@staticmethod
def from_params(
params: Sequence[str], component: Literal["full", "deviatoric", "dc"] = "full"
) -> "_FocalMechanismConvention":
"""
Create a FocalMechanismConvention object from a sequence of parameters.

The method checks if the given parameters are a superset of a known focal
Comment thread
seisman marked this conversation as resolved.
Outdated
mechanism convention to determine the convention. If the parameters are not
sufficient to determine the convention, an exception is raised.

Parameters
----------
params
Sequence of parameters to determine the focal mechanism convention. The
order of the parameters does not matter.

Returns
-------
_FocalMechanismConvention
The FocalMechanismConvention object.

Raises
------
GMTInvalidInput
If the focal mechanism convention cannot be determined from the given
parameters
"""
for convention, param_list in _FocalMechanismConvention._params.items():
if set(param_list).issubset(set(params)):
return _FocalMechanismConvention(convention, component=component)
msg = "Fail to determine focal mechanism convention from the data column names."
raise GMTInvalidInput(msg)
Loading