salvus.flow.simple_config
.sim.output.frequency_domain.filename = "fft.h5" sim.output.frequency_domain.format = "hdf5" sim.output.frequency_domain.fields = ["phi"] sim.output.frequency_domain.frequencies = [50.0, 60.0, 70.0, 100.0]
FFT-Re_N
, FFT-Im_N
, where N
is the index of the frequency.%config Completer.use_jedi = False
import numpy as np
import pathlib
from salvus.mesh import simple_mesh
from salvus.mesh.unstructured_mesh import UnstructuredMesh
from salvus.flow import simple_config as sc
from salvus.flow import api
vp = 1500.0
rho = 1000.0
omega = 500000.0
time_steps_per_cycle = 256
propagation_length_in_meters = 0.04
ncycles_buffer = 1
cycle_in_seconds = 1 / omega
dt = cycle_in_seconds / time_steps_per_cycle
ncycles = int(omega * propagation_length_in_meters / vp)
ncycles_total = ncycles + ncycles_buffer
mesh = simple_mesh.basic_mesh.SphericalHomogeneousAcoustic2D(
vp=vp,
rho=rho,
radius=0.03,
max_frequency=omega,
elements_per_wavelength=6,
tensor_order=4,
).create_mesh()
def create_stf(ncycles_total, time_steps_per_cycle, dt, plot=False):
nt = time_steps_per_cycle * ncycles_total
nt_buffer = 3 * time_steps_per_cycle
taper = np.ones([nt])
t = np.linspace(0, 1, nt_buffer)
taper[:nt_buffer] = np.tanh((t - 0.5) * 2.0 * np.pi) / 2.0 + 0.5
stf = sc.stf.Custom.from_array(
taper
* np.sin(
np.linspace(
0,
ncycles_total * 2 * np.pi,
time_steps_per_cycle * ncycles_total,
)
),
sampling_rate_in_hertz=1 / dt,
start_time_in_seconds=0.0,
)
if plot:
stf.plot()
return stf
stf = create_stf(ncycles_total, time_steps_per_cycle, dt, plot=True)
srcs = [
sc.source.cartesian.ScalarPoint2D(
x=0.0,
y=0.0,
f=1.0,
source_time_function=stf,
)
]
w = sc.simulation.Waveform(mesh=mesh, sources=srcs)
w.physics.wave_equation.start_time_in_seconds = 0.0
w.physics.wave_equation.end_time_in_seconds = (
ncycles_total * time_steps_per_cycle * dt
)
w.physics.wave_equation.time_step_in_seconds = dt
bc = sc.boundary.Absorbing(
side_sets=["r1"], width_in_meters=0.0, taper_amplitude=1.0
)
w.add_boundary_conditions(bc)
w.output.frequency_domain.fields = ["phi"]
w.output.frequency_domain.filename = "fft.h5"
w.output.frequency_domain.frequencies = [omega, 2 * omega]
w.output.frequency_domain.format = "hdf5"
w.output.frequency_domain.start_time_in_seconds = (
ncycles * time_steps_per_cycle * dt
)
w.output.frequency_domain.end_time_in_seconds = (
(ncycles + 1) * time_steps_per_cycle - 1
) * dt
w.validate()
w
<salvus.flow.simple_config.simulation.waveform.Waveform object at 0x7a53adb1f810>
phi
and will record real and imaginary part at the specified frequencies of 500
and 1000
kHz.api.run(
input_file=w,
site_name="local",
ranks=4,
output_folder="out",
overwrite=True,
)
SalvusJob `job_2411201442041174_f8c1d405d3` running on `local` with 4 rank(s). Site information: * Salvus version: 2024.1.2 * Floating point size: 32
* Downloaded 28.3 MB of results to `out`. * Total run time: 19.12 seconds. * Pure simulation time: 18.66 seconds.
<salvus.flow.executors.salvus_job.SalvusJob at 0x7a53ac774190>
UnstructuredMesh
object or open the associated xdmf
file with Paraview.path = pathlib.Path("out", "fft.h5")
m = UnstructuredMesh.from_h5(path)
re = m.elemental_fields[f"FFT-Re_0"]
im = m.elemental_fields[f"FFT-Im_0"]
m.attach_field(f"p_amp", np.sqrt(re * re + im * im))
re = m.elemental_fields[f"FFT-Re_1"]
im = m.elemental_fields[f"FFT-Im_1"]
m.attach_field(f"p_amp_1", np.sqrt(re * re + im * im))
m = m.apply_element_mask(m.get_element_centroid_radius() > 0.005)
m = m.apply_element_mask(m.get_element_centroid_radius() < 0.02)
m
<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x7a53acd7d690>
# normalize fields
m.elemental_fields["p_amp_1"] /= np.max(m.elemental_fields["p_amp"])
m.elemental_fields["p_amp"] /= np.max(m.elemental_fields["p_amp"])
m.attach_field(
"distance", 1 / np.sqrt(np.linalg.norm(m.points[m.connectivity], axis=2))
)
m.elemental_fields["distance"] /= np.max(m.elemental_fields["distance"])
np.testing.assert_allclose(
m.elemental_fields["p_amp"], m.elemental_fields["distance"], rtol=1e-3
)
assert (m.elemental_fields["p_amp_1"] < 1e-3).all()