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()
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.
segy_filepathlib.Path | str — Path to the SEG-Y file.componentstr — The component recorded by the SEG-Y traces (e.g. ‘X’, or ‘Z’). Only single component recordings are supported for now.fieldstyping.Iterable[str] | str — Treat output as this/these field(s).source_callback_functiontyping.Callable — Function creating a source object. Takesx, andburial_depth_in_min 2D and addtionallyyin 3D.receiver_callback_functiontyping.Callable — Function creating a receiver object. Takesx,burial_depth_in_m,network_code,station_code, andfieldsin 2D and additionallyyin 3D.dimint — The dimensionality.event_suffixstr — 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_pointsset[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_byfloat — A factor to multiply all coordinate values by.event_discriminatorstr — Which header field discriminates one event from another? Choices are “EnergySourcePoint” or “FieldRecord”.source_burial_depth_in_metersfloat — Bury the sources this many meters below the “top” surface of the mesh (“y1” or “z1” side sets).receiver_burial_depth_in_metersfloat — Bury the sources this many meters below the “top” surface of the mesh (“y1” or “z1” side sets).
detect_fluid()
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.
umunstructured_mesh.UnstructuredMesh — UnstructuredMesh (with parameters attached) which should be inspected for fluid and solid regions.
gen_taper_spherical()
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.
dsxr.Dataset — Template model in spherical coordinates for which to generate the taper.deg_latfloat | None — Size of taper in degrees of latitude.deg_lonfloat | None — float Size of taper in degrees of longitude.use_nan_maskstr | 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.
generate_mesh_from_splines_2d()
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).
x_minfloat — Minimum x value of the domain (before boundaries).x_maxfloat — Maximum x value of the domain (before boundaries).splineslist[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_frequencyfloat — Maximum frequency which will be simulated in the mesh.slowest_velocitieslist[float] — A list of the slowest velocities in each layer, ordered the same as the interfaces passed tosplines(i.e. top to bottom). These values will be used, along withmaximum_frequencyandelements_per_wavelengthto determine the element sizes in each region.model_orderint — The order of the interpolation to use for the parameter and shape mapping on each element.absorbing_boundariestuple[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_wavelengthfloat — Elements per wavelength at maximum frequency.use_refinementsbool — 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_splinesbool — Extrapolate the interpolating splines along with the absorbing boundaries (if attached).
geographic_to_geocentric_latitude()
geographic_to_geocentric_latitude()def geographic_to_geocentric_latitude(
lat: float, flattening: float = 0.0033528106647474805
) -> float: ...Return a geocentric latitude when given geographic latitude.
latfloat — Latitude in degrees.flatteningfloat — Flattening parameters.
get_interpolating_splines()
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.
layers_xlist[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_ylist[np.ndarray] — List of 1-D y values defining the interpolation points. Each array in the list defines the y-values for one discontinuity.kindlist[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 aslayers_xandlayers_y, then the styles will be individually applied to the associated horizon.
get_linear_feature_mask()
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.
meshunstructured_mesh.UnstructuredMesh — The mesh to compute the mask on.normallist[float] | npt.NDArray — The normal to the linear feature (line or plane).pointlist[float] | npt.NDArray — A point defining the centre of the feature.lengthfloat — The extent of the feature on either side of the point.bufferfloat — A buffer region (in meters) around the feature which should also be included in the refinement.
get_shotgather()
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.
filenamestr | pathlib.Path — The path to Salvus’ HDF5 receiver file.fieldstr — The name of the field to read (i.e. “displacement”, “phi”).cmpint — Index of the component to plot.axisint — Sort the traces by coordinate value along this axis[0: x, 1: y, 2: z].
interpolate_cartesian()
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.
dataxr.Dataset — Xarray Dataset in cartesian coordinates. Must have x, y, (and z in 3-D) as coordinate arrays. SI units are expected (meters).meshunstructured_mesh.UnstructuredMesh — Mesh to interpolate onto. No checks are done regarding whether the mesh is of an appropriate type.extrapolatebool — If the mesh extends beyond the bounds of the model, should we extrapolate? If"spline"is passed formode,extrapolatemust be False. Defaults toTrue.modestr | None — How should the interpolation be performed? If"spline"is passed,scipy.interpolate.RectBivariateSplineis used. If"linear"or"nearest"is passed,scipy.interpolate.RegularGridInterpolatoris used. If"none"is passed, the value will be chosen based on the dimension:"spline"for 2-D and"linear"3-D. Defaults toNone.masknp.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.
interpolate_spherical()
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.
dataxr.Dataset — Xarray Dataset with spherical coordinates. Must have latitude, longitude, and radius as coordinate arrays. Latitude and longitude should be in degrees.meshunstructured_mesh.UnstructuredMesh — Mesh to interpolate onto. Should of course be a spherical chunk mesh.modestr — 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_radiusfloat — The spherical radius of the domain, defaults to the radius of Earth in m.layerslist[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”.globebool — 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.ignoretuple[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 passignore = ("fluid", 1), and to restrict interpolation to only acoustic regions you could passignore = ("fluid, 0).interpolate_parameterslist[str] | None — Only interpolate these parameters.verbosityint — If greater or equal to 1, print the progress of the interpolation.
iris_emc_to_xarray()
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).
filenamestr | pathlib.Path | xr.Dataset — Name of the NetCDF file from the IRIS EMC in CF-1.0 format.planet_radius_in_kmfloat — Radius of the planet in km. Defaults to 6371.0.fill_nabool — 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.
load_cartesian_model()
load_cartesian_model()def load_cartesian_model(
model: pathlib.Path | str | xr.Dataset,
) -> xr.Dataset: ...Load a cartesian model.
modelpathlib.Path | str | xr.Dataset — File or data set.
mesh_from_xarray()
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.
dataxr.Dataset — Xarray Dataset containing the parameters and coordinates of the model.slowest_velocitystr — String specifying which parameter within the Dataset corresponds to the parameter controlling the slowest velocity.maximum_frequencyfloat — Maximum frequency expected to be present in the resultant simulations. Will be used in conjunction withelements_per_wavelengthto determine the critical edge lengths of the mesh.elements_per_wavelengthfloat — Elements per minimum wavelength in the model. Will be used in conjunction withmaximum_frequencyto determine the critical edge lengths of the mesh. model_order: Order of the polynomial interpolation within each element.model_orderint — The polynomial order of the model.absorbing_boundariestuple[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_modestr | None — Set the method of interpolation for the volumetric model stored indata. See the documentation oftoolbox.interpolate_cartesianfor more information.
read_elastic_marmousi()
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.
directorypathlib.Path — Path to the ‘model’ directory of the Elastic Marmousi model.parameterslist[str] — Parameters of the model to read in.decimateint — Decimate the model data for quicker reading.
skeleton_regular_2d()
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.
dataxr.Dataset — Xarray Dataset describing the rectilinear model to be meshed.slowest_velocitystr — String specifying the parameter in the xarray dataset with the slowest velocity.maximum_frequencyfloat — Maximum frequency for which theelements_per_wavelengthparameter should be respected.elements_per_wavelengthfloat — Number of elements per maximum minimum wavelength (computed globally fromslowest_velocityandmaximum_frequency).absorbing_boundariestuple[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_orderint — The polynomial order of the model.
visualize_wavefield_2d()
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.
filenamestr | pathlib.Path — Name of volumetric output file.fieldstr — Field to plot.cmpint — Component of the field to plot.
xy_to_xarray()
xy_to_xarray()def xy_to_xarray(model: str | pathlib.Path | xr.Dataset) -> xr.Dataset: ...modelstr | pathlib.Path | xr.Dataset — Model values.
xyz_to_xarray()
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.
modelstr | pathlib.Path | xr.Dataset — Path to model file or model..