xarray
Python
packages into our notebook.%matplotlib inline
%config Completer.use_jedi = False
import numpy as np
import os
import shutil
import pathlib
import requests
import time
import xarray as xr
import salvus.namespace as sn
from salvus.namespace import simple_config as sc
SALVUS_FLOW_SITE_NAME = os.environ.get("SITE_NAME", "local")
SIMULATION_MAX_FREQUENCY = 5.0
# Download Marmousi model ~150 MB.
target_path = "elastic-marmousi-model.tar.gz"
if not pathlib.Path(target_path).exists():
url = (
"https://s3.amazonaws.com/open.source.geoscience/open_data"
"/elastic-marmousi/elastic-marmousi-model.tar.gz"
)
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(target_path, "wb") as f:
f.write(response.raw.read())
shutil.unpack_archive(target_path)
xarray
as an intermediary to encapsulate both the model parameters and geometry. More info on xarray
, including extensive documentation, can be found here. Reading our Marmousi model into an xarray.Dataset
is trivial, and the process can be inspected by opening the acompanying getting_started_tools.py
file.xarray
dataset with the following information# Read and plot marmousi model.
model_directory = pathlib.Path("./elastic-marmousi-model/model")
marmousi = sn.toolbox.read_elastic_marmousi(
model_directory, ["VP", "RHO", "VS"]
)
xarray.Dataset
object is the ability to quickly plot the contained data. Let's do this below as a way to quickly QC that the model reading went correctly.marmousi["VP"].transpose("y", "x").plot(aspect="auto", figsize=(15, 5))
marmousi["VS"].transpose("y", "x").plot(aspect="auto", figsize=(15, 5))
marmousi["RHO"].transpose("y", "x").plot(aspect="auto", figsize=(15, 5))
<matplotlib.collections.QuadMesh at 0x79f43df3cfd0>
marmousi_elastic = marmousi.where(marmousi["y"] <= 3500 - 37 * 12.5, drop=True)
marmousi_elastic["VP"].transpose("y", "x").plot(aspect="auto", figsize=(15, 5))
marmousi_elastic["VS"].transpose("y", "x").plot(aspect="auto", figsize=(15, 5))
marmousi_elastic["RHO"].transpose("y", "x").plot(
aspect="auto", figsize=(15, 5)
)
<matplotlib.collections.QuadMesh at 0x79f43f1c5450>
from_volume_model
constructor to initialize our project.project_folder = pathlib.Path("project")
# Initialize a volumetric model from an xarray dataset.
mt = sn.model.volume.cartesian.GenericModel(
name="marmousi_elastic", data=marmousi_elastic
)
# Create a project if it does not yet exist or load it from disk.
p = sn.Project.from_volume_model(
path=project_folder, volume_model=mt, load_if_exists=True
)
p.viz.nb.domain()
marmousi = sn.toolbox.read_elastic_marmousi(model_directory, ["VP", "RHO"])
marmousi_acoustic = marmousi.where(
marmousi["y"] <= 3500 - 37 * 12.5, drop=True
)
p.add_to_project(
sn.model.volume.cartesian.GenericModel(
name="marmousi_acoustic", data=marmousi_acoustic
)
)
Events
in SalvusProject are containers to store waveform data. They are associated with sources and receivers.Vector
or MomentTensor
) the simulation would give an error explaining that the given source type could not be attached to any element.source = sn.simple_config.source.cartesian.ScalarPoint2D(
x=8500.0, y=3490.0, f=1
)
SideSetHorizontalPointCollection2D
receiver type. This allows us to choose a side-set (here, "y1"
), and to place an array of receivers a certain distance from this side-set. In this case we choose to place the receivers righ at the ocean bottom. We also need to specify an array of dynamic fields to record. Here we choose pressure, which is equal to "phi_tt"
.receivers = (
sn.simple_config.receiver.cartesian.SideSetHorizontalPointCollection2D(
x=np.linspace(0.0, 17000.0, 1000),
offset=0.0,
station_code="xx",
side_set_name="y1",
fields=["phi_tt"],
)
)
p.events.add(
sn.EventCollection.from_sources(sources=source, receivers=receivers),
check_consistency_with_domain=False,
)
end_time_in_seconds
.
We could additionally provide parameters like time_step_in_seconds
and start_time_in_seconds
. If they are not set, Salvus will choose sensible defaults for all three of these parameters. The start and end times will default to the values needed to smoothly simulate the entire source wavelet, and the time step will be computed inside Salvus as per the CFL criterion. Here we set the end time to 8 seconds, and keep the rest of the parameters at their default values.wsc = sn.WaveformSimulationConfiguration(end_time_in_seconds=8.0)
EventConfiguration
, which brings together the spatial and temporal aspects of our simulation.
We will be using a Ricker wavelet for this simulation, with a center frequency of half the maximum frequency we've set for the mesh.wavelet = sn.simple_config.stf.Ricker(
center_frequency=SIMULATION_MAX_FREQUENCY / 2.0
)
ec = sn.EventConfiguration(
waveform_simulation_configuration=wsc, wavelet=wavelet
)
x = np.linspace(-1e6, 1e6, 10)
y = 37 * 12.5 * np.ones_like(x)
ds = xr.Dataset(
data_vars={"dem": (["x"], y, {"reference_elevation": 0.0})},
coords={"x": x},
)
p.add_to_project(
sn.bathymetry.cartesian.OceanLayer(
name="ocean",
data=ds,
reference_elevation=0.0,
ocean_surface_datum=3500.0,
),
overwrite=True,
)
# Define coupled Clayton-Enqist / Kosloff
# damping boundaries at the the remaining side-sets.
abc = sn.AbsorbingBoundaryParameters(
reference_velocity=2000.0,
number_of_wavelengths=3.5,
reference_frequency=wavelet.center_frequency,
)
p.add_to_project(
sn.SimulationConfiguration(
name="marmousi_elastic",
event_configuration=ec,
absorbing_boundaries=abc,
elements_per_wavelength=1.0,
tensor_order=4,
max_frequency_in_hertz=float(SIMULATION_MAX_FREQUENCY),
model_configuration=sn.ModelConfiguration(
background_model=None, volume_models="marmousi_elastic"
),
bathymetry_configuration=sn.BathymetryConfiguration("ocean"),
),
overwrite=False,
)
p.viz.nb.simulation_setup("marmousi_elastic", events=p.events.list())
[2024-11-15 13:15:08,633] INFO: Creating mesh. Hang on.
<salvus.flow.simple_config.simulation.waveform.Waveform object at 0x79f43e490810>
p.add_to_project(
sn.SimulationConfiguration(
name="marmousi_acoustic",
event_configuration=ec,
absorbing_boundaries=abc,
elements_per_wavelength=1.0,
tensor_order=4,
max_frequency_in_hertz=float(SIMULATION_MAX_FREQUENCY),
model_configuration=sn.ModelConfiguration(
background_model=None, volume_models="marmousi_acoustic"
),
bathymetry_configuration=sn.BathymetryConfiguration("ocean"),
),
overwrite=False,
)
p.viz.nb.simulation_setup("marmousi_acoustic", events=p.events.list())
[2024-11-15 13:15:19,748] INFO: Creating mesh. Hang on.
<salvus.flow.simple_config.simulation.waveform.Waveform object at 0x79f43eafea90>
extra_output_configuration
show how to specify volumetric (2- or 3-D) output for wavefield visualization and analysis. We don't output these by default because the file sizes involved can be quite large (in this case, the parameters below will produce 375 MB of output). If your machine has the capacity, feel free to comment the following in to generate time-varying volumetric output which can be visualized in Paraview.for sim in ["marmousi_elastic", "marmousi_acoustic"]:
p.simulations.launch(
events=p.events.list(),
site_name=SALVUS_FLOW_SITE_NAME,
ranks_per_job=4,
simulation_configuration=sim,
# # uncomment for volume output
# extra_output_configuration={
# "volume_data": {
# "sampling_interval_in_time_steps": 100,
# "fields": ["phi_tt", "stress"],
# },
# }
)
[2024-11-15 13:15:23,003] INFO: Submitting job ... Uploading 1 files... 🚀 Submitted job_2411151315361247_e365797095@local
[2024-11-15 13:15:25,341] INFO: Submitting job ... Uploading 1 files... 🚀 Submitted job_2411151315440829_f90dbef145@local
query()
. This will show update the progress info and wait until we can safely continue with the simulated data. If SalvusProject detects that the simulations have already been run and the results have been stored, the next cell is a no-op.p.simulations.query(block=True)
True
p.waveforms.get(data_name="marmousi_elastic", events=p.events.list())[0].plot(
component="A",
receiver_field="phi_tt",
plot_types=["shotgather", "wiggles"],
)
p.waveforms.get(data_name="marmousi_acoustic", events=p.events.list())[0].plot(
component="A",
receiver_field="phi_tt",
plot_types=["shotgather", "wiggles"],
)