Mondaic

salvus.toolbox.toolbox

salvus.toolbox.toolbox salvus toolbox toolbox

A collection of tools to assist with model and mesh building.

Functions

create_events_from_segy_file()

def create_events_from_segy_file(
    segy_file: pathlib.Path | str,
    component: str,
    fields: typing.Iterable[str] | str,
    source_callback_function: typing.Callable,
    receiver_callback_function: typing.Callable,
    dim: int,
    event_suffix: str = "",
    discrimination_points: set[int] | int | None = None,
    scale_coordinates_by: float = 1.0,
    event_discriminator: str = "EnergySourcePoint",
    source_burial_depth_in_meters: float = 0.0,
    receiver_burial_depth_in_meters: float = 0.0,
) -> EventDataCollection: ...

Create a set of events from a SEG-Y file (experimental).

Currently only works for single-component receivers. Currently only burial depths of 0m are supported.

This experimental action adds events from a SEG-Y file to a project. An “event” in this context is the collection of sources and receivers which share a common trace header (event_discriminator); multiple events can be extracted from a single SEG-Y file. Source locations are derived from the trace headers “SourceX” and “SourceY”. Receiver locations are derived from “GroupX” and “GroupY”. Events will be named by following the pattern “event_{EnergySourcePoint}_{event_suffix}”. Both sources and receivers will be of the “SideSet” variety, with side-set tags “z1” (or “y1” in 2-D). “SourceY” and “GroupY” are ignored if the source is 2-D.

The ‘segyio’ package is required to use this functionality. In practice, SEG-Y files can be extremely large, and you can expect this function to take a bit of time in those cases.

Parameters
  • segy_file pathlib.Path | str — Path to the SEG-Y file.
  • component str — The component recorded by the SEG-Y traces (e.g. ‘X’, or ‘Z’). Only single component recordings are supported for now.
  • fields typing.Iterable[str] | str — Treat output as this/these field(s).
  • source_callback_function typing.Callable — Function creating a source object. Takes x, and burial_depth_in_m in 2D and addtionally y in 3D.
  • receiver_callback_function typing.Callable — Function creating a receiver object. Takes x, burial_depth_in_m, network_code, station_code, and fields in 2D and additionally y in 3D.
  • dim int — The dimensionality.
  • event_suffix str — A string to add at the end of each event ID. Useful to identify events from different SEG-Y file which may have the same EnergySourcePoint.
  • discrimination_points set[int] | int | None — If given, restrict the addition of events to the discriminator ids specified here. By default, all discriminator values will be added.
  • scale_coordinates_by float — A factor to multiply all coordinate values by.
  • event_discriminator str — Which header field discriminates one event from another? Choices are “EnergySourcePoint” or “FieldRecord”.
  • source_burial_depth_in_meters float — Bury the sources this many meters below the “top” surface of the mesh (“y1” or “z1” side sets).
  • receiver_burial_depth_in_meters float — Bury the sources this many meters below the “top” surface of the mesh (“y1” or “z1” side sets).
Returns EventDataCollection

detect_fluid()

def detect_fluid(
    um: unstructured_mesh.UnstructuredMesh,
) -> unstructured_mesh.UnstructuredMesh: ...

Automatically detect fluid regions.

Automatically detects where fluid regions are in a coupled mesh, or marks the entire mesh as fluid if no elastic-only parameters are found.

Parameters
  • um unstructured_mesh.UnstructuredMesh — UnstructuredMesh (with parameters attached) which should be inspected for fluid and solid regions.
Returns unstructured_mesh.UnstructuredMesh — UnstructuredMesh object with fluid flag attached.

gen_taper_spherical()

def gen_taper_spherical(
    ds: xr.Dataset,
    deg_lat: float | None,
    deg_lon: float | None,
    use_nan_mask: str | None = None,
) -> xr.Dataset: ...

Generate an approximate lat/lon taper in spherical coordinates.

Often times models do not taper smoothly into the background model being used. This function can help you generate a taper function which will be used during the model interpolation to smoothly transition from the 3-D model to the values already in the mesh. Two modes are accessible here: either one which generates a taper from the spherical bounding box of the model, or one which uses ‘not-a-number’ (NaN) values in the dataset to restrict the taper to regions where the model is not defined. This can be useful when considering models from the IRIS ERC, as these models often contain NaNs as a means of masking out certain regions.

