import matplotlib.pyplot as plt
import os
import salvus.namespace as sn
import wscan.instruments
SALVUS_FLOW_SITE_NAME = os.environ.get("SALVUS_FLOW_SITE_NAME", "local")
"matrix": This corresponds to the full matrix capture mode. Each
transducer is fired separately and the data is recorded by each of the
remaining transducers individually."linear": In linear mode, the transducers are fired row-wise, meaning
that, for each shot, four transducers fire simultaneously. The data is
then also recorded row-wise, meaning that the rows of 4 transducers are
parallel-connected and their signal is combined into a single output
channel.mira3d_pro_linear = wscan.instruments.get_instrument(
instrument_name="acs-a1040-mira-3d-pro",
acquisition_mode="linear",
)
mira3d_pro_matrix = wscan.instruments.get_instrument(
instrument_name="acs-a1040-mira-3d-pro",
acquisition_mode="matrix",
)mira3d_pro_linear.plot_acquisition_mode()receiver_ids = mira3d_pro_linear.receiver_ids
# 16 events in linear mode
assert len(receiver_ids) == 16
# IDs for the first event
receiver_ids[0]
# IDs for the last event. Note that in linear mode, the instrument records a
# total of 119 traces, as indicated by the last receiver id 119.
receiver_ids[-1][14, 28, 41, 53, 64, 74, 83, 91, 98, 104, 109, 113, 116, 118, 119]
"matrix" mode, which results in data with 2016
traces.mira3d_pro_matrix.plot_acquisition_mode()fig = plt.figure(figsize=(16, 4))
ax = plt.gca()
mira3d_pro_linear.plot(show_measurements=True, ax=ax)stf = mira3d_pro_linear.source_time_function.get_salvus_stf()
stf.plot()stf_filtered = mira3d_pro_linear.source_time_function.bandpass_filter(
min_frequency_in_hertz=40e3, max_frequency_in_hertz=60e3
).get_salvus_stf()
stf_filtered.plot()domain = sn.domain.dim3.BoxDomain(
x0=0.0,
x1=1.0,
y0=0.0,
y1=0.5,
z0=-0.2,
z1=0.0,
)
domain.plot()p = sn.Project.from_domain(
domain=domain, path="project_acs", load_if_exists=True
)orientation to it, which states where it is located and how it is oriented
(the instrument can be aribtrarily rotated in three dimensions). We specify
three properties:origin: The lower left corner of the instrument will be located at this
point.normal_vector: This defines how the instrument is oriented in space. (0, 0, 1) specifies an upward pointing normal vector, meaning the
transducers of the instrument lie in the xy-plane.rotation_angle_in_degrees: This will rotate the instrument by an
aribtrary angle around the normal vector (counter-clockwise).mira_on_block = mira3d_pro_linear.with_orientation(
wscan.instruments.Orientation(
origin=(
0.1,
0.1,
0.0,
), # x, y, and z coordinates of the lower right corner of the instrument
normal_vector=(0, 0, 1), # The normal vector of the instrument
rotation_angle_in_degrees=0, # The rotation of the instrument around the normal vector
)
)mira_on_block.plot(zoom_to_fit=True)get_salvus_events, which should give
us 16 events in linear mode.events = mira_on_block.get_salvus_events(batch_identifier="dummy")
assert len(events) == 16
for event in events:
p.add_to_project(event)p.events.list()['A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_000__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_001__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_002__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_003__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_004__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_005__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_006__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_007__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_008__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_009__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_010__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_011__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_012__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_013__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_014__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear', 'A1040 MIRA 3D PRO__batch_dummy__pos_000__shot_015__ori_x0.1000_y0.1000_z0.0000__nrm_x0.0000_y0.0000_z1.0000__rot_000__tx_mode_S-xline__acq_mode_linear']
EventConfiguration stating the
simulation time and the source time function that we will use. We simply use
the default source time function of the Mira device that we have already seen
above.ec = sn.EventConfiguration(
wavelet=stf,
waveform_simulation_configuration=sn.WaveformSimulationConfiguration(
# We simulate 0.3 milliseconds of data
end_time_in_seconds=0.3e-3
),
)# Define material properties of the concrete
concrete = sn.material.from_params(vp=1.7 * 2800, vs=2800.0, rho=2400.0)p += sn.SimulationConfiguration(
name="simulation",
elements_per_wavelength=1.25,
# Simulation is accurate up to 70 kHz
max_frequency_in_hertz=70_000,
model_configuration=concrete,
event_configuration=ec,
)
# Just show the first event of the firing sequence
p.viz.nb.simulation_setup("simulation", events=p.events.list()[0])[2026-04-21 11:32:42,083] INFO: Creating mesh. Hang on.
<salvus.flow.simple_config.simulation.waveform.Waveform object at 0x7c6c7bbfdc10>
p.simulations.launch(
simulation_configuration="simulation",
events=p.events.list(),
site_name=SALVUS_FLOW_SITE_NAME,
ranks_per_job=4,
)
p.simulations.query(block=True)[2026-04-21 11:32:46,444] INFO: Submitting job array with 16 jobs ...
True
ed_event_0 = p.waveforms.get("simulation", p.events.list()[0])[0]
# Get an xarray DataArray with the displacement data of this event
da = ed_event_0.get_waveform_data_xarray("displacement").unstack()X, so we only need to extract that. Note that this is independent on the
orientation of the instrument. Salvus will always internally rotate the data
so that the first component corresponds to the instrument's main axis.
Let's plot this data.da.sel(component="X").plot()
plt.gca().set_ylim(0.0, 0.3e-3)(0.0, 0.0003)
event_id = 6
ed_event_6 = p.waveforms.get("simulation", p.events.list()[event_id])[0]
# Get an xarray DataArray with the displacement data of this event
ed_event_6.get_waveform_data_xarray("displacement").unstack().sel(
component="X"
).plot()
plt.gca().set_ylim(0.0, 0.3e-3)(0.0, 0.0003)