velocity_modelling.scripts.plot_velocity_model

  1from enum import Enum
  2from pathlib import Path
  3from typing import Annotated, Optional
  4
  5import numpy as np
  6import pandas as pd
  7import pygmt
  8import typer
  9import yaml
 10
 11from pygmt_helper import plotting
 12from qcore import coordinates, grid
 13from velocity_modelling import bounding_box
 14from velocity_modelling.bounding_box import BoundingBox
 15
 16app = typer.Typer()
 17
 18
 19class VelocityModelComponent(str, Enum):
 20    """Enumeration for different components of a velocity model.
 21
 22    Attributes
 23    ----------
 24    p_wave : str
 25        P-wave (Primary wave) velocity component.
 26    s_wave : str
 27        S-wave (Secondary wave) velocity component.
 28    density : str
 29        Density component of the velocity model.
 30    """
 31
 32    p_wave = "p_wave"
 33    s_wave = "s_wave"
 34    density = "rho"
 35
 36
 37CB_LABEL_MAP = {
 38    VelocityModelComponent.s_wave: "S Wave Velocity",
 39    VelocityModelComponent.p_wave: "P Wave Velocity",
 40    VelocityModelComponent.density: "Density",
 41}
 42
 43
 44def plot_velocity_model(
 45    fig: pygmt.Figure,
 46    box: BoundingBox,
 47    velocity_model: np.memmap,
 48    resolution: float,
 49    slice: int,
 50    nx: int,
 51    ny: int,
 52    region: tuple[float, ...],
 53    **kwargs,
 54):
 55    """Plot the velocity model on a pygmt figure.
 56
 57    Parameters
 58    ----------
 59    fig : pygmt.Figure
 60        The figure to plot on.
 61    box : BoundingBox
 62        The bounding box the bounds of the velocity model.
 63    velocity_model : np.memmap
 64        The velocity model data as a memory-mapped array.
 65    resolution : float
 66        The resolution of the grid in meters.
 67    slice : int
 68        The z-level slice of the velocity model to plot.
 69    nx : int
 70        The number of grid points in the x-direction.
 71    ny : int
 72        The number of grid points in the y-direction.
 73    region : tuple of float
 74        The plotting region.
 75    **kwargs
 76        Additional keyword arguments passed to plotting.plot_grid.
 77    """
 78    corners = np.c_[box.corners, np.zeros_like(box.corners[:, 0])]
 79
 80    # The indexing here is to account for:
 81    # 1. The coordinate meshgrid generating along the boundary of
 82    # the domain (hence, 1:-1, 1:-1),
 83    # 2. The fact we don't need depth (hence :2)
 84    lat_lon_grid = grid.coordinate_meshgrid(
 85        corners[-1], corners[0], corners[-2], resolution * 1000
 86    )[1:-1, 1:-1, :2]
 87
 88    # The lat lon grid has shape (nx, ny, nz), so we flip that to
 89    # make the veloity model.
 90    lat_lon_grid = np.transpose(lat_lon_grid, (1, 0, 2))
 91    velocity_slice = velocity_model[:, slice, :].reshape((ny, nx))
 92    velocity_model_df = pd.DataFrame(
 93        {
 94            "lat": lat_lon_grid[:, :, 0].ravel(),
 95            "lon": lat_lon_grid[:, :, 1].ravel(),
 96            "value": velocity_slice.ravel(),
 97        }
 98    )
 99
