Skip to content
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ Trixi.jl follows the interpretation of
used in the Julia ecosystem. Notable changes will be documented in this file
for human readability.

## Changes when updating to v0.16 from v0.15.x

- The implementation of the local DG (`ViscousFormulationLocalDG`) `solver_parabolic` has been changed for the `P4estMesh`.
In particular, instead of computing the `ldg_switch` as the dot product of the normal direction with ones,
i.e., summing up the normal components, the `ldg_switch` is now selected as
the sign of the maximum (in absolute value sense) normal direction component,
which corresponds to the dominant direction of the interface normal.
This might change results slightly for some meshes where the sum of the normal might be close to zero,
thus introducing some spurious switch assignments ([#2871]).

## Changes in the v0.15 lifecycle

#### Added
Expand Down
60 changes: 60 additions & 0 deletions examples/p4est_2d_dgsem/elixir_advection_diffusion_rotated.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using OrdinaryDiffEqLowStorageRK
using Trixi

###############################################################################
# semidiscretization of the linear advection-diffusion equation

diffusivity() = 1.0e-2
advection_velocity = (-1.0, 1.0)
equations = LinearScalarAdvectionEquation2D(advection_velocity)
equations_parabolic = LaplaceDiffusion2D(diffusivity(), equations)

function initial_condition_gauss_damped(x, t, equations)
damping_factor = 1 + 4 * diffusivity() * t
return SVector(exp(-(x[1]^2 + x[2]^2) / damping_factor) / damping_factor)
end
initial_condition = initial_condition_gauss_damped

solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)

# This maps the domain [-1, 1]^2 to a 45-degree rotated increased square
square_size() = 5.0
function mapping(xi, eta)
x = square_size() * xi
y = square_size() * eta
return SVector((x - y) / sqrt(2), (x + y) / sqrt(2))
end

trees_per_dimension = (23, 23)
mesh = P4estMesh(trees_per_dimension,
polydeg = 3, initial_refinement_level = 0,
mapping = mapping, periodicity = true)

semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
initial_condition, solver;
solver_parabolic = ViscousFormulationLocalDG(),
boundary_conditions = (boundary_condition_periodic,
boundary_condition_periodic))

###############################################################################
# ODE solvers, callbacks etc.

n_passes = 2
tspan = (0.0, n_passes * square_size() * sqrt(2))
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval = analysis_interval)

alive_callback = AliveCallback(analysis_interval = analysis_interval)

callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback)

###############################################################################
# run the simulation

time_int_tol = 1.0e-6
sol = solve(ode, RDPK3SpFSAL49(); abstol = time_int_tol, reltol = time_int_tol,
ode_default_options()..., callback = callbacks)
30 changes: 18 additions & 12 deletions src/solvers/solvers_parabolic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ f_{\text{gradient}} = u_{L}
```
on the Cartesian [`TreeMesh`](@ref).

For the [`P4estMesh`](@ref), the `normal_direction` is used to compute the LDG "switch" ``\sigma`` for the upwinding/downwinding.
This is realized by taking the sign of the dot product of the normal and positive-coordinate direction vector:
For the [`P4estMesh`](@ref), the `normal_direction` is used to compute the LDG "switch" ``\sigma`` for the upwinding.
This is realized by selecting the sign of the maximum (in absolute value sense) normal direction component,
which corresponds to the "dominant" direction of the interface normal.
```math
\sigma = \text{sign}(\vec{n} \cdot \vec{1})
f = \frac{1}{2}\Big(f(u_{L}) + f(u_{R}) - \sigma \big[f(u_{R}) - f(u_{L})\big]\Big)
i = \text{argmax} \{ \begin{pmatrix} \vert n_1 \vert \\ \vert n_2 \vert \\ \dots \end{pmatrix} \}
\sigma = \text{sign} (n_i)
```
"""
function flux_parabolic(u_ll, u_rr, # Version for `TreeMesh`
Expand All @@ -121,7 +122,9 @@ end
function flux_parabolic(u_ll, u_rr, normal_direction,
::Gradient, equations_parabolic,
parabolic_scheme::ViscousFormulationLocalDG)
ldg_switch = sign(sum(normal_direction)) # equivalent to sign(dot(normal_direction, ones))
# Use "Upwind in dominant direction" for LDG switch
abs_max_dir = argmax(abs.(normal_direction))
ldg_switch = sign(normal_direction[abs_max_dir])
return 0.5f0 * (u_ll + u_rr - ldg_switch * (u_rr - u_ll))
end

Expand All @@ -145,24 +148,27 @@ f_{\text{divergence}} = u_{R}
```
on the Cartesian [`TreeMesh`](@ref).

