analytic
%matplotlib inline
%config Completer.use_jedi = False
import os
import numpy as np
import salvus.namespace as sn
# Number of processes SalvusCompute will run with.
# Get it from the environment or default to 4.
MPI_RANKS = int(os.environ.get("NUM_MPI_RANKS", 4))
# Choose on which site to run this.
SALVUS_FLOW_SITE_NAME = os.environ.get("SITE_NAME", "local")
def g(a, x):
return (x**a) * (1 - x) ** a
def h(a, x):
return a * (a - 1) * (1 - 2 * x) ** 2 - 2 * a * x * (1 - x)
def boundary_mask(p, tol=1e-15):
bmask_0 = p[:, :, 0] < tol
bmask_1 = p[:, :, 1] < tol
bmask_2 = p[:, :, 0] > 1 - tol
bmask_3 = p[:, :, 1] > 1 - tol
bnd_mask = bmask_0 + bmask_1 + bmask_2 + bmask_3
return bnd_mask
def right_hand_side(p, a=10):
x0 = p[:, :, 0]
x1 = p[:, :, 1]
u_00 = (2 ** (4 * a)) * g(a, x1) * g(a - 2, x0) * h(a, x0)
u_11 = (2 ** (4 * a)) * g(a, x0) * g(a - 2, x1) * h(a, x1)
rhs = -(u_00 + u_11)
assert np.max(np.abs(rhs[boundary_mask(p)])) < 1e-15
return rhs
def exact_solution(p, a=10):
x0 = p[:, :, 0]
x1 = p[:, :, 1]
u = (2 ** (4 * a)) * g(a, x0) * g(a, x1)
assert np.max(np.abs(u[boundary_mask(p)])) < 1e-15
return u
def create_mesh(dim, tensor_order, domain_size, frequency):
if dim == 2:
m = sn.simple_mesh.CartesianHomogeneousAcoustic2D(
vp=1.0,
rho=1.0,
x_max=domain_size,
y_max=domain_size,
max_frequency=frequency,
)
m.advanced.tensor_order = tensor_order
mesh = m.create_mesh()
elif dim == 3 and mode == "Cartesian":
m = sn.simple_mesh.CartesianHomogeneousAcoustic3D(
vp=1.0,
rho=1.0,
x_max=domain_size,
y_max=domain_size,
z_max=domain_size,
max_frequency=frequency,
)
m.advanced.tensor_order = tensor_order
mesh = m.create_mesh()
f = np.ones_like(mesh.elemental_fields["VP"])
del mesh.elemental_fields["VP"]
del mesh.elemental_fields["RHO"]
mesh.attach_field("M0", f)
mesh.attach_field("M1", f)
mesh.attach_field("rhs", right_hand_side(mesh.points[mesh.connectivity]))
mesh.attach_field("u_true", exact_solution(mesh.points[mesh.connectivity]))
return mesh
def simulate(mesh):
mesh.write_h5("mesh.h5")
sim = sn.simple_config.simulation.Poisson(mesh=mesh)
sim.domain.polynomial_order = mesh.shape_order
sim.physics.poisson_equation.right_hand_side.filename = "mesh.h5"
sim.physics.poisson_equation.right_hand_side.format = "hdf5"
sim.physics.poisson_equation.right_hand_side.field = "rhs"
sim.physics.poisson_equation.solution.filename = "solution.h5"
sim.physics.poisson_equation.solution.fields = ["solution", "residuals"]
boundaries = sn.simple_config.boundary.HomogeneousDirichlet(
side_sets=["x0", "x1", "y0", "y1"]
)
# Associate boundaries with our simulation.
sim.add_boundary_conditions(boundaries)
sim.solver.max_iterations = 8000
sim.solver.absolute_tolerance = 0.0
sim.solver.relative_tolerance = 1e-14
sim.solver.preconditioner = True
sim.validate()
sn.api.run(
input_file=sim,
site_name=SALVUS_FLOW_SITE_NAME,
output_folder="poisson_2d",
overwrite=True,
ranks=MPI_RANKS,
)
def analysis(mesh):
tensor_order = mesh.shape_order
if tensor_order == 1:
TOL = 1e-3
elif tensor_order == 2:
TOL = 1e-4
else:
TOL = 1e-5
solution = sn.UnstructuredMesh.from_h5("poisson_2d/solution.h5")
u_salvus = solution.elemental_fields["solution"]
u_analytic = mesh.elemental_fields["u_true"]
diff = u_salvus - u_analytic
u_max = u_salvus.max()
ref_max = u_analytic.max()
solution.attach_field("analytic_solution", u_analytic)
solution.attach_field("solution", u_salvus)
solution.attach_field("diff", diff)
print(
f"|| u ||_inf = {u_max}\n",
f"|| u_exact ||_inf = {ref_max}\n",
f"|| u - u_exact ||_inf = {np.abs(diff).max()}\n",
f"|| u - u_exact ||_inf / || u_exact ||_inf = {np.abs(diff).max() / ref_max}\n",
f"|| A u - f ||_inf = {np.abs(solution.elemental_fields['residuals']).max()}\n",
)
np.testing.assert_allclose(
u_salvus, u_analytic, rtol=1e-6, atol=ref_max * TOL
)
np.testing.assert_allclose(
solution.elemental_fields["residuals"], 0, rtol=1e-6, atol=1e-7
)
return solution
mesh = create_mesh(dim=2, tensor_order=1, domain_size=1.0, frequency=60.0)
simulate(mesh)
solution = analysis(mesh)
solution
SalvusJob `job_2411151454288043_a66e3217cf` running on `local` with 4 rank(s). Site information: * Salvus version: 2024.1.2 * Floating point size: 32
* Downloaded 1.8 MB of results to `poisson_2d`. * Total run time: 2.25 seconds. * Pure simulation time: 1.90 seconds. || u ||_inf = 1.0006816387176514 || u_exact ||_inf = 1.0 || u - u_exact ||_inf = 0.0006816387176513672 || u - u_exact ||_inf / || u_exact ||_inf = 0.0006816387176513672 || A u - f ||_inf = 5.195631702648029e-10
<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x7efc63774c90>
mesh = create_mesh(dim=2, tensor_order=2, domain_size=1.0, frequency=60.0)
simulate(mesh)
solution = analysis(mesh)
solution
SalvusJob `job_2411151454808726_eed7df0b3f` running on `local` with 4 rank(s). Site information: * Salvus version: 2024.1.2 * Floating point size: 32
* Downloaded 4.8 MB of results to `poisson_2d`. * Total run time: 1.17 seconds. * Pure simulation time: 0.95 seconds. || u ||_inf = 0.9999991655349731 || u_exact ||_inf = 1.0 || u - u_exact ||_inf = 1.3682777089130127e-06 || u - u_exact ||_inf / || u_exact ||_inf = 1.3682777089130127e-06 || A u - f ||_inf = 2.1958844464986527e-10
<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x7efc83887d90>
mesh = create_mesh(dim=2, tensor_order=4, domain_size=1.0, frequency=60.0)
simulate(mesh)
solution = analysis(mesh)
solution
SalvusJob `job_2411151454480173_ac986ce9a5` running on `local` with 4 rank(s). Site information: * Salvus version: 2024.1.2 * Floating point size: 32
* Downloaded 15.4 MB of results to `poisson_2d`. * Total run time: 2.57 seconds. * Pure simulation time: 2.20 seconds. || u ||_inf = 0.9999991655349731 || u_exact ||_inf = 1.0 || u - u_exact ||_inf = 2.7651493211822142e-06 || u - u_exact ||_inf / || u_exact ||_inf = 2.7651493211822142e-06 || A u - f ||_inf = 6.056074391969091e-11
<salvus.mesh.data_structures.unstructured_mesh.unstructured_mesh.UnstructuredMesh object at 0x7efc83c9ea50>