Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ plot-mw-contributions = "visualisation.sources.plot_mw_contributions:app"
plot-slip-rise-rake = "visualisation.sources.plot_slip_rise_rake:app"
plot-srf-distribution = "visualisation.sources.plot_srf_distribution:app"
plot-1d-velocity-model = "visualisation.plot_1d_velocity_model:app"
plot-rupture-path = "visualisation.plot_rupture_path:app"
plot-stoch = "visualisation.sources.plot_stoch:app"
plot-ts = "visualisation.plot_ts:app"

Expand Down
27 changes: 21 additions & 6 deletions tests/test_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import diffimg
import pytest

from visualisation import plot_1d_velocity_model, realisation

####### Ancestor
from visualisation import plot_1d_velocity_model, plot_rupture_path, realisation
from visualisation.sources import (
plot_mw_contributions,
plot_rakes,
Expand All @@ -21,10 +19,9 @@

TEST_DATA_DIR = Path(__file__).parent
PLOT_IMAGE_DIRECTORY = Path("wiki/images")
STATIONS_FFP = TEST_DATA_DIR / "realisation" / "stations.ll"
DEFAULT_IMAGE_DIFF_TOLERANCE = 0.05

STATIONS_FFP = Path("tests") / "realisation" / "stations.ll"


@pytest.fixture(scope="module")
def plot_image_dir() -> Path:
Expand Down Expand Up @@ -78,6 +75,12 @@ def output_image_path(tmp_path: Path) -> Path:
return tmp_path / "output.png"


@pytest.fixture(scope="module")
def realisation_base_file() -> Path:
"""Path to the realisation JSON file."""
return TEST_DATA_DIR / "realisation" / "realisation.json"


def assert_images_match(
generated_path: Path,
expected_path: Path,
Expand Down Expand Up @@ -297,7 +300,7 @@ def test_plot_velocity_model(
),
],
)
def test_realisation_plotting(
def test_realisation_plot(
plot_image_dir: Path,
output_image_path: Path,
domain_realisation_ffp: Path,
Expand Down Expand Up @@ -347,3 +350,15 @@ def test_plot_stoch(
)

assert_images_match(output_image_path, expected_image_file)


def test_plot_rupture_path(
velocity_model_plot_file: Path,
output_image_path: Path,
plot_image_dir: Path,
):
expected_image_file = plot_image_dir / "alpine_hope_1_path.png"
plot_rupture_path.plot_rupture_path_to_file(
velocity_model_plot_file, output_image_path
)
assert_images_match(output_image_path, expected_image_file)
157 changes: 157 additions & 0 deletions visualisation/plot_rupture_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""Plot rupture propagation paths for realisations."""

from pathlib import Path
from typing import Annotated

import pygmt
import typer

from pygmt_helper import plotting
from qcore import cli
from visualisation import realisation, utils
from workflow.realisations import RupturePropagationConfig, SourceConfig

app = typer.Typer()


def plot_rupture_path(
fig: pygmt.Figure,
source_config: SourceConfig,
rup_prop_config: RupturePropagationConfig,
):
"""Plot a rupture path.

The rupture is plotted as a series of directed arrows between two
faults, from the parent fault to a subsequent fault.

Parameters
----------
fig : pygmt.Figure
The pygmt figure to plot on.
source_config : SourceConfig
The definition of the sources.
rup_prop_config : RupturePropagationConfig
The rupture propagation containing the rupture path.

Examples
--------
>>> # Create a figure and plot a rupture path
>>> import pygmt
>>> from workflow.realisations import RupturePropagationConfig, SourceConfig
>>>
>>> # Load configurations from files
>>> source_config = SourceConfig.read_from_realisation("path/to/realisation.json")
>>> rup_prop_config = RupturePropagationConfig.read_from_realisation("path/to/realisation.json")
>>>
>>> # Create a PyGMT figure with appropriate region
>>> region = utils.bounding_region_for(
... [fault.geometry for fault in source_config.source_geometries.values()],
... latitude_pad=0.5,
... longitude_pad=0.5
... )
>>> fig = plotting.gen_region_fig("Rupture Path", region, projection="M15c")
>>>
>>> # Plot the rupture path on the figure
>>> plot_rupture_path(fig, source_config, rup_prop_config)
>>>
>>> # Display or save the figure
>>> fig.show()
>>> # Or save to file
>>> fig.savefig("rupture_path.png")
"""
realisation.plot_sources(fig, source_config, fill="white")

for fault_name, parent_name in rup_prop_config.rupture_causality_tree.items():
if not parent_name:
continue

fault = source_config.source_geometries[fault_name]
parent = source_config.source_geometries[parent_name]
parent_point = utils.polygon_nztm_to_pygmt(
parent.geometry
).representative_point()
fault_point = utils.polygon_nztm_to_pygmt(fault.geometry).representative_point()
data_for_plot = [[parent_point.x, parent_point.y, fault_point.x, fault_point.y]]

fig.plot(
data=data_for_plot,
style="=0.3c+ea45+s",
pen="0.5p,black",
fill="black",
)

initial_fault = source_config.source_geometries[rup_prop_config.initial_fault]
hypocentre = initial_fault.fault_coordinates_to_wgs_depth_coordinates(
rup_prop_config.hypocentre
)
fig.plot(
x=hypocentre[1],
y=hypocentre[0],
style="a0.3c",
pen="0.3p,black",
fill="gold",
)


@cli.from_docstring(app)
def plot_rupture_path_to_file(
realisation_ffp: Annotated[Path, typer.Argument(dir_okay=False)],
output_ffp: Annotated[Path, typer.Argument(dir_okay=False, writable=True)],
title: Annotated[str | None, typer.Option()] = None,
latitude_pad: Annotated[float, typer.Option(min=0)] = 0,
longitude_pad: Annotated[float, typer.Option(min=0)] = 0,
width: Annotated[float, typer.Option(min=0)] = 17,
subtitle: Annotated[str | None, typer.Option()] = None,
):
"""Plot a rupture path from a realisation to a file.

Parameters
----------
realisation_ffp : Path
The realisation to plot.
output_ffp : Path
The output image path.
title : str, optional
The title of the plot.
latitude_pad : float
The latitude padding to apply (in degrees).
longitude_pad : float
The longitude padding to apply (in degrees).
width : float
The width of the plot (in cm).
subtitle : str, optional
A plot subtitle.

Examples
--------
>>> from pathlib import Path
>>>
>>> # Plot a rupture path from a realisation file to a PNG
>>> plot_rupture_path_to_file(
... realisation_ffp=Path("realisations/alpine_fault_M7.8.json"),
... output_ffp=Path("outputs/alpine_rupture.png"),
... title="Alpine Fault Rupture Simulation",
... latitude_pad=0.3,
... longitude_pad=0.3,
... width=20,
... subtitle="Northward propagation scenario"
... )
>>>
>>> # Minimal usage with default parameters
>>> plot_rupture_path_to_file(
... Path("realisations/hikurangi_M8.0.json"),
... Path("outputs/hikurangi_rupture.png")
... )
"""
source_config = SourceConfig.read_from_realisation(realisation_ffp)
rup_prop_config = RupturePropagationConfig.read_from_realisation(realisation_ffp)
region = utils.bounding_region_for(
[fault.geometry for fault in source_config.source_geometries.values()],
latitude_pad=latitude_pad,
longitude_pad=longitude_pad,
)
fig = plotting.gen_region_fig(
title, region, projection=f"M{width}c", subtitle=subtitle
)
plot_rupture_path(fig, source_config, rup_prop_config)
fig.savefig(output_ffp)
41 changes: 41 additions & 0 deletions wiki/Domains.md → wiki/Realisations.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,44 @@ $ plot-domain alpine_base_1.json alpine_base_1_with_options.png --latitude-pad 0
triangles), and a legend indicating the number of stations in the domain will be added. See an example station file in [the QuakeCoRE dropbox](https://www.dropbox.com/scl/fi/bb852b1f0rly6cfvs9p9b/geoNet_stats-2023-06-28.ll?rlkey=3y8k5qviy52nbksfdkm1n92yh&st=92bvmzph&dl=0).

![Domain plot showing red station triangles and a legend](images/alpine_base_1_stations.png)
# Plotting Realisation Rupture Paths
The `plot-rupture-path` CLI tool plots a realisation rupture path *prior* to generating an SRF (See [the sources plotting tools](Sources.md) to work with SRFs instead). This can be useful for debugging the rupture propagation process.

You can find the help text for this tool with `plot-rupture-path --help`

```
Usage: plot-rupture-path [OPTIONS] REALISATION_FFP OUTPUT_FFP

Plot a rupture path from a realisation to a file.

╭─ Arguments ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ * realisation_ffp FILE The realisation to plot. [default: None] [required] │
│ * output_ffp FILE The output image path. [default: None] [required] │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --title TEXT The title of the plot. [default: None] │
│ --latitude-pad FLOAT RANGE [x>=0] The latitude padding too apply (in degrees). [default: 0] │
│ --longitude-pad FLOAT RANGE [x>=0] The longitude padding to apply (in degrees). [default: 0] │
│ --width FLOAT RANGE [x>=0] The width of the plot (in cm). [default: 17] │
│ --subtitle TEXT A plot subtitle. [default: None] │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy it or customize the installation. │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

Replace `REALISATION_FFP` with the path to your realisation file (e.g.,
`realisation.json`) and `OUTPUT_PLOT_FFP` with the desired name for your
output image (e.g., `rupture_path.png`).

This basic command will create a map plot showing:

1. The earthquake source geometry (fault plane(s), typically polygons with a thin black line and white fill).
2. The planned path the rupture will take through the faults, indicated by arrows.

> [!NOTE]
> The arrows do not indicate the precise location that the rupture will jump between faults. They only indicate the order the faults will rupture in.

![Basic plot showing source geometry and a rupture path](images/alpine_hope_1_rupture_path.png)

The options `--title`, `--latitude-pad`, `--longitude-pad`, `--width` and `--subtitle` behave as they do for plotting realisation domains.
Binary file added wiki/images/alpine_hope_1_path.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.