In the case of a CF model with NaNs (such as those supplied by the IRIS EMC), one could consider just passing 0.0 for both deg_lat and deg_lon. This simply masks out model regions where the model is not defined, and has been found to be quite robust even in models with complicated and unstructured distributions of NaNs. Nevertheless, the taper widths are always model dependent, and it helps to try a few values before settling on something final.

Parameters
  • ds xr.Dataset — Template model in spherical coordinates for which to generate the taper.
  • deg_lat float | None — Size of taper in degrees of latitude.
  • deg_lon float | None — float Size of taper in degrees of longitude.
  • use_nan_mask str | None — Files using the CF convention can mask out missing data using np.nan. If this is set to true, create a taper which follows the edges of the region marked by np.nan, instead of one based on the absolute spherical min/max coordinates.
Returns xr.Dataset — A new Xarray Dataset with a new member Dataarray called “taper”, which can be used to taper the edges of the model.

generate_mesh_from_splines_2d()

def generate_mesh_from_splines_2d(
    x_min: float,
    x_max: float,
    splines: list[tuple[interp1d, interp1d]],
    maximum_frequency: float,
    slowest_velocities: list[float],
    model_order: int = 2,
    absorbing_boundaries: tuple[list[str], float] | None = None,
    elements_per_wavelength: float = 1.5,
    use_refinements: bool = True,
    extrude_splines: bool = False,
) -> tuple[list[unstructured_mesh.UnstructuredMesh], float]: ...

Generate a 2-D mesh with interfaces defined by 1-D splines.

A common use case is to run simulations on a 2-D model with deformed internal discontinuities. This function helps to mesh such a model, and takes care to add space for absorbing boundaries, and adjust the vertical elemenet size within each layer. Given a list of n splines, a mesh with n

  • 1 layers (chunks) will be returned. These chunks can then be summed together to create a continuous mesh.

The splines must be provided in a top down order. That is, the first set of points defines the top of the mesh (sideset y1), while the last set defines the bottom (sideset y0).

Parameters
  • x_min float — Minimum x value of the domain (before boundaries).
  • x_max float — Maximum x value of the domain (before boundaries).
  • splines list[tuple[interp1d, interp1d]] — A list of scipy.interp1d objects which are used to define the interfaces in the model. For help generating these splines, see the associated “get_interpolating_splines” function.
  • maximum_frequency float — Maximum frequency which will be simulated in the mesh.
  • slowest_velocities list[float] — A list of the slowest velocities in each layer, ordered the same as the interfaces passed to splines (i.e. top to bottom). These values will be used, along with maximum_frequency and elements_per_wavelength to determine the element sizes in each region.
  • model_order int — The order of the interpolation to use for the parameter and shape mapping on each element.
  • absorbing_boundaries tuple[list[str], float] | None — Extend the domain from side sets x by approximately y elements, given a tuple of the form (x, y), where x is a list of side sets to extrude.
  • elements_per_wavelength float — Elements per wavelength at maximum frequency.
  • use_refinements bool — Create a totally unstrucuted mesh by allowing for vertical refinements. Whether or not this will result in a performance increase is dependent on the model, we recommend try with and without refinements to see what works best for you.
  • extrude_splines bool — Extrapolate the interpolating splines along with the absorbing boundaries (if attached).
Returns tuple[list[unstructured_mesh.UnstructuredMesh], float] — A tuple containing of a list of unstructured mesh objects, and the minimum size of the absorbing boundary buffer in meters. The unstructured mesh objects can be summed together with np.sum() to generate the final mesh, or they can be operated on individually.

geographic_to_geocentric_latitude()

def geographic_to_geocentric_latitude(
    lat: float, flattening: float = 0.0033528106647474805
) -> float: ...

Return a geocentric latitude when given geographic latitude.

Parameters
  • lat float — Latitude in degrees.
  • flattening float — Flattening parameters.
Returns float — Geocentric latitude in degrees.

get_interpolating_splines()