100    cur_grid = plotting.create_grid(
101        velocity_model_df,
102        "value",
103        grid_spacing="100e/100e",
104        region=region,
105        set_water_to_nan=False,
106    )
107    cmap_limits = (
108        velocity_model_df["value"].min().round(1),
109        velocity_model_df["value"].max().round(1),
110        (
111            (velocity_model_df["value"].max() - velocity_model_df["value"].min()) / 10
112        ).round(1),
113    )
114
115    plotting.plot_grid(
116        fig,
117        cur_grid,
118        "hot",
119        cmap_limits,
120        ("white", "black"),
121        reverse_cmap=True,
122        plot_contours=False,
123        continuous_cmap=True,
124        **kwargs,
125    )
126
127
128def load_velocity_model_file(
129    velocity_model_ffp: Path,
130    component: VelocityModelComponent,
131    nx: int,
132    ny: int,
133    nz: int,
134) -> np.memmap:
135    """Load a velocity model from a file as a memory-mapped array.
136
137    Parameters
138    ----------
139    velocity_model_ffp : Path
140        The file path to the velocity model data.
141    component : VelocityModelComponent
142        The component of the velocity model to load.
143    nx : int
144        The number of grid points in the x-direction.
145    ny : int
146        The number of grid points in the y-direction.
147    nz : int
148        The number of grid points in the z-direction.
149
150    Returns
151    -------
152    np.memmap
153        The loaded velocity model as a memory-mapped array.
154    """
155    filepath_map = {
156        VelocityModelComponent.p_wave: "vp3dfile.p",
157        VelocityModelComponent.s_wave: "vs3dfile.s",
158        VelocityModelComponent.density: "rho3dfile.d",
159    }
160
161    return np.memmap(
162        velocity_model_ffp / filepath_map[component],
163        shape=(ny, nz, nx),
164        # NOTE: This may require tweaks in the future to account
165        # for differing endianness across machines. For now, this
166        # should be fine.
167        dtype="<f4",
168    )
169
170
171@app.command(
172    help="Plot a velocity model from a velocity model params yaml file (vm_params.yaml).",
173    name="vm-params",
174)
175def plot_vm_params(
176    vm_params_ffp: Annotated[
177        Path, typer.Argument(help="Path to velocity model parameters")
178    ],
179    output_plot_path: Annotated[
180        Path,
181        typer.Argument(
182            help="Path to output velocity model plot.", writable=True, dir_okay=False
183        ),
184    ],
185    velocity_model_ffp: Annotated[
186        Path | None,
187        typer.Option(
188            help="Path to velocity model directory", file_okay=False, exists=True
189        ),
190    ] = None,
191    component: Annotated[
192        VelocityModelComponent,
193        typer.Option(help="Velocity model component to overlay."),
194    ] = VelocityModelComponent.p_wave,
195    slice: Annotated[
196        int, typer.Option(help="z-level slice of velocity model to plot.")
197    ] = 0,
198    transparency: Annotated[
199        int, typer.Option(help="Velocity model overlay transparency, (0 = opaque)")
200    ] = 80,
201    title: Annotated[Optional[str], typer.Option(help="Figure title")] = None,
202    latitude_pad: Annotated[
203        float, typer.Option(help="Latitude padding for figure (in degrees)", min=0)
204    ] = 0,
205    longitude_pad: Annotated[
206        float, typer.Option(help="Longitude padding for figure (in degrees)", min=0)
207    ] = 0,
208    dpi: Annotated[
209        float, typer.Option(help="Plot output DPI (higher is better)")
210    ] = 300,
211) -> None:
212    """Plot a velocity model from a velocity model params yaml file (vm_params.yaml).
213
214    Parameters
215    ----------
216    vm_params_ffp : Path
217        Path to the YAML file containing velocity model parameters.
218    output_plot_path : Path
219        Path to save the generated plot.
220    velocity_model_ffp : Path | None
221        Path to the directory containing velocity model files. If None, no velocity model is overlaid.
222    component : VelocityModelComponent, default = `VelocityModelComponent.p_wave`
223        The velocity model component to overlay.
224    slice : int, optional, default = 0
225        The z-level slice of the velocity model to plot.
226    transparency : int, optional, default = 80
227        Transparency level for the velocity model overlay.
228    title : str, optional
229        Title of the figure.
230    latitude_pad : float, optional, default = 0
231        Padding for latitude (in degrees) for the figure.
232    longitude_pad : float, optional, default = 0
233        Padding for longitude (in degrees) for the figure.
234    dpi : float, optional, default = 300
235        DPI for the output plot.
236    """
237    with open(vm_params_ffp) as vm_params_handle:
238        vm_params = yaml.safe_load(vm_params_handle)
239
240        # Extent x and extent y are swapped in meaning between the old
241        # vm params and the new bounding box class. So we swap them
242        # here when we load them.
243        origin = np.array([vm_params["MODEL_LAT"], vm_params["MODEL_LON"]])
244        great_circle_bearing = vm_params["MODEL_ROT"]
245        extent_x = vm_params["extent_x"]
246        extent_y = vm_params["extent_y"]
247        nztm_bearing = coordinates.great_circle_bearing_to_nztm_bearing(
248            origin, extent_x / 2, great_circle_bearing
249        )
250        box = bounding_box.BoundingBox.from_centroid_bearing_extents(
251            origin, nztm_bearing, extent_x, extent_y
252        )
253
254        resolution = vm_params.get("hh")
255        nx = vm_params["nx"]
256        ny = vm_params["ny"]
257        nz = vm_params["nz"]
258
259    region = (
260        box.corners[:, 1].min() - longitude_pad,
261        box.corners[:, 1].max() + longitude_pad,
262        box.corners[:, 0].min() - latitude_pad,
263        box.corners[:, 0].max() + latitude_pad,
264    )
265    fig = plotting.gen_region_fig(title, region)
266    if velocity_model_ffp:
267        velocity_model = load_velocity_model_file(
268            velocity_model_ffp, component, nx, ny, nz
269        )
270        plot_velocity_model(
271            fig,
272            box,
273            velocity_model,
274            resolution,
275            slice,
276            nx,
277            ny,
278            region,
279            cb_label=CB_LABEL_MAP[component],
280            transparency=transparency,
281        )
282    fig.plot(
283        x=box.corners[:, 1],
284        y=box.corners[:, 0],
285        close=True,
286        pen="1p,black,-",
287    )
288    fig.savefig(
289        output_plot_path,
290        dpi=dpi,
291        anti_alias=True,
292    )
293
294
295if __name__ == "__main__":
296    app()
app = <typer.main.Typer object>
class VelocityModelComponent(builtins.str, enum.Enum):
20class VelocityModelComponent(str, Enum):
21    """Enumeration for different components of a velocity model.
22
23    Attributes
24    ----------
25    p_wave : str
26        P-wave (Primary wave) velocity component.
27    s_wave : str
28        S-wave (Secondary wave) velocity component.
29    density : str
30        Density component of the velocity model.
31    """
32
33    p_wave = "p_wave"
34    s_wave = "s_wave"
35    density = "rho"

