Mondaic

Salvus 2024.1.2

Released: 2024-10-14

This is a minor release with several bug fixes and performance improvements.

New features include utilities for switching the material parameterization and extracting spatial gradients of a model, improved support for meshes stored in the ExodusII file format, the ability to add new sites on the fly, a callback option for post-processing model updates in an inversion, and experimental support for importing sources on a fault using the Standard Rupture Format (SRF).

The performance improvements provide significant speed-ups for interpolating volumetric models and local mesh refinements.

Further details are listed below.

Changes

SalvusFlow

Salvus on local sites typically fully decouples processes (e.g. running computations) from its parent process to allow for async workflows. In some situations this is not desirable (e.g. one wants to ensure that all Salvus runs are terminated when the Python process is terminated). An optional "do_not_decouple_child_processes" flag for local sites has been introduced to support that behavior.

SalvusFlow

Added the possibility to add new executors/sites on-the-fly, skipping all the checks from the salvus-cli init-site step.

Usage as a context manager is optional.

with sn.functions.add_new_executor(
    site_name="my_other_site",
    executor_type="local",
    salvus_binary=salvus_binary,
    run_directory=run_directory_2,
    tmp_directory=tmp_directory_2,
    use_cuda_capable_gpus=False,
    default_ranks=1,
    max_ranks=2,
):
    # Site is available here.
    sn.functions.get_site("my_other_site")
SalvusFlow

Filter out the warning regarding cryptography’s moved import, as with the current environment as distributed, this doesn’t affect Salvus users.

SalvusFlow

Fix for misfit computation when time axis is improperly subsampled. Happens i.e. when the last sample on a receiver (the last simulation time step) doesn’t align with regular time grid. This occurs when using a sampling interval greater than one time step in the receiver output.

SalvusMaterial

Fix bug where attenuating tensor components are not split in base and visco parts when computing wavelengths.

SalvusMesh

Add functions to compute spatial derivatives of arbitrary fields defined at the GLL points on an unstructured mesh using the Lagrangian basis.

import sympy, numpy as np

import salvus.namespace as sn
from salvus.mesh import layered_meshing as lm

n = 4  # polynomial order
domain = sn.domain.dim2.BoxDomain(x0=0, x1=1, y0=0, y1=1)

# Create a simple mesh to use as a test. Let parameters vary only in a
# single direction so that we know their gradients in advance.
mesh = lm.mesh_from_domain(
    domain=domain,
    model=lm.material.from_params(
        vp=1 + sympy.symbols("x"), rho=sympy.symbols("v")
    ),
    mesh_resolution=sn.MeshResolution(
        reference_frequency=10.0,
        elements_per_wavelength=1.0,
        model_order=n,
    ),
)

# Compute the spatial derivatives of the model
mesh.compute_spatial_gradients(fields=["VP", "RHO"])

# VP varies only in the x direction.
np.testing.assert_allclose(mesh.element_nodal_fields["dVP/dx"], 1.0)
np.testing.assert_allclose(
    mesh.element_nodal_fields["dVP/dy"], 0.0, atol=1e-10
)
# RHO varies only in the y direction.
np.testing.assert_allclose(
    mesh.element_nodal_fields["dRHO/dx"], 0.0, atol=1e-10
)
np.testing.assert_allclose(
        mesh.element_nodal_fields["dRHO/dy"],
        1.0,
    )
SalvusMesh

When interpolating xarray datasets using get_interpolator(...) from xarray_tools, faster interpolations are now provided via the fast_interp package. This package will be used automatically if the interpolation source is regularly spaced, 2- or 3-D, and the “linear” interpolation method is selected. Note that if the source grid has a variable grid spacing in any one dimension scipy’s regular grid interpolation routines will be fallen back on.

SalvusMesh

Adds a new argument enclosing_elements_method to the simple_post_refinements generator. The default value is "inverse_coordinate_transform", which retains legacy behavior. Another value "bounding_box" is also supported, which trades the accuracy of the enclosing element search to performance, as only a simple bounding box check is done to determine point ownership.

