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.