import salvus.namespace as sn
import sympy as sp
import numpy as np
import numpy.typing as nptmesh = sn.simple_mesh.CartesianHomogeneousAcoustic2D(
vp=1, rho=1, x_max=1, y_max=1, max_frequency=1
).create_mesh()
mesh.change_tensor_order(7)
mesh<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x78b317576450>
mesh.connectivity
reshapes it to an elemental nodal field: (nelem, nodes_per_element).points = mesh.points
def v(x, y):
# y is unused
return np.sin(2 * np.pi * x)
nodal_velocity = v(*points.T)
elemental_nodal_velocity = nodal_velocity[mesh.connectivity]
mesh.elemental_fields.clear()
mesh.attach_field("VP", elemental_nodal_velocity)
mesh<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x78b317576450>
naive_average = elemental_nodal_velocity.mean(axis=-1)
print("Naive average (all elements):", naive_average)Naive average (all elements): [ 0.43620842 -0.43620842 0.43620842 -0.43620842]
x = sp.Symbol("x")
v_sp = sp.sin(2 * sp.pi * x)
element_length = 1 / (mesh.nelem**0.5)
area = element_length**2
average_analytical = (
sp.integrate(v_sp, (x, 0, element_length)) * element_length
) / area
print(f"Analytical average (element 0): {float(average_analytical):.6f}")
print(
f"Element 0 naive vs analytical: {naive_average[0]:.6f} vs "
f"{float(average_analytical):.6f}"
)Analytical average (element 0): 0.636620 Element 0 naive vs analytical: 0.436208 vs 0.636620
(nelem, nodes_per_element):mm weights are identical.mm = mesh.get_mass_matrix()
mesh.elemental_fields.clear()
mesh.attach_field("mass_matrix", mm)
mesh<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x78b317576450>
print(
"Mass matrix shape:",
mm.shape,
"| ENF shape:",
elemental_nodal_velocity.shape,
)Mass matrix shape: (4, 64) | ENF shape: (4, 64)
VP_elemental = (mm * elemental_nodal_velocity).sum(axis=-1) / mm.sum(axis=-1)
print("Quadrature (SEM) average (all elements):", VP_elemental)
print(
f"Element 0 weighted vs analytical: {VP_elemental[0]:.6f} vs {float(average_analytical):.6f}"
)Quadrature (SEM) average (all elements): [ 0.63661977 -0.63661977 0.63661977 -0.63661977] Element 0 weighted vs analytical: 0.636620 vs 0.636620
def average_enf_to_ef(
enf: npt.NDArray, mesh: sn.UnstructuredMesh
) -> npt.NDArray:
"""
Convert an elemental nodal field (Part 2) into an elemental field using
the lumped mass matrix weights.
"""
mm = mesh.get_mass_matrix()
return (mm * enf).sum(axis=-1) / mm.sum(axis=-1)
average_enf_to_ef(elemental_nodal_velocity, mesh)array([ 0.63661977, -0.63661977, 0.63661977, -0.63661977])