Enumeration for different components of a velocity model.

Attributes
  • p_wave (str): P-wave (Primary wave) velocity component.
  • s_wave (str): S-wave (Secondary wave) velocity component.
  • density (str): Density component of the velocity model.
p_wave = <VelocityModelComponent.p_wave: 'p_wave'>
s_wave = <VelocityModelComponent.s_wave: 's_wave'>
density = <VelocityModelComponent.density: 'rho'>
CB_LABEL_MAP = {<VelocityModelComponent.s_wave: 's_wave'>: 'S Wave Velocity', <VelocityModelComponent.p_wave: 'p_wave'>: 'P Wave Velocity', <VelocityModelComponent.density: 'rho'>: 'Density'}
def plot_velocity_model( fig: pygmt.figure.Figure, box: velocity_modelling.bounding_box.BoundingBox, velocity_model: numpy.memmap, resolution: float, slice: int, nx: int, ny: int, region: tuple[float, ...], **kwargs):
 45def plot_velocity_model(
 46    fig: pygmt.Figure,
 47    box: BoundingBox,
 48    velocity_model: np.memmap,
 49    resolution: float,
 50    slice: int,
 51    nx: int,
 52    ny: int,
 53    region: tuple[float, ...],
 54    **kwargs,
 55):
 56    """Plot the velocity model on a pygmt figure.
 57
 58    Parameters
 59    ----------
 60    fig : pygmt.Figure
 61        The figure to plot on.
 62    box : BoundingBox
 63        The bounding box the bounds of the velocity model.
 64    velocity_model : np.memmap
 65        The velocity model data as a memory-mapped array.
 66    resolution : float
 67        The resolution of the grid in meters.
 68    slice : int
 69        The z-level slice of the velocity model to plot.
 70    nx : int
 71        The number of grid points in the x-direction.
 72    ny : int
 73        The number of grid points in the y-direction.
 74    region : tuple of float
 75        The plotting region.
 76    **kwargs
 77        Additional keyword arguments passed to plotting.plot_grid.
 78    """
 79    corners = np.c_[box.corners, np.zeros_like(box.corners[:, 0])]
 80
 81    # The indexing here is to account for:
 82    # 1. The coordinate meshgrid generating along the boundary of
 83    # the domain (hence, 1:-1, 1:-1),
 84    # 2. The fact we don't need depth (hence :2)
 85    lat_lon_grid = grid.coordinate_meshgrid(
 86        corners[-1], corners[0], corners[-2], resolution * 1000
 87    )[1:-1, 1:-1, :2]
 88
 89    # The lat lon grid has shape (nx, ny, nz), so we flip that to
 90    # make the veloity model.
 91    lat_lon_grid = np.transpose(lat_lon_grid, (1, 0, 2))
 92    velocity_slice = velocity_model[:, slice, :].reshape((ny, nx))
 93    velocity_model_df = pd.DataFrame(
 94        {
 95            "lat": lat_lon_grid[:, :, 0].ravel(),
 96            "lon": lat_lon_grid[:, :, 1].ravel(),
 97            "value": velocity_slice.ravel(),
 98        }
 99    )