def get_interpolating_splines(
    layers_x: list[np.ndarray],
    layers_y: list[np.ndarray],
    kind: list[str] | str,
) -> list[tuple[interp1d, interp1d]]: ...

Get a list of tuples of interpolating splines from x and y values.

The list contains pairs of interpolating functions defining the top and bottom of a given layer. layers_x should be a 1-D numpy array which contains the x values of spline points, while layers_y should be 2-D numpy array which contains the y values definining each layer. If layers_y is an array with shape (n, m) then we have n layers which are each defined by m tie points.

For this function, horizons should not cross each other.

Parameters
  • layers_x list[np.ndarray] — List of 1-D x values defining the interpolation points. Each array in the list defines the x-values for one discontinuity.
  • layers_y list[np.ndarray] — List of 1-D y values defining the interpolation points. Each array in the list defines the y-values for one discontinuity.
  • kind list[str] | str — Interpolation styles to use. If just one value is passed, that method will be used for all interpolations. Alternatively, if a list of styles is passed in the same order as layers_x and layers_y, then the styles will be individually applied to the associated horizon.
Returns list[tuple[interp1d, interp1d]] — A list of n - 1 tuples defining the distinct regions in the model.

get_linear_feature_mask()

def get_linear_feature_mask(
    mesh: unstructured_mesh.UnstructuredMesh,
    normal: list[float] | npt.NDArray,
    point: list[float] | npt.NDArray,
    length: float,
    buffer: float,
) -> npt.NDArray: ...

Return an array which can be used to mask a linear feature.

It is often desirable to refine the mesh around one or more linear features in the model. For instance, one may be interested in a region of anomalously slow velocites, or an unconformity (i.e. a fault). Given a parameterized line, this function returns a mask which can later be used to refine, or otherwise modify, a mesh in the vicinity of the linear feature.

Parameters
  • mesh unstructured_mesh.UnstructuredMesh — The mesh to compute the mask on.
  • normal list[float] | npt.NDArray — The normal to the linear feature (line or plane).
  • point list[float] | npt.NDArray — A point defining the centre of the feature.
  • length float — The extent of the feature on either side of the point.
  • buffer float — A buffer region (in meters) around the feature which should also be included in the refinement.
Returns npt.NDArray — An boolean array defining the mask.

get_shotgather()

def get_shotgather(
    filename: str | pathlib.Path, field: str, cmp: int = 0, axis: int = 0
) -> tuple[np.ndarray, float, tuple[float, float, float, float]]: ...

Get a shotgather from a Salvus HDF5 receiver file.

Populates a 2-D numpy array with one component from a Salvus HDF5 receiver file.

Parameters
  • filename str | pathlib.Path — The path to Salvus’ HDF5 receiver file.
  • field str — The name of the field to read (i.e. “displacement”, “phi”).
  • cmp int — Index of the component to plot.
  • axis int — Sort the traces by coordinate value along this axis [0: x, 1: y, 2: z].
Returns tuple[np.ndarray, float, tuple[float, float, float, float]] — Shotgather as a 2-D numpy array.

interpolate_cartesian()

def interpolate_cartesian(
    data: xr.Dataset,
    mesh: unstructured_mesh.UnstructuredMesh,
    extrapolate: bool = True,
    mode: str | None = None,
    mask: np.ndarray | tuple[str, int] | list[tuple[str, int]] | None = None,
) -> unstructured_mesh.UnstructuredMesh: ...

Interpolate a volumetric model onto a Cartesian mesh.

Either scipy.interpolate.RectBivariateSpline, or scipy.interpolate.RegularGridInterpolater, will be used, depending on the value passed for ‘mode’. "spline" is only accepted for 2-D meshes and models.

Parameters
  • data xr.Dataset — Xarray Dataset in cartesian coordinates. Must have x, y, (and z in 3-D) as coordinate arrays. SI units are expected (meters).
  • mesh unstructured_mesh.UnstructuredMesh — Mesh to interpolate onto. No checks are done regarding whether the mesh is of an appropriate type.
  • extrapolate bool — If the mesh extends beyond the bounds of the model, should we extrapolate? If "spline" is passed for mode, extrapolate must be False. Defaults to True.
  • mode str | None — How should the interpolation be performed? If "spline" is passed, scipy.interpolate.RectBivariateSpline is used. If "linear" or "nearest" is passed, scipy.interpolate.RegularGridInterpolator is used. If "none" is passed, the value will be chosen based on the dimension: "spline" for 2-D and "linear" 3-D. Defaults to None.
  • mask np.ndarray | tuple[str, int] | list[tuple[str, int]] | None — Element mask - if given, interpolation will be restriced to the given elements. A consequence is that all parameters must already be available as element nodal fields.