SalvusMesh

Fix a bug that erroneously tried to truncate discrete relative models when two neighboring interfaces had coincident reference elevations.

SalvusMesh

Fix a bug in the mesher where certain non-monotonic models would cause an error.

SalvusMesh

Salvus can now read Exodus files with multiple element blocks, read them partially and optionally store the element block index in the mesh.

# To attach the block ids to the mesh
m = sn.UnstructuredMesh.from_exodus(
    filename, attach_element_block_indices=True
)

# To only read given blocks
m = sn.UnstructuredMesh.from_exodus(
    filename, select_element_block_indices=[2, 4],
)
SalvusMesh

Allow conversion of unstructured mesh parameters using mesh.transform_parameter, which will automatically try to convert the parameters defined on a mesh to your chosen material, if they are compatible. Returns a new mesh. Ideal when a model in a different parametrization is desired.

When creating meshes from materials, typically the converted mesh will be identical to a mesh generated from the converted material itself.

import salvus.namespace as sn

from salvus import material

d = sn.domain.dim2.BoxDomain(x0 =0, x1=10, y0=0,y1=10)
mr = sn.MeshResolution(reference_frequency=1)

mesh_material_iso = sn.layered_meshing.mesh_from_domain(
    domain=d,
    model=material.acoustic.Velocity.from_params(vp=1.0, rho=1.0),
    mesh_resolution=mr,
)

mesh_material_tho = sn.layered_meshing.mesh_from_domain(
    domain=d,
    model=material.acoustic.elliptical_hexagonal.Velocity.from_params(
        vpv=1.0, vph=1.0, rho=1.0
    ),
    mesh_resolution=mr,
)

converted_mesh = mesh_material_iso.transform_material(
    material.acoustic.elliptical_hexagonal.Velocity
)

assert converted_mesh == mesh_material_tho
SalvusMesh

An elastic version of the Thomsen parameters material (material.elastic.hexagonal.Thomsen) is added, which has from_acoustic which needs injection of just 2 extra parameters. Additionally, it can convert back using to_acoustic.

SalvusProject

Fix a bug where linear solids would not be properly passed to viscoelastic cartesian models in SalvusProject resulting in an error during mesh generation.

Changes to Experimental Features

SalvusFlow

Add a reader for specifying seismic ruptures in the Standard Rupture Format Version 2.0. This is useful for modeling a rupture along a fault by generating a collection of point sources in Salvus.

Example:

from salvus.flow.simple_config.source.srf_file_reader import _read_srf_file
from salvus.flow.simple_config.source.srf_to_salvus_sources import (
    _create_sources_from_srf_data
)
srf_data = _read_srf_file(
    "example.srf",
    plot=True,
)
# To generate the corresponding sources in a UTM domain:
salvus_sources = _create_sources_From_srf_data(
    srf_data,
    utm_crs,
)

This is an experimental feature and the API might change in the future.

SalvusOpt

Add a callback function to the Mapping class which allows to postprocess proposed model updates before validation of the misfit reduction. This feature can be useful to introduce prior knowledge on material properties and to emphasize certain features in the model updates.

Example:

def fixed_vp_vs_ratio(
    proposed_model: sn.UnstructuredMesh,
    prior_model: sn.UnstructuredMesh,
) -> sn.UnstructuredMesh:
    vp_vs_ratio = ... # some value
    model_update = proposed_model.copy()
    model_update.elemental_fields["VP"] = vp_vs_ratio * model_update.elemental_fields["VS"]

    return model_update

mapping = sn.Mapping(
    inversion_parameters=["VS"],
    scaling="relative_deviation_from_prior",
    postprocess_model_update=fixed_vp_vs_ratio,
)

mapping can then be passed to an InverseProblemConfiguration to enforce a fixed VP/VS ratio without inverting for VP.