source_modelling.scripts.plot_srf_cumulative_moment

Utility script to plot cumulative moment over time for an SRF.

  1"""Utility script to plot cumulative moment over time for an SRF."""
  2
  3from pathlib import Path
  4from typing import Annotated, Optional
  5
  6import numpy as np
  7import typer
  8from matplotlib import pyplot as plt
  9
 10from source_modelling import moment, rupture_propagation, srf
 11
 12app = typer.Typer()
 13
 14
 15@app.command(help="Plot cumulative moment for an SRF over time.")
 16def plot_srf_cumulative_moment(
 17    srf_ffp: Annotated[
 18        Path,
 19        typer.Argument(
 20            help="SRF filepath to plot", exists=True, readable=True, dir_okay=False
 21        ),
 22    ],
 23    output_png_ffp: Annotated[
 24        Path, typer.Argument(help="Output plot path", writable=True, dir_okay=False)
 25    ],
 26    dpi: Annotated[
 27        int, typer.Option(help="Plot image pixel density (higher = better)", min=300)
 28    ] = 300,
 29    realisation_ffp: Annotated[
 30        Optional[Path],
 31        typer.Option(
 32            help="Path to realisation, used to plot individual fault contribution."
 33        ),
 34    ] = None,
 35    min_shade_cutoff: Annotated[
 36        float, typer.Option(help="Minimum shading cutoff", min=0, max=1)
 37    ] = 0.05,
 38    max_shade_cutoff: Annotated[
 39        float, typer.Option(help="Maximum shading cutoff", min=0, max=1)
 40    ] = 0.95,
 41):
 42    """Plot cumulative moment for an SRF over time.
 43
 44    Parameters
 45    ----------
 46    srf_ffp : Annotated[ Path, typer.Argument( help
 47        SRF filepath to plot.
 48    output_png_ffp : Annotated[ Path, typer.Argument(help
 49        Output plot path.
 50    dpi : Annotated[ int, typer.Option(help
 51        Plot image pixel density (higher = better).
 52    realisation_ffp : Annotated[ Optional[Path], typer.Option( help
 53        Path to realisation, used to plot individual fault
 54        contribution.
 55    min_shade_cutoff : Annotated[ float, typer.Option(help
 56        Minimum shading cutoff.
 57    max_shade_cutoff : Annotated[ float, typer.Option(help
 58        Maximum shading cutoff.
 59    """
 60    srf_data = srf.read_srf(srf_ffp)
 61
 62    overall_moment = moment.moment_over_time_from_moment_rate(
 63        moment.moment_rate_over_time_from_slip(
 64            srf_data.points["area"],
 65            srf_data.slip,
 66            srf_data.dt,
 67            srf_data.nt,
 68        )
 69    )
 70    total_moment = overall_moment["moment"].iloc[-1]
 71
 72    shaded_moments = overall_moment[
 73        (overall_moment["moment"] >= total_moment * min_shade_cutoff)
 74        & (overall_moment["moment"] <= total_moment * max_shade_cutoff)
 75    ]
 76    fig, ax = plt.subplots()
 77    ax.fill_between(shaded_moments.index.values, shaded_moments["moment"], alpha=0.2)
 78    ax.plot(
 79        overall_moment.index.values, overall_moment["moment"], label="Overall Moment"
 80    )
 81
 82    if realisation_ffp: # pragma: no cover
 83        # NOTE: this import is here because the workflow is, as yet,
 84        # not ready to be installed along-side source modelling.
 85        from workflow.realisations import RupturePropagationConfig, SourceConfig
 86
 87        source_config = SourceConfig.read_from_realisation(realisation_ffp)
 88        rupture_propogation_config = RupturePropagationConfig.read_from_realisation(
 89            realisation_ffp
 90        )
 91        segment_counter = 0
 92        point_counter = 0
 93        for fault_name in rupture_propagation.tree_nodes_in_order(
 94            rupture_propogation_config.rupture_causality_tree
 95        ):
 96            plane_count = len(source_config.source_geometries[fault_name].planes)
 97            segments = srf_data.header.iloc[
 98                segment_counter : segment_counter + plane_count
 99            ]