100
101    cur_grid = plotting.create_grid(
102        velocity_model_df,
103        "value",
104        grid_spacing="100e/100e",
105        region=region,
106        set_water_to_nan=False,
107    )
108    cmap_limits = (
109        velocity_model_df["value"].min().round(1),
110        velocity_model_df["value"].max().round(1),
111        (
112            (velocity_model_df["value"].max() - velocity_model_df["value"].min()) / 10
113        ).round(1),
114    )
115
116    plotting.plot_grid(
117        fig,
118        cur_grid,
119        "hot",
120        cmap_limits,
121        ("white", "black"),
122        reverse_cmap=True,
123        plot_contours=False,
124        continuous_cmap=True,
125        **kwargs,
126    )

Plot the velocity model on a pygmt figure.

Parameters
  • fig (pygmt.Figure): The figure to plot on.
  • box (BoundingBox): The bounding box the bounds of the velocity model.
  • velocity_model (np.memmap): The velocity model data as a memory-mapped array.
  • resolution (float): The resolution of the grid in meters.
  • slice (int): The z-level slice of the velocity model to plot.
  • nx (int): The number of grid points in the x-direction.
  • ny (int): The number of grid points in the y-direction.
  • region (tuple of float): The plotting region.
  • **kwargs: Additional keyword arguments passed to plotting.plot_grid.