Returns unstructured_mesh.UnstructuredMesh — Mesh with the parameters in the Dataset interpolated onto it.

interpolate_spherical()

def interpolate_spherical(
    data: xr.Dataset,
    mesh: unstructured_mesh.UnstructuredMesh,
    mode: str = "perturbation_to_background",
    spherical_max_radius: float = 6371000.0,
    layers: list[int] | str | None = None,
    globe: bool = False,
    ignore: tuple[str, int] | None = None,
    interpolate_parameters: list[str] | None = None,
    verbosity: int = 0,
) -> unstructured_mesh.UnstructuredMesh: ...

Do a model interpolation in spherical coordinates.

Use scipy.interpolate.RectSphereBivariateSpline to create a spherical spline interpolation over the surface of each radial layer. Then use linear interpolation between these radial layers to get values at a general unstructured depth. Function is vectorized, so it should be pretty quick.

Note that the interpolation is done assuming the radius from a 1-D background model. This means factors such as surface topography are ignored when performing the model interpolation.

Parameters
  • data xr.Dataset — Xarray Dataset with spherical coordinates. Must have latitude, longitude, and radius as coordinate arrays. Latitude and longitude should be in degrees.
  • mesh unstructured_mesh.UnstructuredMesh — Mesh to interpolate onto. Should of course be a spherical chunk mesh.
  • mode str — How to perform the model attachment. Accepts either “perturbation_to_background” which adds the provided model to the values already present in the mesh, or “overwrite”, which replaces the values in the mesh with the provided model.
  • spherical_max_radius float — The spherical radius of the domain, defaults to the radius of Earth in m.
  • layers list[int] | str | None — Restrict the interpolation to specific “layers” in the mesh. Looks for an elemental_field called “layers”, and only interpolates onto elements which are part of a particular layer. This field is automatically attached by the mesher in Salvus versions greater than 0.10.3. For spherical and spherical chunk meshes, one can simply pass “crust” for this parameter, and the crustal layers will be automatically deduced from the “moho_idx” variable stored in the mesh (and read from the .bm file). Similarly one can pass “mantle” to restrict interpolation to areas below the discontinuity defined by “moho_idx”.
  • globe bool — Some minor special treatment is required when interpolating models over a full sphere. Set this to True if the mesh you are interpolating onto is a full sphere.
  • ignore tuple[str, int] | None — Restrict interpolation to a region of the mesh which is masked by some flag. Takes a tuple of (elemental_variable_name, flag_value) as input. For instance, to restrict interpolation to only elastic regions you could pass ignore = ("fluid", 1), and to restrict interpolation to only acoustic regions you could pass ignore = ("fluid, 0).
  • interpolate_parameters list[str] | None — Only interpolate these parameters.
  • verbosity int — If greater or equal to 1, print the progress of the interpolation.
Returns unstructured_mesh.UnstructuredMesh — Mesh with the parameters in the Dataset interpolated onto it.

iris_emc_to_xarray()

def iris_emc_to_xarray(
    filename: str | pathlib.Path | xr.Dataset,
    planet_radius_in_km: float = 6371.0,
    fill_na: bool = True,
) -> tuple[xr.Dataset, str]: ...

Read a model from the IRIS EMC.

Most 3-D Earth models from the IRIS EMC are released as a NetCDF file in the CF-1.0 convention. This function will read in such a model, and return it as an Xarray Dataset to be used for interpolation onto a Salvus mesh. The exact formatting of models within the EMC is not entirely consistent, so it is possible that this function will fail for some models. It has been tested and found to work for many of the EMC models, and should be good at least as a starting point for contributing your own read function. The objects returned from this function can be used directly in the interpolate_spherical function, perhaps with some tapering required first (see gen_taper_spherical for information).

Parameters
  • filename str | pathlib.Path | xr.Dataset — Name of the NetCDF file from the IRIS EMC in CF-1.0 format.
  • planet_radius_in_km float — Radius of the planet in km. Defaults to 6371.0.
  • fill_na bool — Some models use NaNs to mask out invalid regions. If this is set to true, the function will copy the original parameter arrays into a dataset called {parameter_name}_nan, and then forward/backfill all NaN values with neighboring values from the same radial layer. Defaults to True.
Returns tuple[xr.Dataset, str] — An Xarray Dataset containing the model from the EMC, reading for tapering and/or interpolation.

load_cartesian_model()

def load_cartesian_model(
    model: pathlib.Path | str | xr.Dataset,
) -> xr.Dataset: ...

Load a cartesian model.

Parameters
  • model pathlib.Path | str | xr.Dataset — File or data set.
Returns xr.Dataset

mesh_from_xarray()

def mesh_from_xarray(
    data: xr.Dataset,
    slowest_velocity: str,
    maximum_frequency: float,
    elements_per_wavelength: float,
    model_order: int = 4,
    absorbing_boundaries: tuple[list[str], float] | None = None,
    interpolation_mode: str | None = None,
) -> unstructured_mesh.UnstructuredMesh: ...

Generate a Salvus mesh from an xarray.Dataset object.

This function is meant to be used as an entry point and way to encapsulate the variety of mesh generation algorithms contained in SalvusMesh, and is optimized for regularly-gridded data. The data can be either 2- or 3-D, and must be contained within a xarray.Dataset object. This object needs to contain coordinate arrays labelled ‘x’ and ‘y’, or ‘x’, ‘y’, and ‘z’ if working in a Cartesian coordinate system. If working in a spherical coordinate system as is common in regional to global scale seismology, one must provide the parameters ‘radius’, ‘latitude’, and ‘longitude’. Material paramters stored as DataArrays within the Dataset should be named according to the Salvus parameter naming conventions.

This function will check the sanity of inputs, and will raise if something incorrect is detected. The sophistication and varitey of meshing algorithms contained within this function will evolve over time, so ensure to check back for updates.

Parameters
  • data xr.Dataset — Xarray Dataset containing the parameters and coordinates of the model.
  • slowest_velocity str — String specifying which parameter within the Dataset corresponds to the parameter controlling the slowest velocity.
  • maximum_frequency float — Maximum frequency expected to be present in the resultant simulations. Will be used in conjunction with elements_per_wavelength to determine the critical edge lengths of the mesh.
  • elements_per_wavelength float — Elements per minimum wavelength in the model. Will be used in conjunction with maximum_frequency to determine the critical edge lengths of the mesh. model_order: Order of the polynomial interpolation within each element.
  • model_order int — The polynomial order of the model.
  • absorbing_boundaries tuple[list[str], float] | None — Extend the domain from side sets x by approximately y elements, given a tuple of the form (x, y), where x is a list of side sets to extrude.
  • interpolation_mode str | None — Set the method of interpolation for the volumetric model stored in data. See the documentation of toolbox.interpolate_cartesian for more information.
Returns unstructured_mesh.UnstructuredMesh — An unstructured mesh object populated with parameters, which is ready for use within Salvus.

read_elastic_marmousi()

def read_elastic_marmousi(
    directory: pathlib.Path, parameters: list[str], decimate: int = 10
) -> xr.Dataset: ...

Read the ‘AGL Elastic Marmousi model’ into an Xarray Dataset.

The resulting dataset suitable for use by SalvusMesh. To use another SEG-Y file, RSF file, or even your own regularly-gridded format, you need to modify the function below to accept your data, while ensuring that the structure of the returned xarray.Dataset remains the same.

Parameters
  • directory pathlib.Path — Path to the ‘model’ directory of the Elastic Marmousi model.
  • parameters list[str] — Parameters of the model to read in.
  • decimate int — Decimate the model data for quicker reading.
Returns xr.Dataset — An Xarray Dataset object that can be used to generate a Salvus mesh.

skeleton_regular_2d()

def skeleton_regular_2d(
    data: xr.Dataset,
    slowest_velocity: str,
    maximum_frequency: float,
    elements_per_wavelength: float,
    absorbing_boundaries: tuple[list[str], float] | None = None,
    model_order: int = 1,
) -> unstructured_mesh.UnstructuredMesh: ...

Return a simple rectilinear skeleton mesh from Salvus’ mesher.

Given a string specifying the parameter with the slowest velocity, this function will return a mesh which ensures that at least the specified number of elements per wavelength are specified given the specified maximum frequency. This returns the simplest mesh possible — no local optimizations are done here to improve solver efficiency.

The mesh will be returned with elemental nodal parameters initialized to the minimum values of each of the parameters in data. As such, the resultant mesh is not ready for immediate consumption, and is instead meant to serve as a skeleton on which to interpolate a variable model.

Parameters
  • data xr.Dataset — Xarray Dataset describing the rectilinear model to be meshed.
  • slowest_velocity str — String specifying the parameter in the xarray dataset with the slowest velocity.
  • maximum_frequency float — Maximum frequency for which the elements_per_wavelength parameter should be respected.
  • elements_per_wavelength float — Number of elements per maximum minimum wavelength (computed globally from slowest_velocity and maximum_frequency).
  • absorbing_boundaries tuple[list[str], float] | None — Extend the domain from side sets x by approximately y elements, given a tuple of the form (x, y), where x is a list of side sets to extrude.
  • model_order int — The polynomial order of the model.
Returns unstructured_mesh.UnstructuredMesh — An unstructured mesh object with no attached parameters.

visualize_wavefield_2d()

def visualize_wavefield_2d(
    filename: str | pathlib.Path, field: str, cmp: int = 0
) -> tuple[tri.Triangulation, np.ndarray]: ...

Get an object that can be used to plot simple 2-D wavefields.

This method works by using the tricontourf method in matplotlib, and so comes with some limitations. For instance, if the mesh is not convex, the triangulation will fill in any holes and thus lead to unexpected results. This function is meant to quickly plot wavefields on simple domains for use in jupyter-notebooks and the like — for more detailed volumetric plotting (including in 3-D) please use Paraview.

Parameters
  • filename str | pathlib.Path — Name of volumetric output file.
  • field str — Field to plot.
  • cmp int — Component of the field to plot.
Returns tuple[tri.Triangulation, np.ndarray] — A tuple of a triangulation object, followed by the data which can be plotted with respect to that object, for instance plt.tricontourf(t, d[ts, :]) will plot the output at time-step ts.

xy_to_xarray()

def xy_to_xarray(model: str | pathlib.Path | xr.Dataset) -> xr.Dataset: ...
Parameters
  • model str | pathlib.Path | xr.Dataset — Model values.
Returns xr.Dataset

xyz_to_xarray()

def xyz_to_xarray(model: str | pathlib.Path | xr.Dataset) -> xr.Dataset: ...

Read a file in NetCDF format into an Xarray Dataset.

When storing 3-D models parameterized on a regular grid, a simple NetCDF file in the xyz format is all that is required. A typical file should have something like the following structure (output of ncdump -h):

    dimensions:
            x = 100 ;
            y = 100 ;
            z = 10 ;
    variables:
            float vp(z, y, x) ;
                vp:units = "mps" ;
            float vs(z, y, x) ;
                vs:units = "mps" ;
            float rho(z, y, x) ;
                rho:units = "kgpcm" ;
            float x(x) ;
                x:units = "m" ;
                x:standard_name = "projection_x_coordinate" ;
                x:long_name = "x coordinate of projection" ;
            float y(y) ;
                y:units = "m" ;
                y:standard_name = "projection_y_coordinate" ;
                y:long_name = "y coordinate of projection" ;
            float z(z) ;
                z:units = "m" ;
                z:standard_name = "projection_z_coordinate" ;
                z:long_name = "z coordinate of projection" ;

Such a file can also be opened directly in Paraview using the built-in NetCDF reader. You can see an example of how to build such a file in Salvus’ online documentation, or the test suite for this package.

Parameters
  • model str | pathlib.Path | xr.Dataset — Path to model file or model..
Returns xr.Dataset — An Xarray Dataset ready to be interpolated onto a Salvus mesh.

Used in tutorials