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 unstructured_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="event", sources=[point_src1, point_src2], receivers=receivers, ) ... EventConfiguration( waveform_simulation_configuration=..., wavelet=[ simple_config.stf.Ricker(center_frequency=1.0), simple_config.stf.Ricker(center_frequency=2.0), ], )
int
instead of float
types.p.viz.shotgather( data="simulation", event="event", receiver_field="phi", component="A" )
extra_output_configuration
to the launch function.p.simulations.launch( ranks_per_job=4, site_name="local", events=p.events.list(), simulation_configuration="simulation", extra_output_configuration={ "frequency_domain": { "fields": ["displacement"], "frequencies": [1.0, 2.0, 3.0], } }, )
event_misfit = EventMisfit( ..., # Optionally downsample to the given number of npts. max_samples_for_misfit_computation=max_samples_for_misfit_computation )
mc = MisfitConfiguration( ..., # Optionally downsample to the given number of npts. max_samples_for_misfit_computation=max_samples_for_misfit_computation
from salvus.modules.near_surface.processing import convert_point_to_line_source new_st = convert_point_to_line_source( st=st, source_coordinates=[22.0, 1.0], receiver_coordinates=[132.0, 0.0], transform_type="single_velocity_exact", velocity_m_s=550.0 )
import numpy as np from salvus.modules.near_surface.processing import geophone_response frequencies = np.logspace(0.1, 2, 2000) response = geophone_response.compute_geophone_response( frequencies=frequencies, geophone_frequency=4.5, damping_ratio=0.4, calibration_factor=11.2, ) geophone_response.plot_response(frequencies=frequencies, response=response)
L. Métivier, R. Brossier, Q. Mérigot, and E. Oudet. 2019."A graph space optimal transport distance as a generalization of Lp distances: application to a seismic imaging inverse problem"Inverse Problems, Volume 35, Number 8, https://doi.org/10.1088/1361-6420/ab206f
"graph_space_optimal_tranport"
everywhere Salvus
accepts misfit functionals. The one tuning parameters is the
"max_expected_time_shift"
which, as the name implies, should be set to the
maximum expected time shift in seconds for individual wiggles between observed
and synthetic data."EXTERNAL_DATA:raw_data | bandpass(1.0, 2.0) | normalize"
as the data name.time_shift({SHIFT})
normalize
scale({FACTOR})
flip
bandpass({FREQ_MIN}, {FREQ_MAX}[, zerophase][, corners={CORNERS}])
p.viz.shotgather( data=[ "PROCESSED_DATA:corrected_data_10_15_hz | normalize", "SYNTHETIC_DATA:starting_model_10_15_hz | normalize", ], event="shot_049", receiver_field="velocity", component="Y", sort_by=lambda r: r.location[0] )