This documentation is not for the latest stable Salvus version.
import os
import numpy as np
import xarray as xr
import salvus.namespace as sn
SALVUS_FLOW_SITE_NAME = os.environ.get("SITE_NAME", "local")# Generate a ring with 5 scalar sources.
sources = sn.simple_config.source.cartesian.collections.ScalarPoint2DRing(
x=0, y=0, radius=0.09, count=5, f=1.0
)
events = []
# Use the same receiver ring for each but mask out the receivers close to each
# source.
for i, s in enumerate(sources._sources):
all_receivers = (
sn.simple_config.receiver.cartesian.collections.RingPoint2D(
x=0, y=0, radius=0.09, count=100, fields=["phi"]
)
)
receivers = [
r
for r in all_receivers._receivers
if np.sqrt(
(s.location[0] - r.location[0]) ** 2
+ (s.location[1] - r.location[1]) ** 2
)
> 0.03
]
events.append(
sn.Event(event_name=f"event_{i}", sources=s, receivers=receivers)
)def get_spherical_inclusion() -> xr.Dataset:
nx, ny = 200, 200
x = np.linspace(-0.1, +0.1, nx)
y = np.linspace(-0.1, +0.1, ny)
xx, yy = np.meshgrid(x, y, indexing="ij")
# Add 3 spherical inclusions
vp = 1500.0 * np.ones_like(xx)
rho = 980.0 * np.ones_like(xx)
mask = np.sqrt(xx**2 + yy**2) < 0.05
vp[mask] = 1480.0
rho[mask] = 1000.0
mask = np.sqrt(xx**2 + (yy - 0.025) ** 2) < 0.015
vp[mask] = 1550.0
rho[mask] = 1040.0
mask = np.sqrt(xx**2 + (yy + 0.025) ** 2) < 0.015
vp[mask] = 1460.0
rho[mask] = 1010.0
ds = xr.Dataset(
data_vars={
"vp": (["x", "y"], vp),
"rho": (["x", "y"], rho),
},
coords={"x": x, "y": y},
)
return ds
true_model = get_spherical_inclusion()
ax = true_model.vp.T.plot(figsize=(10, 6))
ax.axes.set_aspect("equal")# Use the volume model to set up the project.
volume_model = sn.model.volume.cartesian.GenericModel(
name="true_model", data=true_model
)
p = sn.Project.from_volume_model(
path="project_full_waveform_inversion",
volume_model=volume_model,
load_if_exists=True,
)
# Add the events.
for event in events:
p.add_to_project(event)
p.visualizations.notebook.domain()# Generate the data for the target model.
wsc = sn.WaveformSimulationConfiguration(end_time_in_seconds=0.00015)
ec = sn.EventConfiguration(
waveform_simulation_configuration=wsc,
wavelet=sn.simple_config.stf.Ricker(center_frequency=50000.0),
)
p += sn.SimulationConfiguration(
name="true_model_100kHz",
elements_per_wavelength=2,
tensor_order=4,
max_frequency_in_hertz=100000.0,
model_configuration=sn.ModelConfiguration(
background_model=None, volume_models="true_model"
),
event_configuration=ec,
)
p.simulations.launch(
simulation_configuration="true_model_100kHz",
events=p.events.list(),
site_name=SALVUS_FLOW_SITE_NAME,
ranks_per_job=1,
)
p.simulations.query(block=True)[2026-05-01 14:06:22,720] INFO: Creating mesh. Hang on. [2026-05-01 14:06:22,824] INFO: Submitting job array with 5 jobs ...
True
bm = sn.model.background.homogeneous.IsotropicAcoustic(vp=1500.0, rho=980.0)
mc = sn.ModelConfiguration(background_model=bm)
wsc = sn.WaveformSimulationConfiguration(end_time_in_seconds=0.00015)
p += sn.SimulationConfiguration(
name="initial_model",
elements_per_wavelength=2,
tensor_order=2,
max_frequency_in_hertz=100000.0,
model_configuration=mc,
event_configuration=sn.EventConfiguration(
waveform_simulation_configuration=wsc,
wavelet=sn.simple_config.stf.Ricker(center_frequency=50000.0),
),
)# The misfit configuration defines how synthetics are compared to observed data.
p += sn.MisfitConfiguration(
name="L2",
# Could be observed data. Here we compare to the synthetic target.
observed_data="true_model_100kHz",
# Salvus comes with a variety of misfit functions. You can
# also define your own.
misfit_function="L2",
# This is an acoustic simulation so we'll use recordings of phi.
receiver_field="phi",
)
# Now we define the actual inverse problem
p += sn.InverseProblemConfiguration(
name="my_inversion",
# Starting model.
prior_model="initial_model",
# The events to use.
events=p.events.list(),
# What parameters to invert for.
mapping=sn.Mapping(scaling="absolute", inversion_parameters=["VP", "RHO"]),
# The smoothing.
preconditioner=sn.ConstantSmoothing({"VP": 0.01, "RHO": 0.01}),
# The inversion method.
method=sn.TrustRegion(initial_trust_region_linf=10.0),
# The misfit configuration we defined above.
misfit_configuration="L2",
# Compress the forward wavefield by subsampling in time.
wavefield_compression=sn.WavefieldCompression(
forward_wavefield_sampling_interval=10
),
# Job submission settings.
job_submission=sn.SiteConfig(
site_name=SALVUS_FLOW_SITE_NAME, ranks_per_job=1
),
)[2026-05-01 14:06:26,911] INFO: Creating mesh. Hang on.
# Lastly we perform two iterations, and have a look at the results.
for i in range(2):
p.inversions.iterate(
inverse_problem_configuration="my_inversion",
timeout_in_seconds=360,
ping_interval_in_seconds=2,
delete_disposable_files="all",
)[2026-05-01 14:06:26,965] INFO: Adding new iteration #0. [2026-05-01 14:06:26,976] INFO: Resuming iteration #0. [2026-05-01 14:06:26,981] INFO: 1 new tasks have been issued. [2026-05-01 14:06:26,988] INFO: Processing task `misfit_and_gradient` [2026-05-01 14:06:27,364] INFO: Submitting job array with 5 jobs ... [2026-05-01 14:06:27,503] INFO: Launched simulations for 5 events. Please check again to see if they are finished. [2026-05-01 14:06:27,505] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:29,506] INFO: Processing task `misfit_and_gradient` [2026-05-01 14:06:29,617] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:31,618] INFO: Processing task `misfit_and_gradient` [2026-05-01 14:06:34,594] INFO: Submitting job array with 5 jobs ... [2026-05-01 14:06:34,658] INFO: Launched adjoint simulations for 5 events. Please check again to see if they are finished. [2026-05-01 14:06:34,659] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:36,660] INFO: Processing task `misfit_and_gradient` [2026-05-01 14:06:36,903] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:38,905] INFO: Processing task `misfit_and_gradient` [2026-05-01 14:06:39,508] INFO: Iteration 0: Number of events: 5 chi = 0.015278809366937164 ||g|| = 0.01996766071007446 pred = --- ared = --- norm_update = --- tr_radius = --- [2026-05-01 14:06:39,509] INFO: 1 new tasks have been issued. [2026-05-01 14:06:39,510] INFO: Processing task `preconditioner` [2026-05-01 14:06:39,606] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:41,619] INFO: Processing task `preconditioner` [2026-05-01 14:06:41,769] INFO: 1 new tasks have been issued. [2026-05-01 14:06:41,770] INFO: Processing task `misfit` [2026-05-01 14:06:41,818] INFO: Submitting job array with 5 jobs ... [2026-05-01 14:06:41,955] INFO: Launched simulations for 5 events. Please check again to see if they are finished. [2026-05-01 14:06:41,956] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:43,995] INFO: Processing task `misfit` [2026-05-01 14:06:44,069] INFO: Some tasks of iteration #0 are still running. Please check again later. [2026-05-01 14:06:46,097] INFO: Processing task `misfit` [2026-05-01 14:06:47,096] INFO: old misfit control group: 0.015278809366937162 new misfit control group: 0.005913131187795305 predicted reduction control group: -0.007919777905044612 actual reduction control group: -0.009365678179141856 5 out of 5 event(s) improved the misfit. [2026-05-01 14:06:47,097] INFO: Model update accepted. [2026-05-01 14:06:47,098] INFO: 1 new tasks have been issued. [2026-05-01 14:06:47,098] INFO: Processing task `finalize_iteration` [2026-05-01 14:06:47,139] INFO: ... searching for obsolete files in project_full_waveform_inversion/INVERSIONS/my_inversion/00000 [2026-05-01 14:06:47,151] INFO: Freed up 613.0 KB of space. [2026-05-01 14:06:47,158] INFO: Successfully completed iteration #0. [2026-05-01 14:06:47,160] INFO: Adding new iteration #1. [2026-05-01 14:06:47,167] INFO: Resuming iteration #1. [2026-05-01 14:06:47,168] INFO: 1 new tasks have been issued. [2026-05-01 14:06:47,169] INFO: Processing task `gradient` [2026-05-01 14:06:47,345] INFO: Submitting job array with 5 jobs ... [2026-05-01 14:06:47,426] INFO: Launched adjoint simulations for 5 events. Please check again to see if they are finished. [2026-05-01 14:06:47,427] INFO: Some tasks of iteration #1 are still running. Please check again later. [2026-05-01 14:06:49,429] INFO: Processing task `gradient` [2026-05-01 14:06:49,602] INFO: Some tasks of iteration #1 are still running. Please check again later. [2026-05-01 14:06:51,604] INFO: Processing task `gradient` [2026-05-01 14:06:52,196] INFO: Iteration 1: Number of events: 5 chi = 0.005913131187795306 ||g|| = 0.009340556997480898 pred = -0.007919777905044612 ared = -0.009365678179141856 norm_update = 0.8813270007035139 tr_radius = 0.8813270316788413 [2026-05-01 14:06:52,208] INFO: 1 new tasks have been issued. [2026-05-01 14:06:52,209] INFO: Processing task `preconditioner` [2026-05-01 14:06:52,294] INFO: Some tasks of iteration #1 are still running. Please check again later. [2026-05-01 14:06:54,304] INFO: Processing task `preconditioner` [2026-05-01 14:06:54,432] INFO: 1 new tasks have been issued. [2026-05-01 14:06:54,433] INFO: Processing task `misfit` [2026-05-01 14:06:54,484] INFO: Submitting job array with 5 jobs ... [2026-05-01 14:06:54,617] INFO: Launched simulations for 5 events. Please check again to see if they are finished. [2026-05-01 14:06:54,619] INFO: Some tasks of iteration #1 are still running. Please check again later. [2026-05-01 14:06:56,647] INFO: Processing task `misfit` [2026-05-01 14:06:56,773] INFO: Some tasks of iteration #1 are still running. Please check again later. [2026-05-01 14:06:58,799] INFO: Processing task `misfit` [2026-05-01 14:06:59,823] INFO: old misfit control group: 0.005913131187795305 new misfit control group: 0.004217192287619691 predicted reduction control group: -0.0008834066261442786 actual reduction control group: -0.0016959389001756139 5 out of 5 event(s) improved the misfit. [2026-05-01 14:06:59,823] INFO: Model update accepted. [2026-05-01 14:06:59,824] INFO: 1 new tasks have been issued. [2026-05-01 14:06:59,824] INFO: Processing task `finalize_iteration` [2026-05-01 14:06:59,888] INFO: ... searching for obsolete files in project_full_waveform_inversion/INVERSIONS/my_inversion/00001 [2026-05-01 14:07:00,005] INFO: Freed up 3.2 MB of space. [2026-05-01 14:07:00,005] INFO: Successfully completed iteration #1. [2026-05-01 14:07:00,008] INFO: Adding new iteration #2.
p.viz.nb.inversion(inverse_problem_configuration="my_inversion")