def load_velocity_model_file( velocity_model_ffp: pathlib.Path, component: VelocityModelComponent, nx: int, ny: int, nz: int) -> numpy.memmap:
129def load_velocity_model_file(
130    velocity_model_ffp: Path,
131    component: VelocityModelComponent,
132    nx: int,
133    ny: int,
134    nz: int,
135) -> np.memmap:
136    """Load a velocity model from a file as a memory-mapped array.
137
138    Parameters
139    ----------
140    velocity_model_ffp : Path
141        The file path to the velocity model data.
142    component : VelocityModelComponent
143        The component of the velocity model to load.
144    nx : int
145        The number of grid points in the x-direction.
146    ny : int
147        The number of grid points in the y-direction.
148    nz : int
149        The number of grid points in the z-direction.
150
151    Returns
152    -------
153    np.memmap
154        The loaded velocity model as a memory-mapped array.
155    """
156    filepath_map = {
157        VelocityModelComponent.p_wave: "vp3dfile.p",
158        VelocityModelComponent.s_wave: "vs3dfile.s",
159        VelocityModelComponent.density: "rho3dfile.d",
160    }
161
162    return np.memmap(
163        velocity_model_ffp / filepath_map[component],
164        shape=(ny, nz, nx),
165        # NOTE: This may require tweaks in the future to account
166        # for differing endianness across machines. For now, this
167        # should be fine.
168        dtype="<f4",
169    )

Load a velocity model from a file as a memory-mapped array.

Parameters
  • velocity_model_ffp (Path): The file path to the velocity model data.
  • component (VelocityModelComponent): The component of the velocity model to load.
  • nx (int): The number of grid points in the x-direction.
  • ny (int): The number of grid points in the y-direction.
  • nz (int): The number of grid points in the z-direction.
Returns
  • np.memmap: The loaded velocity model as a memory-mapped array.
