Salvus 2025.1.1
This release brings a range of improvements, new features, and bug fixes across the Salvus ecosystem. Key public developments include a faster regular grid extraction for Cartesian meshes, new customization options for preconditioners and the trust region optimization, and a QC measures for meshes and material.
Furthermore, we improved the documentation and fixed a few bugs listed below.
Changes
The computation of the Jacobians for reference -> physical element mappings has been updated and improved, which should result in speedups for the simulation setup stage.
Fixed an edge case in the serialization logic of functions containing paths on Windows.
Fix component names for rotated receivers and HDF5 receiver file output.
Special paramiko connection arguments can now be separately configured for the direct connection + the jump host connection to deal with tricky cases.
Usage in the site config file:
[sites.my_site.ssh_settings]
hostname = "some_host"
username = "some_user"
# Example: Disable different pubkey algorithms for the direct + jump host.
[sites.my_site.ssh_settings.extra_paramiko_connect_arguments.disabled_algorithms]
pubkeys = ["rsa-sha2-512"]
[sites.my_site.ssh_settings.extra_paramiko_connect_arguments_jump_host.disabled_algorithms]
pubkeys = ["rsa2-sha2-256"]Fix the __repr__ methods of the oriented and attenuating materials.
Bugfix for a case where realization information would be lost when copying symmetric tensor components.
extract_model_to_regular_grid() is now significantly more performant for
cartesian meshes. Since the underlying algorithm was changed, the results can
be slightly different compared to what was obtained with the previous version.
The updated function is parallelized and, by default, uses the maximum number
of cores available on your system. You can limit the number of cores like
this:
from salvus.mesh.algorithms.unstructured_mesh.utils (
import extract_model_to_regular_grid
)
extract_model_to_regular_grid(number_of_threads=4)Added better documentation to UnstructuredMesh.rotate_coordinates. It should
now be clear in which convention the rotations are applied, and what the units
of the angles are.
Add QC system to materials and meshes, accessed by the member function mesh.qc_test() or material.qc_test(). This will run a set of tests on the mesh or material and return a dictionary with possible issues of varying severity. The tests are not exhaustive, but should catch common issues.
Fix a bug that could cause a meshing failure when using anisotropic elements
per wavelength with absorbing boundaries. The fix involved changing the
signature of some public-facing layered model reductions. In particular, the
signatures for both get_min_wavelength_reduction_function and
get_max_element_size_reduction_function have been updated to be more explicit,
and now directly take the reference frequency and elements per wavelength as
parameters.
Add a new model filter in
salvus.mesh.layered_meshing.layered_model.filters that allows for the
selective resolution of s-waves based on the VP vs ratio. Primarily, this
function take a parameter alpha. If alpha is 0, this means that the mesh’s
resolution criterion is set only by the P-wave model, and when it is 1 it means
the S-wave model is also resolved to the same degree. Values between 0 and 1
indicate partial resolution of the S-wave model, with a linear taper between
wavelength oracles for the P- and S-wave components.
Users can now pass a custom preconditioner to an
InverseProblemConfiguration. Custom preconditioners have to be defined as a
callback function with a single input argument unstructured_mesh,
corresponding to an UnstructuredMesh object and must again return an
UnstructuredMesh object with the custom preconditioner applied.
Example:
import salvus.namespace as sn
# Define a custom preconditioner that applies a depth-dependent scaling
# to the gradient
def custom_preconditioner(unstructured_mesh):
# Apply a depth-dependent scaling
alpha = 1.0
beta = 2.0
scaling = (
alpha * (1.0 + np.abs(unstructured_mesh.get_element_nodes()[..., -1]))
) ** beta
inversion_parameters = ["VP", "VS", "RHO"]
for field in inversion_parameters:
unstructured_mesh.elemental_fields[field] *= scaling
return unstructured_mesh
sn.InverseProblemConfiguration(
name="inversion",
prior_model="prior_model",
events=["event"],
method=sn.TrustRegion(initial_trust_region_linf=0.1),
misfit_configuration="L2",
mapping=sn.Mapping(
scaling="relative_deviation_from_prior",
inversion_parameters=["VP", "VS", "RHO"],
),
preconditioner=custom_preconditioner,
job_submission=sn.SiteConfig("local", 4),
)TrustRegion objects now accept an optional input argument to set a maximum
number of trial model rejections. Once this number is exceeded, the inversion
will either fail or accept the final model of the previous iteration and
continue with the next iteration, depending on the setting chosen by the user.
Example:
import salvus.namespace as sn
sn.TrustRegion(
max_trial_models_per_iteration=5,
fail_if_max_trial_models_reached=False,
)Relax validation of the mapping class in the presence of a region of interest. The parameter fields will only be checked for consistency inside the region of interest. This is necessary, for instance, when using a custom scaling when inverting elastic parameters in a coupled medium.
Add the ability to determine the initial trust region radius in a callback function. This is useful when the desired magnitude of the update is space- dependent.
Example:
def initial_radius_callback(
current_model: UnstructuredModel, proposed_update: UnstructuredModel
) -> float:
'''
Simple callback function to return the step size.
Args:
current_model: The current model in the inversion.
proposed_update: The proposed model update.
Returns:
A scalar factor by which the proposed model will be scaled.
'''
factor = 1.0 / proposed_update.norm_inf()
return factor
tr = TrustRegion(
initial_trust_region_linf=initial_radius_callback,
)Fixed a bug that resulted in the waveform visualization widget and the window picking function to be significantly less responsive and slower for large seismological data sets.
Preserve the order of events in project operations.
p.waveforms.get(..., events=["event2", "event1"]) will for example
now return the events in the order specified by the user.
p.simulations.launch() now raises a consistent and correct error
message it the site requires a wall time to be set.
Fix a bug for Cartesian meshes where the material properties of an explicitly defined ocean layer would be overwritten by a volume model.