100            num_points = (segments["nstk"] * segments["ndip"]).sum()
101            segment_points = srf_data.points.iloc[
102                point_counter : point_counter + num_points
103            ]
104
105            individual_moment = moment.moment_over_time_from_moment_rate(
106                moment.moment_rate_over_time_from_slip(
107                    segment_points["area"],
108                    srf_data.slip[point_counter : point_counter + num_points],
109                    srf_data.dt,
110                    srf_data.nt,
111                )
112            )
113
114            ax.plot(
115                individual_moment.index.values,
116                individual_moment["moment"],
117                label=fault_name,
118            )
119            total_moment = individual_moment["moment"].iloc[-1]
120            shaded_moments = individual_moment[
121                (individual_moment["moment"] >= total_moment * min_shade_cutoff)
122                & (individual_moment["moment"] <= total_moment * max_shade_cutoff)
123            ]
124            ax.fill_between(
125                shaded_moments.index.values, shaded_moments["moment"], alpha=0.2
126            )
127            segment_counter += plane_count
128            point_counter += num_points
129
130    ax.set_ylabel("Cumulative Moment (Nm)")
131    ax.set_xlabel("Time (s)")
132    ax.legend()
133    min_shade_percent = int(np.round(min_shade_cutoff * 100))
134    max_shade_percent = int(np.round(max_shade_cutoff * 100))
135    ax.set_title(
136        f"Cumulative Moment over Time (Shaded Area: {min_shade_percent}% - {max_shade_percent}%)"
137    )
138
139    fig.savefig(output_png_ffp, dpi=dpi)
140
141
142if __name__ == "__main__":
143    app()
app = <typer.main.Typer object>
@app.command(help='Plot cumulative moment for an SRF over time.')
def plot_srf_cumulative_moment( srf_ffp: Annotated[pathlib.Path, <typer.models.ArgumentInfo object>], output_png_ffp: Annotated[pathlib.Path, <typer.models.ArgumentInfo object>], dpi: Annotated[int, <typer.models.OptionInfo object>] = 300, realisation_ffp: Annotated[Optional[pathlib.Path], <typer.models.OptionInfo object>] = None, min_shade_cutoff: Annotated[float, <typer.models.OptionInfo object>] = 0.05, max_shade_cutoff: Annotated[float, <typer.models.OptionInfo object>] = 0.95):
 16@app.command(help="Plot cumulative moment for an SRF over time.")
 17def plot_srf_cumulative_moment(
 18    srf_ffp: Annotated[
 19        Path,
 20        typer.Argument(
 21            help="SRF filepath to plot", exists=True, readable=True, dir_okay=False
 22        ),
 23    ],
 24    output_png_ffp: Annotated[
 25        Path, typer.Argument(help="Output plot path", writable=True, dir_okay=False)
 26    ],
 27    dpi: Annotated[
 28        int, typer.Option(help="Plot image pixel density (higher = better)", min=300)
 29    ] = 300,
 30    realisation_ffp: Annotated[
 31        Optional[Path],
 32        typer.Option(
 33            help="Path to realisation, used to plot individual fault contribution."
 34        ),
 35    ] = None,
 36    min_shade_cutoff: Annotated[
 37        float, typer.Option(help="Minimum shading cutoff", min=0, max=1)
 38    ] = 0.05,
 39    max_shade_cutoff: Annotated[
 40        float, typer.Option(help="Maximum shading cutoff", min=0, max=1)
 41    ] = 0.95,
 42):
 43    """Plot cumulative moment for an SRF over time.
 44
 45    Parameters
 46    ----------
 47    srf_ffp : Annotated[ Path, typer.Argument( help
 48        SRF filepath to plot.
 49    output_png_ffp : Annotated[ Path, typer.Argument(help
 50        Output plot path.
 51    dpi : Annotated[ int, typer.Option(help
 52        Plot image pixel density (higher = better).
 53    realisation_ffp : Annotated[ Optional[Path], typer.Option( help
 54        Path to realisation, used to plot individual fault
 55        contribution.
 56    min_shade_cutoff : Annotated[ float, typer.Option(help
 57        Minimum shading cutoff.
 58    max_shade_cutoff : Annotated[ float, typer.Option(help
 59        Maximum shading cutoff.
 60    """
 61    srf_data = srf.read_srf(srf_ffp)
 62
 63    overall_moment = moment.moment_over_time_from_moment_rate(
 64        moment.moment_rate_over_time_from_slip(
 65            srf_data.points["area"],
 66            srf_data.slip,
 67            srf_data.dt,
 68            srf_data.nt,
 69        )
 70    )
 71    total_moment = overall_moment["moment"].iloc[-1]
 72
 73    shaded_moments = overall_moment[
 74        (overall_moment["moment"] >= total_moment * min_shade_cutoff)
 75        & (overall_moment["moment"] <= total_moment * max_shade_cutoff)
 76    ]
 77    fig, ax = plt.subplots()
 78    ax.fill_between(shaded_moments.index.values, shaded_moments["moment"], alpha=0.2)
 79    ax.plot(
 80        overall_moment.index.values, overall_moment["moment"], label="Overall Moment"
 81    )
 82
 83    if realisation_ffp: # pragma: no cover
 84        # NOTE: this import is here because the workflow is, as yet,
 85        # not ready to be installed along-side source modelling.
 86        from workflow.realisations import RupturePropagationConfig, SourceConfig
 87
 88        source_config = SourceConfig.read_from_realisation(realisation_ffp)
 89        rupture_propogation_config = RupturePropagationConfig.read_from_realisation(
 90            realisation_ffp
 91        )
 92        segment_counter = 0
 93        point_counter = 0
 94        for fault_name in rupture_propagation.tree_nodes_in_order(
 95            rupture_propogation_config.rupture_causality_tree
 96        ):
 97            plane_count = len(source_config.source_geometries[fault_name].planes)
 98            segments = srf_data.header.iloc[
 99                segment_counter : segment_counter + plane_count
100            ]
101            num_points = (segments["nstk"] * segments["ndip"]).sum()
102            segment_points = srf_data.points.iloc[
103                point_counter : point_counter + num_points
104            ]
105
106            individual_moment = moment.moment_over_time_from_moment_rate(
107                moment.moment_rate_over_time_from_slip(
108                    segment_points["area"],
109                    srf_data.slip[point_counter : point_counter + num_points],
110                    srf_data.dt,
111                    srf_data.nt,
112                )
113            )
114
115            ax.plot(
116                individual_moment.index.values,
117                individual_moment["moment"],
118                label=fault_name,
119            )
120            total_moment = individual_moment["moment"].iloc[-1]
121            shaded_moments = individual_moment[
122                (individual_moment["moment"] >= total_moment * min_shade_cutoff)
123                & (individual_moment["moment"] <= total_moment * max_shade_cutoff)
124            ]
125            ax.fill_between(
126                shaded_moments.index.values, shaded_moments["moment"], alpha=0.2
127            )
128            segment_counter += plane_count
129            point_counter += num_points
130
131    ax.set_ylabel("Cumulative Moment (Nm)")
132    ax.set_xlabel("Time (s)")
133    ax.legend()
134    min_shade_percent = int(np.round(min_shade_cutoff * 100))
135    max_shade_percent = int(np.round(max_shade_cutoff * 100))
136    ax.set_title(
137        f"Cumulative Moment over Time (Shaded Area: {min_shade_percent}% - {max_shade_percent}%)"
138    )
139
140    fig.savefig(output_png_ffp, dpi=dpi)

Plot cumulative moment for an SRF over time.

Parameters
  • srf_ffp (Annotated[ Path, typer.Argument( help): SRF filepath to plot.
  • output_png_ffp (Annotated[ Path, typer.Argument(help): Output plot path.
  • dpi (Annotated[ int, typer.Option(help): Plot image pixel density (higher = better).
  • realisation_ffp (Annotated[ Optional[Path], typer.Option( help): Path to realisation, used to plot individual fault contribution.
  • min_shade_cutoff (Annotated[ float, typer.Option(help): Minimum shading cutoff.
  • max_shade_cutoff (Annotated[ float, typer.Option(help): Maximum shading cutoff.