You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LinearEuler 2D/3D: variable background density via static nodal rho0 (#139)
* LinearEuler 2D/3D: variable background density via static nodal rho0
Promote the background density rho0 from a scalar model attribute to a
static per-node solution variable, mirroring the heterogeneous
sound-speed pattern from #138. This lets rho0 vary in space (e.g. across
material interfaces) alongside c, so heterogeneous media with both
density and sound-speed contrast are supported.
- 2D: nvar 5->6 (rho0 = variable 6), nstepped stays 4.
- 3D: nvar 6->7 (rho0 = variable 7); set nstepped=5 (previously unset,
relying only on zero flux/source to hold the auxiliary fields static).
- Interior flux, entropy_func, and the impedance-matched Riemann flux now
read per-node rho0. The Riemann solver uses per-side impedance
Z = rho0*c, so material interfaces (density and/or sound-speed jumps)
get the physically correct reflection/transmission; the reconstructed
density/momentum/pressure fluxes use the face-averaged rho0 and c^2 (the
same pragmatic treatment already used for c^2). The scalar rho0
attribute is retained as the reference value that fills the field in the
built-in initial conditions.
- GPU: fluxmethod/boundaryflux kernels read per-node rho0 and drop the
scalar rho0 argument; BC kernels copy rho0 through to the exterior
state (signatures now mirror the c handling in #138).
- Entropy stability: for piecewise-constant material regions aligned with
element boundaries, element interiors have grad(rho0)=grad(c)=0 (so the
flux-divergence form is exact) and the impedance-matched Riemann flux
carries the entropy-stable interface dissipation.
Also in this change:
- Add a CPU radiation BC to the 2D model (previously only the GPU backend
registered one); it zeros the acoustic perturbation and copies c and
rho0, mirroring the 3D model. Register it in AdditionalInit_LinearEuler2D_t.
- Add a PML entropy_func override so the PML variant keeps using its scalar
rho0 (its variable 6 is the auxiliary phi_rho, not rho0).
- Fix the 2D entropy_func s(3)*(3) typo (already fixed for 3D in #138).
- boneandmarrow example: stamp a per-material rho0 (muscle/bone/marrow)
alongside c, exercising a genuinely heterogeneous impedance field.
- planewave examples: set the new rho0 variable in the initial condition
and the prescribed boundary state.
- Update the 2D/3D model docs.
CPU (gfortran) build is clean and all 7 LinearEuler ctests pass;
boneandmarrow entropy is finite and monotonically decreasing. GPU kernels
to be validated by CI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: fix LinearEuler doc staleness after variable-density change
- 2D model: the base LinearEuler2D_t now registers a CPU radiation
handler (not GPU-only), so update the BC section accordingly.
- 3D model: remove two dead example links (spherical_soundwave_closeddomain
and planewave_propagation examples do not exist); keep the radiation
example, which is the only 3D example present.
- PML model: document the new entropy_func override and clarify that the
PML variant keeps rho0 as a scalar (its slot 6 is phi_rho, whereas the
parent model now carries rho0 as a per-node solution variable).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/Models/linear-euler-2d-model.md
+21-16Lines changed: 21 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,11 +18,12 @@ $$
18
18
u \\
19
19
v \\
20
20
p \\
21
-
c
21
+
c \\
22
+
\rho_0
22
23
\end{pmatrix}
23
24
$$
24
25
25
-
where $\rho$ is a density anomaly, referenced to the density $\rho_0$, $u$ and $v$ are the $x$ and $y$ components of the fluid velocity (respectively), $p$ is the pressure, and $c$ is the speed of sound. The sound speed is carried as a per-node solution variable so that it can vary in space (e.g. across material interfaces); its flux and source are identically zero, so $c$ is held fixed in time at each spatial location.
26
+
where $\rho$ is a density anomaly, referenced to the background density $\rho_0$, $u$ and $v$ are the $x$ and $y$ components of the fluid velocity (respectively), $p$ is the pressure, $c$ is the speed of sound, and $\rho_0$ is the background density. Both the sound speed $c$ and the background density $\rho_0$ are carried as per-node solution variables so that they can vary in space (e.g. across material interfaces); their flux and source are identically zero, so $c$ and $\rho_0$ are held fixed in time at each spatial location. This is entropy-stable for piecewise-constant material regions aligned with element boundaries: element interiors have $\nabla \rho_0 = \nabla c = 0$ (so the flux-divergence form is exact) and the impedance-matched Riemann flux handles the jumps at faces.
26
27
27
28
When we assume an ideal gas, and a motionless background state, the conservative fluxes are
28
29
@@ -33,6 +34,7 @@ $$
33
34
\frac{p}{\rho_0} \hat{x} \\
34
35
\frac{p}{\rho_0} \hat{y} \\
35
36
\rho_0 c^2 (u \hat{x} + v \hat{y}) \\
37
+
\vec{0} \\
36
38
\vec{0}
37
39
\end{pmatrix}
38
40
$$
@@ -50,7 +52,7 @@ $$
50
52
$$
51
53
52
54
## Implementation
53
-
The Linear Euler 2D model is implemented as a type extension of the [`DGModel2D` class](../ford/type/dgmodel2d_t.html). The [`LinearEuler2D_t` class](../ford/type/lineareuler2d_t.html)adds a parameter for the reference density $\rho_0$ and overrides `SetNumberOfVariables` (to declare `nvar = 5`), `SetMetadata`, `AdditionalInit`, `entropy_func`, `flux2d`, and `riemannflux2d`. The sound speed is no longer a scalar attribute of the model — it lives in `solution(:,:,:,5)` and can be set independently per node when initializing the simulation.
55
+
The Linear Euler 2D model is implemented as a type extension of the [`DGModel2D` class](../ford/type/dgmodel2d_t.html). The [`LinearEuler2D_t` class](../ford/type/lineareuler2d_t.html)keeps a scalar `rho0` attribute (used as the reference value that fills variable 6 in the built-in initial conditions) and overrides `SetNumberOfVariables` (to declare `nvar = 6` with `nstepped = 4`, so the last two variables are static), `SetMetadata`, `AdditionalInit`, `entropy_func`, `flux2d`, and `riemannflux2d`. The sound speed lives in `solution(:,:,:,5)` and the background density in `solution(:,:,:,6)`; both can be set independently per node when initializing the simulation.
54
56
55
57
### Riemann Solver
56
58
The `LinearEuler2D` class is defined using the conservative form of the conservation law. The interface flux is the exact upwind (Godunov) solver for the linearized acoustic system obtained by characteristic decomposition. The normal-flux Jacobian has eigenstructure
with acoustic impedance $Z = \rho_0 c$. This reduces correctly to Fresnel reflection / transmission across an impedance jump (e.g. $c_L \neq c_R$ at a material interface). The interface flux is then
72
+
with the per-side acoustic impedance $Z = \rho_0 c$, where each side uses its own background density $\rho_0$ and sound speed $c$. This reduces correctly to Fresnel reflection / transmission across an impedance jump (e.g. $Z_L \neq Z_R$ at a material interface, whether from a density jump, a sound-speed jump, or both). The interface flux is then
The pressure-flux uses an averaged $c^2$ as a pragmatic treatment of the non-conservative product $\rho_0c^2 \nabla \cdot \vec{v}$ at a face where $c$ jumps. The previously used local Lax-Friedrichs solver was found to over-dissipate the tangential and entropy modes and to fail to stably handle impedance mismatch at high polynomial order (aliasing instability at material interfaces), and has been replaced by the characteristic flux above. Details are in [`self_lineareuler2d_t.f90`](../ford/sourcefile/self_lineareuler2d_t.f90.html).
88
+
The reconstructed density/momentum/pressure fluxes use the face-averaged $\overline{\rho_0}$ and $\overline{c^2}$ as a pragmatic treatment of the non-conservative products $p/\rho_0$ and $\rho_0 c^2 \nabla \cdot \vec{v}$ at a face where the coefficients jump; the physically important reflection/transmission is carried exactly by the per-side impedances above. The previously used local Lax-Friedrichs solver was found to over-dissipate the tangential and entropy modes and to fail to stably handle impedance mismatch at high polynomial order (aliasing instability at material interfaces), and has been replaced by the characteristic flux above. Details are in [`self_lineareuler2d_t.f90`](../ford/sourcefile/self_lineareuler2d_t.f90.html).
85
89
86
90
### Boundary conditions
87
-
Boundary conditions are managed through the [extensible boundary-condition system](../Learning/BoundaryConditions.md). Each model registers its hyperbolic boundary conditions inside `AdditionalInit` by calling `hyperbolicBCs%RegisterBoundaryCondition(id, name, fn)`. `LinearEuler2D_t` registers a no-normal-flow handler out of the box; the GPU build additionally registers a radiation handler that runs on the device. Prescribed boundary conditions are registered by user code (or by an example subclass) so that the external state can be set as a function of position and time.
91
+
Boundary conditions are managed through the [extensible boundary-condition system](../Learning/BoundaryConditions.md). Each model registers its hyperbolic boundary conditions inside `AdditionalInit` by calling `hyperbolicBCs%RegisterBoundaryCondition(id, name, fn)`. `LinearEuler2D_t` registers both a no-normal-flow and a radiation handler out of the box (CPU); the GPU build overwrites both registrations with equivalent device kernels. Prescribed boundary conditions are registered by user code (or by an example subclass) so that the external state can be set as a function of position and time.
88
92
89
93
The built-in boundary identifiers used with the mesh generators are
90
94
91
95
*`SELF_BC_RADIATION` — set the external state on the boundary to zero in the Riemann solver (open/non-reflecting).
92
-
*`SELF_BC_NONORMALFLOW` — reflect the velocity vector about the boundary normal and prolong $\rho$, $p$, and $c$. This produces a reflecting (free-slip) wall and works for arbitrarily oriented normals.
96
+
*`SELF_BC_NONORMALFLOW` — reflect the velocity vector about the boundary normal and prolong $\rho$, $p$, $c$, and $\rho_0$. This produces a reflecting (free-slip) wall and works for arbitrarily oriented normals.
93
97
*`SELF_BC_PRESCRIBED` — use a user-registered handler to fill the external state.
94
98
95
99
As an example, when using the built-in structured mesh generator,
@@ -115,17 +119,18 @@ integer :: bcids(1:4)
115
119
See the [Structured Mesh documentation](../MeshGeneration/StructuredMesh.md) for details on using the `structuredmesh` procedure, and the [Boundary Condition System](../Learning/BoundaryConditions.md) for how to register new BC handlers.
116
120
117
121
!!! note
118
-
To set a prescribed state as a function of position and time, create a type-extension of `LinearEuler2D` and register a custom BC method against `SELF_BC_PRESCRIBED` from `AdditionalInit`. Remember that your handler must also fill `solution%extBoundary(:,:,:,5)` with the appropriate sound-speed value at the boundary — the planewave examples show this pattern.
122
+
To set a prescribed state as a function of position and time, create a type-extension of `LinearEuler2D` and register a custom BC method against `SELF_BC_PRESCRIBED` from `AdditionalInit`. Remember that your handler must also fill `solution%extBoundary(:,:,:,5)`(sound speed) and `solution%extBoundary(:,:,:,6)` (background density) with the appropriate values at the boundary — the planewave examples show this pattern.
119
123
120
-
### Setting the sound speed
124
+
### Setting the sound speed and background density
121
125
122
-
Because $c$ is a solution variable, you initialize it the same way you initialize $\rho$, $u$, $v$, and $p$:
126
+
Because $c$ and $\rho_0$ are solution variables, you initialize them the same way you initialize $\rho$, $u$, $v$, and $p$:
For a uniform background, set every node to the same constant. For a piecewise-constant medium (e.g. bone embedded in marrow), assign the local material's $c$. The `SphericalSoundWave` initializer takes the (uniform) sound speed as an explicit argument:
133
+
For a uniform background, set every node to the same constants. For a piecewise-constant medium (e.g. bone embedded in marrow), assign the local material's $c$ and $\rho_0$. The `SphericalSoundWave` initializer takes the (uniform) sound speed as an explicit argument and fills the background density from the scalar `this%rho0`:
@@ -157,4 +162,4 @@ For examples, see any of the following
157
162
*[`examples/lineareuler2d_spherical_soundwave_closeddomain.f90`](https://github.com/FluidNumerics/SELF/blob/main/examples/linear_euler2d_spherical_soundwave_closeddomain.f90) - Simulation with a gaussian pressure and density anomaly as an initial condition in a domain with no-normal-flow boundary conditions on all sides. Demonstrates uniform sound speed via the `SphericalSoundWave` initializer.
158
163
*[`examples/linear_euler2d_planewave_propagation.f90`](https://github.com/FluidNumerics/SELF/blob/main/examples/linear_euler2d_planewave_propagation.f90) - Gaussian plane wave that propagates at a $45^\circ$ angle through a square domain. The initial and boundary conditions are an exact plane-wave solution to the linear Euler equations. The example subclass carries its own `c` attribute and writes it into `solution(...,5)` for both the initial condition and the prescribed boundary state.
159
164
*[`examples/linear_euler2d_planewave_reflection.f90`](https://github.com/FluidNumerics/SELF/blob/main/examples/linear_euler2d_planewave_reflection.f90) - Gaussian plane wave reflected off a wall at $x=1$ via the method of images. Combines prescribed boundary conditions with no-normal-flow on the reflecting side.
160
-
*[`examples/linear_euler2d_boneandmarrow.f90`](https://github.com/FluidNumerics/SELF/blob/main/examples/linear_euler2d_boneandmarrow.f90) - Heterogeneous-medium test on a HOHQMesh ISM-MM mesh tagged with three materials (muscle/bone/marrow). Each material is mapped to a representative sound speed written into `solution(...,5)`, and a Gaussian acoustic pulse refracts and reflects at the material interfaces. Exercises the impedance-matched Riemann solver across $c$ discontinuities.
165
+
*[`examples/linear_euler2d_boneandmarrow.f90`](https://github.com/FluidNumerics/SELF/blob/main/examples/linear_euler2d_boneandmarrow.f90) - Heterogeneous-medium test on a HOHQMesh ISM-MM mesh tagged with three materials (muscle/bone/marrow). Each material is mapped to a representative sound speed and background density, written into `solution(...,5)` and `solution(...,6)`, and a Gaussian acoustic pulse refracts and reflects at the material interfaces. Exercises the impedance-matched Riemann solver across $\rho_0 c$ (impedance) discontinuities.
Copy file name to clipboardExpand all lines: docs/Models/linear-euler-2d-pml-model.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -76,7 +76,8 @@ Both paths are covered step-by-step in the [PML tutorial](../Tutorials/LinearEul
76
76
*`SetMetadata` — registers names and units for the four PML auxiliaries (plus `sigma_x`, `sigma_y`).
77
77
*`AdditionalInit` — allocates `sigma_x` and `sigma_y` and registers PML-aware no-normal-flow and radiation boundary handlers.
78
78
*`AdditionalFree` — releases the damping fields.
79
-
*`flux2d`, `riemannflux2d` — reuse the parent linear-Euler flux for variables 1–5 and return zero for variables 6–9 (auxiliaries carry no flux).
79
+
*`flux2d`, `riemannflux2d` — use the parent linear-Euler acoustic flux form for variables 1–5 and return zero for variables 6–9 (auxiliaries carry no flux). Note the PML variant uses the **scalar** background density `rho0`: unlike the parent model (which carries `rho0` as a per-node solution variable in slot 6), the PML model repurposes slot 6 for the auxiliary `phi_rho`.
80
+
*`entropy_func` — acoustic energy using the scalar `rho0` and the per-node sound speed `s(5)`. This override is required because the parent's `entropy_func` reads `rho0` from `s(6)`, which the PML model uses for `phi_rho`.
80
81
*`sourcemethod` — implements the ADE source term node-by-node. We override `sourcemethod` rather than `source2d` because the pure `source2d(s, dsdx)` signature has no access to the per-node $\sigma_x(i,j,iel)$, $\sigma_y(i,j,iel)$ lookups.
81
82
82
83
A new procedure `SetPMLProfile(x_interior_min, x_interior_max, y_interior_min, y_interior_max, pml_width, sigma_max, ramp_exponent)` populates `sigma_x` and `sigma_y` from the per-element material tags and the geometric ramp.
0 commit comments