import os
import matplotlib.pyplot as plt
import numpy as np
import salvus.namespace as sn
from salvus.mesh.simple_mesh import rho_from_gardners, vs_from_poisson
SALVUS_FLOW_SITE_NAME = os.environ.get("SITE_NAME", "local")
x_min = 0.0
x_max = 5000.0
get_interpolating_splines
function from SalvusToolbox
. This function
requires a set of interpolation points which can be used to anchor the
splines, and these points in turn are defined by a set of locations in
and . In the cell below we define a series of discontinuities by specifying
their interpolation points in each coordiante. We multiply by here simply for brevity.layers_x = [
np.array([0.0, 0.2, 1.0, 2.0, 3.0, 4.0, 4.8, 5.0]) * 1000,
np.array([0.0, 0.2, 1.0, 2.0, 3.0, 4.0, 4.8, 5.0]) * 1000,
np.array([0.0, 0.2, 1.0, 2.0, 3.0, 4.0, 4.8, 5.0]) * 1000,
np.array([0.0, 1.5, 3.5, 5.0]) * 1000,
np.array([0.0, 2.5, 5.0]) * 1000,
]
layers_y = [
np.array([2.0, 2.0, 1.9, 1.7, 2.0, 2.1, 2.0, 2.0]) * 1000,
np.array([1.6, 1.6, 1.5, 1.4, 1.3, 1.4, 1.5, 1.5]) * 1000,
np.array([0.5, 0.5, 0.7, 0.6, 1.1, 0.9, 1.2, 1.2]) * 1000,
np.array([0.2, 0.2, 0.4, 0.4]) * 1000,
np.array([0.0, 0.0, 0.0]) * 1000,
]
# Define p-velocities.
vp = np.array([2000.0, 2500.0, 2800.0, 3200.0])
# Compute vs and rho.
vs = vs_from_poisson(vp)
rho = rho_from_gardners(vp)
get_interpolating_splines
function also accepts an array of interpolations methods. Internally the function uses interp1d
from Scipy, so please check out the documentation here for a complete list of interpolation styles.interpolation_styles = [
"quadratic",
"quadratic",
"quadratic",
"linear",
"linear",
]
splines = sn.toolbox.get_interpolating_splines(
layers_x, layers_y, kind=interpolation_styles
)
# Plot the interfaces.
f = plt.figure(figsize=(10, 5))
x_plot = np.linspace(x_min, x_max)
for top, bot in splines:
plt.plot(x_plot, top(x_plot))
plt.plot(x_plot, bot(x_plot))
plt.xlabel("x (m)")
plt.ylabel("y (m)")
plt.title("Interfaces")
Text(0.5, 1.0, 'Interfaces')
# Maximum frequency to resolve with elements_per_wavelength.
max_frequency = 20.0
# Generate the mesh
mesh, bnd = sn.toolbox.generate_mesh_from_splines_2d(
x_min=0,
x_max=x_max,
splines=splines,
elements_per_wavelength=2,
maximum_frequency=max_frequency,
use_refinements=True,
slowest_velocities=vs,
absorbing_boundaries=(["x0", "x1", "y0"], 10.0),
)
region
attached to it, and we'll use this variable later on to track which region is which when we're attaching parameters. To sum the mesh regions into one continuous mesh all we need to do is simply call the sum
operation on the array of sub-meshes as below...mesh = np.sum(mesh)
# Add info about absorbing boundaries
mesh.attach_global_variable("max_dist_ABC", bnd)
mesh.attach_global_variable("ABC_side_sets", ", ".join(["x0", "x1", "y0"]))
mesh.attach_global_variable("ABC_vel", min(vs))
mesh.attach_global_variable("ABC_freq", max_frequency / 2.0)
mesh.attach_global_variable("ABC_nwave", 5.0)
mesh # Visualize the mesh.
<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x7607aec80310>
nodes = mesh.get_element_nodes()[:, :, 0]
vp_a, vs_a, ro_a = np.ones((3, *nodes.shape))
for _i, (vp_val, vs_val, ro_val) in enumerate(zip(vp, vs, rho)):
# Find which elements are in a given region.
idx = np.where(mesh.elemental_fields["region"] == _i)
# Set parameters in that region to a constant value.
vp_a[idx] = vp_val
vs_a[idx] = vs_val
ro_a[idx] = ro_val
# Attach parameters.
for k, v in zip(["VP", "VS", "RHO"], [vp_a, vs_a, ro_a]):
mesh.attach_field(k, v)
# Attach acoustic / elastic flag.
mesh = sn.toolbox.detect_fluid(mesh)
mesh
<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x7607aec80310>
SideSetVectorPoint2D
class. Finally, we run a quick simulation and visualize the result in a shotgather.p = sn.Project.from_domain(
path="project",
domain=sn.domain.dim2.BoxDomain(x0=x_min, x1=x_max, y0=0.0, y1=2700.0),
load_if_exists=True,
)
p.viz.nb.domain()
# Location of source (this will snap to the closet side-set).
loc = [x_max / 2, 2500.0]
# Create the source.
source = sn.simple_config.source.cartesian.SideSetVectorPoint2D(
fx=1,
fy=1,
point=loc,
direction="y",
side_set_name="y1",
)
receivers = (
sn.simple_config.receiver.cartesian.SideSetHorizontalPointCollection2D(
x=np.linspace(x_min, x_max, 1000),
offset=-1.0,
side_set_name="y1",
station_code="xx",
fields=["velocity", "strain"],
)
)
p.add_to_project(
sn.Event(event_name="event", sources=source, receivers=receivers)
)
UnstructuredMeshSimulationConfiguration
to the project. Note that in this case it is the user's responsibility to ensure the mesh is accurate up to the desired frequency content of the source, and that boundary conditions have been properly set already.EventConfiguration
is the same as for the ordinary SimulationConfiguration
. After adding the UnstructuredMeshSimulationConfiguration
to the project, it works like a simulation configuration, and we can refer to its name to launch simulations or query data.if "sim" not in p.simulations.list():
p.add_to_project(
sn.UnstructuredMeshSimulationConfiguration(
name="sim",
unstructured_mesh=mesh,
event_configuration=sn.EventConfiguration(
wavelet=sn.simple_config.stf.Ricker(
center_frequency=max_frequency / 2
),
waveform_simulation_configuration=sn.WaveformSimulationConfiguration(
end_time_in_seconds=2.0
),
),
),
)
p.viz.nb.simulation_setup("sim", events=["event"])
<salvus.flow.simple_config.simulation.waveform.Waveform object at 0x7607a1c751d0>
launch
and submit the simulation.p.simulations.launch(
simulation_configuration="sim",
events="event",
site_name=SALVUS_FLOW_SITE_NAME,
ranks_per_job=2,
)
p.simulations.query(block=True)
[2024-11-15 13:09:09,340] INFO: Submitting job ... Uploading 1 files... 🚀 Submitted job_2411151309663895_cd8d742f6b@local
True
waveform_data = p.waveforms.get(data_name="sim", events="event")[0]
X
and Y
.waveform_data.plot(
component="X",
receiver_field="velocity",
plot_types=["shotgather"],
)
waveform_data.plot(
component="Y",
receiver_field="velocity",
plot_types=["shotgather"],
)
0
, 1
, 2
) in Voigt order.waveform_data.plot(
component="0",
receiver_field="strain",
plot_types=["shotgather"],
)
waveform_data.plot(
component="1",
receiver_field="strain",
plot_types=["shotgather"],
)
waveform_data.plot(
component="2",
receiver_field="strain",
plot_types=["shotgather"],
)