Skip to content

Figure.meca: Add the private _FocalMechanismConvention class to simplify codes #3551

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
214 changes: 213 additions & 1 deletion pygmt/src/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
Common functions used in multiple PyGMT functions/methods.
"""

from collections.abc import Sequence
from enum import StrEnum
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 +42,212 @@ def _data_geometry_is_point(data: Any, kind: str) -> bool:
except FileNotFoundError:
pass
return False


class _FocalMechanismConventionCode(StrEnum):
"""
Enum to handle focal mechanism convention codes.

The enum names are in the format of ``CONVENTION_COMPONENT``, where ``CONVENTION``
is the focal mechanism convention and ``COMPONENT`` is the component of the seismic
moment tensor to plot. The enum values are the single-letter codes that can be used
in meca/coupe's ``-S`` option.

For some conventions, ``COMPONENT`` is not applicable, but we still define the enums
to simplify the code logic.
"""

AKI_DC = "a"
AKI_DEVIATORIC = "a"
AKI_FULL = "a"
GCMT_DC = "c"
GCMT_DEVIATORIC = "c"
GCMT_FULL = "c"
PARTIAL_DC = "p"
PARTIAL_DEVIATORIC = "p"
PARTIAL_FULL = "p"
MT_DC = "d"
MT_DEVIATORIC = "z"
MT_FULL = "m"
PRINCIPAL_AXIS_DC = "y"
PRINCIPAL_AXIS_DEVIATORIC = "t"
PRINCIPAL_AXIS_FULL = "x"


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

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

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

>>> conv = _FocalMechanismConvention("mt")
>>> conv.code
<_FocalMechanismConventionCode.MT_FULL: 'm'>
>>> conv.params
['mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf', 'exponent']

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

>>> conv = _FocalMechanismConvention("a")
>>> conv.code
<_FocalMechanismConventionCode.AKI_DC: 'a'>
>>> conv.params
['strike', 'dip', 'rake', 'magnitude']

>>> conv = _FocalMechanismConvention.from_params(
... ["strike", "dip", "rake", "magnitude"]
... )
>>> conv.code
<_FocalMechanismConventionCode.AKI_DC: 'a'>

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

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

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

# 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 from ``convention`` and
``component``.

If the convention is specified via a single-letter code, ``convention`` and
``component`` are determined from the code.

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 seismic moment tensor
- ``"dc"``: the closest double couple defined from the moment tensor (zero
trace and zero determinant)
- ``"deviatoric"``: deviatoric part of the moment tensor (zero trace)

Doesn't apply to the conventions ``"aki"``, ``"gcmt"``, and ``"partial"``.
"""
# TODO(Python>=3.12): Simplify to "convention in _FocalMechanismConventionCode".
if convention in _FocalMechanismConventionCode.__members__.values():
# Convention is specified via the actual single-letter convention code.
self.code = _FocalMechanismConventionCode(convention)
# Parse the convention from the convention code name.
self._convention = "_".join(self.code.name.split("_")[:-1]).lower()
else: # Convention is specified via "convention" and "component".
name = f"{convention.upper()}_{component.upper()}" # e.g., "AKI_DC"
if name not in _FocalMechanismConventionCode.__members__:
msg = (
"Invalid focal mechanism convention with "
f"convention='{convention}' and component='{component}'."
)
raise GMTInvalidInput(msg)
self.code = _FocalMechanismConventionCode[name]
self._convention = convention

@property
def params(self):
"""
The parameters associated with the focal mechanism convention.
"""
return self._params[self._convention]

@classmethod
def from_params(
cls,
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 supported focal
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 cls._params.items():
if set(param_list).issubset(set(params)):
return cls(convention, component=component)
msg = (
"Fail to determine focal mechanism convention from the given parameters: "
f"{', '.join(params)}."
)
raise GMTInvalidInput(msg)
Loading