@app.command(help='Plot a velocity model from a velocity model params yaml file (vm_params.yaml).', name='vm-params')
def plot_vm_params( vm_params_ffp: Annotated[pathlib.Path, <typer.models.ArgumentInfo object>], output_plot_path: Annotated[pathlib.Path, <typer.models.ArgumentInfo object>], velocity_model_ffp: Annotated[pathlib.Path | None, <typer.models.OptionInfo object>] = None, component: Annotated[VelocityModelComponent, <typer.models.OptionInfo object>] = <VelocityModelComponent.p_wave: 'p_wave'>, slice: Annotated[int, <typer.models.OptionInfo object>] = 0, transparency: Annotated[int, <typer.models.OptionInfo object>] = 80, title: Annotated[Optional[str], <typer.models.OptionInfo object>] = None, latitude_pad: Annotated[float, <typer.models.OptionInfo object>] = 0, longitude_pad: Annotated[float, <typer.models.OptionInfo object>] = 0, dpi: Annotated[float, <typer.models.OptionInfo object>] = 300) -> None:
172@app.command(
173    help="Plot a velocity model from a velocity model params yaml file (vm_params.yaml).",
174    name="vm-params",
175)
176def plot_vm_params(
177    vm_params_ffp: Annotated[
178        Path, typer.Argument(help="Path to velocity model parameters")
179    ],
180    output_plot_path: Annotated[
181        Path,
182        typer.Argument(
183            help="Path to output velocity model plot.", writable=True, dir_okay=False
184        ),
185    ],
186    velocity_model_ffp: Annotated[
187        Path | None,
188        typer.Option(
189            help="Path to velocity model directory", file_okay=False, exists=True
190        ),
191    ] = None,
192    component: Annotated[
193        VelocityModelComponent,
194        typer.Option(help="Velocity model component to overlay."),
195    ] = VelocityModelComponent.p_wave,
196    slice: Annotated[
197        int, typer.Option(help="z-level slice of velocity model to plot.")
198    ] = 0,
199    transparency: Annotated[
200        int, typer.Option(help="Velocity model overlay transparency, (0 = opaque)")
201    ] = 80,
202    title: Annotated[Optional[str], typer.Option(help="Figure title")] = None,
203    latitude_pad: Annotated[
204        float, typer.Option(help="Latitude padding for figure (in degrees)", min=0)
205    ] = 0,
206    longitude_pad: Annotated[
207        float, typer.Option(help="Longitude padding for figure (in degrees)", min=0)
208    ] = 0,
209    dpi: Annotated[
210        float, typer.Option(help="Plot output DPI (higher is better)")
211    ] = 300,
212) -> None:
213    """Plot a velocity model from a velocity model params yaml file (vm_params.yaml).
214
215    Parameters
216    ----------
217    vm_params_ffp : Path
218        Path to the YAML file containing velocity model parameters.
219    output_plot_path : Path
220        Path to save the generated plot.
221    velocity_model_ffp : Path | None
222        Path to the directory containing velocity model files. If None, no velocity model is overlaid.
223    component : VelocityModelComponent, default = `VelocityModelComponent.p_wave`
224        The velocity model component to overlay.
225    slice : int, optional, default = 0
226        The z-level slice of the velocity model to plot.
227    transparency : int, optional, default = 80
228        Transparency level for the velocity model overlay.
229    title : str, optional
230        Title of the figure.
231    latitude_pad : float, optional, default = 0
232        Padding for latitude (in degrees) for the figure.
233    longitude_pad : float, optional, default = 0
234        Padding for longitude (in degrees) for the figure.
235    dpi : float, optional, default = 300
236        DPI for the output plot.
237    """
238    with open(vm_params_ffp) as vm_params_handle:
239        vm_params = yaml.safe_load(vm_params_handle)
240
241        # Extent x and extent y are swapped in meaning between the old
242        # vm params and the new bounding box class. So we swap them
243        # here when we load them.
244        origin = np.array([vm_params["MODEL_LAT"], vm_params["MODEL_LON"]])
245        great_circle_bearing = vm_params["MODEL_ROT"]
246        extent_x = vm_params["extent_x"]
247        extent_y = vm_params["extent_y"]
248        nztm_bearing = coordinates.great_circle_bearing_to_nztm_bearing(
249            origin, extent_x / 2, great_circle_bearing
250        )
251        box = bounding_box.BoundingBox.from_centroid_bearing_extents(
252            origin, nztm_bearing, extent_x, extent_y
253        )
254
255        resolution = vm_params.get("hh")
256        nx = vm_params["nx"]
257        ny = vm_params["ny"]
258        nz = vm_params["nz"]
259
260    region = (
261        box.corners[:, 1].min() - longitude_pad,
262        box.corners[:, 1].max() + longitude_pad,
263        box.corners[:, 0].min() - latitude_pad,
264        box.corners[:, 0].max() + latitude_pad,
265    )
266    fig = plotting.gen_region_fig(title, region)
267    if velocity_model_ffp:
268        velocity_model = load_velocity_model_file(
269            velocity_model_ffp, component, nx, ny, nz
270        )
271        plot_velocity_model(
272            fig,
273            box,
274            velocity_model,
275            resolution,
276            slice,
277            nx,
278            ny,
279            region,
280            cb_label=CB_LABEL_MAP[component],
281            transparency=transparency,
282        )
283    fig.plot(
284        x=box.corners[:, 1],
285        y=box.corners[:, 0],
286        close=True,
287        pen="1p,black,-",
288    )
289    fig.savefig(
290        output_plot_path,
291        dpi=dpi,
292        anti_alias=True,
293    )

Plot a velocity model from a velocity model params yaml file (vm_params.yaml).

Parameters
  • vm_params_ffp (Path): Path to the YAML file containing velocity model parameters.
  • output_plot_path (Path): Path to save the generated plot.
  • velocity_model_ffp (Path | None): Path to the directory containing velocity model files. If None, no velocity model is overlaid.
  • component (VelocityModelComponent, default = VelocityModelComponent.p_wave): The velocity model component to overlay.
  • slice (int, optional, default = 0): The z-level slice of the velocity model to plot.
  • transparency (int, optional, default = 80): Transparency level for the velocity model overlay.
  • title (str, optional): Title of the figure.
  • latitude_pad (float, optional, default = 0): Padding for latitude (in degrees) for the figure.
  • longitude_pad (float, optional, default = 0): Padding for longitude (in degrees) for the figure.
  • dpi (float, optional, default = 300): DPI for the output plot.