"do_not_decouple_child_processes"
flag for local sites has been introduced to
support that behavior.salvus-cli init-site
step.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")
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, )
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.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.# 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], )
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.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
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
.from salvus.flow.simple_config.source.srf_file_reader import _read_srf_file srf_data = _read_srf_file( "example.srf", plot=True, )
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.%load_ext salvus %salvus_snippets
salvus.data.external_data_to_hdf5
takes an
xarray.Dataset
, validates it, and converts it to a Salvus
compatible HDF5 file, which can be added to a project.from salvus.data import external_data_to_hdf5 for event in project.events.list(): external_data_to_hdf5( data=data_per_event, receivers=event.receivers, receiver_field="displacement", output_filename=external_data_path, ) project.waveforms.add_external( data_name="observed_data", event=event.event_name, data_filename=external_data_path, )
get_bm_string
should now contain
only duplicated pairs at most. This issue may have affected those generating
background models in SalvusProject from an n-dimensional xarray dataset.salvus.mesh.layered_mesher.material
), accessing it this way is not
encouraged.# Acoustic isotropic material material_sandstone = material.acoustic.Velocity.from_params( rho=1500, vp=1875 ) # Or something more exotic material_weird_sandstone = material.elastic.triclinic.TensorComponents( ... ) mc_sandstone = sn.ModelConfiguration( background_model=sn.model.background.homogeneous.FromMaterial( material_sandstone ) ) sc_sandstone = sn.SimulationConfiguration( name="sandstone", model_configuration=mc_sandstone, ... )
find_side_sets_enclosing
as a method to meshes to find a all side sets
that enclose the mesh, by first finding all planar surfaces and constructing
the remainder surface from all unclaimed facets.def forward_norm(data_synthetic, data_observed, sampling_rate_in_hertz): ... def jacobian_norm( adjoint_source, data_synthetic, data_observed, sampling_rate_in_hertz ): ... norm = sn.TraceNormalization(forward=forward_norm, jacobian=jacobian_norm) p += sn.MisfitConfiguration( ..., normalization=norm, )
events
as a setting to p.viz.nb
's misfits
, to visualize
misfits only for selected events. If not passed, to previous default of all
events will be used. Also allows silent query for p.simulations.query()
,
useful when calling query often and keeping output down.fast_unsafe
as a setting to p.viz.nb
's shotgather
and
custom_gather
, to visualize data not on the most dense but on the most sparse
time axis of the selected datasets. This might alias data.receiver_sampling_rate_in_time_steps
of a simulation configuration. By
default, Salvus will now drop the final data point if it does not lie on the
regular time grid of the subsampled simulation output, e.g. with 100 time
steps, but with a receiver sampling interval of 11. These are the sampling
rates that can be set via:sn.WaveformSimulationConfiguration( receiver_sampling_rate_in_time_steps=11, ... )
EventData.get_waveform_data(..., enforce_regular_time_grid=False)
, but note
that the resulting data will only be regularly spaced for all but the last
sample.get_time_axis_from_meta_json
now optionally allows passing which
output type the time axis needs to be retrieved for, defaulting to the
simulation's time axis (no subsampling).extra_output_configuration
.p.simulations.get_simulation_output_directories( simulation_configuration="my_simulation", events=p.events.list() )
salvus.material.from_params()
and
salvus.material.from_dataset()
methods that will automatically find and
initialize the appropriate material class.p.simulations.run()
. This function waits for all simulations to finish before
returning and enables a new way of parallelism.sn.SiteConfig
, this new
function can also run several simulations in parallel on a local (or ssh)
system, which can give a substantial speed up for use cases with many small
simulations.task_chain_config = TaskChainSiteConfig( site_name="test_site", number_of_parallel_workers=5, ranks_per_salvus_simulation=2, max_threads_per_worker=2, shared_file_system=True, ) p.simulations.run( events=p.events.list(), simulation_configuration="my_simulation", site_config=task_chain_config, )
p.simulations.launch()
for those
instead.libmpicxx
has been removed. Salvus runs with any
ABI-compatible MPI implementation such as MPICH and only the C-interface and
library is required at runtime.libmpicxx
.salvus-cli status
that would throw an index error when trying
to query empty job arrays from the database.%load_ext salvus %salvus_snippets
sn.simple_mesh.linear_solid.LinearSolid
has been removed and
superseded. The class sn.LinearSolids
can be used directly for a constant Q
approximation. Alternatively, and for more fine-grained control, the
coefficients of the standard linear solids (SLS) can be obtained using
salvus.material.attenuation.lsqr_fit_q_factor_model
.salvus.mesh.algorithms.unstructured_mesh_utils
: retain_unique_facets
,
uniquefy_side_sets
, get_internal_side_set_facets
,
disconnect_along_side_set
.p.inversions.get_misfits()
and p.entities.get_inverse_problem_configurations()
.ObsPy
. If the wurlitzer
library is installed, it will now
be used to suppress these warnings.EventData.get_wavefield_output()
method parsing outputs to
WavefieldOutput
objects.EventData.get_associated_salvus_job()
method returning a potentially
still existing Salvus job for the EventData
object.EventData.get_remote_extra_output_filenames()
method to get paths of
remote output files.EventData.download_extra_outputs()
method to download remote wavefield
output data.EventData.delete_associated_job_and_data()
method to delete potentially
still existing remote wavefield data.split_layered_model
: This function allows for a layered model to be split
into two based on conditions targeting the model's interfaces or materials.flood
: This function allows for a layer's material parameters to be
"flooded", or extruded, in the vertical direction.blend
: This function supports the blending of two materials, and currently
supports the averaging of constant parameters, as well as the linear or
cosine-taper-based blending of discrete models along their vertical coordinate.salvus.mesh.layered_meshing.utils
module.numpy>=1.26.0
and xarray>=2023.10
.extra_output_configuration
argument passed
to p.simulations.launch()
. It will detect if the simulations have
previously been run with different output settings and will optionally
overwrite existing results.ValueError
which was hard to recover
without manually removing the delinquent stations from the observed data.get_misfit_comparison_table()
, which would trigger an exception
when passing the same data for comparison multiple times.final_time_data
it is possible to output the primary fields
(displacement
, velocity
, phi
, phi_t
) at final time on an unstructured
mesh. The output file will thus be independent from the partition and number of
ranks used in the simulation.extra_output_configuration
in p.simulations.launch
:extra_output_configuration={ "final_time_data": {"fields": ["displacement"]}, },
simple_config
simulation object:w.output.final_time_data.format = "hdf5-minimal" w.output.final_time_data.fields = ["displacement"] w.output.final_time_data.filename = "final_time.h5"
shotgather
; it will no longer accept width, height and DPI, but
instead accepts a dataclass with those fields, or an existing axes, passed as
plot_using
.compute_max_distance_in_m()
function that computes the maximum
distance between any two points in a given set in Cartesian or spherical
coordinates..estimate_max_travel_distance_in_m()
method is available
for all domain object utilizing the new function. This is useful for example to
judge the approximate time waves will take to travel through a given domain.from salvus.mesh.tools.transforms import interpolate_mesh_to_mesh int_mesh = interpolate_mesh_to_mesh( ..., fields_to_interpolate=["custom"], )
EventBlockData
object in a
custom axes frame.pandas>=2.1.0
.ValueError
in
some cases when adding events through p.actions.seismology.add_asdf_file()
.layer
id, which increments
the layer id at the surface by one.ZeroDivisionError
if there were one or more events without windows in the
data selection.TypeError
for
constant traces.salvus-cli upgrade
command now works in cases where the upgrade changes
the initialization logic of Salvus.salvus.mesh.chunked_interface.create_mesh_chunkwise()
function is now
deprecated.start_time_in_seconds
and
end_time_in_seconds
can now be passed to the respective output groups
in a simulation.Waveform(...)
object. Times passed are always truncated
(floored) to the preceding time step.w = sn.simple_config.simulation.Waveform(mesh=m) # If all settings for the same type of boundary condition are the same, # the side sets will be merged. w.add_boundary_conditions([ sn.simple_config.boundary.HomogeneousDirichlet(side_sets=["x0"]), sn.simple_config.boundary.HomogeneousDirichlet(side_sets=["x1"]) ]) # However, using different settings for a boundary type will raise an error. w.add_boundary_conditions([ sn.simple_config.boundary.Absorbing( side_sets=["x0"], taper_amplitude=0.0, width_in_meters=0.0 ) sn.simple_config.boundary.Absorbing( side_sets=["x1"], taper_amplitude=1.0, width_in_meters=1.0 ) ])
event_origin_time
keyword argument. We believe this should not affect
any users, if it does: the error message is very clear and fixing it is
straight forward.WavefieldOutput
object to an xarray
.salvus.modules.source_inversion
submodule by using
one of the following functions:invert_wavelet
: Accepts the observed and synthetically generated data as
NumPy arrays and returns the inverted wavelet as a NumPy array.invert_wavelet_from_event_data
: Performs the same inversion as within
invert_wavelet
, but accepts EventData
objects instead of NumPy arrays.
This is generally the more convenient approach when used together with
SalvusProject.custom_commands
setting for Slurm sites to add custom
bash commands to a job script.mesh.adjust_side_sets.adjust_site_set
that allows for the
re-interpolation of a mesh's vertical side set to a new digital elevation model,
potentially at a higher order than originally used. This can be useful, for
instance, when changing frequency bands and taking advantage of the smaller mesh
size to better resolve topography, changing a mesh's model order, or
re-interpolating after adding local refinements. This re-interpolation also now
happens by default when using the layered meshing interface.p.simulations.launch( simulation_configuration="my_simulation", events=p.events.list(), ranks_per_job=2, site_name="local", extra_output_configuration={ "frequency_domain": { "frequencies": [10.0], "fields": ["phi"], "start_time_in_seconds": 1.0, "end_time_in_seconds": 1.1, } } )
salvus.utils.logging.log_timing()
context manager that logs the
time the context takes to execute.salvus.toolbox.helpers.wavefield_output
module. To use this, one simply needs
to pass "surface" as an output type when loading the wavefield output. Also
added the ability to drop dimensions from outputs, so 3-D surface outputs can be
visualized in 2-D planar plots (such as in matplotlib). This can be accessed by
calling the .drop(...)
function on a WavefieldOutput
instance.name_free_side_set
to salvus_mesh_utils
. This
function finds all mesh surfaces not currently assigned to a side set and either
a) creates a new side set with the name passed, assigning the found surfaces, or
b) appends to an existing side set of the same name if it already exists in the
mesh.salvus.toolbox.helpers.wavefield_output
to help
encapsulate and manipulate raw volumetric wavefield output from SalvusCompute.
Additional functions allows for the extraction of time-dependent wavefield data
from said files to regular grids in space and time.frequency-domain
for (time-dependent) volume and surface
data. In combination with the static frequency-domain output, this enables
storing the time-evolution of the discrete Fourier transform with the
polynomial degree of the SEM shape functions.job_script_shebang
setting for Slurm sites to allow
overwriting the job script shebang.apply_element_mask
, which can lead to significant
speed-ups for meshes that neither have nodal parameters nor layered topography.max_concurrent_chains
can be passed to the
TaskChainSiteConfig
to limit the maximum number of concurrent chains.
Note that when using more than one site, this parameter has to be consistent
for all sites in the current implementation.p.simulations.cancel( simulation_configuration="my_simulation", events=p.events.list() )
p.simulations.cancel_all()
will cancel all simulations from
the simulations store.AbsorbingBoundary
constructor.shutil.copyfile()
instead of shutil.copy()
and shutil.copy2()
to
avoid problems copying between file systems with and without permission bits.use_event_dependent_gradients
for the trust-region method.
When set to True
, only the accumulated gradient for all events is used, and
tasks of type TaskMisfitsAndSummedGradient
are issued instead of
TaskMisfitsAndGradients
.discontinuous_model_blocks
to the mapping function.
When used in combination with a homogeneous
scaling, this function allows
for the parameterization of piecewise constant models.ipywidgets>=8.0.0
. The changes are backward compatible with older
versions of ipywidgets
.control_group_events
in the constructor of an iteration.max_events_per_job_submission
for inversion action
component to limit the maximum number of events that are simultaneously
submitted per job to reduce the memory overhead.event_batch_selection
to the
InverseProblemConfiguration
, which allows to define an iteration-dependent
selection of events and/or control group to check the trial model only for a
subset of events.job_submission
settings can now optionally receive a TaskChainSiteConfig
to compute misfits in parallel, and to chain forward and adjoint simulations.omit_tasks_per_node
setting for Slurm sites.ntasks-per-node
to be omitted for both the #SBATCH
command as well as the call to srun
for the rare site where this is
necessary."EXTERNAL_DATA:raw_data | bandpass(1.0e3, 2.0e3) | normalize"
strain
, gradient-of-displacement
and
gradient-of-phi
as valid receiver fields in the misfit configuration and
related event data and event misfit objects.XXX
, XXY
, XYX
, XYY
XG0
, XG1
, XG2
, XG3
XXX
, XXY
, XXZ
, XYX
, XYY
, XYZ
, XZX
, XZY
, XZZ
XG0
, XG1
, XG2
, XG3
, XG4
, XG5
, XG6
, XG7
, XG8
p.viz.waveforms()
, which caused to function to fail with a
cryptic error message when data
was passed as a string.QKAPPA
as a material
parameter.SegyEvent
, but it should be straightforward to
upgrade.paramiko.SSHClient.connect()
to allow more fine tuning for some SSH connections.[sites.my_site.ssh_settings] hostname = "some_host" username = "some_user" [sites.my_site.ssh_settings.extra_paramiko_connect_arguments.disabled_algorithms] pubkeys = ["rsa-sha2-512", "rsa2-sha2-256"]
init-site
command now has a --verbose
flag to facilitate
debugging tricky connections:salvus-cli init-site my_site --verbose
mpirun_template
parameter for ssh
and local
site types that allows
full customization of the actual call to mpirun
in case it is necessary.[sites.my_site.site_specific] mpirun_template = "/custom/mpirun -machinefile ~/mf -n {RANKS}"
mpirun
executable with a non-standard
argument for all Salvus runs on that site. The {RANKS}
argument will be
filled in by Salvus with the number of ranks for each simulation.UnstructuredMeshSimulationConfiguration
objects are now properly recognized when trying to overwrite an existing
configuration of the same name.StructuredModel
to SalvusOpt to invert models
parameterized on a regular grid using xarray.Datasets.WaveformSimulationConfiguration
.Stats
tab of the iteration widget.WaveformSimulationConfiguration
. This is useful for Dirichlet-type
boundaries or absorbing boundaries of a
UnstructuredMeshSimulationConfiguration
.
Boundaries specified here will be applied in addition to ocean load and/or
absorbing boundaries specified as AbsorbingBoundaryParameters
in the
SimulationConfiguration
. A ValueError
is raised for duplicated conditions
on a side set.compute_misfits
, the simulation results are
considered corrupted and will be automatically deleted. This means that those
simulations will be resubmitted when calling compute_misfits
again.TaskChain
.StructuredGrid2D
, StructuredGrid3D
and Skeleton
classes and
replaced them with new MeshBlock
and MeshBlockCollection
classes.iterate()
and resume()
. Specifying site_name
,
ranks_per_job
and wall_time_in_seconds_per_job
is no longer
supported. Instead, the job_submission_settings
either need to be passed to
the constructor of the InverseProblemConfiguration
or by using
p.inversions.set_job_submission_configuration()
.p.simulations.query( simulation_configuration="my_simulation", misfit_configuration="my_misfit", wavefield_compression=sn.WavefieldCompression( forward_wavefield_sampling_interval=15 ), events=p.events.list(), )
wget https://mondaic.com/environment.yml -O ~/environment.yml conda env update -n salvus -f ~/environment.yml
TaskChain
workflow primitive that can be used to run multiple Salvus jobs
and/or Python scripts in a linear chain within a single local/ssh/HPC job.# Construct a forward simulation object. w_forward = sn.simple_config.simulation.Waveform( ..., store_adjoint_checkpoints=True ) # Construct an adjoint simulation object. w_adjoint = sn.simple_config.simulation.Waveform(...) ... # There is a new `PROMISE_FILE:` prefix to tell SalvusFlow that a file does # not exist yet but it will exist when the simulation is run. w_adjoint.adjoint.point_source_block = { "filename": "PROMISE_FILE:task_2_of_2/input/adjoint_source.h5", "groups": ["adjoint_sources"], } # Define a Python function that is run between forward and adjoint simulations # to generate the adjoint sources. def compute_adjoint_source( task_index: int, task_index_folder_map: typing.Dict[int, pathlib.Path] ): folder_forward = task_index_folder_map[task_index - 1] folder_adjoint = task_index_folder_map[task_index + 1] output_folder = folder_forward / "output" event = sn.EventData.from_output_folder(output_folder=output_folder) event_misfit = sn.EventMisfit( synthetic_event=event, misfit_function="L2_energy_no_observed_data", receiver_field="displacement", ) input_folder_adjoint = folder_adjoint / "input" event_misfit.write(input_folder_adjoint / "adjoint_source.h5") # Launch the task chain. It will serialize the Python function and launches # everything either locally or in Slurm/other supported systems. tc = sn.api.run_task_chain_async( site_name="local", tasks=[w_forward, compute_adjoint_source, w_adjoint], ranks=4, ) # Wait until it finishes. tc.wait()
mesh = p.simulations.get_mesh("sim") # Modify scaling parameters for all fields mesh.elemental_fields["VP"] = ... mesh.elemental_fields["RHO"] = ... m = Mapping( scaling=mesh, inversion_parameters=["M"], map_to_physical_parameters={"VP": "M", "RHO": "M"}, )
model.background.one_dimensional.FromBm
.sn.WavefieldCompression( forward_wavefield_sampling_interval=N )
N
during the
adjoint run.
forward_wavefield_sampling_interval=1
corresponds to no compression and is
equivalent to what was done prior to this release.forward_wavefield_sampling_interval=1
,
which is consistent with Salvus <= 0.11.47
.# Inversion actions: p.action.inversion.compute_misfits( ..., derived_job_config=sn.WavefieldCompression( forward_wavefield_sampling_interval=5 ), ... ) p.action.inversion.compute_gradients( ..., wavefield_compression=sn.WavefieldCompression( forward_wavefield_sampling_interval=5 ), ... ) p.action.inversion.sum_gradients( ..., wavefield_compression=sn.WavefieldCompression( forward_wavefield_sampling_interval=5 ), ... ) # InverseProblemConfiguration sn.InverseProblemConfiguration( ... wavefield_compression=sn.WavefieldCompression( forward_wavefield_sampling_interval=5 ), ... )
store_adjoint_checkpoints
in p.simulations.launch(...)
and
store_checkpoints
in p.actions.inversion.compute_misfits(...)
are
deprecated. Instead, usederived_job_config=WavefieldCompression( forward_wavefield_sampling_interval=N ),
derived_job_config=None
is the new default, which corresponds
to the deprecated store_adjoint_checkpoints=False
or
store_checkpoints=False
, respectively.compute_misfits
and compute_gradients
in p.action.inversion
are now
keyword-only functions. Additionally, we made the wavefield compression
settings mandatory arguments of a few lower-level functions, such as:p.misfits.get_gradient_filenames() p.simulations.get_adjoint_input_files() p.action.validation.validate_model_gradients()
TypeError: compute_gradients() needs keyword-only argument wavefield_compression
UnstructuredMeshSimulationConfiguration
.[[sites.site_name.site_specific.additional_qsub_arguments]] name = "pe" value = "mpi {NODES * TASKS_PER_NODE}" [[sites.site_name.site_specific.additional_qsub_arguments]] name = "l" value = "ngpus={int(ceil(RANKS / 12))}"
lat_lng_to_utm_crs()
helper routine to directly get a pyproj UTM
CRS object from a point given in latitude and longitude.salvus-cli
alias for the salvus-flow
command line call. This will
become the default at one point.p.misfits.compute_adjoint_source()
EventWindowAndWeightSet
in case
the window selection routine did not pick a single window for a chosen event.p.actions.seismology.get_events_with_windows("DSC_NAME")
UTMDomain
objects no longer require the ellipsoid to be passed.salvus-flow upgrade
and salvus-flow upgrade-site
for
double precision versions using the salvus_f64
binary.init-site
script will wait a bit longer for the stderr
in case it is
not yet available due to some synchronization delays on shared and parallel
file systems.UnstructuredMesh.extrude_side_set_2D()
method now also works as expected
for higher order meshes. Additionally fixed an issue with inverted elements
in certain scenarios.InverseProblemConfiguration
after
the project has been transferred to another python environment with a
different site configuration.Event( event_name