For the [`P4estMesh`](@ref), the `normal_direction` is used to compute the LDG "switch" ``\sigma`` for the upwinding/downwinding.
This is realized by taking the sign of the dot product of the normal and positive-coordinate direction vector:
For the [`P4estMesh`](@ref), the `normal_direction` is used to compute the LDG "switch" ``\sigma`` for the downwinding.
This is realized by selecting the sign of the maximum (in absolute value sense) normal direction component,
which corresponds to the "dominant" direction of the interface normal.
```math
\sigma = \text{sign}(\vec{n} \cdot \vec{1})
f = \frac{1}{2}\Big(f(u_{L}) + f(u_{R}) + \sigma \big[f(u_{R}) - f(u_{L})\big]\Big)
i = \text{argmax} \{ \begin{pmatrix} \vert n_1 \vert \\ \vert n_2 \vert \\ \dots \end{pmatrix} \}
\sigma = -\text{sign} (n_i)
```
"""
function flux_parabolic(u_ll, u_rr, # Version for `TreeMesh`
::Divergence, equations_parabolic,
parabolic_scheme::ViscousFormulationLocalDG)
return u_rr # Use the downwind value for the divergence interface flux
end
# Version or `P4estMesh`
# Version for `P4estMesh`
function flux_parabolic(u_ll, u_rr, normal_direction,
::Divergence, equations_parabolic,
parabolic_scheme::ViscousFormulationLocalDG)
ldg_switch = sign(sum(normal_direction)) # equivalent to sign(dot(normal_direction, ones))
return 0.5f0 * (u_ll + u_rr + ldg_switch * (u_rr - u_ll))
# Use "Downwind in dominant direction" for LDG switch
abs_max_dir = argmax(abs.(normal_direction))
ldg_switch = -sign(normal_direction[abs_max_dir])
return 0.5f0 * (u_ll + u_rr - ldg_switch * (u_rr - u_ll))
end

default_parabolic_solver() = ViscousFormulationBassiRebay1()
10 changes: 10 additions & 0 deletions test/test_parabolic_2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,16 @@ end
@test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000)
end

@trixi_testset "P4estMesh2D: elixir_advection_diffusion_rotated.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_2d_dgsem",
"elixir_advection_diffusion_rotated.jl"),
l2=[4.8533724384822306e-5], linf=[0.0006284491001110615])
# Ensure that we do not have excessive memory allocations
# (e.g., from type instabilities)
@test_allocations(Trixi.rhs!, semi, sol, 1000)
@test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000)
end

@trixi_testset "P4estMesh2D: elixir_advection_diffusion_periodic_curved.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_2d_dgsem",
"elixir_advection_diffusion_periodic_curved.jl"),
Expand Down
2 changes: 1 addition & 1 deletion test/test_parabolic_3d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ end
"elixir_advection_diffusion_nonperiodic.jl"),
solver_parabolic=ViscousFormulationLocalDG(),
cfl_diffusive=0.07,
l2=[0.0041854757843498725], linf=[0.05166356737492643])
l2=[0.004185076476662267], linf=[0.05166349548111486])
# Ensure that we do not have excessive memory allocations
# (e.g., from type instabilities)
@test_allocations(Trixi.rhs!, semi, sol, 1000)
Expand Down
Loading