From 21a572dadc87ffa9f4ba15b30c12fd80f1f174c5 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 17 Mar 2026 17:06:03 +0100 Subject: [PATCH 01/68] rename viscous to parabolic in non-fluid contexts --- NEWS.md | 4 +- .../src/files/adding_new_parabolic_terms.jl | 14 +- .../src/files/parabolic_source_terms.jl | 4 +- .../dgmulti_2d/elixir_advection_diffusion.jl | 2 +- .../elixir_advection_diffusion_nonperiodic.jl | 2 +- .../elixir_navierstokes_convergence.jl | 2 +- .../elixir_navierstokes_convergence_curved.jl | 2 +- .../elixir_navierstokes_lid_driven_cavity.jl | 2 +- .../elixir_navierstokes_convergence.jl | 2 +- .../elixir_navierstokes_convergence_curved.jl | 2 +- ...xir_advection_diffusion_nonperiodic_amr.jl | 2 +- .../elixir_navierstokes_convergence.jl | 2 +- ...ir_navierstokes_convergence_nonperiodic.jl | 2 +- .../elixir_navierstokes_freestream_ldg.jl | 2 +- ...r_navierstokes_shearlayer_nonconforming.jl | 2 +- .../elixir_navierstokes_vortex_street.jl | 4 +- .../elixir_advection_diffusion_amr_curved.jl | 2 +- ...lixir_advection_diffusion_nonconforming.jl | 2 +- .../elixir_advection_diffusion_nonperiodic.jl | 2 +- .../elixir_navierstokes_blast_wave_amr.jl | 2 +- .../elixir_navierstokes_convergence.jl | 2 +- ...ixir_navierstokes_freestream_boundaries.jl | 2 +- ...lixir_advection_diffusion_dirichlet_amr.jl | 2 +- ...vection_diffusion_gradient_source_terms.jl | 2 +- .../elixir_advection_diffusion_ldg.jl | 2 +- .../elixir_advection_diffusion_neumann_amr.jl | 2 +- .../tree_1d_dgsem/elixir_diffusion_ldg.jl | 2 +- .../elixir_diffusion_ldg_newton_krylov.jl | 2 +- .../elixir_navierstokes_convergence_walls.jl | 2 +- ...ixir_navierstokes_convergence_walls_amr.jl | 2 +- .../elixir_navierstokes_viscous_shock.jl | 2 +- .../elixir_navierstokes_viscous_shock_imex.jl | 2 +- .../elixir_advection_diffusion.jl | 2 +- .../elixir_advection_diffusion_amr.jl | 2 +- ...vection_diffusion_gradient_source_terms.jl | 2 +- .../elixir_advection_diffusion_nonperiodic.jl | 2 +- ...xir_advection_diffusion_nonperiodic_amr.jl | 2 +- ...lixir_diffusion_steady_state_linear_map.jl | 4 +- .../elixir_navierstokes_convergence.jl | 2 +- ...r_navierstokes_shearlayer_nonconforming.jl | 2 +- .../elixir_navierstokes_viscous_shock.jl | 2 +- .../elixir_advection_diffusion_amr.jl | 2 +- ...vection_diffusion_gradient_source_terms.jl | 2 +- ...lixir_advection_diffusion_nonconforming.jl | 2 +- .../elixir_advection_diffusion_nonperiodic.jl | 2 +- .../elixir_navierstokes_convergence.jl | 2 +- src/Trixi.jl | 2 +- src/callbacks_step/amr_dg1d.jl | 8 +- src/callbacks_step/amr_dg2d.jl | 8 +- src/callbacks_step/analysis.jl | 4 +- src/callbacks_step/analysis_dg2d.jl | 2 +- src/callbacks_step/analysis_dg3d.jl | 2 +- .../analysis_surface_integral.jl | 6 +- .../analysis_surface_integral_2d.jl | 6 +- .../compressible_navier_stokes_1d.jl | 6 +- .../compressible_navier_stokes_2d.jl | 10 +- .../compressible_navier_stokes_3d.jl | 12 +- src/equations/laplace_diffusion_2d.jl | 2 +- src/equations/laplace_diffusion_3d.jl | 2 +- ...semidiscretization_hyperbolic_parabolic.jl | 2 +- src/solvers/dgmulti/dg_parabolic.jl | 109 ++--- src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 147 +++--- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 162 ++++--- .../dgsem_tree/container_parabolic_1d.jl | 58 +++ .../dgsem_tree/container_parabolic_2d.jl | 84 ++++ .../dgsem_tree/container_parabolic_3d.jl | 99 ++++ .../dgsem_tree/container_viscous_1d.jl | 58 --- .../dgsem_tree/container_viscous_2d.jl | 84 ---- .../dgsem_tree/container_viscous_3d.jl | 98 ---- src/solvers/dgsem_tree/containers_viscous.jl | 6 +- src/solvers/dgsem_tree/dg.jl | 2 +- src/solvers/dgsem_tree/dg_1d.jl | 31 +- src/solvers/dgsem_tree/dg_1d_parabolic.jl | 71 +-- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 260 ++++++----- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 441 +++++++++--------- src/solvers/solvers_parabolic.jl | 44 +- test/test_parabolic_2d.jl | 24 +- test/test_parabolic_3d.jl | 8 +- test/test_type.jl | 4 +- 79 files changed, 1000 insertions(+), 974 deletions(-) create mode 100644 src/solvers/dgsem_tree/container_parabolic_1d.jl create mode 100644 src/solvers/dgsem_tree/container_parabolic_2d.jl create mode 100644 src/solvers/dgsem_tree/container_parabolic_3d.jl delete mode 100644 src/solvers/dgsem_tree/container_viscous_1d.jl delete mode 100644 src/solvers/dgsem_tree/container_viscous_2d.jl delete mode 100644 src/solvers/dgsem_tree/container_viscous_3d.jl diff --git a/NEWS.md b/NEWS.md index 711bad308be..2f053cd66ca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ for human readability. #### Added -- It is now possible to use `ViscousFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. +- It is now possible to use `ParabolicFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. This is useful for (locally) diffusion-dominated problems. This enables in particular adaptive mesh refinement for that solver-mesh combination ([#2712]). - Added functionality to `ScalarPlotData2D` allowing visualization a field provided by a user-defined scalar function ([#2796]). @@ -160,7 +160,7 @@ This enables in particular adaptive mesh refinement for that solver-mesh combina - Added symmetry plane/reflective wall velocity+stress boundary conditions for the compressible Navier-Stokes equations in 2D and 3D. Currently available only for the `P4estMesh` mesh type, `GradientVariablesPrimitive`, and `Adiabatic` heat boundary condition ([#2416]). - Added `LaplaceDiffusionEntropyVariables1D`, `LaplaceDiffusionEntropyVariables2D`, and `LaplaceDiffusionEntropyVariables3D`. These add scalar diffusion to each - equation of a system, but apply diffusion in terms of the entropy variables, which symmetrizes the viscous formulation and ensures semi-discrete entropy dissipation ([#2406]). + equation of a system, but apply diffusion in terms of the entropy variables, which symmetrizes the parabolic formulation and ensures semi-discrete entropy dissipation ([#2406]). - Added the three-dimensional multi-ion magneto-hydrodynamics (MHD) equations with a generalized Lagrange multipliers (GLM) divergence cleaning technique ([#2215]). - New time integrator `PairedExplicitRK4`, implementing the fourth-order diff --git a/docs/literate/src/files/adding_new_parabolic_terms.jl b/docs/literate/src/files/adding_new_parabolic_terms.jl index 9ec30998eb8..fe0239141cd 100644 --- a/docs/literate/src/files/adding_new_parabolic_terms.jl +++ b/docs/literate/src/files/adding_new_parabolic_terms.jl @@ -35,14 +35,14 @@ function varnames(variable_mapping, equations_parabolic::ConstantAnisotropicDiff return varnames(variable_mapping, equations_parabolic.equations_hyperbolic) end -# Next, we define the viscous flux function. We assume that the mixed hyperbolic-parabolic system +# Next, we define the parabolic flux function. We assume that the mixed hyperbolic-parabolic system # is of the form # ```math # \partial_t u(t,x) + \partial_x (f_1(u) - g_1(u, \nabla u)) # + \partial_y (f_2(u) - g_2(u, \nabla u)) = 0 # ``` # where ``f_1(u)``, ``f_2(u)`` are the hyperbolic fluxes and ``g_1(u, \nabla u)``, ``g_2(u, \nabla u)`` denote -# the viscous fluxes. For anisotropic diffusion, the viscous fluxes are the first and second components +# the parabolic fluxes. For anisotropic diffusion, the parabolic fluxes are the first and second components # of the matrix-vector product involving `diffusivity` and the gradient vector. # # Here, we specialize the flux to our new parabolic equation type `ConstantAnisotropicDiffusion2D`. @@ -66,12 +66,12 @@ end # \begin{aligned} # \bm{q} &= \nabla u \\ # \bm{\sigma} &= \begin{pmatrix} g_1(u, \bm{q}) \\ g_2(u, \bm{q}) \end{pmatrix} \\ -# \text{viscous contribution } &= \nabla \cdot \bm{\sigma} +# \text{parabolic contribution } &= \nabla \cdot \bm{\sigma} # \end{aligned} # ``` # # Boundary data must be specified for all spatial derivatives, e.g., for both the gradient -# equation ``\bm{q} = \nabla u`` and the divergence of the viscous flux +# equation ``\bm{q} = \nabla u`` and the divergence of the parabolic flux # ``\nabla \cdot \bm{\sigma}``. We account for this by introducing internal `Gradient` # and `Divergence` types which are used to dispatch on each type of boundary condition. # @@ -98,7 +98,7 @@ end return boundary_condition.boundary_value end -# While the gradient acts on the solution `u`, the divergence acts on the viscous flux ``\bm{\sigma}``. +# While the gradient acts on the solution `u`, the divergence acts on the parabolic flux ``\bm{\sigma}``. # Thus, we have to supply boundary data for the `Divergence` operator that corresponds to ``\bm{\sigma}``. # However, we've already imposed boundary data on `u` for a Dirichlet boundary condition, and imposing # boundary data for ``\bm{\sigma}`` might overconstrain our problem. @@ -119,7 +119,7 @@ end # ### A note on the choice of gradient variables # # It is often simpler to transform the solution variables (and solution gradients) to another set of -# variables prior to computing the viscous fluxes (see [`CompressibleNavierStokesDiffusion2D`](@ref) +# variables prior to computing the parabolic fluxes (see [`CompressibleNavierStokesDiffusion2D`](@ref) # for an example of this). If this is done, then the boundary condition for the `Gradient` operator # should be modified accordingly as well. # @@ -182,7 +182,7 @@ plot(sol) # To be able to do so, we need to define [`max_diffusivity`](@ref) and # [`have_constant_diffusivity`](@ref) for the new parabolic terms. # In Trixi.jl, currently only the standard Laplace Diffusion and Compressible Navier-Stokes-Fourier -# viscous terms are implemented. +# parabolic terms are implemented. # Since these equations have **isotropic** diffusivity, i.e., direction-independent coefficients, # [`max_diffusivity`](@ref) is expected to return a scalar value. # diff --git a/docs/literate/src/files/parabolic_source_terms.jl b/docs/literate/src/files/parabolic_source_terms.jl index 458510ce6b0..4ed7e8059c1 100644 --- a/docs/literate/src/files/parabolic_source_terms.jl +++ b/docs/literate/src/files/parabolic_source_terms.jl @@ -62,7 +62,7 @@ end # to OrdinaryDiffEq.jl. # # Note that for this problem, since viscosity `nu` is relatively large, we utilize -# `ViscousFormulationLocalDG` instead of the default `ViscousFormulationBassiRebay1` +# `ParabolicFormulationLocalDG` instead of the default `ParabolicFormulationBassiRebay1` # parabolic solver, since the Bassi-Rebay 1 formulation is not accurate when the # diffusivity is large relative to the mesh size. @@ -76,7 +76,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), source_terms = source_terms, source_terms_parabolic = source_terms_parabolic, boundary_conditions = (boundary_conditions, diff --git a/examples/dgmulti_2d/elixir_advection_diffusion.jl b/examples/dgmulti_2d/elixir_advection_diffusion.jl index 8eed206f315..df9935f1d54 100644 --- a/examples/dgmulti_2d/elixir_advection_diffusion.jl +++ b/examples/dgmulti_2d/elixir_advection_diffusion.jl @@ -34,7 +34,7 @@ boundary_conditions = (; left = boundary_condition_left, top = boundary_condition_do_nothing, right = boundary_condition_do_nothing) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; left = boundary_condition_left, bottom = boundary_condition_zero, top = boundary_condition_zero, diff --git a/examples/dgmulti_2d/elixir_advection_diffusion_nonperiodic.jl b/examples/dgmulti_2d/elixir_advection_diffusion_nonperiodic.jl index 59ffbf181ea..4f339968256 100644 --- a/examples/dgmulti_2d/elixir_advection_diffusion_nonperiodic.jl +++ b/examples/dgmulti_2d/elixir_advection_diffusion_nonperiodic.jl @@ -53,7 +53,7 @@ boundary_conditions = (; left = boundary_condition, bottom = boundary_condition, right = boundary_condition_do_nothing) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; entire_boundary = boundary_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), diff --git a/examples/dgmulti_2d/elixir_navierstokes_convergence.jl b/examples/dgmulti_2d/elixir_navierstokes_convergence.jl index 63e4285d6b9..c7f0c5ca498 100644 --- a/examples/dgmulti_2d/elixir_navierstokes_convergence.jl +++ b/examples/dgmulti_2d/elixir_navierstokes_convergence.jl @@ -198,7 +198,7 @@ boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_to # define inviscid boundary conditions boundary_conditions = (; top_bottom = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; top_bottom = boundary_condition_top_bottom) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), diff --git a/examples/dgmulti_2d/elixir_navierstokes_convergence_curved.jl b/examples/dgmulti_2d/elixir_navierstokes_convergence_curved.jl index a4ca0c60791..b0b7601a9a9 100644 --- a/examples/dgmulti_2d/elixir_navierstokes_convergence_curved.jl +++ b/examples/dgmulti_2d/elixir_navierstokes_convergence_curved.jl @@ -206,7 +206,7 @@ boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_to # define inviscid boundary conditions boundary_conditions = (; top_bottom = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; top_bottom = boundary_condition_top_bottom) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), diff --git a/examples/dgmulti_2d/elixir_navierstokes_lid_driven_cavity.jl b/examples/dgmulti_2d/elixir_navierstokes_lid_driven_cavity.jl index 38c8234b839..167bc54e57c 100644 --- a/examples/dgmulti_2d/elixir_navierstokes_lid_driven_cavity.jl +++ b/examples/dgmulti_2d/elixir_navierstokes_lid_driven_cavity.jl @@ -51,7 +51,7 @@ boundary_condition_cavity = BoundaryConditionNavierStokesWall(velocity_bc_cavity boundary_conditions = (; top = boundary_condition_slip_wall, rest_of_boundary = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; top = boundary_condition_lid, rest_of_boundary = boundary_condition_cavity) diff --git a/examples/dgmulti_3d/elixir_navierstokes_convergence.jl b/examples/dgmulti_3d/elixir_navierstokes_convergence.jl index 9adb48efa0a..3c174196457 100644 --- a/examples/dgmulti_3d/elixir_navierstokes_convergence.jl +++ b/examples/dgmulti_3d/elixir_navierstokes_convergence.jl @@ -241,7 +241,7 @@ boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_to # define inviscid boundary conditions boundary_conditions = (; top_bottom = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; top_bottom = boundary_condition_top_bottom) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), diff --git a/examples/dgmulti_3d/elixir_navierstokes_convergence_curved.jl b/examples/dgmulti_3d/elixir_navierstokes_convergence_curved.jl index 96469770bd1..bdc997f8953 100644 --- a/examples/dgmulti_3d/elixir_navierstokes_convergence_curved.jl +++ b/examples/dgmulti_3d/elixir_navierstokes_convergence_curved.jl @@ -249,7 +249,7 @@ boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_to # define inviscid boundary conditions boundary_conditions = (; top_bottom = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; top_bottom = boundary_condition_top_bottom) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), diff --git a/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl b/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl index 5490425f558..eec4ea0d79b 100644 --- a/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl +++ b/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl @@ -50,7 +50,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl index 11ae50eec90..5341299e572 100644 --- a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl @@ -199,7 +199,7 @@ boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_to boundary_conditions = (; y_neg = boundary_condition_slip_wall, y_pos = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; y_neg = boundary_condition_top_bottom, y_pos = boundary_condition_top_bottom) diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl index a5025116087..4e11d052d96 100644 --- a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl +++ b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl @@ -203,7 +203,7 @@ boundary_conditions = (; x_neg = boundary_condition_left_right, y_neg = boundary_condition_slip_wall, y_pos = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; x_neg = boundary_condition_left_right, x_pos = boundary_condition_left_right, y_neg = boundary_condition_top_bottom, diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_freestream_ldg.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_freestream_ldg.jl index a5280d1a3e8..3efd44f1afb 100644 --- a/examples/p4est_2d_dgsem/elixir_navierstokes_freestream_ldg.jl +++ b/examples/p4est_2d_dgsem/elixir_navierstokes_freestream_ldg.jl @@ -19,7 +19,7 @@ initial_condition = initial_condition_const polydeg = 3 solver = DGSEM(polydeg = polydeg, surface_flux = flux_lax_friedrichs, volume_integral = VolumeIntegralFluxDifferencing(flux_ranocha)) -solver_parabolic = ViscousFormulationLocalDG() +solver_parabolic = ParabolicFormulationLocalDG() mu() = 0.5 prandtl_number() = 0.72 diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl index c4595b85180..70fe918a476 100644 --- a/examples/p4est_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl +++ b/examples/p4est_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl @@ -68,7 +68,7 @@ Trixi.refine_p4est!(mesh.p4est, true, refine_fn_c, C_NULL) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_condition_periodic, boundary_condition_periodic)) diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_vortex_street.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_vortex_street.jl index c206202af03..932926770e9 100644 --- a/examples/p4est_2d_dgsem/elixir_navierstokes_vortex_street.jl +++ b/examples/p4est_2d_dgsem/elixir_navierstokes_vortex_street.jl @@ -119,8 +119,8 @@ function Trixi.get_node_variable(::Val{:vorticity}, u, mesh, equations, dg, cach n_nodes, n_nodes, # equivalent: `ntuple(_ -> n_nodes, ndims(mesh))...,` n_elements) - @unpack viscous_container = cache_parabolic - @unpack gradients = viscous_container + @unpack parabolic_container = cache_parabolic + @unpack gradients = parabolic_container gradients_x, gradients_y = gradients # We can accelerate the computation by thread-parallelizing the loop over elements diff --git a/examples/p4est_3d_dgsem/elixir_advection_diffusion_amr_curved.jl b/examples/p4est_3d_dgsem/elixir_advection_diffusion_amr_curved.jl index 4e39dac1e69..aacca586c23 100644 --- a/examples/p4est_3d_dgsem/elixir_advection_diffusion_amr_curved.jl +++ b/examples/p4est_3d_dgsem/elixir_advection_diffusion_amr_curved.jl @@ -64,7 +64,7 @@ mesh = P4estMesh{3}(mesh_file, polydeg = 2, semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions)) diff --git a/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonconforming.jl b/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonconforming.jl index 4498efe0936..68e6c6ffb6e 100644 --- a/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonconforming.jl +++ b/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonconforming.jl @@ -70,7 +70,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl b/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl index d098dc0579a..90cdc336a85 100644 --- a/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl +++ b/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl @@ -58,7 +58,7 @@ boundary_conditions = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions)) diff --git a/examples/p4est_3d_dgsem/elixir_navierstokes_blast_wave_amr.jl b/examples/p4est_3d_dgsem/elixir_navierstokes_blast_wave_amr.jl index 497c0a741e5..2c3b563df00 100644 --- a/examples/p4est_3d_dgsem/elixir_navierstokes_blast_wave_amr.jl +++ b/examples/p4est_3d_dgsem/elixir_navierstokes_blast_wave_amr.jl @@ -67,7 +67,7 @@ mesh = P4estMesh(trees_per_dimension, polydeg = 3, semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_condition_periodic, boundary_condition_periodic)) diff --git a/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl index 27ccde81b66..f2e7eb08028 100644 --- a/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl @@ -243,7 +243,7 @@ boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_to boundary_conditions = (; y_neg = boundary_condition_slip_wall, y_pos = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; y_neg = boundary_condition_top_bottom, y_pos = boundary_condition_top_bottom) diff --git a/examples/p4est_3d_dgsem/elixir_navierstokes_freestream_boundaries.jl b/examples/p4est_3d_dgsem/elixir_navierstokes_freestream_boundaries.jl index 51d19f5302e..4ca0cb88c41 100644 --- a/examples/p4est_3d_dgsem/elixir_navierstokes_freestream_boundaries.jl +++ b/examples/p4est_3d_dgsem/elixir_navierstokes_freestream_boundaries.jl @@ -19,7 +19,7 @@ initial_condition = initial_condition_const polydeg = 3 solver = DGSEM(polydeg = polydeg, surface_flux = flux_lax_friedrichs) -solver_parabolic = ViscousFormulationBassiRebay1() +solver_parabolic = ParabolicFormulationBassiRebay1() mu() = 0.5 prandtl_number() = 0.72 diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_dirichlet_amr.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_dirichlet_amr.jl index a2caf612b24..13d8d03f784 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_dirichlet_amr.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_dirichlet_amr.jl @@ -42,7 +42,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl index 7908945fdae..93a6f423643 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl @@ -40,7 +40,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), source_terms = source_terms, source_terms_parabolic = source_terms_parabolic, boundary_conditions = (boundary_conditions, diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_ldg.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_ldg.jl index 4a5c9776e62..8cc4fa8aad9 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_ldg.jl @@ -54,7 +54,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_neumann_amr.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_neumann_amr.jl index 0a8531679b2..e34a594c4fd 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_neumann_amr.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_neumann_amr.jl @@ -26,7 +26,7 @@ boundary_condition_neumann_zero = BoundaryConditionNeumann((x, t, equations_para boundary_conditions = (; x_neg = boundary_condition_left, x_pos = boundary_condition_do_nothing) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; x_neg = boundary_condition_left, x_pos = boundary_condition_neumann_zero) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl index 143a9cc9840..8f79de2d3c4 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl @@ -41,7 +41,7 @@ boundary_conditions = boundary_condition_periodic boundary_conditions_parabolic = boundary_condition_periodic # A semidiscretization collects data structures and functions for the spatial discretization -solver_parabolic = ViscousFormulationLocalDG() +solver_parabolic = ParabolicFormulationLocalDG() semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; solver_parabolic, diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl index 8fc08add88a..f52b62738cd 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl @@ -33,7 +33,7 @@ function initial_condition_pure_diffusion_1d_convergence_test(x, t, end initial_condition = initial_condition_pure_diffusion_1d_convergence_test -solver_parabolic = ViscousFormulationLocalDG() +solver_parabolic = ParabolicFormulationLocalDG() semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; solver_parabolic, diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl index c0239f5c08d..f31dd16be61 100644 --- a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl +++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl @@ -152,7 +152,7 @@ boundary_condition_right = BoundaryConditionNavierStokesWall(velocity_bc_left_ri boundary_conditions = (; x_neg = boundary_condition_slip_wall, x_pos = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; x_neg = boundary_condition_left, x_pos = boundary_condition_right) diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl index 998f5bf107d..0a387965160 100644 --- a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl +++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl @@ -152,7 +152,7 @@ boundary_condition_right = BoundaryConditionNavierStokesWall(velocity_bc_left_ri boundary_conditions = (; x_neg = boundary_condition_slip_wall, x_pos = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; x_neg = boundary_condition_left, x_pos = boundary_condition_right) diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock.jl b/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock.jl index a7a91921604..5707c6b3efd 100644 --- a/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock.jl +++ b/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock.jl @@ -141,7 +141,7 @@ boundary_conditions_parabolic = (; x_neg = boundary_condition_parabolic, # Since this is a diffusion-dominated problem, using the LDG scheme should achieve optimal rates of convergence. # In contrast, BR-1 may achieve suboptimal rates of convergence in diffusion-dominated regimes. # The LDG scheme can be used by specifying the keyword -# solver_parabolic = ViscousFormulationLocalDG() +# solver_parabolic = ParabolicFormulationLocalDG() # in the semidiscretization call below. semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock_imex.jl b/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock_imex.jl index c3166cab961..428ba79535d 100644 --- a/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock_imex.jl +++ b/examples/tree_1d_dgsem/elixir_navierstokes_viscous_shock_imex.jl @@ -133,7 +133,7 @@ boundary_conditions_parabolic = (; x_neg = boundary_condition_parabolic, semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion.jl index ed4ed2432e5..f1d209c7ea6 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion.jl @@ -47,7 +47,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl index b4277434129..d91fd2226bf 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl @@ -43,7 +43,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl index 740594ee954..10dd308d64c 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl @@ -49,7 +49,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), source_terms = source_terms, source_terms_parabolic = source_terms_parabolic, boundary_conditions = (boundary_conditions, diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic.jl index 93abfd49a3a..2234d8fdf76 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic.jl @@ -51,7 +51,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl index 95f9c46f2b8..e7667f9808d 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_nonperiodic_amr.jl @@ -49,7 +49,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl index c92140a6613..51530f3286f 100644 --- a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl +++ b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl @@ -44,11 +44,11 @@ boundary_conditions = (; x_neg = bc_homogeneous_dirichlet, y_pos = bc_sin_dirichlet, x_pos = bc_homogeneous_dirichlet) -# `solver_parabolic = ViscousFormulationLocalDG()` strictly required for elliptic/diffusion-dominated problem +# `solver_parabolic = ParabolicFormulationLocalDG()` strictly required for elliptic/diffusion-dominated problem semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), boundary_conditions = (boundary_conditions, boundary_conditions)) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 16ea4742315..38aed3028c6 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -205,7 +205,7 @@ boundary_conditions = (; x_neg = boundary_condition_periodic, y_neg = boundary_condition_slip_wall, y_pos = boundary_condition_slip_wall) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; x_neg = boundary_condition_periodic, x_pos = boundary_condition_periodic, y_neg = boundary_condition_top_bottom, diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl b/examples/tree_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl index 257d3293448..ef623715a20 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_shearlayer_nonconforming.jl @@ -55,7 +55,7 @@ mesh = TreeMesh(coordinates_min, coordinates_max, semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_condition_periodic, boundary_condition_periodic)) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl b/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl index 5f34292a840..dd6e0dc13b2 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl @@ -153,7 +153,7 @@ boundary_conditions_parabolic = (x_neg = boundary_condition_parabolic, semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_3d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_3d_dgsem/elixir_advection_diffusion_amr.jl index 5d886b01cf4..e79f495bef5 100644 --- a/examples/tree_3d_dgsem/elixir_advection_diffusion_amr.jl +++ b/examples/tree_3d_dgsem/elixir_advection_diffusion_amr.jl @@ -43,7 +43,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl b/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl index 1a107511344..93f06a99f22 100644 --- a/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl @@ -49,7 +49,7 @@ boundary_conditions_parabolic = boundary_condition_periodic semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationLocalDG(), + solver_parabolic = ParabolicFormulationLocalDG(), source_terms = source_terms, source_terms_parabolic = source_terms_parabolic, boundary_conditions = (boundary_conditions, diff --git a/examples/tree_3d_dgsem/elixir_advection_diffusion_nonconforming.jl b/examples/tree_3d_dgsem/elixir_advection_diffusion_nonconforming.jl index 9ddd6f875e3..226d583b105 100644 --- a/examples/tree_3d_dgsem/elixir_advection_diffusion_nonconforming.jl +++ b/examples/tree_3d_dgsem/elixir_advection_diffusion_nonconforming.jl @@ -55,7 +55,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl b/examples/tree_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl index a8a6d27edb0..03d2ffff6c7 100644 --- a/examples/tree_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl +++ b/examples/tree_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl @@ -52,7 +52,7 @@ boundary_conditions_parabolic = BoundaryConditionDirichlet(initial_condition) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; - solver_parabolic = ViscousFormulationBassiRebay1(), + solver_parabolic = ParabolicFormulationBassiRebay1(), boundary_conditions = (boundary_conditions, boundary_conditions_parabolic)) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl index 1f1c734ba00..83f60647e61 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl @@ -246,7 +246,7 @@ boundary_conditions = (; x_neg = boundary_condition_periodic, z_neg = boundary_condition_periodic, z_pos = boundary_condition_periodic) -# define viscous boundary conditions +# define parabolic boundary conditions boundary_conditions_parabolic = (; x_neg = boundary_condition_periodic, x_pos = boundary_condition_periodic, y_neg = boundary_condition_top_bottom, diff --git a/src/Trixi.jl b/src/Trixi.jl index 167e01189c8..4a01bd75bc4 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -336,7 +336,7 @@ export convergence_test, export DGMulti, DGMultiBasis, estimate_dt, DGMultiMesh, GaussSBP -export ViscousFormulationBassiRebay1, ViscousFormulationLocalDG +export ParabolicFormulationBassiRebay1, ParabolicFormulationLocalDG # Visualization-related exports export PlotData1D, PlotData2D, ScalarPlotData2D, getmesh, adapt_to_mesh_level!, diff --git a/src/callbacks_step/amr_dg1d.jl b/src/callbacks_step/amr_dg1d.jl index 8b36edfee91..346b8d6da69 100644 --- a/src/callbacks_step/amr_dg1d.jl +++ b/src/callbacks_step/amr_dg1d.jl @@ -70,8 +70,8 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, refine!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_refine) # Resize parabolic helper variables - @unpack viscous_container = cache_parabolic - resize!(viscous_container, equations, dg, cache) + @unpack parabolic_container = cache_parabolic + resize!(parabolic_container, equations, dg, cache) return nothing end @@ -195,8 +195,8 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, coarsen!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_remove) # Resize parabolic helper variables - @unpack viscous_container = cache_parabolic - resize!(viscous_container, equations, dg, cache) + @unpack parabolic_container = cache_parabolic + resize!(parabolic_container, equations, dg, cache) return nothing end diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 2141c2713bc..fd4cceb9efc 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -190,8 +190,8 @@ function refine!(u_ode::AbstractVector, adaptor, refine!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_refine) # Resize parabolic helper variables - @unpack viscous_container = cache_parabolic - resize!(viscous_container, equations, dg, cache) + @unpack parabolic_container = cache_parabolic + resize!(parabolic_container, equations, dg, cache) return nothing end @@ -385,8 +385,8 @@ function coarsen!(u_ode::AbstractVector, adaptor, coarsen!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_remove) # Resize parabolic helper variables - @unpack viscous_container = cache_parabolic - resize!(viscous_container, equations, dg, cache) + @unpack parabolic_container = cache_parabolic + resize!(parabolic_container, equations, dg, cache) return nothing end diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 8659119fa36..ab4736582ec 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -702,11 +702,11 @@ end # Special analyze for `SemidiscretizationHyperbolicParabolic` such that # precomputed gradients are available. Required for `enstrophy` (see above) and viscous forces. # Note that this needs to be included after `analysis_surface_integral_2d.jl` to -# have `VariableViscous` available. +# have `VariableParabolic` available. function analyze(quantity::AnalysisSurfaceIntegral{Variable}, du, u, t, semi::SemidiscretizationHyperbolicParabolic) where {Variable <: - VariableViscous} + VariableParabolic} mesh, equations, solver, cache = mesh_equations_solver_cache(semi) equations_parabolic = semi.equations_parabolic cache_parabolic = semi.cache_parabolic diff --git a/src/callbacks_step/analysis_dg2d.jl b/src/callbacks_step/analysis_dg2d.jl index 077d01240b1..9ab551799cb 100644 --- a/src/callbacks_step/analysis_dg2d.jl +++ b/src/callbacks_step/analysis_dg2d.jl @@ -376,7 +376,7 @@ function integrate(func::Func, u, mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations, equations_parabolic, dg::DGSEM, cache, cache_parabolic; normalize = true) where {Func} - gradients_x, gradients_y = cache_parabolic.viscous_container.gradients + gradients_x, gradients_y = cache_parabolic.parabolic_container.gradients integrate_via_indices(u, mesh, equations, dg, cache; normalize = normalize) do u, i, j, element, equations, dg u_local = get_node_vars(u, equations, dg, i, j, element) diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl index 634db48de29..9b9ef873606 100644 --- a/src/callbacks_step/analysis_dg3d.jl +++ b/src/callbacks_step/analysis_dg3d.jl @@ -423,7 +423,7 @@ function integrate(func::Func, u, mesh::Union{TreeMesh{3}, P4estMesh{3}}, equations, equations_parabolic, dg::DGSEM, cache, cache_parabolic; normalize = true) where {Func} - gradients_x, gradients_y, gradients_z = cache_parabolic.viscous_container.gradients + gradients_x, gradients_y, gradients_z = cache_parabolic.parabolic_container.gradients integrate_via_indices(u, mesh, equations, dg, cache; normalize = normalize) do u, i, j, k, element, equations, dg u_local = get_node_vars(u, equations, dg, i, j, k, element) diff --git a/src/callbacks_step/analysis_surface_integral.jl b/src/callbacks_step/analysis_surface_integral.jl index 1366c8c73a9..33d4262429a 100644 --- a/src/callbacks_step/analysis_surface_integral.jl +++ b/src/callbacks_step/analysis_surface_integral.jl @@ -54,7 +54,7 @@ end # Abstract base type used for dispatch of `analyze` for quantities # requiring gradients of the velocity field. -abstract type VariableViscous end +abstract type VariableParabolic end struct LiftCoefficientPressure{RealT <: Real, NDIMS} force_state::ForceState{RealT, NDIMS} @@ -64,11 +64,11 @@ struct DragCoefficientPressure{RealT <: Real, NDIMS} force_state::ForceState{RealT, NDIMS} end -struct LiftCoefficientShearStress{RealT <: Real, NDIMS} <: VariableViscous +struct LiftCoefficientShearStress{RealT <: Real, NDIMS} <: VariableParabolic force_state::ForceState{RealT, NDIMS} end -struct DragCoefficientShearStress{RealT <: Real, NDIMS} <: VariableViscous +struct DragCoefficientShearStress{RealT <: Real, NDIMS} <: VariableParabolic force_state::ForceState{RealT, NDIMS} end diff --git a/src/callbacks_step/analysis_surface_integral_2d.jl b/src/callbacks_step/analysis_surface_integral_2d.jl index fef4b9872d1..9c1899ef750 100644 --- a/src/callbacks_step/analysis_surface_integral_2d.jl +++ b/src/callbacks_step/analysis_surface_integral_2d.jl @@ -261,7 +261,7 @@ function analyze(surface_variable::AnalysisSurfaceIntegral{Variable}, du, u, t, mesh::P4estMesh{2}, equations, equations_parabolic, dg::DGSEM, cache, semi, - cache_parabolic) where {Variable <: VariableViscous} + cache_parabolic) where {Variable <: VariableParabolic} @unpack boundaries = cache @unpack node_coordinates, contravariant_vectors = cache.elements @unpack weights = dg.basis @@ -271,8 +271,8 @@ function analyze(surface_variable::AnalysisSurfaceIntegral{Variable}, du, u, t, boundary_indices = get_boundary_indices(boundary_symbols, boundary_symbol_indices) # Additions for parabolic - @unpack viscous_container = cache_parabolic - @unpack gradients = viscous_container + @unpack parabolic_container = cache_parabolic + @unpack gradients = parabolic_container gradients_x, gradients_y = gradients diff --git a/src/equations/compressible_navier_stokes_1d.jl b/src/equations/compressible_navier_stokes_1d.jl index f37a751d3a5..f6af3611042 100644 --- a/src/equations/compressible_navier_stokes_1d.jl +++ b/src/equations/compressible_navier_stokes_1d.jl @@ -176,7 +176,7 @@ function flux(u, gradients, orientation::Integer, # by dispatching on the type of `equations.mu`. mu = dynamic_viscosity(u, equations) - # viscous flux components in the x-direction + # parabolic flux components in the x-direction f1 = 0 f2 = tau_11 * mu f3 = (v1 * tau_11 + q1) * mu @@ -242,7 +242,7 @@ function entropy2cons(w, equations::CompressibleNavierStokesDiffusion1D) end # the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables. -# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed +# For CNS, it is simplest to formulate the parabolic terms in primitive variables, so we transform the transformed # variables into primitive variables. @inline function convert_transformed_to_primitive(u_transformed, equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive}) @@ -260,7 +260,7 @@ end # reverse engineers the gradients to be terms of the primitive variables (v1, T). # Helpful because then the diffusive fluxes have the same form as on paper. # Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused. -# TODO: parabolic; entropy stable viscous terms +# TODO: parabolic; entropy stable parabolic terms @inline function convert_derivative_to_primitive(u, gradient, ::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive}) return gradient diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl index 0deb1599742..125c94cba32 100644 --- a/src/equations/compressible_navier_stokes_2d.jl +++ b/src/equations/compressible_navier_stokes_2d.jl @@ -185,7 +185,7 @@ function flux(u, gradients, orientation::Integer, mu = dynamic_viscosity(u, equations) if orientation == 1 - # viscous flux components in the x-direction + # parabolic flux components in the x-direction f1 = 0 f2 = tau_11 * mu f3 = tau_12 * mu @@ -193,7 +193,7 @@ function flux(u, gradients, orientation::Integer, return SVector(f1, f2, f3, f4) else # if orientation == 2 - # viscous flux components in the y-direction + # parabolic flux components in the y-direction # Note, symmetry is exploited for tau_12 = tau_21 g1 = 0 g2 = tau_12 * mu # tau_21 * mu @@ -263,7 +263,7 @@ function entropy2cons(w, equations::CompressibleNavierStokesDiffusion2D) end # the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables. -# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed +# For CNS, it is simplest to formulate the parabolic terms in primitive variables, so we transform the transformed # variables into primitive variables. @inline function convert_transformed_to_primitive(u_transformed, equations::CompressibleNavierStokesDiffusion2D{GradientVariablesPrimitive}) @@ -281,7 +281,7 @@ end # reverse engineers the gradients to be terms of the primitive variables (v1, v2, T). # Helpful because then the diffusive fluxes have the same form as on paper. # Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused. -# TODO: parabolic; entropy stable viscous terms +# TODO: parabolic; entropy stable parabolic terms @inline function convert_derivative_to_primitive(u, gradient, ::CompressibleNavierStokesDiffusion2D{GradientVariablesPrimitive}) return gradient @@ -579,7 +579,7 @@ end x, t, operator_type::Divergence, equations::CompressibleNavierStokesDiffusion2D{GradientVariablesPrimitive}) - # for Dirichlet boundary conditions, we do not impose any conditions on the viscous fluxes + # for Dirichlet boundary conditions, we do not impose any conditions on the parabolic fluxes return flux_inner end end # @muladd diff --git a/src/equations/compressible_navier_stokes_3d.jl b/src/equations/compressible_navier_stokes_3d.jl index b24f8467033..fe4cc365e2a 100644 --- a/src/equations/compressible_navier_stokes_3d.jl +++ b/src/equations/compressible_navier_stokes_3d.jl @@ -198,7 +198,7 @@ function flux(u, gradients, orientation::Integer, mu = dynamic_viscosity(u, equations) if orientation == 1 - # viscous flux components in the x-direction + # parabolic flux components in the x-direction f1 = 0 f2 = tau_11 * mu f3 = tau_12 * mu @@ -207,7 +207,7 @@ function flux(u, gradients, orientation::Integer, return SVector(f1, f2, f3, f4, f5) elseif orientation == 2 - # viscous flux components in the y-direction + # parabolic flux components in the y-direction # Note, symmetry is exploited for tau_12 = tau_21 g1 = 0 g2 = tau_12 * mu # tau_21 * mu @@ -217,7 +217,7 @@ function flux(u, gradients, orientation::Integer, return SVector(g1, g2, g3, g4, g5) else # if orientation == 3 - # viscous flux components in the z-direction + # parabolic flux components in the z-direction # Note, symmetry is exploited for tau_13 = tau_31, tau_23 = tau_32 h1 = 0 h2 = tau_13 * mu # tau_31 * mu @@ -289,7 +289,7 @@ function entropy2cons(w, equations::CompressibleNavierStokesDiffusion3D) end # the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables. -# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed +# For CNS, it is simplest to formulate the parabolic terms in primitive variables, so we transform the transformed # variables into primitive variables. @inline function convert_transformed_to_primitive(u_transformed, equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive}) @@ -307,7 +307,7 @@ end # reverse engineers the gradients to be terms of the primitive variables (v1, v2, v3, T). # Helpful because then the diffusive fluxes have the same form as on paper. # Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused. -# TODO: parabolic; entropy stable viscous terms +# TODO: parabolic; entropy stable parabolic terms @inline function convert_derivative_to_primitive(u, gradient, ::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive}) return gradient @@ -619,7 +619,7 @@ end x, t, operator_type::Divergence, equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive}) - # for Dirichlet boundary conditions, we do not impose any conditions on the viscous fluxes + # for Dirichlet boundary conditions, we do not impose any conditions on the parabolic fluxes return flux_inner end end # @muladd diff --git a/src/equations/laplace_diffusion_2d.jl b/src/equations/laplace_diffusion_2d.jl index 3741116f1bb..c243f3de364 100644 --- a/src/equations/laplace_diffusion_2d.jl +++ b/src/equations/laplace_diffusion_2d.jl @@ -32,7 +32,7 @@ end # The penalization depends on the solver, but also depends explicitly on physical parameters, # and would probably need to be specialized for every different equation. function penalty(u_outer, u_inner, inv_h, equations_parabolic::LaplaceDiffusion2D, - dg::ViscousFormulationLocalDG) + dg::ParabolicFormulationLocalDG) return dg.penalty_parameter * (u_outer - u_inner) * equations_parabolic.diffusivity end diff --git a/src/equations/laplace_diffusion_3d.jl b/src/equations/laplace_diffusion_3d.jl index c2e3c49afee..ec3957676a2 100644 --- a/src/equations/laplace_diffusion_3d.jl +++ b/src/equations/laplace_diffusion_3d.jl @@ -35,7 +35,7 @@ end # The penalization depends on the solver, but also depends explicitly on physical parameters, # and would probably need to be specialized for every different equation. function penalty(u_outer, u_inner, inv_h, equations_parabolic::LaplaceDiffusion3D, - dg::ViscousFormulationLocalDG) + dg::ParabolicFormulationLocalDG) return dg.penalty_parameter * (u_outer - u_inner) * equations_parabolic.diffusivity end diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index c730439017c..bb6487d46f4 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -79,7 +79,7 @@ function SemidiscretizationHyperbolicParabolic(mesh, equations::Tuple, @assert ndims(mesh) == ndims(equations_parabolic) if !(nvariables(equations) == nvariables(equations_parabolic)) - throw(ArgumentError("Current implementation of viscous terms requires the same number of conservative and gradient variables.")) + throw(ArgumentError("Current implementation of parabolic terms requires the same number of conservative and gradient variables.")) end boundary_conditions, boundary_conditions_parabolic = boundary_conditions diff --git a/src/solvers/dgmulti/dg_parabolic.jl b/src/solvers/dgmulti/dg_parabolic.jl index cb500094ca4..507f91c7890 100644 --- a/src/solvers/dgmulti/dg_parabolic.jl +++ b/src/solvers/dgmulti/dg_parabolic.jl @@ -30,7 +30,7 @@ function create_cache_parabolic(mesh::DGMultiMesh, (dg.basis.Nq, mesh.md.num_elements)), ndims(mesh))) - flux_viscous = similar.(gradients) + flux_parabolic = similar.(gradients) u_face_values = allocate_nested_array(uEltype, nvars, size(md.xf), dg) scalar_flux_face_values = similar(u_face_values) @@ -38,20 +38,20 @@ function create_cache_parabolic(mesh::DGMultiMesh, local_u_values_threaded = [similar(u_transformed, dg.basis.Nq) for _ in 1:Threads.maxthreadid()] - local_flux_viscous_threaded = [SVector{ndims(mesh)}(ntuple(_ -> similar(u_transformed, - dg.basis.Nq), - ndims(mesh))) - for _ in 1:Threads.maxthreadid()] + local_flux_parabolic_threaded = [SVector{ndims(mesh)}(ntuple(_ -> similar(u_transformed, + dg.basis.Nq), + ndims(mesh))) + for _ in 1:Threads.maxthreadid()] local_flux_face_values_threaded = [similar(scalar_flux_face_values[:, 1]) for _ in 1:Threads.maxthreadid()] - return (; u_transformed, gradients, flux_viscous, + return (; u_transformed, gradients, flux_parabolic, weak_differentiation_matrices, strong_differentiation_matrices, gradient_lift_matrix, projection_face_interpolation_matrix, divergence_lift_matrix, dxidxhatj, J, invJ, # geometric terms u_face_values, gradients_face_values, scalar_flux_face_values, - local_u_values_threaded, local_flux_viscous_threaded, + local_u_values_threaded, local_flux_parabolic_threaded, local_flux_face_values_threaded) end @@ -115,13 +115,13 @@ end function calc_volume_integral_gradient!(gradients, u, mesh::DGMultiMesh{NDIMS, <:NonAffine}, equations::AbstractEquationsParabolic, dg::DGMulti, cache, cache_parabolic) where {NDIMS} - (; strong_differentiation_matrices, dxidxhatj, local_flux_viscous_threaded) = cache_parabolic + (; strong_differentiation_matrices, dxidxhatj, local_flux_parabolic_threaded) = cache_parabolic # compute volume contributions to gradients @threaded for e in eachelement(mesh, dg) # compute gradients with respect to reference coordinates - local_reference_gradients = local_flux_viscous_threaded[Threads.threadid()] + local_reference_gradients = local_flux_parabolic_threaded[Threads.threadid()] for i in eachdim(mesh) apply_to_each_field(mul_by!(strong_differentiation_matrices[i]), local_reference_gradients[i], view(u, :, e)) @@ -143,7 +143,7 @@ end function calc_interface_flux_gradient!(scalar_flux_face_values, mesh::DGMultiMesh, equations, dg::DGMulti, - parabolic_scheme::ViscousFormulationBassiRebay1, + parabolic_scheme::ParabolicFormulationBassiRebay1, cache, cache_parabolic) (; u_face_values) = cache_parabolic (; mapM, mapP) = mesh.md @@ -298,11 +298,11 @@ function calc_single_boundary_flux!(flux_face_values, u_face_values, t, return nothing end -function calc_viscous_fluxes!(flux_viscous, u, gradients, mesh::DGMultiMesh, - equations::AbstractEquationsParabolic, - dg::DGMulti, cache, cache_parabolic) +function calc_parabolic_fluxes!(flux_parabolic, u, gradients, mesh::DGMultiMesh, + equations::AbstractEquationsParabolic, + dg::DGMulti, cache, cache_parabolic) for dim in eachdim(mesh) - set_zero!(flux_viscous[dim], dg) + set_zero!(flux_parabolic[dim], dg) end (; local_u_values_threaded) = cache_parabolic @@ -315,13 +315,13 @@ function calc_viscous_fluxes!(flux_viscous, u, gradients, mesh::DGMultiMesh, fill!(local_u_values, zero(eltype(local_u_values))) apply_to_each_field(mul_by!(dg.basis.Vq), local_u_values, view(u, :, e)) - # compute viscous flux at quad points + # compute parabolic flux at quad points for i in eachindex(local_u_values) u_i = local_u_values[i] gradients_i = getindex.(gradients, i, e) for dim in eachdim(mesh) - flux_viscous_i = flux(u_i, gradients_i, dim, equations) - setindex!(flux_viscous[dim], flux_viscous_i, i, e) + flux_parabolic_i = flux(u_i, gradients_i, dim, equations) + setindex!(flux_parabolic[dim], flux_parabolic_i, i, e) end end end @@ -330,18 +330,19 @@ function calc_viscous_fluxes!(flux_viscous, u, gradients, mesh::DGMultiMesh, end # no penalization for a BR1 parabolic solver -function calc_viscous_penalty!(scalar_flux_face_values, u_face_values, t, - boundary_conditions, - mesh, equations::AbstractEquationsParabolic, - dg::DGMulti, parabolic_scheme::ViscousFormulationBassiRebay1, - cache, cache_parabolic) +function calc_parabolic_penalty!(scalar_flux_face_values, u_face_values, t, + boundary_conditions, + mesh, equations::AbstractEquationsParabolic, + dg::DGMulti, + parabolic_scheme::ParabolicFormulationBassiRebay1, + cache, cache_parabolic) return nothing end -function calc_viscous_penalty!(scalar_flux_face_values, u_face_values, t, - boundary_conditions, mesh, - equations::AbstractEquationsParabolic, - dg::DGMulti, parabolic_scheme, cache, cache_parabolic) +function calc_parabolic_penalty!(scalar_flux_face_values, u_face_values, t, + boundary_conditions, mesh, + equations::AbstractEquationsParabolic, + dg::DGMulti, parabolic_scheme, cache, cache_parabolic) # compute fluxes at interfaces (; scalar_flux_face_values) = cache_parabolic (; mapM, mapP) = mesh.md @@ -354,7 +355,7 @@ function calc_viscous_penalty!(scalar_flux_face_values, u_face_values, t, return nothing end -function calc_volume_integral_divergence!(du, u, flux_viscous, mesh::DGMultiMesh, +function calc_volume_integral_divergence!(du, u, flux_parabolic, mesh::DGMultiMesh, equations::AbstractEquationsParabolic, dg::DGMulti, cache, cache_parabolic) (; weak_differentiation_matrices) = cache_parabolic @@ -364,36 +365,36 @@ function calc_volume_integral_divergence!(du, u, flux_viscous, mesh::DGMultiMesh for i in eachdim(mesh), j in eachdim(mesh) dxidxhatj = mesh.md.rstxyzJ[i, j][1, e] # assumes mesh is affine apply_to_each_field(mul_by_accum!(weak_differentiation_matrices[j], dxidxhatj), - view(du, :, e), view(flux_viscous[i], :, e)) + view(du, :, e), view(flux_parabolic[i], :, e)) end end return nothing end -function calc_volume_integral_divergence!(du, u, flux_viscous, +function calc_volume_integral_divergence!(du, u, flux_parabolic, mesh::DGMultiMesh{NDIMS, <:NonAffine}, equations::AbstractEquationsParabolic, dg::DGMulti, cache, cache_parabolic) where {NDIMS} - (; weak_differentiation_matrices, dxidxhatj, local_flux_viscous_threaded) = cache_parabolic + (; weak_differentiation_matrices, dxidxhatj, local_flux_parabolic_threaded) = cache_parabolic # compute volume contributions to divergence @threaded for e in eachelement(mesh, dg) - local_viscous_flux = local_flux_viscous_threaded[Threads.threadid()][1] + local_parabolic_flux = local_flux_parabolic_threaded[Threads.threadid()][1] for i in eachdim(mesh) # rotate flux to reference coordinates - fill!(local_viscous_flux, zero(eltype(local_viscous_flux))) + fill!(local_parabolic_flux, zero(eltype(local_parabolic_flux))) for j in eachdim(mesh) - for node in eachindex(local_viscous_flux) - local_viscous_flux[node] = local_viscous_flux[node] + - dxidxhatj[j, i][node, e] * - flux_viscous[j][node, e] + for node in eachindex(local_parabolic_flux) + local_parabolic_flux[node] = local_parabolic_flux[node] + + dxidxhatj[j, i][node, e] * + flux_parabolic[j][node, e] end end # differentiate with respect to reference coordinates apply_to_each_field(mul_by_accum!(weak_differentiation_matrices[i]), - view(du, :, e), local_viscous_flux) + view(du, :, e), local_parabolic_flux) end end @@ -402,9 +403,9 @@ end function calc_interface_flux_divergence!(scalar_flux_face_values, mesh, equations, dg, - parabolic_scheme::ViscousFormulationBassiRebay1, + parabolic_scheme::ParabolicFormulationBassiRebay1, cache, cache_parabolic) - flux_viscous_face_values = cache_parabolic.gradients_face_values # reuse storage + flux_parabolic_face_values = cache_parabolic.gradients_face_values # reuse storage (; mapM, mapP, nxyzJ) = mesh.md @threaded for face_node_index in each_face_node_global(mesh, dg, cache, cache_parabolic) @@ -413,8 +414,8 @@ function calc_interface_flux_divergence!(scalar_flux_face_values, # compute f(u, ∇u) ⋅ n flux_face_value = zero(eltype(scalar_flux_face_values)) for dim in eachdim(mesh) - fM = flux_viscous_face_values[dim][idM] - fP = flux_viscous_face_values[dim][idP] + fM = flux_parabolic_face_values[dim][idM] + fP = flux_parabolic_face_values[dim][idP] # Here, we use the "weak" formulation to compute the divergence (to ensure stability on curved meshes). flux_face_value = flux_face_value + 0.5f0 * (fP + fM) * nxyzJ[dim][face_node_index] @@ -425,21 +426,21 @@ function calc_interface_flux_divergence!(scalar_flux_face_values, return nothing end -function calc_divergence!(du, u::StructArray, t, flux_viscous, mesh::DGMultiMesh, +function calc_divergence!(du, u::StructArray, t, flux_parabolic, mesh::DGMultiMesh, equations::AbstractEquationsParabolic, boundary_conditions, dg::DGMulti, parabolic_scheme, cache, cache_parabolic) set_zero!(du, dg) - calc_volume_integral_divergence!(du, u, flux_viscous, mesh, equations, dg, cache, + calc_volume_integral_divergence!(du, u, flux_parabolic, mesh, equations, dg, cache, cache_parabolic) # interpolates from solution coefficients to face quadrature points (; projection_face_interpolation_matrix) = cache_parabolic - flux_viscous_face_values = cache_parabolic.gradients_face_values # reuse storage + flux_parabolic_face_values = cache_parabolic.gradients_face_values # reuse storage for dim in eachdim(mesh) apply_to_each_field(mul_by!(projection_face_interpolation_matrix), - flux_viscous_face_values[dim], flux_viscous[dim]) + flux_parabolic_face_values[dim], flux_parabolic[dim]) end # compute fluxes at interfaces @@ -452,9 +453,9 @@ function calc_divergence!(du, u::StructArray, t, flux_viscous, mesh::DGMultiMesh Divergence(), boundary_conditions, mesh, equations, dg, cache, cache_parabolic) - calc_viscous_penalty!(scalar_flux_face_values, cache_parabolic.u_face_values, t, - boundary_conditions, mesh, equations, dg, parabolic_scheme, - cache, cache_parabolic) + calc_parabolic_penalty!(scalar_flux_face_values, cache_parabolic.u_face_values, t, + boundary_conditions, mesh, equations, dg, parabolic_scheme, + cache, cache_parabolic) # surface contributions apply_to_each_field(mul_by_accum!(cache_parabolic.divergence_lift_matrix), du, @@ -476,7 +477,7 @@ function rhs_parabolic!(du, u, t, mesh::DGMultiMesh, set_zero!(du, dg) @trixi_timeit timer() "transform variables" begin - (; u_transformed, gradients, flux_viscous) = cache_parabolic + (; u_transformed, gradients, flux_parabolic) = cache_parabolic transform_variables!(u_transformed, u, mesh, equations_parabolic, dg, cache) end @@ -486,13 +487,13 @@ function rhs_parabolic!(du, u, t, mesh::DGMultiMesh, boundary_conditions, dg, parabolic_scheme, cache, cache_parabolic) end - @trixi_timeit timer() "calc viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, u_transformed, gradients, - mesh, equations_parabolic, dg, cache, cache_parabolic) + @trixi_timeit timer() "calc parabolic fluxes" begin + calc_parabolic_fluxes!(flux_parabolic, u_transformed, gradients, + mesh, equations_parabolic, dg, cache, cache_parabolic) end @trixi_timeit timer() "calc divergence" begin - calc_divergence!(du, u_transformed, t, flux_viscous, mesh, equations_parabolic, + calc_divergence!(du, u_transformed, t, flux_parabolic, mesh, equations_parabolic, boundary_conditions, dg, parabolic_scheme, cache, cache_parabolic) end @@ -500,7 +501,7 @@ function rhs_parabolic!(du, u, t, mesh::DGMultiMesh, # Note: we do not flip the sign of the geometric Jacobian here. # This is because the parabolic fluxes are assumed to be of the form # `du/dt + df/dx = dg/dx + source(x,t)`, - # where f(u) is the inviscid flux and g(u) is the viscous flux. + # where f(u) is the inviscid flux and g(u) is the parabolic flux. invert_jacobian!(du, mesh, equations_parabolic, dg, cache; scaling = 1) end diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl index 35372eecea7..b68bd64729f 100644 --- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl @@ -10,7 +10,7 @@ Reusing `rhs_parabolic!` for `P4estMesh`es is not easily possible as for `P4estMesh`es we call ``` - prolong2mortars_divergence!(cache, flux_viscous, mesh, equations_parabolic, + prolong2mortars_divergence!(cache, flux_parabolic, mesh, equations_parabolic, dg.mortar, dg) calc_mortar_flux_divergence!(cache_parabolic.elements.surface_flux_values, @@ -18,7 +18,7 @@ for `P4estMesh`es we call ``` instead of ``` - prolong2mortars!(cache, flux_viscous, mesh, equations_parabolic, + prolong2mortars!(cache, flux_parabolic, mesh, equations_parabolic, dg.mortar, dg) calc_mortar_flux!(cache_parabolic.elements.surface_flux_values, @@ -30,10 +30,10 @@ function rhs_parabolic!(du, u, t, mesh::Union{P4estMesh{2}, P4estMesh{3}}, equations_parabolic::AbstractEquationsParabolic, boundary_conditions_parabolic, source_terms_parabolic, dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack viscous_container = cache_parabolic - @unpack u_transformed, gradients, flux_viscous = viscous_container + @unpack parabolic_container = cache_parabolic + @unpack u_transformed, gradients, flux_parabolic = parabolic_container - # Convert conservative variables to a form more suitable for viscous flux calculations + # Convert conservative variables to a form more suitable for parabolic flux calculations @trixi_timeit timer() "transform variables" begin transform_variables!(u_transformed, u, mesh, equations_parabolic, dg, cache) @@ -46,20 +46,20 @@ function rhs_parabolic!(du, u, t, mesh::Union{P4estMesh{2}, P4estMesh{3}}, dg, parabolic_scheme, cache) end - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache) + # Compute and store the parabolic fluxes + @trixi_timeit timer() "calculate parabolic fluxes" begin + calc_parabolic_fluxes!(flux_parabolic, gradients, u_transformed, mesh, + equations_parabolic, dg, cache) end # The remainder of this function is essentially a regular rhs! for parabolic - # equations (i.e., it computes the divergence of the viscous fluxes) + # equations (i.e., it computes the divergence of the parabolic fluxes) # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # OBS! In `calc_parabolic_fluxes!`, the parabolic flux values at the volume nodes of each element have + # been computed and stored in `flux_parabolic`. In the following, we *reuse* (abuse) the # `interfaces` and `boundaries` containers in `cache` to interpolate and store the # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *parabolic flux values* # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we # do not need to recreate the existing data structure only with a different name, and c) we do not # need to interpolate solutions *and* gradients to the surfaces. @@ -68,32 +68,32 @@ function rhs_parabolic!(du, u, t, mesh::Union{P4estMesh{2}, P4estMesh{3}}, @trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache) # Calculate volume integral - # This calls the specialized version for the viscous fluxes from + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + calc_volume_integral!(du, flux_parabolic, mesh, equations_parabolic, dg, cache) end # Prolong solution to interfaces. - # This calls the specialized version for the viscous fluxes from + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache, flux_viscous, mesh, equations_parabolic, dg) + prolong2interfaces!(cache, flux_parabolic, mesh, equations_parabolic, dg) end # Calculate interface fluxes - # This calls the specialized version for the viscous fluxes from + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "interface flux" begin calc_interface_flux!(cache.elements.surface_flux_values, mesh, equations_parabolic, dg, parabolic_scheme, cache) end - # Prolong viscous fluxes to boundaries. - # This calls the specialized version for the viscous fluxes from + # Prolong parabolic fluxes to boundaries. + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache, flux_viscous, mesh, equations_parabolic, dg) + prolong2boundaries!(cache, flux_parabolic, mesh, equations_parabolic, dg) end # Calculate boundary fluxes. @@ -108,7 +108,7 @@ function rhs_parabolic!(du, u, t, mesh::Union{P4estMesh{2}, P4estMesh{3}}, # Prolong solution to mortars. # This calls the specialized version for parabolic equations. @trixi_timeit timer() "prolong2mortars" begin - prolong2mortars_divergence!(cache, flux_viscous, mesh, equations_parabolic, + prolong2mortars_divergence!(cache, flux_parabolic, mesh, equations_parabolic, dg.mortar, dg) end @@ -337,7 +337,7 @@ function calc_interface_flux_gradient!(surface_flux_values, return nothing end -# This is the version used when calculating the gradient of the viscous fluxes (called from above) +# This is the version used when calculating the gradient of the parabolic fluxes (called from above) @inline function calc_interface_flux_gradient!(surface_flux_values, mesh::P4estMesh{2}, equations_parabolic, dg::DG, parabolic_scheme, cache, @@ -366,22 +366,22 @@ end return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. +# This is the version used when calculating the divergence of the parabolic fluxes. # Identical to weak-form volume integral/kernel for the purely hyperbolic case, -# except that the fluxes are here already precomputed in `calc_viscous_fluxes!` -function calc_volume_integral!(du, flux_viscous, mesh::P4estMesh{2}, +# except that the fluxes are here already precomputed in `calc_parabolic_fluxes!` +function calc_volume_integral!(du, flux_parabolic, mesh::P4estMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM, cache) (; derivative_hat) = dg.basis (; contravariant_vectors) = cache.elements - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for element in eachelement(dg, cache) # Calculate volume terms in one element for j in eachnode(dg), i in eachnode(dg) - flux1 = get_node_vars(flux_viscous_x, equations_parabolic, dg, + flux1 = get_node_vars(flux_parabolic_x, equations_parabolic, dg, i, j, element) - flux2 = get_node_vars(flux_viscous_y, equations_parabolic, dg, + flux2 = get_node_vars(flux_parabolic_y, equations_parabolic, dg, i, j, element) # Compute the contravariant flux by taking the scalar product of the @@ -411,18 +411,18 @@ function calc_volume_integral!(du, flux_viscous, mesh::P4estMesh{2}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2interfaces!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2interfaces!(cache, flux_viscous::Tuple, +function prolong2interfaces!(cache, flux_parabolic::Tuple, mesh::Union{P4estMesh{2}, P4estMeshView{2}}, equations_parabolic::AbstractEquationsParabolic, dg::DG) (; interfaces) = cache (; contravariant_vectors) = cache.elements index_range = eachnode(dg) - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for interface in eachinterface(dg, cache) # Copy solution data from the primary element using "delayed indexing" with @@ -450,12 +450,12 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, for v in eachvariable(equations_parabolic) # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous = SVector(flux_viscous_x[v, i_primary, j_primary, - primary_element], - flux_viscous_y[v, i_primary, j_primary, - primary_element]) + flux_parabolic = SVector(flux_parabolic_x[v, i_primary, j_primary, + primary_element], + flux_parabolic_y[v, i_primary, j_primary, + primary_element]) - interfaces.u[1, v, i, interface] = dot(flux_viscous, normal_direction) + interfaces.u[1, v, i, interface] = dot(flux_parabolic, normal_direction) end i_primary += i_primary_step j_primary += j_primary_step @@ -485,13 +485,14 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, for v in eachvariable(equations_parabolic) # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous = SVector(flux_viscous_x[v, i_secondary, j_secondary, - secondary_element], - flux_viscous_y[v, i_secondary, j_secondary, - secondary_element]) + flux_parabolic = SVector(flux_parabolic_x[v, i_secondary, j_secondary, + secondary_element], + flux_parabolic_y[v, i_secondary, j_secondary, + secondary_element]) # store the normal flux with respect to the primary normal direction, # which is the negative of the secondary normal direction - interfaces.u[2, v, i, interface] = -dot(flux_viscous, normal_direction) + interfaces.u[2, v, i, interface] = -dot(flux_parabolic, + normal_direction) end i_secondary += i_secondary_step j_secondary += j_secondary_step @@ -547,15 +548,15 @@ function calc_interface_flux!(surface_flux_values, mesh::P4estMesh{2}, i_primary, j_primary, primary_element) - # We prolong the viscous flux dotted with respect the outward normal on the + # We prolong the parabolic flux dotted with respect the outward normal on the # primary element. - viscous_flux_normal_ll, viscous_flux_normal_rr = get_surface_node_vars(cache.interfaces.u, - equations_parabolic, - dg, - i, - interface) + parabolic_flux_normal_ll, parabolic_flux_normal_rr = get_surface_node_vars(cache.interfaces.u, + equations_parabolic, + dg, + i, + interface) - flux_ = flux_parabolic(viscous_flux_normal_ll, viscous_flux_normal_rr, + flux_ = flux_parabolic(parabolic_flux_normal_ll, parabolic_flux_normal_rr, normal_direction, Divergence(), equations_parabolic, parabolic_scheme) @@ -577,7 +578,7 @@ function calc_interface_flux!(surface_flux_values, mesh::P4estMesh{2}, return nothing end -function prolong2mortars_divergence!(cache, flux_viscous, +function prolong2mortars_divergence!(cache, flux_parabolic, mesh::P4estMesh{2}, equations_parabolic, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM) @@ -585,7 +586,7 @@ function prolong2mortars_divergence!(cache, flux_viscous, @unpack contravariant_vectors = cache.elements index_range = eachnode(dg) - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for mortar in eachmortar(dg, cache) # Copy solution data from the small elements using "delayed indexing" with @@ -608,10 +609,12 @@ function prolong2mortars_divergence!(cache, flux_viscous, i_small, j_small, element) for v in eachvariable(equations_parabolic) - flux_viscous = SVector(flux_viscous_x[v, i_small, j_small, element], - flux_viscous_y[v, i_small, j_small, element]) + flux_parabolic = SVector(flux_parabolic_x[v, i_small, j_small, + element], + flux_parabolic_y[v, i_small, j_small, + element]) - cache.mortars.u[1, v, position, i, mortar] = dot(flux_viscous, + cache.mortars.u[1, v, position, i, mortar] = dot(flux_parabolic, normal_direction) end i_small += i_small_step @@ -642,14 +645,14 @@ function prolong2mortars_divergence!(cache, flux_viscous, i_large, j_large, element) for v in eachvariable(equations_parabolic) - flux_viscous = SVector(flux_viscous_x[v, i_large, j_large, element], - flux_viscous_y[v, i_large, j_large, element]) + flux_parabolic = SVector(flux_parabolic_x[v, i_large, j_large, element], + flux_parabolic_y[v, i_large, j_large, element]) - # We prolong the viscous flux dotted with respect the outward normal + # We prolong the parabolic flux dotted with respect the outward normal # on the small element. We scale by -1/2 here because the normal # direction on the large element is negative 2x that of the small # element (these normal directions are "scaled" by the surface Jacobian) - u_buffer[v, i] = -0.5f0 * dot(flux_viscous, normal_direction) + u_buffer[v, i] = -0.5f0 * dot(flux_parabolic, normal_direction) end i_large += i_large_step j_large += j_large_step @@ -699,11 +702,13 @@ function calc_mortar_flux_divergence!(surface_flux_values, mesh::P4estMesh{2}, i_small, j_small, element) for v in eachvariable(equations_parabolic) - viscous_flux_normal_ll = cache.mortars.u[1, v, position, i, mortar] - viscous_flux_normal_rr = cache.mortars.u[2, v, position, i, mortar] + parabolic_flux_normal_ll = cache.mortars.u[1, v, position, i, + mortar] + parabolic_flux_normal_rr = cache.mortars.u[2, v, position, i, + mortar] - flux_ = flux_parabolic(viscous_flux_normal_ll, - viscous_flux_normal_rr, + flux_ = flux_parabolic(parabolic_flux_normal_ll, + parabolic_flux_normal_rr, normal_direction, Divergence(), equations_parabolic, parabolic_scheme) @@ -792,7 +797,7 @@ end # We structure `calc_mortar_flux_gradient!` similarly to "calc_mortar_flux!" for # hyperbolic equations with no nonconservative terms. # The reasoning is that parabolic fluxes are treated like conservative -# terms (e.g., we compute a viscous conservative "flux") and thus no +# terms (e.g., we compute a parabolic conservative "flux") and thus no # non-conservative terms are present. @inline function calc_mortar_flux_gradient!(fstar_primary, fstar_secondary, mesh::P4estMesh{2}, equations_parabolic, @@ -818,10 +823,10 @@ end return nothing end -# Specialization `flux_viscous::Tuple` needed to +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2boundaries!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2boundaries!(cache, flux_viscous::Tuple, +function prolong2boundaries!(cache, flux_parabolic::Tuple, mesh::P4estMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DG) @@ -829,7 +834,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, (; contravariant_vectors) = cache.elements index_range = eachnode(dg) - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for boundary in eachboundary(dg, cache) # Copy solution data from the element using "delayed indexing" with @@ -849,10 +854,10 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, i_node, j_node, element) for v in eachvariable(equations_parabolic) - flux_viscous = SVector(flux_viscous_x[v, i_node, j_node, element], - flux_viscous_y[v, i_node, j_node, element]) + flux_parabolic = SVector(flux_parabolic_x[v, i_node, j_node, element], + flux_parabolic_y[v, i_node, j_node, element]) - boundaries.u[v, i, boundary] = dot(flux_viscous, normal_direction) + boundaries.u[v, i, boundary] = dot(flux_parabolic, normal_direction) end i_node += i_node_step j_node += j_node_step @@ -1190,7 +1195,7 @@ end # Needed to *not* flip the sign of the inverse Jacobian. # This is because the parabolic fluxes are assumed to be of the form # `du/dt + df/dx = dg/dx + source(x,t)`, -# where f(u) is the inviscid flux and g(u) is the viscous flux. +# where f(u) is the inviscid flux and g(u) is the parabolic flux. function apply_jacobian_parabolic!(du::AbstractArray, mesh::P4estMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DG, cache) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 635fb9b72bc..f731a31c66b 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -195,24 +195,24 @@ end return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. +# This is the version used when calculating the divergence of the parabolic fluxes. # Identical to weak-form volume integral/kernel for the purely hyperbolic case, -# except that the fluxes are here already precomputed in `calc_viscous_fluxes!` -function calc_volume_integral!(du, flux_viscous, mesh::P4estMesh{3}, +# except that the fluxes are here already precomputed in `calc_parabolic_fluxes!` +function calc_volume_integral!(du, flux_parabolic, mesh::P4estMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM, cache) (; derivative_hat) = dg.basis (; contravariant_vectors) = cache.elements - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for element in eachelement(dg, cache) # Calculate volume terms in one element for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg) - flux1 = get_node_vars(flux_viscous_x, equations_parabolic, dg, + flux1 = get_node_vars(flux_parabolic_x, equations_parabolic, dg, i, j, k, element) - flux2 = get_node_vars(flux_viscous_y, equations_parabolic, dg, + flux2 = get_node_vars(flux_parabolic_y, equations_parabolic, dg, i, j, k, element) - flux3 = get_node_vars(flux_viscous_z, equations_parabolic, dg, + flux3 = get_node_vars(flux_parabolic_z, equations_parabolic, dg, i, j, k, element) # Compute the contravariant flux by taking the scalar product of the @@ -253,18 +253,18 @@ function calc_volume_integral!(du, flux_viscous, mesh::P4estMesh{3}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2interfaces!` in dg_3d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 5}`. -function prolong2interfaces!(cache, flux_viscous::Tuple, +function prolong2interfaces!(cache, flux_parabolic::Tuple, mesh::P4estMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DG) (; interfaces) = cache (; contravariant_vectors) = cache.elements index_range = eachnode(dg) - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for interface in eachinterface(dg, cache) # Copy solution data from the primary element using "delayed indexing" with @@ -298,14 +298,17 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, for v in eachvariable(equations_parabolic) # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous = SVector(flux_viscous_x[v, i_primary, j_primary, - k_primary, primary_element], - flux_viscous_y[v, i_primary, j_primary, - k_primary, primary_element], - flux_viscous_z[v, i_primary, j_primary, - k_primary, primary_element]) - - interfaces.u[1, v, i, j, interface] = dot(flux_viscous, + flux_parabolic = SVector(flux_parabolic_x[v, i_primary, j_primary, + k_primary, + primary_element], + flux_parabolic_y[v, i_primary, j_primary, + k_primary, + primary_element], + flux_parabolic_z[v, i_primary, j_primary, + k_primary, + primary_element]) + + interfaces.u[1, v, i, j, interface] = dot(flux_parabolic, normal_direction) end i_primary += i_primary_step_i @@ -346,18 +349,21 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, for v in eachvariable(equations_parabolic) # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous = SVector(flux_viscous_x[v, i_secondary, j_secondary, - k_secondary, - secondary_element], - flux_viscous_y[v, i_secondary, j_secondary, - k_secondary, - secondary_element], - flux_viscous_z[v, i_secondary, j_secondary, - k_secondary, - secondary_element]) + flux_parabolic = SVector(flux_parabolic_x[v, i_secondary, + j_secondary, + k_secondary, + secondary_element], + flux_parabolic_y[v, i_secondary, + j_secondary, + k_secondary, + secondary_element], + flux_parabolic_z[v, i_secondary, + j_secondary, + k_secondary, + secondary_element]) # store the normal flux with respect to the primary normal direction, # which is the negative of the secondary normal direction - interfaces.u[2, v, i, j, interface] = -dot(flux_viscous, + interfaces.u[2, v, i, j, interface] = -dot(flux_parabolic, normal_direction) end i_secondary += i_secondary_step_i @@ -423,16 +429,17 @@ function calc_interface_flux!(surface_flux_values, mesh::P4estMesh{3}, contravariant_vectors, i_primary, j_primary, k_primary, primary_element) - # We prolong the viscous flux dotted with respect the outward normal on the + # We prolong the parabolic flux dotted with respect the outward normal on the # primary element. - viscous_flux_normal_ll, viscous_flux_normal_rr = get_surface_node_vars(cache.interfaces.u, - equations_parabolic, - dg, - i, - j, - interface) - - flux_ = flux_parabolic(viscous_flux_normal_ll, viscous_flux_normal_rr, + parabolic_flux_normal_ll, parabolic_flux_normal_rr = get_surface_node_vars(cache.interfaces.u, + equations_parabolic, + dg, + i, + j, + interface) + + flux_ = flux_parabolic(parabolic_flux_normal_ll, + parabolic_flux_normal_rr, normal_direction, Divergence(), equations_parabolic, parabolic_scheme) @@ -464,7 +471,7 @@ function calc_interface_flux!(surface_flux_values, mesh::P4estMesh{3}, return nothing end -function prolong2mortars_divergence!(cache, flux_viscous, +function prolong2mortars_divergence!(cache, flux_parabolic, mesh::P4estMesh{3}, equations_parabolic, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM) @@ -473,7 +480,7 @@ function prolong2mortars_divergence!(cache, flux_viscous, @unpack contravariant_vectors = cache.elements index_range = eachnode(dg) - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for mortar in eachmortar(dg, cache) # Copy solution data from the small elements using "delayed indexing" with @@ -501,14 +508,14 @@ function prolong2mortars_divergence!(cache, flux_viscous, element) for v in eachvariable(equations_parabolic) - flux_viscous = SVector(flux_viscous_x[v, i_small, j_small, - k_small, element], - flux_viscous_y[v, i_small, j_small, - k_small, element], - flux_viscous_z[v, i_small, j_small, - k_small, element]) - - cache.mortars.u[1, v, position, i, j, mortar] = dot(flux_viscous, + flux_parabolic = SVector(flux_parabolic_x[v, i_small, j_small, + k_small, element], + flux_parabolic_y[v, i_small, j_small, + k_small, element], + flux_parabolic_z[v, i_small, j_small, + k_small, element]) + + cache.mortars.u[1, v, position, i, j, mortar] = dot(flux_parabolic, normal_direction) end i_small += i_small_step_i @@ -551,18 +558,21 @@ function prolong2mortars_divergence!(cache, flux_viscous, element) for v in eachvariable(equations_parabolic) - flux_viscous = SVector(flux_viscous_x[v, i_large, j_large, k_large, - element], - flux_viscous_y[v, i_large, j_large, k_large, - element], - flux_viscous_z[v, i_large, j_large, k_large, - element]) - - # We prolong the viscous flux dotted with respect the outward normal + flux_parabolic = SVector(flux_parabolic_x[v, i_large, j_large, + k_large, + element], + flux_parabolic_y[v, i_large, j_large, + k_large, + element], + flux_parabolic_z[v, i_large, j_large, + k_large, + element]) + + # We prolong the parabolic flux dotted with respect the outward normal # on the small element. We scale by -1/2 here because the normal # direction on the large element is negative 2x that of the small # element (these normal directions are "scaled" by the surface Jacobian) - u_buffer[v, i, j] = -0.5f0 * dot(flux_viscous, normal_direction) + u_buffer[v, i, j] = -0.5f0 * dot(flux_parabolic, normal_direction) end i_large += i_large_step_i j_large += j_large_step_i @@ -634,13 +644,13 @@ function calc_mortar_flux_divergence!(surface_flux_values, element) for v in eachvariable(equations_parabolic) - viscous_flux_normal_ll = cache.mortars.u[1, v, position, i, j, - mortar] - viscous_flux_normal_rr = cache.mortars.u[2, v, position, i, j, - mortar] + parabolic_flux_normal_ll = cache.mortars.u[1, v, position, i, j, + mortar] + parabolic_flux_normal_rr = cache.mortars.u[2, v, position, i, j, + mortar] - flux_ = flux_parabolic(viscous_flux_normal_ll, - viscous_flux_normal_rr, + flux_ = flux_parabolic(parabolic_flux_normal_ll, + parabolic_flux_normal_rr, normal_direction, Divergence(), equations_parabolic, parabolic_scheme) @@ -748,7 +758,7 @@ end # We structure `calc_mortar_flux_gradient!` similarly to "calc_mortar_flux!" for # hyperbolic equations with no nonconservative terms. # The reasoning is that parabolic fluxes are treated like conservative -# terms (e.g., we compute a viscous conservative "flux") and thus no +# terms (e.g., we compute a parabolic conservative "flux") and thus no # non-conservative terms are present. @inline function calc_mortar_flux_gradient!(fstar_primary, fstar_secondary, mesh::P4estMesh{3}, @@ -852,10 +862,10 @@ function calc_volume_integral_gradient!(gradients, u_transformed, return nothing end -# Specialization `flux_viscous::Tuple` needed to +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2boundaries!` in dg_3d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 5}`. -function prolong2boundaries!(cache, flux_viscous::Tuple, +function prolong2boundaries!(cache, flux_parabolic::Tuple, mesh::P4estMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DG) @@ -863,7 +873,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, (; contravariant_vectors) = cache.elements index_range = eachnode(dg) - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for boundary in eachboundary(dg, cache) # Copy solution data from the element using "delayed indexing" with @@ -891,14 +901,14 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, i_node, j_node, k_node, element) for v in eachvariable(equations_parabolic) - flux_viscous = SVector(flux_viscous_x[v, i_node, j_node, k_node, - element], - flux_viscous_y[v, i_node, j_node, k_node, - element], - flux_viscous_z[v, i_node, j_node, k_node, - element]) - - boundaries.u[v, i, j, boundary] = dot(flux_viscous, + flux_parabolic = SVector(flux_parabolic_x[v, i_node, j_node, k_node, + element], + flux_parabolic_y[v, i_node, j_node, k_node, + element], + flux_parabolic_z[v, i_node, j_node, k_node, + element]) + + boundaries.u[v, i, j, boundary] = dot(flux_parabolic, normal_direction) end i_node += i_node_step_i @@ -1092,7 +1102,7 @@ end # Needed to *not* flip the sign of the inverse Jacobian. # This is because the parabolic fluxes are assumed to be of the form # `du/dt + df/dx = dg/dx + source(x,t)`, -# where f(u) is the inviscid flux and g(u) is the viscous flux. +# where f(u) is the inviscid flux and g(u) is the parabolic flux. function apply_jacobian_parabolic!(du::AbstractArray, mesh::P4estMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DG, cache) diff --git a/src/solvers/dgsem_tree/container_parabolic_1d.jl b/src/solvers/dgsem_tree/container_parabolic_1d.jl new file mode 100644 index 00000000000..af9a6841636 --- /dev/null +++ b/src/solvers/dgsem_tree/container_parabolic_1d.jl @@ -0,0 +1,58 @@ +mutable struct ParabolicContainer1D{uEltype <: Real} + u_transformed::Array{uEltype, 3} + gradients::Array{uEltype, 3} + flux_parabolic::Array{uEltype, 3} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + _gradients::Vector{uEltype} + _flux_parabolic::Vector{uEltype} + + function ParabolicContainer1D{uEltype}(n_vars::Integer, n_nodes::Integer, + n_elements::Integer) where {uEltype <: Real} + return new(Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements)) + end +end + +function init_parabolic_container_1d(n_vars::Integer, n_nodes::Integer, + n_elements::Integer, + ::Type{uEltype}) where {uEltype <: Real} + return ParabolicContainer1D{uEltype}(n_vars, n_nodes, n_elements) +end + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(parabolic_container::ParabolicContainer1D, equations, dg, cache) + capacity = nvariables(equations) * nnodes(dg) * nelements(dg, cache) + resize!(parabolic_container._u_transformed, capacity) + resize!(parabolic_container._gradients, capacity) + resize!(parabolic_container._flux_parabolic, capacity) + + parabolic_container.u_transformed = unsafe_wrap(Array, + pointer(parabolic_container._u_transformed), + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + + parabolic_container.gradients = unsafe_wrap(Array, + pointer(parabolic_container._gradients), + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + + parabolic_container.flux_parabolic = unsafe_wrap(Array, + pointer(parabolic_container._flux_parabolic), + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + + return nothing +end diff --git a/src/solvers/dgsem_tree/container_parabolic_2d.jl b/src/solvers/dgsem_tree/container_parabolic_2d.jl new file mode 100644 index 00000000000..7eee9c1302d --- /dev/null +++ b/src/solvers/dgsem_tree/container_parabolic_2d.jl @@ -0,0 +1,84 @@ +mutable struct ParabolicContainer2D{uEltype <: Real} + u_transformed::Array{uEltype, 4} + gradients::NTuple{2, Array{uEltype, 4}} + flux_parabolic::NTuple{2, Array{uEltype, 4}} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + # Use Tuple for outer, fixed-size datastructure + _gradients::Tuple{Vector{uEltype}, Vector{uEltype}} + _flux_parabolic::Tuple{Vector{uEltype}, Vector{uEltype}} + + function ParabolicContainer2D{uEltype}(n_vars::Integer, n_nodes::Integer, + n_elements::Integer) where {uEltype <: Real} + return new(Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), # `u_transformed` + # `gradients` + (Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), + Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements)), + # `flux_parabolic` + (Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), + Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements)), + # `_u_transformed` + Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), + # `_gradients` + (Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements)), + # `_flux_parabolic` + (Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements))) + end +end + +function init_parabolic_container_2d(n_vars::Integer, n_nodes::Integer, + n_elements::Integer, + ::Type{uEltype}) where {uEltype <: Real} + return ParabolicContainer2D{uEltype}(n_vars, n_nodes, n_elements) +end + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(parabolic_container::ParabolicContainer2D, equations, dg, cache) + capacity = nvariables(equations) * nnodes(dg)^2 * nelements(dg, cache) + resize!(parabolic_container._u_transformed, capacity) + for dim in 1:2 + resize!(parabolic_container._gradients[dim], capacity) + resize!(parabolic_container._flux_parabolic[dim], capacity) + end + + parabolic_container.u_transformed = unsafe_wrap(Array, + pointer(parabolic_container._u_transformed), + (nvariables(equations), + nnodes(dg), nnodes(dg), + nelements(dg, cache))) + + gradients_1 = unsafe_wrap(Array, + pointer(parabolic_container._gradients[1]), + (nvariables(equations), + nnodes(dg), nnodes(dg), + nelements(dg, cache))) + gradients_2 = unsafe_wrap(Array, + pointer(parabolic_container._gradients[2]), + (nvariables(equations), + nnodes(dg), nnodes(dg), + nelements(dg, cache))) + + parabolic_container.gradients = (gradients_1, gradients_2) + + flux_parabolic_1 = unsafe_wrap(Array, + pointer(parabolic_container._flux_parabolic[1]), + (nvariables(equations), + nnodes(dg), nnodes(dg), + nelements(dg, cache))) + flux_parabolic_2 = unsafe_wrap(Array, + pointer(parabolic_container._flux_parabolic[2]), + (nvariables(equations), + nnodes(dg), nnodes(dg), + nelements(dg, cache))) + + parabolic_container.flux_parabolic = (flux_parabolic_1, flux_parabolic_2) + + return nothing +end diff --git a/src/solvers/dgsem_tree/container_parabolic_3d.jl b/src/solvers/dgsem_tree/container_parabolic_3d.jl new file mode 100644 index 00000000000..07a1403b438 --- /dev/null +++ b/src/solvers/dgsem_tree/container_parabolic_3d.jl @@ -0,0 +1,99 @@ +mutable struct ParabolicContainer3D{uEltype <: Real} + u_transformed::Array{uEltype, 5} + gradients::NTuple{3, Array{uEltype, 5}} + flux_parabolic::NTuple{3, Array{uEltype, 5}} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + # Use Tuple for outer, fixed-size datastructure + _gradients::Tuple{Vector{uEltype}, Vector{uEltype}, Vector{uEltype}} + _flux_parabolic::Tuple{Vector{uEltype}, Vector{uEltype}, Vector{uEltype}} + + function ParabolicContainer3D{uEltype}(n_vars::Integer, n_nodes::Integer, + n_elements::Integer) where {uEltype <: Real} + return new(Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), # `u_transformed` + # `gradients` + (Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), + Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), + Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements)), + # `flux_parabolic` + (Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), + Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), + Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements)), + # `u_transformed` + Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), + # `_gradients` + (Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements)), + # `_flux_parabolic` + (Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements))) + end +end + +function init_parabolic_container_3d(n_vars::Integer, n_nodes::Integer, + n_elements::Integer, + ::Type{uEltype}) where {uEltype <: Real} + return ParabolicContainer3D{uEltype}(n_vars, n_nodes, n_elements) +end + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(parabolic_container::ParabolicContainer3D, equations, dg, cache) + capacity = nvariables(equations) * nnodes(dg)^3 * nelements(dg, cache) + resize!(parabolic_container._u_transformed, capacity) + for dim in 1:3 + resize!(parabolic_container._gradients[dim], capacity) + resize!(parabolic_container._flux_parabolic[dim], capacity) + end + + parabolic_container.u_transformed = unsafe_wrap(Array, + pointer(parabolic_container._u_transformed), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + + gradients_1 = unsafe_wrap(Array, + pointer(parabolic_container._gradients[1]), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + gradients_2 = unsafe_wrap(Array, + pointer(parabolic_container._gradients[2]), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + gradients_3 = unsafe_wrap(Array, + pointer(parabolic_container._gradients[3]), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + + parabolic_container.gradients = (gradients_1, gradients_2, gradients_3) + + flux_parabolic_1 = unsafe_wrap(Array, + pointer(parabolic_container._flux_parabolic[1]), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + flux_parabolic_2 = unsafe_wrap(Array, + pointer(parabolic_container._flux_parabolic[2]), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + flux_parabolic_3 = unsafe_wrap(Array, + pointer(parabolic_container._flux_parabolic[3]), + (nvariables(equations), + nnodes(dg), nnodes(dg), nnodes(dg), + nelements(dg, cache))) + + parabolic_container.flux_parabolic = (flux_parabolic_1, flux_parabolic_2, + flux_parabolic_3) + + return nothing +end diff --git a/src/solvers/dgsem_tree/container_viscous_1d.jl b/src/solvers/dgsem_tree/container_viscous_1d.jl deleted file mode 100644 index 661fbfb237f..00000000000 --- a/src/solvers/dgsem_tree/container_viscous_1d.jl +++ /dev/null @@ -1,58 +0,0 @@ -mutable struct ViscousContainer1D{uEltype <: Real} - u_transformed::Array{uEltype, 3} - gradients::Array{uEltype, 3} - flux_viscous::Array{uEltype, 3} - - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - _gradients::Vector{uEltype} - _flux_viscous::Vector{uEltype} - - function ViscousContainer1D{uEltype}(n_vars::Integer, n_nodes::Integer, - n_elements::Integer) where {uEltype <: Real} - return new(Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), - Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), - Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), - Vector{uEltype}(undef, n_vars * n_nodes * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes * n_elements)) - end -end - -function init_viscous_container_1d(n_vars::Integer, n_nodes::Integer, - n_elements::Integer, - ::Type{uEltype}) where {uEltype <: Real} - return ViscousContainer1D{uEltype}(n_vars, n_nodes, n_elements) -end - -# Only one-dimensional `Array`s are `resize!`able in Julia. -# Hence, we use `Vector`s as internal storage and `resize!` -# them whenever needed. Then, we reuse the same memory by -# `unsafe_wrap`ping multi-dimensional `Array`s around the -# internal storage. -function Base.resize!(viscous_container::ViscousContainer1D, equations, dg, cache) - capacity = nvariables(equations) * nnodes(dg) * nelements(dg, cache) - resize!(viscous_container._u_transformed, capacity) - resize!(viscous_container._gradients, capacity) - resize!(viscous_container._flux_viscous, capacity) - - viscous_container.u_transformed = unsafe_wrap(Array, - pointer(viscous_container._u_transformed), - (nvariables(equations), - nnodes(dg), - nelements(dg, cache))) - - viscous_container.gradients = unsafe_wrap(Array, - pointer(viscous_container._gradients), - (nvariables(equations), - nnodes(dg), - nelements(dg, cache))) - - viscous_container.flux_viscous = unsafe_wrap(Array, - pointer(viscous_container._flux_viscous), - (nvariables(equations), - nnodes(dg), - nelements(dg, cache))) - - return nothing -end diff --git a/src/solvers/dgsem_tree/container_viscous_2d.jl b/src/solvers/dgsem_tree/container_viscous_2d.jl deleted file mode 100644 index a4e69643d3f..00000000000 --- a/src/solvers/dgsem_tree/container_viscous_2d.jl +++ /dev/null @@ -1,84 +0,0 @@ -mutable struct ViscousContainer2D{uEltype <: Real} - u_transformed::Array{uEltype, 4} - gradients::NTuple{2, Array{uEltype, 4}} - flux_viscous::NTuple{2, Array{uEltype, 4}} - - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - # Use Tuple for outer, fixed-size datastructure - _gradients::Tuple{Vector{uEltype}, Vector{uEltype}} - _flux_viscous::Tuple{Vector{uEltype}, Vector{uEltype}} - - function ViscousContainer2D{uEltype}(n_vars::Integer, n_nodes::Integer, - n_elements::Integer) where {uEltype <: Real} - return new(Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), # `u_transformed` - # `gradients` - (Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), - Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements)), - # `flux_viscous` - (Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), - Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements)), - # `_u_transformed` - Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), - # `_gradients` - (Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements)), - # `_flux_viscous` - (Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements))) - end -end - -function init_viscous_container_2d(n_vars::Integer, n_nodes::Integer, - n_elements::Integer, - ::Type{uEltype}) where {uEltype <: Real} - return ViscousContainer2D{uEltype}(n_vars, n_nodes, n_elements) -end - -# Only one-dimensional `Array`s are `resize!`able in Julia. -# Hence, we use `Vector`s as internal storage and `resize!` -# them whenever needed. Then, we reuse the same memory by -# `unsafe_wrap`ping multi-dimensional `Array`s around the -# internal storage. -function Base.resize!(viscous_container::ViscousContainer2D, equations, dg, cache) - capacity = nvariables(equations) * nnodes(dg)^2 * nelements(dg, cache) - resize!(viscous_container._u_transformed, capacity) - for dim in 1:2 - resize!(viscous_container._gradients[dim], capacity) - resize!(viscous_container._flux_viscous[dim], capacity) - end - - viscous_container.u_transformed = unsafe_wrap(Array, - pointer(viscous_container._u_transformed), - (nvariables(equations), - nnodes(dg), nnodes(dg), - nelements(dg, cache))) - - gradients_1 = unsafe_wrap(Array, - pointer(viscous_container._gradients[1]), - (nvariables(equations), - nnodes(dg), nnodes(dg), - nelements(dg, cache))) - gradients_2 = unsafe_wrap(Array, - pointer(viscous_container._gradients[2]), - (nvariables(equations), - nnodes(dg), nnodes(dg), - nelements(dg, cache))) - - viscous_container.gradients = (gradients_1, gradients_2) - - flux_viscous_1 = unsafe_wrap(Array, - pointer(viscous_container._flux_viscous[1]), - (nvariables(equations), - nnodes(dg), nnodes(dg), - nelements(dg, cache))) - flux_viscous_2 = unsafe_wrap(Array, - pointer(viscous_container._flux_viscous[2]), - (nvariables(equations), - nnodes(dg), nnodes(dg), - nelements(dg, cache))) - - viscous_container.flux_viscous = (flux_viscous_1, flux_viscous_2) - - return nothing -end diff --git a/src/solvers/dgsem_tree/container_viscous_3d.jl b/src/solvers/dgsem_tree/container_viscous_3d.jl deleted file mode 100644 index a55fc5147d9..00000000000 --- a/src/solvers/dgsem_tree/container_viscous_3d.jl +++ /dev/null @@ -1,98 +0,0 @@ -mutable struct ViscousContainer3D{uEltype <: Real} - u_transformed::Array{uEltype, 5} - gradients::NTuple{3, Array{uEltype, 5}} - flux_viscous::NTuple{3, Array{uEltype, 5}} - - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - # Use Tuple for outer, fixed-size datastructure - _gradients::Tuple{Vector{uEltype}, Vector{uEltype}, Vector{uEltype}} - _flux_viscous::Tuple{Vector{uEltype}, Vector{uEltype}, Vector{uEltype}} - - function ViscousContainer3D{uEltype}(n_vars::Integer, n_nodes::Integer, - n_elements::Integer) where {uEltype <: Real} - return new(Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), # `u_transformed` - # `gradients` - (Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), - Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), - Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements)), - # `flux_viscous` - (Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), - Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), - Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements)), - # `u_transformed` - Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), - # `_gradients` - (Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements)), - # `_flux_viscous` - (Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements))) - end -end - -function init_viscous_container_3d(n_vars::Integer, n_nodes::Integer, - n_elements::Integer, - ::Type{uEltype}) where {uEltype <: Real} - return ViscousContainer3D{uEltype}(n_vars, n_nodes, n_elements) -end - -# Only one-dimensional `Array`s are `resize!`able in Julia. -# Hence, we use `Vector`s as internal storage and `resize!` -# them whenever needed. Then, we reuse the same memory by -# `unsafe_wrap`ping multi-dimensional `Array`s around the -# internal storage. -function Base.resize!(viscous_container::ViscousContainer3D, equations, dg, cache) - capacity = nvariables(equations) * nnodes(dg)^3 * nelements(dg, cache) - resize!(viscous_container._u_transformed, capacity) - for dim in 1:3 - resize!(viscous_container._gradients[dim], capacity) - resize!(viscous_container._flux_viscous[dim], capacity) - end - - viscous_container.u_transformed = unsafe_wrap(Array, - pointer(viscous_container._u_transformed), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - - gradients_1 = unsafe_wrap(Array, - pointer(viscous_container._gradients[1]), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - gradients_2 = unsafe_wrap(Array, - pointer(viscous_container._gradients[2]), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - gradients_3 = unsafe_wrap(Array, - pointer(viscous_container._gradients[3]), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - - viscous_container.gradients = (gradients_1, gradients_2, gradients_3) - - flux_viscous_1 = unsafe_wrap(Array, - pointer(viscous_container._flux_viscous[1]), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - flux_viscous_2 = unsafe_wrap(Array, - pointer(viscous_container._flux_viscous[2]), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - flux_viscous_3 = unsafe_wrap(Array, - pointer(viscous_container._flux_viscous[3]), - (nvariables(equations), - nnodes(dg), nnodes(dg), nnodes(dg), - nelements(dg, cache))) - - viscous_container.flux_viscous = (flux_viscous_1, flux_viscous_2, flux_viscous_3) - - return nothing -end diff --git a/src/solvers/dgsem_tree/containers_viscous.jl b/src/solvers/dgsem_tree/containers_viscous.jl index 444f2cb7303..f90eac9dcb0 100644 --- a/src/solvers/dgsem_tree/containers_viscous.jl +++ b/src/solvers/dgsem_tree/containers_viscous.jl @@ -1,4 +1,4 @@ # Dimension-specific implementations -include("container_viscous_1d.jl") -include("container_viscous_2d.jl") -include("container_viscous_3d.jl") +include("container_parabolic_1d.jl") +include("container_parabolic_2d.jl") +include("container_parabolic_3d.jl") diff --git a/src/solvers/dgsem_tree/dg.jl b/src/solvers/dgsem_tree/dg.jl index c389af89880..5887367bc2b 100644 --- a/src/solvers/dgsem_tree/dg.jl +++ b/src/solvers/dgsem_tree/dg.jl @@ -35,7 +35,7 @@ include("containers.jl") include("dg_parallel.jl") # Helper structs for parabolic AMR -include("containers_viscous.jl") +include("containers_parabolic.jl") # Some functions for a second-order Finite-Volume (MUSCL) alike # scheme on DG-subcells. diff --git a/src/solvers/dgsem_tree/dg_1d.jl b/src/solvers/dgsem_tree/dg_1d.jl index 6bc81900953..628cdafa5d0 100644 --- a/src/solvers/dgsem_tree/dg_1d.jl +++ b/src/solvers/dgsem_tree/dg_1d.jl @@ -379,8 +379,8 @@ end end # Used for both the purely hyperbolic conserved variables `u` -# and the viscous flux in x-direction in the 1D parabolic case. -function prolong2interfaces!(cache, u_or_flux_viscous, +# and the parabolic flux in x-direction in the 1D parabolic case. +function prolong2interfaces!(cache, u_or_flux_parabolic, mesh::TreeMesh{1}, equations, dg::DG) @unpack interfaces = cache @unpack neighbor_ids = interfaces @@ -392,16 +392,16 @@ function prolong2interfaces!(cache, u_or_flux_viscous, # interface in x-direction for v in eachvariable(equations) - interfaces_u[1, v, interface] = u_or_flux_viscous[v, nnodes(dg), - left_element] - interfaces_u[2, v, interface] = u_or_flux_viscous[v, 1, right_element] + interfaces_u[1, v, interface] = u_or_flux_parabolic[v, nnodes(dg), + left_element] + interfaces_u[2, v, interface] = u_or_flux_parabolic[v, 1, right_element] end end return nothing end -function prolong2interfaces!(cache, u_or_flux_viscous, +function prolong2interfaces!(cache, u_or_flux_parabolic, mesh::TreeMesh{1}, equations, dg::DGSEM{<:GaussLegendreBasis}) @unpack interfaces = cache @@ -424,11 +424,11 @@ function prolong2interfaces!(cache, u_or_flux_viscous, # (see comment at the top of the file) # Need `boundary_interpolation` at right (+1) node for left element interface_u_1 = (interface_u_1 + - u_or_flux_viscous[v, ii, left_element] * + u_or_flux_parabolic[v, ii, left_element] * boundary_interpolation[ii, 2]) # Need `boundary_interpolation` at left (-1) node for right element interface_u_2 = (interface_u_2 + - u_or_flux_viscous[v, ii, right_element] * + u_or_flux_parabolic[v, ii, right_element] * boundary_interpolation[ii, 1]) end interfaces_u[1, v, interface] = interface_u_1 @@ -513,8 +513,8 @@ function calc_interface_flux!(surface_flux_values, end # Used for both the purely hyperbolic conserved variables `u` -# and the viscous flux in x-direction in the 1D parabolic case. -function prolong2boundaries!(cache, u_or_flux_viscous, +# and the parabolic flux in x-direction in the 1D parabolic case. +function prolong2boundaries!(cache, u_or_flux_parabolic, mesh::TreeMesh{1}, equations, dg::DG) @unpack boundaries = cache @unpack neighbor_sides = boundaries @@ -526,11 +526,12 @@ function prolong2boundaries!(cache, u_or_flux_viscous, if neighbor_sides[boundary] == 1 # element in -x direction of boundary for v in eachvariable(equations) - boundaries.u[1, v, boundary] = u_or_flux_viscous[v, nnodes(dg), element] + boundaries.u[1, v, boundary] = u_or_flux_parabolic[v, nnodes(dg), + element] end else # Element in +x direction of boundary for v in eachvariable(equations) - boundaries.u[2, v, boundary] = u_or_flux_viscous[v, 1, element] + boundaries.u[2, v, boundary] = u_or_flux_parabolic[v, 1, element] end end end @@ -538,7 +539,7 @@ function prolong2boundaries!(cache, u_or_flux_viscous, return nothing end -function prolong2boundaries!(cache, u_or_flux_viscous, +function prolong2boundaries!(cache, u_or_flux_parabolic, mesh::TreeMesh{1}, equations, dg::DGSEM{<:GaussLegendreBasis}) @unpack boundaries = cache @@ -559,7 +560,7 @@ function prolong2boundaries!(cache, u_or_flux_viscous, # Not += to allow `@muladd` to turn these into FMAs # (see comment at the top of the file) boundary_u_1 = (boundary_u_1 + - u_or_flux_viscous[v, ii, element] * + u_or_flux_parabolic[v, ii, element] * boundary_interpolation[ii, 2]) end boundaries.u[1, v, boundary] = boundary_u_1 @@ -569,7 +570,7 @@ function prolong2boundaries!(cache, u_or_flux_viscous, boundary_u_2 = zero(eltype(boundaries.u)) for ii in eachnode(dg) boundary_u_2 = (boundary_u_2 + - u_or_flux_viscous[v, ii, element] * + u_or_flux_parabolic[v, ii, element] * boundary_interpolation[ii, 1]) end boundaries.u[2, v, boundary] = boundary_u_2 diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl index a2d4fc445fd..5f72b97ba29 100644 --- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl @@ -11,11 +11,11 @@ function create_cache_parabolic(mesh::TreeMesh{1}, equations_hyperbolic::AbstractEquations, dg::DG, n_elements, uEltype) - viscous_container = init_viscous_container_1d(nvariables(equations_hyperbolic), - nnodes(dg), n_elements, - uEltype) + parabolic_container = init_parabolic_container_1d(nvariables(equations_hyperbolic), + nnodes(dg), n_elements, + uEltype) - cache_parabolic = (; viscous_container) + cache_parabolic = (; parabolic_container) return cache_parabolic end @@ -32,10 +32,10 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1}, equations_parabolic::AbstractEquationsParabolic, boundary_conditions_parabolic, source_terms_parabolic, dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack viscous_container = cache_parabolic - @unpack u_transformed, gradients, flux_viscous = viscous_container + @unpack parabolic_container = cache_parabolic + @unpack u_transformed, gradients, flux_parabolic = parabolic_container - # Convert conservative variables to a form more suitable for viscous flux calculations + # Convert conservative variables to a form more suitable for parabolic flux calculations @trixi_timeit timer() "transform variables" begin transform_variables!(u_transformed, u, mesh, equations_parabolic, dg, cache) @@ -48,20 +48,20 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1}, parabolic_scheme, cache) end - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache) + # Compute and store the parabolic fluxes + @trixi_timeit timer() "calculate parabolic fluxes" begin + calc_parabolic_fluxes!(flux_parabolic, gradients, u_transformed, mesh, + equations_parabolic, dg, cache) end # The remainder of this function is essentially a regular rhs! for - # parabolic equations (i.e., it computes the divergence of the viscous fluxes) + # parabolic equations (i.e., it computes the divergence of the parabolic fluxes) # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # OBS! In `calc_parabolic_fluxes!`, the parabolic flux values at the volume nodes of each element have + # been computed and stored in `flux_parabolic`. In the following, we *reuse* (abuse) the # `interfaces` and `boundaries` containers in `cache` to interpolate and store the # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *parabolic flux values* # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we # do not need to recreate the existing data structure only with a different name, and c) we do not # need to interpolate solutions *and* gradients to the surfaces. @@ -70,19 +70,19 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1}, @trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache) # Calculate volume integral - # This calls the specialized version for the viscous flux. + # This calls the specialized version for the parabolic flux. @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + calc_volume_integral!(du, flux_parabolic, mesh, equations_parabolic, dg, cache) end # Prolong solution to interfaces # This reuses `prolong2interfaces!` for the purely hyperbolic case. @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache, flux_viscous, mesh, equations_parabolic, dg) + prolong2interfaces!(cache, flux_parabolic, mesh, equations_parabolic, dg) end # Calculate interface fluxes. - # This calls the specialized version for the viscous flux. + # This calls the specialized version for the parabolic flux. @trixi_timeit timer() "interface flux" begin calc_interface_flux!(cache.elements.surface_flux_values, mesh, equations_parabolic, dg, @@ -92,7 +92,7 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1}, # Prolong solution to boundaries. # This reuses `prolong2boundaries!` for the purely hyperbolic case. @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache, flux_viscous, mesh, equations_parabolic, dg) + prolong2boundaries!(cache, flux_parabolic, mesh, equations_parabolic, dg) end # Calculate boundary fluxes. @@ -146,10 +146,10 @@ function transform_variables!(u_transformed, u, mesh::TreeMesh{1}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. +# This is the version used when calculating the divergence of the parabolic fluxes. # Identical to weak-form volume integral/kernel for the purely hyperbolic case, -# except that the fluxes are here already precomputed in `calc_viscous_fluxes!` -function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{1}, +# except that the fluxes are here already precomputed in `calc_parabolic_fluxes!` +function calc_volume_integral!(du, flux_parabolic, mesh::TreeMesh{1}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM, cache) @unpack derivative_hat = dg.basis @@ -157,7 +157,7 @@ function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{1}, @threaded for element in eachelement(dg, cache) # Calculate volume terms in one element for i in eachnode(dg) - flux_1_node = get_node_vars(flux_viscous, equations_parabolic, dg, i, + flux_1_node = get_node_vars(flux_parabolic, equations_parabolic, dg, i, element) for ii in eachnode(dg) @@ -170,7 +170,7 @@ function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{1}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes +# This is the version used when calculating the divergence of the parabolic fluxes function calc_interface_flux!(surface_flux_values, mesh::TreeMesh{1}, equations_parabolic, dg::DG, parabolic_scheme, cache) @@ -205,9 +205,10 @@ function calc_interface_flux!(surface_flux_values, mesh::TreeMesh{1}, return nothing end -function calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh::TreeMesh{1}, - equations_parabolic::AbstractEquationsParabolic, - dg::DG, cache) +function calc_parabolic_fluxes!(flux_parabolic, gradients, u_transformed, + mesh::TreeMesh{1}, + equations_parabolic::AbstractEquationsParabolic, + dg::DG, cache) @threaded for element in eachelement(dg, cache) for i in eachnode(dg) # Get solution and gradients @@ -215,10 +216,10 @@ function calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh::Tree gradients_1_node = get_node_vars(gradients, equations_parabolic, dg, i, element) - # Calculate viscous flux and store each component for later use - flux_viscous_node = flux(u_node, (gradients_1_node,), 1, - equations_parabolic) - set_node_vars!(flux_viscous, flux_viscous_node, equations_parabolic, dg, + # Calculate parabolic flux and store each component for later use + flux_parabolic_node = flux(u_node, (gradients_1_node,), 1, + equations_parabolic) + set_node_vars!(flux_parabolic, flux_parabolic_node, equations_parabolic, dg, i, element) end end @@ -348,14 +349,14 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra @unpack surface_flux = surface_integral # Note: cache.boundaries.u contains the unsigned normal component (using "orientation", not "direction") - # of the viscous flux, as computed in `prolong2boundaries!` + # of the parabolic flux, as computed in `prolong2boundaries!` @unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = cache.boundaries @threaded for boundary in first_boundary:last_boundary # Get neighboring element neighbor = neighbor_ids[boundary] - # Get viscous boundary fluxes + # Get parabolic boundary fluxes flux_ll, flux_rr = get_surface_node_vars(u, equations_parabolic, dg, boundary) if neighbor_sides[boundary] == 1 # Element is on the left, boundary on the right flux_inner = flux_ll @@ -566,7 +567,7 @@ end # Needed to *not* flip the sign of the inverse Jacobian. # This is because the parabolic fluxes are assumed to be of the form # `du/dt + df/dx = dg/dx + source(x,t)`, -# where f(u) is the inviscid flux and g(u) is the viscous flux. +# where f(u) is the inviscid flux and g(u) is the parabolic flux. function apply_jacobian_parabolic!(du::AbstractArray, mesh::TreeMesh{1}, equations_parabolic::AbstractEquationsParabolic, dg::DG, cache) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 42930688aff..28a1134cf77 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -11,11 +11,11 @@ function create_cache_parabolic(mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations_hyperbolic::AbstractEquations, dg::DG, n_elements, uEltype) - viscous_container = init_viscous_container_2d(nvariables(equations_hyperbolic), - nnodes(dg), n_elements, - uEltype) + parabolic_container = init_parabolic_container_2d(nvariables(equations_hyperbolic), + nnodes(dg), n_elements, + uEltype) - cache_parabolic = (; viscous_container) + cache_parabolic = (; parabolic_container) return cache_parabolic end @@ -32,10 +32,10 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, TreeMesh{3}}, equations_parabolic::AbstractEquationsParabolic, boundary_conditions_parabolic, source_terms_parabolic, dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack viscous_container = cache_parabolic - @unpack u_transformed, gradients, flux_viscous = viscous_container + @unpack parabolic_container = cache_parabolic + @unpack u_transformed, gradients, flux_parabolic = parabolic_container - # Convert conservative variables to a form more suitable for viscous flux calculations + # Convert conservative variables to a form more suitable for parabolic flux calculations @trixi_timeit timer() "transform variables" begin transform_variables!(u_transformed, u, mesh, equations_parabolic, dg, cache) @@ -48,20 +48,20 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, TreeMesh{3}}, dg, parabolic_scheme, cache) end - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache) + # Compute and store the parabolic fluxes + @trixi_timeit timer() "calculate parabolic fluxes" begin + calc_parabolic_fluxes!(flux_parabolic, gradients, u_transformed, mesh, + equations_parabolic, dg, cache) end # The remainder of this function is essentially a regular rhs! for parabolic - # equations (i.e., it computes the divergence of the viscous fluxes) + # equations (i.e., it computes the divergence of the parabolic fluxes) # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # OBS! In `calc_parabolic_fluxes!`, the parabolic flux values at the volume nodes of each element have + # been computed and stored in `flux_parabolic`. In the following, we *reuse* (abuse) the # `interfaces` and `boundaries` containers in `cache` to interpolate and store the # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *parabolic flux values* # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we # do not need to recreate the existing data structure only with a different name, and c) we do not # need to interpolate solutions *and* gradients to the surfaces. @@ -70,21 +70,21 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, TreeMesh{3}}, @trixi_timeit timer() "reset ∂u/∂t" set_zero!(du, dg, cache) # Calculate volume integral. - # This calls the specialized version for the viscous fluxes from + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + calc_volume_integral!(du, flux_parabolic, mesh, equations_parabolic, dg, cache) end # Prolong solution to interfaces. - # This calls the specialized version for the viscous fluxes from + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache, flux_viscous, mesh, equations_parabolic, dg) + prolong2interfaces!(cache, flux_parabolic, mesh, equations_parabolic, dg) end # Calculate interface fluxes - # This calls the specialized version for the viscous fluxes from + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "interface flux" begin calc_interface_flux!(cache.elements.surface_flux_values, @@ -92,11 +92,11 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, TreeMesh{3}}, parabolic_scheme, cache) end - # Prolong viscous fluxes to boundaries. - # This calls the specialized version for the viscous fluxes from + # Prolong parabolic fluxes to boundaries. + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache, flux_viscous, mesh, equations_parabolic, dg) + prolong2boundaries!(cache, flux_parabolic, mesh, equations_parabolic, dg) end # Calculate boundary fluxes. @@ -108,11 +108,11 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, TreeMesh{3}}, dg.surface_integral, dg) end - # Prolong viscous fluxes to mortars. - # This calls the specialized version for the viscous fluxes from + # Prolong parabolic fluxes to mortars. + # This calls the specialized version for the parabolic fluxes from # `dg_2d_parabolic.jl` or `dg_3d_parabolic.jl`. @trixi_timeit timer() "prolong2mortars" begin - prolong2mortars!(cache, flux_viscous, mesh, equations_parabolic, + prolong2mortars!(cache, flux_parabolic, mesh, equations_parabolic, dg.mortar, dg) end @@ -166,21 +166,21 @@ function transform_variables!(u_transformed, u, mesh::Union{TreeMesh{2}, P4estMe return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. +# This is the version used when calculating the divergence of the parabolic fluxes. # Identical to weak-form volume integral/kernel for the purely hyperbolic case, -# except that the fluxes are here already precomputed in `calc_viscous_fluxes!` -function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{2}, +# except that the fluxes are here already precomputed in `calc_parabolic_fluxes!` +function calc_volume_integral!(du, flux_parabolic, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM, cache) @unpack derivative_hat = dg.basis - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for element in eachelement(dg, cache) # Calculate volume terms in one element for j in eachnode(dg), i in eachnode(dg) - flux_1_node = get_node_vars(flux_viscous_x, equations_parabolic, dg, + flux_1_node = get_node_vars(flux_parabolic_x, equations_parabolic, dg, i, j, element) - flux_2_node = get_node_vars(flux_viscous_y, equations_parabolic, dg, + flux_2_node = get_node_vars(flux_parabolic_y, equations_parabolic, dg, i, j, element) for ii in eachnode(dg) @@ -198,11 +198,11 @@ function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{2}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2interfaces!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2interfaces!(cache, flux_viscous::Tuple, +function prolong2interfaces!(cache, flux_parabolic::Tuple, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DG) @@ -212,7 +212,7 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*! interfaces_u = interfaces.u - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for interface in eachinterface(dg, cache) left_element = neighbor_ids[1, interface] @@ -221,18 +221,18 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, if orientations[interface] == 1 # interface in x-direction for j in eachnode(dg), v in eachvariable(equations_parabolic) - interfaces_u[1, v, j, interface] = flux_viscous_x[v, nnodes(dg), j, - left_element] - interfaces_u[2, v, j, interface] = flux_viscous_x[v, 1, j, - right_element] + interfaces_u[1, v, j, interface] = flux_parabolic_x[v, nnodes(dg), j, + left_element] + interfaces_u[2, v, j, interface] = flux_parabolic_x[v, 1, j, + right_element] end else # if orientations[interface] == 2 # interface in y-direction for i in eachnode(dg), v in eachvariable(equations_parabolic) - interfaces_u[1, v, i, interface] = flux_viscous_y[v, i, nnodes(dg), - left_element] - interfaces_u[2, v, i, interface] = flux_viscous_y[v, i, 1, - right_element] + interfaces_u[1, v, i, interface] = flux_parabolic_y[v, i, nnodes(dg), + left_element] + interfaces_u[2, v, i, interface] = flux_parabolic_y[v, i, 1, + right_element] end end end @@ -240,11 +240,11 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2interfaces!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2interfaces!(cache, flux_viscous::Tuple, +function prolong2interfaces!(cache, flux_parabolic::Tuple, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM{<:GaussLegendreBasis}) @@ -255,7 +255,7 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*! interfaces_u = interfaces.u - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for interface in eachinterface(dg, cache) left_element = neighbor_ids[1, interface] @@ -274,11 +274,11 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, # (see comment at the top of the file) # Need `boundary_interpolation` at right (+1) node for left element interface_u_1 = (interface_u_1 + - flux_viscous_x[v, ii, j, left_element] * + flux_parabolic_x[v, ii, j, left_element] * boundary_interpolation[ii, 2]) # Need `boundary_interpolation` at left (-1) node for right element interface_u_2 = (interface_u_2 + - flux_viscous_x[v, ii, j, right_element] * + flux_parabolic_x[v, ii, j, right_element] * boundary_interpolation[ii, 1]) end interfaces_u[1, v, j, interface] = interface_u_1 @@ -294,11 +294,11 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, for jj in eachnode(dg) # Need `boundary_interpolation` at right (+1) node for left element interface_u_1 = (interface_u_1 + - flux_viscous_y[v, i, jj, left_element] * + flux_parabolic_y[v, i, jj, left_element] * boundary_interpolation[jj, 2]) # Need `boundary_interpolation` at left (-1) node for right element interface_u_2 = (interface_u_2 + - flux_viscous_y[v, i, jj, right_element] * + flux_parabolic_y[v, i, jj, right_element] * boundary_interpolation[jj, 1]) end interfaces_u[1, v, i, interface] = interface_u_1 @@ -311,7 +311,7 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes +# This is the version used when calculating the divergence of the parabolic fluxes function calc_interface_flux!(surface_flux_values, mesh::TreeMesh{2}, equations_parabolic, dg::DG, parabolic_scheme, cache) @@ -348,11 +348,11 @@ function calc_interface_flux!(surface_flux_values, mesh::TreeMesh{2}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2boundaries!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2boundaries!(cache, flux_viscous::Tuple, +function prolong2boundaries!(cache, flux_parabolic::Tuple, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DG) @@ -361,7 +361,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, # OBS! `boundaries_u` stores the "interpolated" *fluxes* and *not the solution*! boundaries_u = boundaries.u - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for boundary in eachboundary(dg, cache) element = neighbor_ids[boundary] @@ -371,13 +371,13 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, if neighbor_sides[boundary] == 1 # element in -x direction of boundary for l in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[1, v, l, boundary] = flux_viscous_x[v, nnodes(dg), l, - element] + boundaries_u[1, v, l, boundary] = flux_parabolic_x[v, nnodes(dg), l, + element] end else # Element in +x direction of boundary for l in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[2, v, l, boundary] = flux_viscous_x[v, 1, l, - element] + boundaries_u[2, v, l, boundary] = flux_parabolic_x[v, 1, l, + element] end end else # if orientations[boundary] == 2 @@ -385,14 +385,14 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, if neighbor_sides[boundary] == 1 # element in -y direction of boundary for l in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[1, v, l, boundary] = flux_viscous_y[v, l, nnodes(dg), - element] + boundaries_u[1, v, l, boundary] = flux_parabolic_y[v, l, nnodes(dg), + element] end else # element in +y direction of boundary for l in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[2, v, l, boundary] = flux_viscous_y[v, l, 1, - element] + boundaries_u[2, v, l, boundary] = flux_parabolic_y[v, l, 1, + element] end end end @@ -401,11 +401,11 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2boundaries!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2boundaries!(cache, flux_viscous::Tuple, +function prolong2boundaries!(cache, flux_parabolic::Tuple, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM{<:GaussLegendreBasis}) @@ -415,7 +415,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*! boundaries_u = boundaries.u - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for boundary in eachboundary(dg, cache) element = neighbor_ids[boundary] @@ -433,7 +433,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, # Not += to allow `@muladd` to turn these into FMAs # (see comment at the top of the file) boundary_u = (boundary_u + - flux_viscous_x[v, ii, l, element] * + flux_parabolic_x[v, ii, l, element] * boundary_interpolation[ii, 2]) end boundaries_u[1, v, l, boundary] = boundary_u @@ -445,7 +445,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, boundary_u = zero(eltype(boundaries_u)) for ii in eachnode(dg) boundary_u = (boundary_u + - flux_viscous_x[v, ii, l, element] * + flux_parabolic_x[v, ii, l, element] * boundary_interpolation[ii, 1]) end boundaries_u[2, v, l, boundary] = boundary_u @@ -461,7 +461,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, boundary_u = zero(eltype(boundaries_u)) for jj in eachnode(dg) boundary_u = (boundary_u + - flux_viscous_y[v, l, jj, element] * + flux_parabolic_y[v, l, jj, element] * boundary_interpolation[jj, 2]) end boundaries_u[1, v, l, boundary] = boundary_u @@ -473,7 +473,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, boundary_u = zero(eltype(boundaries_u)) for jj in eachnode(dg) boundary_u = (boundary_u + - flux_viscous_y[v, l, jj, element] * + flux_parabolic_y[v, l, jj, element] * boundary_interpolation[jj, 1]) end boundaries_u[2, v, l, boundary] = boundary_u @@ -486,13 +486,13 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, return nothing end -function calc_viscous_fluxes!(flux_viscous, - gradients, u_transformed, - mesh::Union{TreeMesh{2}, P4estMesh{2}}, - equations_parabolic::AbstractEquationsParabolic, - dg::DG, cache) +function calc_parabolic_fluxes!(flux_parabolic, + gradients, u_transformed, + mesh::Union{TreeMesh{2}, P4estMesh{2}}, + equations_parabolic::AbstractEquationsParabolic, + dg::DG, cache) gradients_x, gradients_y = gradients - flux_viscous_x, flux_viscous_y = flux_viscous # output arrays + flux_parabolic_x, flux_parabolic_y = flux_parabolic # output arrays @threaded for element in eachelement(dg, cache) for j in eachnode(dg), i in eachnode(dg) @@ -504,14 +504,18 @@ function calc_viscous_fluxes!(flux_viscous, gradients_2_node = get_node_vars(gradients_y, equations_parabolic, dg, i, j, element) - # Calculate viscous flux and store each component for later use - flux_viscous_node_x = flux(u_node, (gradients_1_node, gradients_2_node), 1, - equations_parabolic) - flux_viscous_node_y = flux(u_node, (gradients_1_node, gradients_2_node), 2, - equations_parabolic) - set_node_vars!(flux_viscous_x, flux_viscous_node_x, equations_parabolic, dg, + # Calculate parabolic flux and store each component for later use + flux_parabolic_node_x = flux(u_node, (gradients_1_node, gradients_2_node), + 1, + equations_parabolic) + flux_parabolic_node_y = flux(u_node, (gradients_1_node, gradients_2_node), + 2, + equations_parabolic) + set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, + dg, i, j, element) - set_node_vars!(flux_viscous_y, flux_viscous_node_y, equations_parabolic, dg, + set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, + dg, i, j, element) end end @@ -674,7 +678,7 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra @unpack surface_flux = surface_integral # Note: cache.boundaries.u contains the unsigned normal component (using "orientation", not "direction") - # of the viscous flux, as computed in `prolong2boundaries!` + # of the parabolic flux, as computed in `prolong2boundaries!` @unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = cache.boundaries @threaded for boundary in first_boundary:last_boundary @@ -682,7 +686,7 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra neighbor = neighbor_ids[boundary] for i in eachnode(dg) - # Get viscous boundary fluxes + # Get parabolic boundary fluxes flux_ll, flux_rr = get_surface_node_vars(u, equations_parabolic, dg, i, boundary) if neighbor_sides[boundary] == 1 # Element is on the left, boundary on the right @@ -713,15 +717,15 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end -# Specialization `flux_viscous::Tuple` needed to +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2mortars!` in dg_2d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 4}`. -function prolong2mortars!(cache, flux_viscous::Tuple, +function prolong2mortars!(cache, flux_parabolic::Tuple, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM) - flux_viscous_x, flux_viscous_y = flux_viscous + flux_parabolic_x, flux_parabolic_y = flux_parabolic @threaded for mortar in eachmortar(dg, cache) large_element = cache.mortars.neighbor_ids[3, mortar] upper_element = cache.mortars.neighbor_ids[2, mortar] @@ -733,28 +737,28 @@ function prolong2mortars!(cache, flux_viscous::Tuple, # L2 mortars in x-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_x[v, - 1, - l, - upper_element] - cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_x[v, - 1, - l, - lower_element] + cache.mortars.u_upper[2, v, l, mortar] = flux_parabolic_x[v, + 1, + l, + upper_element] + cache.mortars.u_lower[2, v, l, mortar] = flux_parabolic_x[v, + 1, + l, + lower_element] end end else # L2 mortars in y-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_y[v, - l, - 1, - upper_element] - cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_y[v, - l, - 1, - lower_element] + cache.mortars.u_upper[2, v, l, mortar] = flux_parabolic_y[v, + l, + 1, + upper_element] + cache.mortars.u_lower[2, v, l, mortar] = flux_parabolic_y[v, + l, + 1, + lower_element] end end end @@ -763,28 +767,28 @@ function prolong2mortars!(cache, flux_viscous::Tuple, # L2 mortars in x-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_x[v, - nnodes(dg), - l, - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_x[v, - nnodes(dg), - l, - lower_element] + cache.mortars.u_upper[1, v, l, mortar] = flux_parabolic_x[v, + nnodes(dg), + l, + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = flux_parabolic_x[v, + nnodes(dg), + l, + lower_element] end end else # L2 mortars in y-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_y[v, - l, - nnodes(dg), - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_y[v, - l, - nnodes(dg), - lower_element] + cache.mortars.u_upper[1, v, l, mortar] = flux_parabolic_y[v, + l, + nnodes(dg), + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = flux_parabolic_y[v, + l, + nnodes(dg), + lower_element] end end end @@ -795,12 +799,12 @@ function prolong2mortars!(cache, flux_viscous::Tuple, leftright = 1 if cache.mortars.orientations[mortar] == 1 # L2 mortars in x-direction - u_large = view(flux_viscous_x, :, nnodes(dg), :, large_element) + u_large = view(flux_parabolic_x, :, nnodes(dg), :, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large) else # L2 mortars in y-direction - u_large = view(flux_viscous_y, :, :, nnodes(dg), large_element) + u_large = view(flux_parabolic_y, :, :, nnodes(dg), large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large) end @@ -808,12 +812,12 @@ function prolong2mortars!(cache, flux_viscous::Tuple, leftright = 2 if cache.mortars.orientations[mortar] == 1 # L2 mortars in x-direction - u_large = view(flux_viscous_x, :, 1, :, large_element) + u_large = view(flux_parabolic_x, :, 1, :, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large) else # L2 mortars in y-direction - u_large = view(flux_viscous_y, :, :, 1, large_element) + u_large = view(flux_parabolic_y, :, :, 1, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large) end @@ -1241,7 +1245,7 @@ end # Needed to *not* flip the sign of the inverse Jacobian. # This is because the parabolic fluxes are assumed to be of the form # `du/dt + df/dx = dg/dx + source(x,t)`, -# where f(u) is the inviscid flux and g(u) is the viscous flux. +# where f(u) is the inviscid flux and g(u) is the parabolic flux. function apply_jacobian_parabolic!(du::AbstractArray, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, dg::DG, cache) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 79b9aaddcec..3226e9bad59 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -11,11 +11,11 @@ function create_cache_parabolic(mesh::Union{TreeMesh{3}, P4estMesh{3}}, equations_hyperbolic::AbstractEquations, dg::DG, n_elements, uEltype) - viscous_container = init_viscous_container_3d(nvariables(equations_hyperbolic), - nnodes(dg), n_elements, - uEltype) + parabolic_container = init_parabolic_container_3d(nvariables(equations_hyperbolic), + nnodes(dg), n_elements, + uEltype) - cache_parabolic = (; viscous_container) + cache_parabolic = (; parabolic_container) return cache_parabolic end @@ -51,23 +51,23 @@ function reset_gradients!(gradients::NTuple{3}, dg::DG, cache) return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. +# This is the version used when calculating the divergence of the parabolic fluxes. # Identical to weak-form volume integral/kernel for the purely hyperbolic case, -# except that the fluxes are here already precomputed in `calc_viscous_fluxes!` -function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{3}, +# except that the fluxes are here already precomputed in `calc_parabolic_fluxes!` +function calc_volume_integral!(du, flux_parabolic, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DGSEM, cache) @unpack derivative_hat = dg.basis - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for element in eachelement(dg, cache) # Calculate volume terms in one element for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg) - flux_1_node = get_node_vars(flux_viscous_x, equations_parabolic, dg, + flux_1_node = get_node_vars(flux_parabolic_x, equations_parabolic, dg, i, j, k, element) - flux_2_node = get_node_vars(flux_viscous_y, equations_parabolic, dg, + flux_2_node = get_node_vars(flux_parabolic_y, equations_parabolic, dg, i, j, k, element) - flux_3_node = get_node_vars(flux_viscous_z, equations_parabolic, dg, + flux_3_node = get_node_vars(flux_parabolic_z, equations_parabolic, dg, i, j, k, element) for ii in eachnode(dg) @@ -90,11 +90,11 @@ function calc_volume_integral!(du, flux_viscous, mesh::TreeMesh{3}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2interfaces!` in dg_3d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 5}`. -function prolong2interfaces!(cache, flux_viscous::Tuple, +function prolong2interfaces!(cache, flux_parabolic::Tuple, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DG) @@ -104,7 +104,7 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*! interfaces_u = interfaces.u - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for interface in eachinterface(dg, cache) left_element = neighbor_ids[1, interface] @@ -115,36 +115,36 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations_parabolic) - interfaces_u[1, v, j, k, interface] = flux_viscous_x[v, - nnodes(dg), j, k, - left_element] - interfaces_u[2, v, j, k, interface] = flux_viscous_x[v, - 1, j, k, - right_element] + interfaces_u[1, v, j, k, interface] = flux_parabolic_x[v, + nnodes(dg), j, k, + left_element] + interfaces_u[2, v, j, k, interface] = flux_parabolic_x[v, + 1, j, k, + right_element] end elseif orientations[interface] == 2 # interface in y-direction for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations_parabolic) - interfaces_u[1, v, i, k, interface] = flux_viscous_y[v, - i, nnodes(dg), k, - left_element] - interfaces_u[2, v, i, k, interface] = flux_viscous_y[v, - i, 1, k, - right_element] + interfaces_u[1, v, i, k, interface] = flux_parabolic_y[v, + i, nnodes(dg), k, + left_element] + interfaces_u[2, v, i, k, interface] = flux_parabolic_y[v, + i, 1, k, + right_element] end else # if orientations[interface] == 3 # interface in z-direction for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations_parabolic) - interfaces_u[1, v, i, j, interface] = flux_viscous_z[v, - i, j, nnodes(dg), - left_element] - interfaces_u[2, v, i, j, interface] = flux_viscous_z[v, - i, j, 1, - right_element] + interfaces_u[1, v, i, j, interface] = flux_parabolic_z[v, + i, j, nnodes(dg), + left_element] + interfaces_u[2, v, i, j, interface] = flux_parabolic_z[v, + i, j, 1, + right_element] end end end @@ -152,7 +152,7 @@ function prolong2interfaces!(cache, flux_viscous::Tuple, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes +# This is the version used when calculating the divergence of the parabolic fluxes function calc_interface_flux!(surface_flux_values, mesh::TreeMesh{3}, equations_parabolic, dg::DG, parabolic_scheme, cache) @@ -190,11 +190,11 @@ function calc_interface_flux!(surface_flux_values, mesh::TreeMesh{3}, return nothing end -# This is the version used when calculating the divergence of the viscous fluxes. -# Specialization `flux_viscous::Tuple` needed to +# This is the version used when calculating the divergence of the parabolic fluxes. +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2boundaries!` in dg_3d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 5}`. -function prolong2boundaries!(cache, flux_viscous::Tuple, +function prolong2boundaries!(cache, flux_parabolic::Tuple, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DG) @@ -203,7 +203,7 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, # OBS! `boundaries_u` stores the "interpolated" *fluxes* and *not the solution*! boundaries_u = boundaries.u - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for boundary in eachboundary(dg, cache) element = neighbor_ids[boundary] @@ -215,21 +215,21 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[1, v, j, k, boundary] = flux_viscous_x[v, - nnodes(dg), - j, - k, - element] + boundaries_u[1, v, j, k, boundary] = flux_parabolic_x[v, + nnodes(dg), + j, + k, + element] end else # Element in +x direction of boundary for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[2, v, j, k, boundary] = flux_viscous_x[v, - 1, - j, - k, - element] + boundaries_u[2, v, j, k, boundary] = flux_parabolic_x[v, + 1, + j, + k, + element] end end elseif orientations[boundary] == 2 @@ -239,22 +239,22 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[1, v, i, k, boundary] = flux_viscous_y[v, - i, - nnodes(dg), - k, - element] + boundaries_u[1, v, i, k, boundary] = flux_parabolic_y[v, + i, + nnodes(dg), + k, + element] end else # element in +y direction of boundary for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[2, v, i, k, boundary] = flux_viscous_y[v, - i, - 1, - k, - element] + boundaries_u[2, v, i, k, boundary] = flux_parabolic_y[v, + i, + 1, + k, + element] end end else # if orientations[boundary] == 3 @@ -264,22 +264,22 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[1, v, i, j, boundary] = flux_viscous_z[v, - i, - j, - nnodes(dg), - element] + boundaries_u[1, v, i, j, boundary] = flux_parabolic_z[v, + i, + j, + nnodes(dg), + element] end else # element in +z direction of boundary for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations_parabolic) - boundaries_u[2, v, i, j, boundary] = flux_viscous_z[v, - i, - j, - 1, - element] + boundaries_u[2, v, i, j, boundary] = flux_parabolic_z[v, + i, + j, + 1, + element] end end end @@ -288,13 +288,13 @@ function prolong2boundaries!(cache, flux_viscous::Tuple, return nothing end -function calc_viscous_fluxes!(flux_viscous, - gradients, u_transformed, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, - equations_parabolic::AbstractEquationsParabolic, - dg::DG, cache) +function calc_parabolic_fluxes!(flux_parabolic, + gradients, u_transformed, + mesh::Union{TreeMesh{3}, P4estMesh{3}}, + equations_parabolic::AbstractEquationsParabolic, + dg::DG, cache) gradients_x, gradients_y, gradients_z = gradients - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous # output arrays + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic # output arrays @threaded for element in eachelement(dg, cache) for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg) @@ -308,21 +308,24 @@ function calc_viscous_fluxes!(flux_viscous, gradients_3_node = get_node_vars(gradients_z, equations_parabolic, dg, i, j, k, element) - # Calculate viscous flux and store each component for later use - flux_viscous_node_x = flux(u_node, - (gradients_1_node, gradients_2_node, - gradients_3_node), 1, equations_parabolic) - flux_viscous_node_y = flux(u_node, - (gradients_1_node, gradients_2_node, - gradients_3_node), 2, equations_parabolic) - flux_viscous_node_z = flux(u_node, - (gradients_1_node, gradients_2_node, - gradients_3_node), 3, equations_parabolic) - set_node_vars!(flux_viscous_x, flux_viscous_node_x, equations_parabolic, dg, + # Calculate parabolic flux and store each component for later use + flux_parabolic_node_x = flux(u_node, + (gradients_1_node, gradients_2_node, + gradients_3_node), 1, equations_parabolic) + flux_parabolic_node_y = flux(u_node, + (gradients_1_node, gradients_2_node, + gradients_3_node), 2, equations_parabolic) + flux_parabolic_node_z = flux(u_node, + (gradients_1_node, gradients_2_node, + gradients_3_node), 3, equations_parabolic) + set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, + dg, i, j, k, element) - set_node_vars!(flux_viscous_y, flux_viscous_node_y, equations_parabolic, dg, + set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, + dg, i, j, k, element) - set_node_vars!(flux_viscous_z, flux_viscous_node_z, equations_parabolic, dg, + set_node_vars!(flux_parabolic_z, flux_parabolic_node_z, equations_parabolic, + dg, i, j, k, element) end end @@ -510,7 +513,7 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra @unpack surface_flux = surface_integral # Note: cache.boundaries.u contains the unsigned normal component (using "orientation", not "direction") - # of the viscous flux, as computed in `prolong2boundaries!` + # of the parabolic flux, as computed in `prolong2boundaries!` @unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = cache.boundaries @threaded for boundary in first_boundary:last_boundary @@ -518,7 +521,7 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra neighbor = neighbor_ids[boundary] for j in eachnode(dg), i in eachnode(dg) - # Get viscous boundary fluxes + # Get parabolic boundary fluxes flux_ll, flux_rr = get_surface_node_vars(u, equations_parabolic, dg, i, j, boundary) if neighbor_sides[boundary] == 1 # Element is on the left, boundary on the right @@ -550,17 +553,17 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end -# Specialization `flux_viscous::Tuple` needed to +# Specialization `flux_parabolic::Tuple` needed to # avoid amibiguity with the hyperbolic version of `prolong2mortars!` in dg_3d.jl # which is for the variables itself, i.e., `u::Array{uEltype, 5}`. -function prolong2mortars!(cache, flux_viscous::Tuple, +function prolong2mortars!(cache, flux_parabolic::Tuple, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM) # temporary buffer for projections @unpack fstar_tmp1_threaded = cache - flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + flux_parabolic_x, flux_parabolic_y, flux_parabolic_z = flux_parabolic @threaded for mortar in eachmortar(dg, cache) fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()] @@ -576,78 +579,78 @@ function prolong2mortars!(cache, flux_viscous::Tuple, # L2 mortars in x-direction for k in eachnode(dg), j in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, j, k, mortar] = flux_viscous_x[v, - 1, - j, - k, - upper_left_element] - cache.mortars.u_upper_right[2, v, j, k, mortar] = flux_viscous_x[v, - 1, - j, - k, - upper_right_element] - cache.mortars.u_lower_left[2, v, j, k, mortar] = flux_viscous_x[v, - 1, - j, - k, - lower_left_element] - cache.mortars.u_lower_right[2, v, j, k, mortar] = flux_viscous_x[v, - 1, - j, - k, - lower_right_element] + cache.mortars.u_upper_left[2, v, j, k, mortar] = flux_parabolic_x[v, + 1, + j, + k, + upper_left_element] + cache.mortars.u_upper_right[2, v, j, k, mortar] = flux_parabolic_x[v, + 1, + j, + k, + upper_right_element] + cache.mortars.u_lower_left[2, v, j, k, mortar] = flux_parabolic_x[v, + 1, + j, + k, + lower_left_element] + cache.mortars.u_lower_right[2, v, j, k, mortar] = flux_parabolic_x[v, + 1, + j, + k, + lower_right_element] end end elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction for k in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, k, mortar] = flux_viscous_y[v, - i, - 1, - k, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, k, mortar] = flux_viscous_y[v, - i, - 1, - k, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, k, mortar] = flux_viscous_y[v, - i, - 1, - k, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, k, mortar] = flux_viscous_y[v, - i, - 1, - k, - lower_right_element] + cache.mortars.u_upper_left[2, v, i, k, mortar] = flux_parabolic_y[v, + i, + 1, + k, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, k, mortar] = flux_parabolic_y[v, + i, + 1, + k, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, k, mortar] = flux_parabolic_y[v, + i, + 1, + k, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, k, mortar] = flux_parabolic_y[v, + i, + 1, + k, + lower_right_element] end end else # orientations[mortar] == 3 # L2 mortars in z-direction for j in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - 1, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - 1, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - 1, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - 1, - lower_right_element] + cache.mortars.u_upper_left[2, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + 1, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + 1, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + 1, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + 1, + lower_right_element] end end end @@ -656,78 +659,78 @@ function prolong2mortars!(cache, flux_viscous::Tuple, # L2 mortars in x-direction for k in eachnode(dg), j in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, - k, - upper_left_element] - cache.mortars.u_upper_right[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, - k, - upper_right_element] - cache.mortars.u_lower_left[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, - k, - lower_left_element] - cache.mortars.u_lower_right[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, - k, - lower_right_element] + cache.mortars.u_upper_left[1, v, j, k, mortar] = flux_parabolic_x[v, + nnodes(dg), + j, + k, + upper_left_element] + cache.mortars.u_upper_right[1, v, j, k, mortar] = flux_parabolic_x[v, + nnodes(dg), + j, + k, + upper_right_element] + cache.mortars.u_lower_left[1, v, j, k, mortar] = flux_parabolic_x[v, + nnodes(dg), + j, + k, + lower_left_element] + cache.mortars.u_lower_right[1, v, j, k, mortar] = flux_parabolic_x[v, + nnodes(dg), + j, + k, + lower_right_element] end end elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction for k in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, k, mortar] = flux_viscous_y[v, - i, - nnodes(dg), - k, - upper_left_element] - cache.mortars.u_upper_right[1, v, i, k, mortar] = flux_viscous_y[v, - i, - nnodes(dg), - k, - upper_right_element] - cache.mortars.u_lower_left[1, v, i, k, mortar] = flux_viscous_y[v, - i, - nnodes(dg), - k, - lower_left_element] - cache.mortars.u_lower_right[1, v, i, k, mortar] = flux_viscous_y[v, - i, - nnodes(dg), - k, - lower_right_element] + cache.mortars.u_upper_left[1, v, i, k, mortar] = flux_parabolic_y[v, + i, + nnodes(dg), + k, + upper_left_element] + cache.mortars.u_upper_right[1, v, i, k, mortar] = flux_parabolic_y[v, + i, + nnodes(dg), + k, + upper_right_element] + cache.mortars.u_lower_left[1, v, i, k, mortar] = flux_parabolic_y[v, + i, + nnodes(dg), + k, + lower_left_element] + cache.mortars.u_lower_right[1, v, i, k, mortar] = flux_parabolic_y[v, + i, + nnodes(dg), + k, + lower_right_element] end end else # if cache.mortars.orientations[mortar] == 3 # L2 mortars in z-direction for j in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - nnodes(dg), - upper_left_element] - cache.mortars.u_upper_right[1, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - nnodes(dg), - upper_right_element] - cache.mortars.u_lower_left[1, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - nnodes(dg), - lower_left_element] - cache.mortars.u_lower_right[1, v, i, j, mortar] = flux_viscous_z[v, - i, - j, - nnodes(dg), - lower_right_element] + cache.mortars.u_upper_left[1, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + nnodes(dg), + upper_left_element] + cache.mortars.u_upper_right[1, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + nnodes(dg), + upper_right_element] + cache.mortars.u_lower_left[1, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + nnodes(dg), + lower_left_element] + cache.mortars.u_lower_right[1, v, i, j, mortar] = flux_parabolic_z[v, + i, + j, + nnodes(dg), + lower_right_element] end end end @@ -738,17 +741,17 @@ function prolong2mortars!(cache, flux_viscous::Tuple, leftright = 1 if cache.mortars.orientations[mortar] == 1 # L2 mortars in x-direction - u_large = view(flux_viscous_x, :, nnodes(dg), :, :, large_element) + u_large = view(flux_parabolic_x, :, nnodes(dg), :, :, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large, fstar_tmp1) elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction - u_large = view(flux_viscous_y, :, :, nnodes(dg), :, large_element) + u_large = view(flux_parabolic_y, :, :, nnodes(dg), :, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large, fstar_tmp1) else # cache.mortars.orientations[mortar] == 3 # L2 mortars in z-direction - u_large = view(flux_viscous_z, :, :, :, nnodes(dg), large_element) + u_large = view(flux_parabolic_z, :, :, :, nnodes(dg), large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large, fstar_tmp1) end @@ -756,17 +759,17 @@ function prolong2mortars!(cache, flux_viscous::Tuple, leftright = 2 if cache.mortars.orientations[mortar] == 1 # L2 mortars in x-direction - u_large = view(flux_viscous_x, :, 1, :, :, large_element) + u_large = view(flux_parabolic_x, :, 1, :, :, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large, fstar_tmp1) elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction - u_large = view(flux_viscous_y, :, :, 1, :, large_element) + u_large = view(flux_parabolic_y, :, :, 1, :, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large, fstar_tmp1) else # cache.mortars.orientations[mortar] == 3 # L2 mortars in z-direction - u_large = view(flux_viscous_z, :, :, :, 1, large_element) + u_large = view(flux_parabolic_z, :, :, :, 1, large_element) element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, mortar, u_large, fstar_tmp1) end @@ -1128,7 +1131,7 @@ end # Needed to *not* flip the sign of the inverse Jacobian. # This is because the parabolic fluxes are assumed to be of the form # `du/dt + df/dx = dg/dx + source(x,t)`, -# where f(u) is the inviscid flux and g(u) is the viscous flux. +# where f(u) is the inviscid flux and g(u) is the parabolic flux. function apply_jacobian_parabolic!(du::AbstractArray, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, dg::DG, cache) diff --git a/src/solvers/solvers_parabolic.jl b/src/solvers/solvers_parabolic.jl index 77ee2c64c9b..5602c40e774 100644 --- a/src/solvers/solvers_parabolic.jl +++ b/src/solvers/solvers_parabolic.jl @@ -1,5 +1,5 @@ """ - ViscousFormulationBassiRebay1() + ParabolicFormulationBassiRebay1() The classical BR1 flux from @@ -15,59 +15,59 @@ A more detailed study of the BR1 scheme for the DGSEM can be found in The BR1 scheme works well for convection-dominated problems, but may cause instabilities or reduced convergence for diffusion-dominated problems. -In the latter case, the [`ViscousFormulationLocalDG`](@ref) scheme is recommended. +In the latter case, the [`ParabolicFormulationLocalDG`](@ref) scheme is recommended. """ -struct ViscousFormulationBassiRebay1 end +struct ParabolicFormulationBassiRebay1 end """ flux_parabolic(u_ll, u_rr, gradient_or_divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationBassiRebay1) + parabolic_scheme::ParabolicFormulationBassiRebay1) flux_parabolic(u_ll, u_rr, normal_direction::AbstractVector, gradient_or_divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationBassiRebay1) + parabolic_scheme::ParabolicFormulationBassiRebay1) This computes the classical BR1 flux. Since the interface flux for both the DG gradient and DG divergence under BR1 are identical, this function does not need to be specialized for `Gradient` and `Divergence`. `normal_direction` is not used in the BR1 flux, -but is included as an argument for consistency with the [`ViscousFormulationLocalDG`](@ref) flux, +but is included as an argument for consistency with the [`ParabolicFormulationLocalDG`](@ref) flux, which does use the `normal_direction` to compute the LDG "switch" on the generally non-Cartesian [`P4estMesh`](@ref). """ function flux_parabolic(u_ll, u_rr, # Version for `TreeMesh` gradient_or_divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationBassiRebay1) + parabolic_scheme::ParabolicFormulationBassiRebay1) return 0.5f0 * (u_ll + u_rr) end # Version for `P4estMesh` function flux_parabolic(u_ll, u_rr, normal_direction::AbstractVector, gradient_or_divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationBassiRebay1) + parabolic_scheme::ParabolicFormulationBassiRebay1) return 0.5f0 * (u_ll + u_rr) end """ - ViscousFormulationLocalDG(penalty_parameter) + ParabolicFormulationLocalDG(penalty_parameter) The local DG (LDG) flux from "The Local Discontinuous Galerkin Method for Time-Dependent Convection-Diffusion Systems" by Cockburn and Shu (1998). The parabolic "upwinding" vector is currently implemented for `TreeMesh`; for all other mesh types, -the LDG solver is equivalent to [`ViscousFormulationBassiRebay1`](@ref) with an LDG-type penalization. +the LDG solver is equivalent to [`ParabolicFormulationBassiRebay1`](@ref) with an LDG-type penalization. - Cockburn and Shu (1998). The Local Discontinuous Galerkin Method for Time-Dependent Convection-Diffusion Systems [DOI: 10.1137/S0036142997316712](https://doi.org/10.1137/S0036142997316712) """ -struct ViscousFormulationLocalDG{P} +struct ParabolicFormulationLocalDG{P} penalty_parameter::P end """ - ViscousFormulationLocalDG() + ParabolicFormulationLocalDG() The minimum dissipation local DG (LDG) flux from "An Analysis of the Minimal Dissipation Local Discontinuous Galerkin Method for Convection–Diffusion Problems" by Cockburn and Dong (2007). @@ -79,16 +79,16 @@ Cockburn and Dong proved that this scheme is still stable despite the zero penal Galerkin Method for Convection–Diffusion Problems. [DOI: 10.1007/s10915-007-9130-3](https://doi.org/10.1007/s10915-007-9130-3) """ -ViscousFormulationLocalDG() = ViscousFormulationLocalDG(nothing) +ParabolicFormulationLocalDG() = ParabolicFormulationLocalDG(nothing) @doc raw""" flux_parabolic(u_ll, u_rr, ::Gradient, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) flux_parabolic(u_ll, u_rr, normal_direction, ::Gradient, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) These fluxes computes the gradient and divergence interface fluxes for the local DG method. The local DG method uses an "upwind/downwind" flux for the @@ -109,7 +109,7 @@ f = \frac{1}{2}\Big(f(u_{L}) + f(u_{R}) - \sigma \big[f(u_{R}) - f(u_{L})\big]\B """ function flux_parabolic(u_ll, u_rr, # Version for `TreeMesh` ::Gradient, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) # The LDG flux is {{f}} + beta * [[f]], where beta is the LDG "switch", # which we set to -1 on the left and +1 on the right in 1D. The sign of the # jump term should be opposite that of the sign used in the divergence flux. @@ -120,7 +120,7 @@ end # Version for `P4estMesh` function flux_parabolic(u_ll, u_rr, normal_direction, ::Gradient, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) 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)) end @@ -128,11 +128,11 @@ end @doc raw""" flux_parabolic(u_ll, u_rr, ::Divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) flux_parabolic(u_ll, u_rr, normal_direction, ::Divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) These fluxes computes the gradient and divergence interface fluxes for the local DG method. The local DG method uses an "upwind/downwind" flux for the @@ -154,15 +154,15 @@ f = \frac{1}{2}\Big(f(u_{L}) + f(u_{R}) + \sigma \big[f(u_{R}) - f(u_{L})\big]\B """ function flux_parabolic(u_ll, u_rr, # Version for `TreeMesh` ::Divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) return u_rr # Use the downwind value for the divergence interface flux end # Version or `P4estMesh` function flux_parabolic(u_ll, u_rr, normal_direction, ::Divergence, equations_parabolic, - parabolic_scheme::ViscousFormulationLocalDG) + parabolic_scheme::ParabolicFormulationLocalDG) 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)) end -default_parabolic_solver() = ViscousFormulationBassiRebay1() +default_parabolic_solver() = ParabolicFormulationBassiRebay1() diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 19953579506..b3652929172 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -68,9 +68,9 @@ isdir(outdir) && rm(outdir, recursive = true) @test getindex.(gradients[2], 1) ≈ xq .^ 2 u_flux = similar.(gradients) - Trixi.calc_viscous_fluxes!(u_flux, u0, gradients, mesh, - equations_parabolic, - dg, cache, cache_parabolic) + Trixi.calc_parabolic_fluxes!(u_flux, u0, gradients, mesh, + equations_parabolic, + dg, cache, cache_parabolic) @test u_flux[1] ≈ gradients[1] @test u_flux[2] ≈ gradients[2] @@ -223,7 +223,7 @@ end @trixi_testset "TreeMesh2D: elixir_advection_diffusion.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_diffusion.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), initial_refinement_level=2, tspan=(0.0, 0.4), polydeg=5, l2=[6.193056910594806e-6], linf=[4.918855889635143e-5]) # Ensure that we do not have excessive memory allocations @@ -247,7 +247,7 @@ end @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_diffusion_gradient_source_terms.jl"), initial_refinement_level=2, tspan=(0.0, 0.4), - solver_parabolic=ViscousFormulationBassiRebay1(), nu=1e-3, + solver_parabolic=ParabolicFormulationBassiRebay1(), nu=1e-3, stepsize_callback=TrivialCallback(), dt=1e-1, l2=[0.0017395186758592685], linf=[0.007481527467476025]) # Ensure that we do not have excessive memory allocations @@ -264,7 +264,7 @@ end coordinates_max = coordinates_max, periodicity = true), tspan=(0.0, 0.4), - solver_parabolic=ViscousFormulationBassiRebay1(), nu=1e-3, + solver_parabolic=ParabolicFormulationBassiRebay1(), nu=1e-3, stepsize_callback=TrivialCallback(), dt=1e-1, l2=[0.0017395186758592685], linf=[0.007481527467476025]) # Ensure that we do not have excessive memory allocations @@ -358,7 +358,7 @@ end @trixi_testset "TreeMesh2D: elixir_advection_diffusion_nonperiodic_amr.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_diffusion_nonperiodic_amr.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), tspan=(0.0, 0.01), l2=[0.000684755734524055], linf=[0.01141444199847298]) @@ -385,7 +385,7 @@ end @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_diffusion_nonperiodic.jl"), initial_refinement_level=2, tspan=(0.0, 0.1), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), l2=[0.007009146246373517], linf=[0.09535203925012649]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @@ -637,7 +637,7 @@ end @trixi_testset "TreeMesh2D: elixir_navierstokes_shearlayer_nonconforming.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_navierstokes_shearlayer_nonconforming.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), l2=[ 0.005352370793583371, 0.5969444914287823, @@ -704,7 +704,7 @@ end "elixir_navierstokes_viscous_shock.jl"), solver=DGSEM(polydeg = 3, surface_flux = flux_hlle, basis_type = GaussLegendreBasis), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), cfl_diffusive=0.04, l2=[ 6.599006355897759e-6, @@ -807,7 +807,7 @@ end @trixi_testset "P4estMesh2D: elixir_advection_diffusion_nonperiodic_amr.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_2d_dgsem", "elixir_advection_diffusion_nonperiodic_amr.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), tspan=(0.0, 0.01), l2=[0.0006847533999311489], linf=[0.01141430509080712]) @@ -938,7 +938,7 @@ end @trixi_testset "P4estMesh2D: elixir_navierstokes_shearlayer_nonconforming.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_2d_dgsem", "elixir_navierstokes_shearlayer_nonconforming.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), l2=[ 0.0053523707935916025, 0.5969444914278867, diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index f8aa7e85afa..92187aa0433 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -457,7 +457,7 @@ end @trixi_testset "TreeMesh3D: elixir_advection_diffusion_amr.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_3d_dgsem", "elixir_advection_diffusion_amr.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), initial_refinement_level=2, base_level=2, med_level=3, @@ -495,7 +495,7 @@ end @trixi_testset "TreeMesh3D: elixir_advection_diffusion_nonperiodic.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_3d_dgsem", "elixir_advection_diffusion_nonperiodic.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), l2=[0.0009432415534931421], linf=[0.016955330290404563]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @@ -525,7 +525,7 @@ end @trixi_testset "P4estMesh3D: elixir_advection_diffusion_nonperiodic.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_3d_dgsem", "elixir_advection_diffusion_nonperiodic.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), cfl_diffusive=0.07, l2=[0.0041854757843498725], linf=[0.05166356737492643]) # Ensure that we do not have excessive memory allocations @@ -547,7 +547,7 @@ end @trixi_testset "P4estMesh3D: elixir_advection_diffusion_amr_curved.jl (LDG)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_3d_dgsem", "elixir_advection_diffusion_amr_curved.jl"), - solver_parabolic=ViscousFormulationLocalDG(), + solver_parabolic=ParabolicFormulationLocalDG(), l2=[0.0006853004145232737], linf=[0.02352694543085776]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) diff --git a/test/test_type.jl b/test/test_type.jl index 2427593fe24..06d9c4b1192 100644 --- a/test/test_type.jl +++ b/test/test_type.jl @@ -1878,7 +1878,7 @@ isdir(outdir) && rm(outdir, recursive = true) RealT end - parabolic_solver = ViscousFormulationLocalDG(RealT(0.1)) + parabolic_solver = ParabolicFormulationLocalDG(RealT(0.1)) @test eltype(@inferred Trixi.penalty(u_outer, u_inner, inv_h, equations_parabolic, parabolic_solver)) == RealT @@ -1901,7 +1901,7 @@ isdir(outdir) && rm(outdir, recursive = true) RealT end - parabolic_solver = ViscousFormulationLocalDG(RealT(0.1)) + parabolic_solver = ParabolicFormulationLocalDG(RealT(0.1)) @test eltype(@inferred Trixi.penalty(u_outer, u_inner, inv_h, equations_parabolic, parabolic_solver)) == RealT From f6da116a12ea94e0fb3c4ca58b879145576efe8e Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 17 Mar 2026 17:37:52 +0100 Subject: [PATCH 02/68] rename files --- .../dgsem_tree/{containers_viscous.jl => containers_parabolic.jl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/solvers/dgsem_tree/{containers_viscous.jl => containers_parabolic.jl} (100%) diff --git a/src/solvers/dgsem_tree/containers_viscous.jl b/src/solvers/dgsem_tree/containers_parabolic.jl similarity index 100% rename from src/solvers/dgsem_tree/containers_viscous.jl rename to src/solvers/dgsem_tree/containers_parabolic.jl From 159bd64c52833556f6c45d414911daf3cdbe6472 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 18 Mar 2026 12:14:06 +0100 Subject: [PATCH 03/68] first step to parabolic solver --- ...lixir_diffusion_mixed_dirichlet_neumann.jl | 62 ++++++ src/Trixi.jl | 3 + .../semidiscretization_parabolic.jl | 205 ++++++++++++++++++ test/test_parabolic_1d.jl | 44 ++++ 4 files changed, 314 insertions(+) create mode 100644 examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl create mode 100644 src/semidiscretization/semidiscretization_parabolic.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl new file mode 100644 index 00000000000..c1d2723033b --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl @@ -0,0 +1,62 @@ +using Trixi + +############################################################################### +# semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs + +diffusivity() = 0.5 +forcing_amplitude() = 0.4 +forcing_frequency() = 4.0 +dirichlet_mean() = 1.0 +angular_frequency() = 2 * pi * forcing_frequency() + +# The equations object still uses an auxiliary 1D hyperbolic scalar equation for variable metadata. +equations_hyperbolic = LinearScalarAdvectionEquation1D(0.0) +equations = LaplaceDiffusion1D(diffusivity(), equations_hyperbolic) + +solver = DGSEM(polydeg = 3, surface_flux = flux_central) + +mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 3, + periodicity = false, + n_cells_max = 30_000) + +complex_wavenumber() = sqrt(im * angular_frequency() / diffusivity()) +harmonic_shape(x) = cosh(complex_wavenumber() * (1 - x)) / cosh(complex_wavenumber()) + +exact_solution(x, t) = dirichlet_mean() + + imag(forcing_amplitude() * exp(im * angular_frequency() * t) * + harmonic_shape(x[1])) + +initial_condition(x, t, equations) = SVector(exact_solution(x, t)) + +dirichlet_left(x, t) = dirichlet_mean() + forcing_amplitude() * sin(angular_frequency() * t) +neumann_right(x, t) = 0.0 + +boundary_conditions = (; x_neg = BoundaryConditionDirichlet((x, t, equations) -> SVector(dirichlet_left(x, t))), + x_pos = BoundaryConditionNeumann((x, t, equations) -> SVector(neumann_right(x, t)))) + +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + boundary_conditions = boundary_conditions, + solver_parabolic = ViscousFormulationLocalDG()) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.25) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 200 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + analysis_integrals = ()) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) + +############################################################################### +# run the simulation + +sol = Trixi.solve(ode, Trixi.CarpenterKennedy2N54(); dt = 5.0e-5, adaptive = false, + ode_default_options()..., callback = callbacks) diff --git a/src/Trixi.jl b/src/Trixi.jl index 167e01189c8..4cfbf56d858 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -144,6 +144,7 @@ include("solvers/solvers.jl") include("equations/equations_parabolic.jl") # these depend on parabolic solver types include("semidiscretization/semidiscretization.jl") include("semidiscretization/semidiscretization_hyperbolic.jl") +include("semidiscretization/semidiscretization_parabolic.jl") include("semidiscretization/semidiscretization_hyperbolic_parabolic.jl") include("semidiscretization/semidiscretization_euler_acoustics.jl") include("semidiscretization/semidiscretization_coupled.jl") @@ -294,6 +295,8 @@ export nelements, nnodes, nvariables, export SemidiscretizationHyperbolic, semidiscretize, compute_coefficients, integrate +export SemidiscretizationParabolic + export SemidiscretizationHyperbolicParabolic export have_constant_diffusivity, max_diffusivity diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl new file mode 100644 index 00000000000..460620625c6 --- /dev/null +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -0,0 +1,205 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +""" + SemidiscretizationParabolic + +A struct containing everything needed to describe a spatial semidiscretization +of a purely parabolic conservation law. +""" +mutable struct SemidiscretizationParabolic{Mesh, EquationsParabolic, InitialCondition, + BoundaryConditions, SourceTermsParabolic, + Solver, SolverParabolic, + Cache, CacheParabolic} <: + AbstractSemidiscretization + mesh::Mesh + equations::EquationsParabolic + + # This guy is a bit messy since we abuse it as some kind of "exact solution" + # although this doesn't really exist... + const initial_condition::InitialCondition + + const boundary_conditions::BoundaryConditions + const source_terms::SourceTermsParabolic + + const solver::Solver + const solver_parabolic::SolverParabolic + + cache::Cache + cache_parabolic::CacheParabolic + + performance_counter::PerformanceCounter +end +# We assume some properties of the fields of the semidiscretization, e.g., +# the `equations` and the `mesh` should have the same dimension. We check these +# properties in the outer constructor defined below. While we could ensure +# them even better in an inner constructor, we do not use this approach to +# simplify the integration with Adapt.jl for GPU usage, see +# https://github.com/trixi-framework/Trixi.jl/pull/2677#issuecomment-3591789921 + +""" + SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic=default_parabolic_solver(), + source_terms=nothing, + boundary_conditions, + RealT=real(solver), + uEltype=RealT) + +Construct a semidiscretization of a purely parabolic PDE. + +Boundary conditions must be provided explicitly either as a `NamedTuple` or as a +single boundary condition that gets applied to all boundaries. +""" +function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic, + initial_condition, solver; + solver_parabolic = default_parabolic_solver(), + source_terms = nothing, + boundary_conditions, + # `RealT` is used as real type for node locations etc. + # while `uEltype` is used as element type of solutions etc. + RealT = real(solver), uEltype = RealT) + @assert ndims(mesh) == ndims(equations) + + cache = create_cache(mesh, equations, solver, RealT, uEltype) + _boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver, + cache) + check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions) + + cache_parabolic = create_cache_parabolic(mesh, equations, solver, + nelements(solver, cache), uEltype) + + performance_counter = PerformanceCounter() + + return SemidiscretizationParabolic{typeof(mesh), typeof(equations), + typeof(initial_condition), + typeof(_boundary_conditions), + typeof(source_terms), + typeof(solver), typeof(solver_parabolic), + typeof(cache), typeof(cache_parabolic)}(mesh, + equations, + initial_condition, + _boundary_conditions, + source_terms, + solver, + solver_parabolic, + cache, + cache_parabolic, + performance_counter) +end + +# @eval due to @muladd +@eval Adapt.@adapt_structure(SemidiscretizationParabolic) + +# Create a new semidiscretization but change some parameters compared to the input. +# `Base.similar` follows a related concept but would require us to `copy` the `mesh`, +# which would impact the performance. Instead, `SciMLBase.remake` has exactly the +# semantics we want to use here. In particular, it allows us to re-use mutable parts, +# e.g. `remake(semi).mesh === semi.mesh`. +function remake(semi::SemidiscretizationParabolic; uEltype = real(semi.solver), + mesh = semi.mesh, + equations = semi.equations, + initial_condition = semi.initial_condition, + solver = semi.solver, + solver_parabolic = semi.solver_parabolic, + source_terms = semi.source_terms, + boundary_conditions = semi.boundary_conditions) + return SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + source_terms = source_terms, + boundary_conditions = boundary_conditions, + uEltype = uEltype) +end + +function Base.show(io::IO, semi::SemidiscretizationParabolic) + @nospecialize semi # reduce precompilation time + + print(io, "SemidiscretizationParabolic(") + print(io, semi.mesh) + print(io, ", ", semi.equations) + print(io, ", ", semi.initial_condition) + print(io, ", ", semi.boundary_conditions) + print(io, ", ", semi.source_terms) + print(io, ", ", semi.solver) + print(io, ", ", semi.solver_parabolic) + print(io, ", cache(") + for (idx, key) in enumerate(keys(semi.cache)) + idx > 1 && print(io, " ") + print(io, key) + end + print(io, "))") + return nothing +end + +function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationParabolic) + @nospecialize semi # reduce precompilation time + + if get(io, :compact, false) + show(io, semi) + else + summary_header(io, "SemidiscretizationParabolic") + summary_line(io, "#spatial dimensions", ndims(semi.equations)) + summary_line(io, "mesh", semi.mesh) + summary_line(io, "parabolic equations", semi.equations |> typeof |> nameof) + summary_line(io, "initial condition", semi.initial_condition) + summary_line(io, "source terms parabolic", semi.source_terms) + summary_line(io, "solver", semi.solver |> typeof |> nameof) + summary_line(io, "parabolic solver", semi.solver_parabolic |> typeof |> nameof) + summary_line(io, "total #DOFs per field", ndofsglobal(semi)) + summary_footer(io) + end +end + +@inline Base.ndims(semi::SemidiscretizationParabolic) = ndims(semi.mesh) + +@inline function nvariables(semi::SemidiscretizationParabolic) + return nvariables(semi.equations) +end + +@inline Base.real(semi::SemidiscretizationParabolic) = real(semi.solver) + +@inline function mesh_equations_solver_cache(semi::SemidiscretizationParabolic) + @unpack mesh, equations, solver, cache = semi + return mesh, equations, solver, cache +end + +function calc_error_norms(func, u_ode, t, analyzer, + semi::SemidiscretizationParabolic, cache_analysis) + @unpack mesh, equations, initial_condition, solver, cache = semi + u = wrap_array(u_ode, mesh, equations, solver, cache) + + return calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition, + solver, cache, cache_analysis) +end + +function compute_coefficients(t, semi::SemidiscretizationParabolic) + # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` + return compute_coefficients(semi.initial_condition, t, semi) +end + +function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) + return compute_coefficients!(u_ode, semi.initial_condition, t, semi) +end + +function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) + @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, + cache, cache_parabolic = semi + + u = wrap_array(u_ode, mesh, equations, solver, cache) + du = wrap_array(du_ode, mesh, equations, solver, cache) + + time_start = time_ns() + @trixi_timeit timer() "rhs!" rhs_parabolic!(du, u, t, mesh, equations, + boundary_conditions, source_terms, + solver, solver_parabolic, cache, + cache_parabolic) + runtime = time_ns() - time_start + put!(semi.performance_counter, runtime) + + return nothing +end + +end # @muladd diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 839d7e4ccdd..7c5bcbd0bc4 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -14,6 +14,50 @@ isdir(outdir) && rm(outdir, recursive = true) @testset "SemidiscretizationHyperbolicParabolic (1D)" begin #! format: noindent +@trixi_testset "TreeMesh1D: SemidiscretizationParabolic (purely parabolic)" begin + solver = DGSEM(polydeg = 2, surface_flux = flux_central) + mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 2, + n_cells_max = 30_000, + periodicity = false) + + equations = LinearScalarAdvectionEquation1D(0.0) + equations_parabolic = LaplaceDiffusion1D(0.1, equations) + + initial_condition(x, t, equations) = SVector(sinpi(x[1])) + boundary_conditions = (; x_neg = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)), + x_pos = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0))) + + semi = SemidiscretizationParabolic(mesh, equations_parabolic, initial_condition, solver; + boundary_conditions = boundary_conditions, + solver_parabolic = ViscousFormulationLocalDG()) + + @trixi_test_nowarn show(stdout, semi) + @trixi_test_nowarn show(stdout, MIME"text/plain"(), semi) + + @test semi isa SemidiscretizationParabolic + @test semi.solver_parabolic isa ViscousFormulationLocalDG + @test nvariables(semi) == 1 + @test ndims(semi) == 1 + @test real(semi) == real(solver) + + ode = semidiscretize(semi, (0.0, 0.01)) + u0 = copy(ode.u0) + du0 = similar(u0) + Trixi.rhs!(du0, u0, semi, 0.0) + @test all(isfinite, du0) + + u0_check = similar(ode.u0) + Trixi.compute_coefficients!(u0_check, 0.0, semi) + @test u0_check ≈ ode.u0 +end + +@trixi_testset "TreeMesh1D: elixir_diffusion_mixed_dirichlet_neumann.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_mixed_dirichlet_neumann.jl"), + tspan = (0.0, 0.05)) +end + @trixi_testset "TreeMesh1D: elixir_advection_diffusion.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_advection_diffusion.jl"), From 015fd25f58ddff77d0a002d7157b40e4ed1220a2 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 19 Mar 2026 11:53:10 +0100 Subject: [PATCH 04/68] remove hyperbolic part from parabolic solver --- ...lixir_diffusion_mixed_dirichlet_neumann.jl | 40 +++++-------- src/Trixi.jl | 3 +- src/equations/equations_parabolic.jl | 1 + src/equations/linear_diffusion_equation_1d.jl | 32 +++++++++++ .../semidiscretization_parabolic.jl | 57 ++++++++----------- test/test_parabolic_1d.jl | 9 ++- 6 files changed, 79 insertions(+), 63 deletions(-) create mode 100644 src/equations/linear_diffusion_equation_1d.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl index c1d2723033b..707b97eeb16 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl @@ -1,17 +1,14 @@ +using OrdinaryDiffEqLowStorageRK using Trixi ############################################################################### # semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs -diffusivity() = 0.5 -forcing_amplitude() = 0.4 -forcing_frequency() = 4.0 -dirichlet_mean() = 1.0 -angular_frequency() = 2 * pi * forcing_frequency() +diffusivity = 0.5 +amplitude = 0.4 +wave_number = 0.5 * pi -# The equations object still uses an auxiliary 1D hyperbolic scalar equation for variable metadata. -equations_hyperbolic = LinearScalarAdvectionEquation1D(0.0) -equations = LaplaceDiffusion1D(diffusivity(), equations_hyperbolic) +equations = LinearDiffusionEquation1D(diffusivity) solver = DGSEM(polydeg = 3, surface_flux = flux_central) @@ -20,29 +17,21 @@ mesh = TreeMesh((0.0,), (1.0,), periodicity = false, n_cells_max = 30_000) -complex_wavenumber() = sqrt(im * angular_frequency() / diffusivity()) -harmonic_shape(x) = cosh(complex_wavenumber() * (1 - x)) / cosh(complex_wavenumber()) +initial_condition = (x, t, equations) -> SVector(1.0 + amplitude * exp(-diffusivity * wave_number^2 * t) * sin(wave_number * x[1])) -exact_solution(x, t) = dirichlet_mean() + - imag(forcing_amplitude() * exp(im * angular_frequency() * t) * - harmonic_shape(x[1])) +boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(1.0)) +boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) -initial_condition(x, t, equations) = SVector(exact_solution(x, t)) - -dirichlet_left(x, t) = dirichlet_mean() + forcing_amplitude() * sin(angular_frequency() * t) -neumann_right(x, t) = 0.0 - -boundary_conditions = (; x_neg = BoundaryConditionDirichlet((x, t, equations) -> SVector(dirichlet_left(x, t))), - x_pos = BoundaryConditionNeumann((x, t, equations) -> SVector(neumann_right(x, t)))) +boundary_conditions = (; x_neg = boundary_condition_dirichlet, + x_pos = boundary_condition_neumann) semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; - boundary_conditions = boundary_conditions, - solver_parabolic = ViscousFormulationLocalDG()) + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. -tspan = (0.0, 0.25) +tspan = (0.0, 1.0) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() @@ -58,5 +47,6 @@ callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) ############################################################################### # run the simulation -sol = Trixi.solve(ode, Trixi.CarpenterKennedy2N54(); dt = 5.0e-5, adaptive = false, - ode_default_options()..., callback = callbacks) +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); + dt = 5.0e-5, adaptive = false, + ode_default_options()..., callback = callbacks) diff --git a/src/Trixi.jl b/src/Trixi.jl index 4cfbf56d858..b4b16f593bf 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -186,7 +186,8 @@ export AcousticPerturbationEquations2D, export NonIdealCompressibleEulerEquations1D, NonIdealCompressibleEulerEquations2D export IdealGas, VanDerWaals, PengRobinson -export LaplaceDiffusion1D, LaplaceDiffusion2D, LaplaceDiffusion3D, +export LinearDiffusionEquation1D, + LaplaceDiffusion1D, LaplaceDiffusion2D, LaplaceDiffusion3D, LaplaceDiffusionEntropyVariables1D, LaplaceDiffusionEntropyVariables2D, LaplaceDiffusionEntropyVariables3D, CompressibleNavierStokesDiffusion1D, CompressibleNavierStokesDiffusion2D, diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl index bfd33cfae78..b7d9246e18e 100644 --- a/src/equations/equations_parabolic.jl +++ b/src/equations/equations_parabolic.jl @@ -39,6 +39,7 @@ see [`StepsizeCallback`](@ref). return equations_parabolic.diffusivity end +include("linear_diffusion_equation_1d.jl") include("laplace_diffusion_1d.jl") include("laplace_diffusion_2d.jl") include("laplace_diffusion_3d.jl") diff --git a/src/equations/linear_diffusion_equation_1d.jl b/src/equations/linear_diffusion_equation_1d.jl new file mode 100644 index 00000000000..09b20768efb --- /dev/null +++ b/src/equations/linear_diffusion_equation_1d.jl @@ -0,0 +1,32 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +@doc raw""" + LinearDiffusionEquation1D(diffusivity) + +The linear diffusion equation (or heat equation) in one space dimension with constant diffusivity `\kappa`: +```math +\partial_t u = \partial_1 \left( \kappa \partial_1 u \right) +``` +""" +struct LinearDiffusionEquation1D{RealT <: Real} <: AbstractLaplaceDiffusion{1, 1} + diffusivity::RealT +end + +varnames(::typeof(cons2cons), ::LinearDiffusionEquation1D) = ("scalar",) +varnames(::typeof(cons2prim), ::LinearDiffusionEquation1D) = ("scalar",) + +@inline cons2prim(u, equations::LinearDiffusionEquation1D) = u + +@inline function flux(u, gradients, orientation::Integer, + equations::LinearDiffusionEquation1D) + dudx, = gradients + # orientation == 1 + return equations.diffusivity * dudx +end + +end # @muladd diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 460620625c6..a208f78a172 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -11,26 +11,24 @@ A struct containing everything needed to describe a spatial semidiscretization of a purely parabolic conservation law. """ -mutable struct SemidiscretizationParabolic{Mesh, EquationsParabolic, InitialCondition, - BoundaryConditions, SourceTermsParabolic, - Solver, SolverParabolic, - Cache, CacheParabolic} <: +mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition, + BoundaryConditions, SourceTerms, + Solver, Cache, ViscousCache} <: AbstractSemidiscretization mesh::Mesh - equations::EquationsParabolic + equations::Equations # This guy is a bit messy since we abuse it as some kind of "exact solution" # although this doesn't really exist... const initial_condition::InitialCondition const boundary_conditions::BoundaryConditions - const source_terms::SourceTermsParabolic + const source_terms::SourceTerms const solver::Solver - const solver_parabolic::SolverParabolic cache::Cache - cache_parabolic::CacheParabolic + cache_viscous::ViscousCache performance_counter::PerformanceCounter end @@ -43,7 +41,6 @@ end """ SemidiscretizationParabolic(mesh, equations, initial_condition, solver; - solver_parabolic=default_parabolic_solver(), source_terms=nothing, boundary_conditions, RealT=real(solver), @@ -56,7 +53,6 @@ single boundary condition that gets applied to all boundaries. """ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic, initial_condition, solver; - solver_parabolic = default_parabolic_solver(), source_terms = nothing, boundary_conditions, # `RealT` is used as real type for node locations etc. @@ -69,8 +65,8 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic cache) check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions) - cache_parabolic = create_cache_parabolic(mesh, equations, solver, - nelements(solver, cache), uEltype) + cache_viscous = create_cache_parabolic(mesh, equations, solver, + nelements(solver, cache), uEltype) performance_counter = PerformanceCounter() @@ -78,17 +74,16 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic typeof(initial_condition), typeof(_boundary_conditions), typeof(source_terms), - typeof(solver), typeof(solver_parabolic), - typeof(cache), typeof(cache_parabolic)}(mesh, - equations, - initial_condition, - _boundary_conditions, - source_terms, - solver, - solver_parabolic, - cache, - cache_parabolic, - performance_counter) + typeof(solver), + typeof(cache), typeof(cache_viscous)}(mesh, + equations, + initial_condition, + _boundary_conditions, + source_terms, + solver, + cache, + cache_viscous, + performance_counter) end # @eval due to @muladd @@ -104,11 +99,9 @@ function remake(semi::SemidiscretizationParabolic; uEltype = real(semi.solver), equations = semi.equations, initial_condition = semi.initial_condition, solver = semi.solver, - solver_parabolic = semi.solver_parabolic, source_terms = semi.source_terms, boundary_conditions = semi.boundary_conditions) return SemidiscretizationParabolic(mesh, equations, initial_condition, solver; - solver_parabolic = solver_parabolic, source_terms = source_terms, boundary_conditions = boundary_conditions, uEltype = uEltype) @@ -124,7 +117,6 @@ function Base.show(io::IO, semi::SemidiscretizationParabolic) print(io, ", ", semi.boundary_conditions) print(io, ", ", semi.source_terms) print(io, ", ", semi.solver) - print(io, ", ", semi.solver_parabolic) print(io, ", cache(") for (idx, key) in enumerate(keys(semi.cache)) idx > 1 && print(io, " ") @@ -145,9 +137,8 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationParabolic summary_line(io, "mesh", semi.mesh) summary_line(io, "parabolic equations", semi.equations |> typeof |> nameof) summary_line(io, "initial condition", semi.initial_condition) - summary_line(io, "source terms parabolic", semi.source_terms) + summary_line(io, "source terms", semi.source_terms) summary_line(io, "solver", semi.solver |> typeof |> nameof) - summary_line(io, "parabolic solver", semi.solver_parabolic |> typeof |> nameof) summary_line(io, "total #DOFs per field", ndofsglobal(semi)) summary_footer(io) end @@ -185,17 +176,19 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) end function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) - @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, - cache, cache_parabolic = semi + @unpack mesh, equations, boundary_conditions, source_terms, solver, + cache, cache_viscous = semi u = wrap_array(u_ode, mesh, equations, solver, cache) du = wrap_array(du_ode, mesh, equations, solver, cache) + viscous_formulation = default_parabolic_solver() + time_start = time_ns() @trixi_timeit timer() "rhs!" rhs_parabolic!(du, u, t, mesh, equations, boundary_conditions, source_terms, - solver, solver_parabolic, cache, - cache_parabolic) + solver, viscous_formulation, cache, + cache_viscous) runtime = time_ns() - time_start put!(semi.performance_counter, runtime) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 7c5bcbd0bc4..a0b5a105870 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -21,22 +21,21 @@ isdir(outdir) && rm(outdir, recursive = true) n_cells_max = 30_000, periodicity = false) - equations = LinearScalarAdvectionEquation1D(0.0) - equations_parabolic = LaplaceDiffusion1D(0.1, equations) + equations_parabolic = LinearDiffusionEquation1D(0.1) initial_condition(x, t, equations) = SVector(sinpi(x[1])) boundary_conditions = (; x_neg = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)), x_pos = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0))) semi = SemidiscretizationParabolic(mesh, equations_parabolic, initial_condition, solver; - boundary_conditions = boundary_conditions, - solver_parabolic = ViscousFormulationLocalDG()) + boundary_conditions = boundary_conditions) @trixi_test_nowarn show(stdout, semi) @trixi_test_nowarn show(stdout, MIME"text/plain"(), semi) @test semi isa SemidiscretizationParabolic - @test semi.solver_parabolic isa ViscousFormulationLocalDG + @test semi.solver isa DGSEM + @test !hasproperty(semi, :solver_parabolic) @test nvariables(semi) == 1 @test ndims(semi) == 1 @test real(semi) == real(solver) From e42ea22768a01d0fb14f674276d91eb3f6ab38ee Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 19 Mar 2026 12:27:37 +0100 Subject: [PATCH 05/68] rename advective/diffusive cfl to hyperbolic/parabolic --- .../src/files/adding_new_parabolic_terms.jl | 10 +-- .../src/files/parabolic_source_terms.jl | 8 +- ...vection_diffusion_gradient_source_terms.jl | 8 +- .../elixir_advection_diffusion_nonperiodic.jl | 2 +- .../elixir_advection_diffusion_cfl.jl | 4 +- ...vection_diffusion_gradient_source_terms.jl | 8 +- ...r_navierstokes_convergence_periodic_cfl.jl | 4 +- .../elixir_viscous_burgers_n_wave.jl | 4 +- .../elixir_viscous_burgers_shock.jl | 4 +- ...vection_diffusion_gradient_source_terms.jl | 8 +- .../elixir_navierstokes_viscous_shock.jl | 8 +- ...vection_diffusion_gradient_source_terms.jl | 8 +- .../elixir_navierstokes_viscous_shock.jl | 4 +- src/callbacks_step/stepsize.jl | 88 +++++++++---------- src/equations/compressible_navier_stokes.jl | 2 +- .../compressible_navier_stokes_1d.jl | 2 +- .../compressible_navier_stokes_2d.jl | 2 +- .../compressible_navier_stokes_3d.jl | 2 +- src/equations/equations_parabolic.jl | 4 +- .../semidiscretization_coupled.jl | 5 +- .../semidiscretization_coupled_p4est.jl | 4 +- test/test_parabolic_1d.jl | 2 +- test/test_parabolic_2d.jl | 8 +- test/test_parabolic_3d.jl | 6 +- 24 files changed, 103 insertions(+), 102 deletions(-) diff --git a/docs/literate/src/files/adding_new_parabolic_terms.jl b/docs/literate/src/files/adding_new_parabolic_terms.jl index fe0239141cd..ca471a6a70d 100644 --- a/docs/literate/src/files/adding_new_parabolic_terms.jl +++ b/docs/literate/src/files/adding_new_parabolic_terms.jl @@ -204,11 +204,11 @@ end return lambda_max() end -# We supply now the advective(hyperbolic) and diffusive(parabolic) CFL numbers -cfl_advective = 2.0 # Not restrictive for this example -cfl_diffusive = 0.21 # Restricts the timestep -stepsize_callback = StepsizeCallback(cfl = cfl_advective, - cfl_diffusive = cfl_diffusive) +# We now supply the hyperbolic and parabolic CFL numbers +cfl_hyperbolic = 2.0 # Not restrictive for this example +cfl_parabolic = 0.21 # Restricts the timestep +stepsize_callback = StepsizeCallback(cfl = cfl_hyperbolic, + cfl_parabolic = cfl_parabolic) # Add the stepsize callback to the existing callbacks callbacks = CallbackSet(SummaryCallback(), stepsize_callback); diff --git a/docs/literate/src/files/parabolic_source_terms.jl b/docs/literate/src/files/parabolic_source_terms.jl index 4ed7e8059c1..f98f340da7f 100644 --- a/docs/literate/src/files/parabolic_source_terms.jl +++ b/docs/literate/src/files/parabolic_source_terms.jl @@ -89,10 +89,10 @@ ode = semidiscretize(semi, tspan) # stable time-step is $O(h^2)$ due to the dominant parabolic term. We enforce this more stringent # parabolic CFL condition using a diffusion-aware `StepsizeCallback`. -cfl_advective = 0.5 -cfl_diffusive = 0.05 -stepsize_callback = StepsizeCallback(cfl = cfl_advective, - cfl_diffusive = cfl_diffusive) +cfl_hyperbolic = 0.5 +cfl_parabolic = 0.05 +stepsize_callback = StepsizeCallback(cfl = cfl_hyperbolic, + cfl_parabolic = cfl_parabolic) callbacks = CallbackSet(SummaryCallback(), stepsize_callback) sol = solve(ode, RDPK3SpFSAL35(); adaptive = false, dt = stepsize_callback(ode), ode_default_options()..., callback = callbacks) diff --git a/examples/dgmulti_1d/elixir_advection_diffusion_gradient_source_terms.jl b/examples/dgmulti_1d/elixir_advection_diffusion_gradient_source_terms.jl index 29ee42de3f3..f2e7b8a542e 100644 --- a/examples/dgmulti_1d/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/dgmulti_1d/elixir_advection_diffusion_gradient_source_terms.jl @@ -58,10 +58,10 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval, uEltype alive_callback = AliveCallback(analysis_interval = 100) -cfl_advective = 0.5 # Not restrictive for this example -cfl_diffusive = 0.025 # Restricts the timestep -stepsize_callback = StepsizeCallback(cfl = cfl_advective, - cfl_diffusive = cfl_diffusive) +cfl_hyperbolic = 0.5 # Not restrictive for this example +cfl_parabolic = 0.025 # Restricts the timestep +stepsize_callback = StepsizeCallback(cfl = cfl_hyperbolic, + cfl_parabolic = cfl_parabolic) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl b/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl index 90cdc336a85..a440f570767 100644 --- a/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl +++ b/examples/p4est_3d_dgsem/elixir_advection_diffusion_nonperiodic.jl @@ -76,7 +76,7 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) stepsize_callback = StepsizeCallback(cfl = 1.6, - cfl_diffusive = 0.25) + cfl_parabolic = 0.25) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_cfl.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_cfl.jl index d06efc92334..d9969ccade6 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_cfl.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_cfl.jl @@ -62,10 +62,10 @@ analysis_callback = AnalysisCallback(semi, interval = 100) alive_callback = AliveCallback(analysis_interval = 100) # Stepsize callback which selects the timestep according to the most restrictive CFL condition. -# For coarser grids, linear stability is governed by the advective CFL condition, +# For coarser grids, linear stability is governed by the hyperbolic CFL condition, # while for high refinements the flow becomes diffusion-dominated. stepsize_callback = StepsizeCallback(cfl = 1.6, - cfl_diffusive = 0.3) + cfl_parabolic = 0.3) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl index 93a6f423643..83459b535bc 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl @@ -58,10 +58,10 @@ analysis_callback = AnalysisCallback(semi, interval = 100) alive_callback = AliveCallback(analysis_interval = 100) -cfl_advective = 0.5 -cfl_diffusive = 0.05 -stepsize_callback = StepsizeCallback(cfl = cfl_advective, - cfl_diffusive = cfl_diffusive) +cfl_hyperbolic = 0.5 +cfl_parabolic = 0.05 +stepsize_callback = StepsizeCallback(cfl = cfl_hyperbolic, + cfl_parabolic = cfl_parabolic) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic_cfl.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic_cfl.jl index ea61ce8c547..7f0006b9c1b 100644 --- a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic_cfl.jl +++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic_cfl.jl @@ -125,10 +125,10 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) # Stepsize callback which selects the timestep according to the most restrictive CFL condition. -# For coarser grids, linear stability is governed by the advective/convective CFL condition, +# For coarser grids, linear stability is governed by the hyperbolic CFL condition, # while for high refinements (e.g. initial_refinement_level = 8) the flow becomes diffusion-dominated. stepsize_callback = StepsizeCallback(cfl = 2.7, - cfl_diffusive = 0.2) + cfl_parabolic = 0.2) callbacks = CallbackSet(summary_callback, analysis_callback, diff --git a/examples/tree_1d_dgsem/elixir_viscous_burgers_n_wave.jl b/examples/tree_1d_dgsem/elixir_viscous_burgers_n_wave.jl index 091ab163eae..6555c3fcba2 100644 --- a/examples/tree_1d_dgsem/elixir_viscous_burgers_n_wave.jl +++ b/examples/tree_1d_dgsem/elixir_viscous_burgers_n_wave.jl @@ -53,8 +53,8 @@ analysis_callback = AnalysisCallback(semi, interval = 100) alive_callback = AliveCallback(analysis_interval = 100) -# Timestep is limited by standard/advective/convective CFL -stepsize_callback = StepsizeCallback(cfl = 0.6, cfl_diffusive = 0.1) +# Timestep is limited by the hyperbolic CFL +stepsize_callback = StepsizeCallback(cfl = 0.6, cfl_parabolic = 0.1) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, diff --git a/examples/tree_1d_dgsem/elixir_viscous_burgers_shock.jl b/examples/tree_1d_dgsem/elixir_viscous_burgers_shock.jl index 83bc8498b76..e04f14d640d 100644 --- a/examples/tree_1d_dgsem/elixir_viscous_burgers_shock.jl +++ b/examples/tree_1d_dgsem/elixir_viscous_burgers_shock.jl @@ -51,8 +51,8 @@ analysis_callback = AnalysisCallback(semi, interval = 100) alive_callback = AliveCallback(analysis_interval = 100) -# Timestep is limited by diffusive CFL -stepsize_callback = StepsizeCallback(cfl = 0.8, cfl_diffusive = 0.15) +# Timestep is limited by parabolic CFL +stepsize_callback = StepsizeCallback(cfl = 0.8, cfl_parabolic = 0.15) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl index 10dd308d64c..a68f0c1eeda 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl @@ -67,10 +67,10 @@ analysis_callback = AnalysisCallback(semi, interval = 100) alive_callback = AliveCallback(analysis_interval = 100) -cfl_advective = 0.5 -cfl_diffusive = 0.01 -stepsize_callback = StepsizeCallback(cfl = cfl_advective, - cfl_diffusive = cfl_diffusive) +cfl_hyperbolic = 0.5 +cfl_parabolic = 0.01 +stepsize_callback = StepsizeCallback(cfl = cfl_hyperbolic, + cfl_parabolic = cfl_parabolic) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl b/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl index dd6e0dc13b2..4f45baa4a88 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_viscous_shock.jl @@ -171,12 +171,12 @@ alive_callback = AliveCallback(alive_interval = 10) analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -# Admissible stepsize is governed by the diffusive CFL condition. -# Unless the advective cfl number `cfl` is not reduced to e.g. `0.1` +# Admissible stepsize is governed by the parabolic CFL condition. +# Unless the hyperbolic CFL number `cfl` is reduced to e.g. `0.1` # (which is overly restrictive for this problem), -# the diffusive CFL restricts the timestep for this problem. +# the parabolic CFL restricts the timestep for this problem. stepsize_callback = StepsizeCallback(cfl = 0.2, - cfl_diffusive = 0.2) + cfl_parabolic = 0.2) callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, stepsize_callback) diff --git a/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl b/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl index 93f06a99f22..d8379ba0972 100644 --- a/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl +++ b/examples/tree_3d_dgsem/elixir_advection_diffusion_gradient_source_terms.jl @@ -67,10 +67,10 @@ analysis_callback = AnalysisCallback(semi, interval = 100) alive_callback = AliveCallback(analysis_interval = 100) -cfl_advective = 0.5 # Not restrictive for this example -cfl_diffusive = 0.01 # Restricts the timestep -stepsize_callback = StepsizeCallback(cfl = cfl_advective, - cfl_diffusive = cfl_diffusive) +cfl_hyperbolic = 0.5 # Not restrictive for this example +cfl_parabolic = 0.01 # Restricts the timestep +stepsize_callback = StepsizeCallback(cfl = cfl_hyperbolic, + cfl_parabolic = cfl_parabolic) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_viscous_shock.jl b/examples/tree_3d_dgsem/elixir_navierstokes_viscous_shock.jl index 8df31a0561a..858029fdd96 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_viscous_shock.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_viscous_shock.jl @@ -174,10 +174,10 @@ alive_callback = AliveCallback(alive_interval = 10) analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -# For this setup, both advective and diffusive time step restrictions are relevant, i.e., +# For this setup, both hyperbolic and parabolic timestep restrictions are relevant, i.e., # may not be increased beyond the given values. stepsize_callback = StepsizeCallback(cfl = 0.4, - cfl_diffusive = 0.2) + cfl_parabolic = 0.2) callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, stepsize_callback) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index febda8252b7..269f6ee5198 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -6,30 +6,30 @@ #! format: noindent """ - StepsizeCallback(; cfl=1.0, cfl_diffusive = 0.0, + StepsizeCallback(; cfl=1.0, cfl_parabolic = 0.0, interval = 1) -Set the time step size according to a CFL condition with CFL number `cfl` +Set the time step size according to a CFL condition with hyperbolic CFL number `cfl` if the time integration method isn't adaptive itself. -The keyword argument `cfl` must be either a `Real` number, corresponding to a constant +The hyperbolic CFL number must be either a `Real` number, corresponding to a constant CFL number, or a function of time `t` returning a `Real` number. The latter approach allows for variable CFL numbers that can be used to realize, e.g., a ramp-up of the time step. -One can additionally supply a diffusive CFL number `cfl_diffusive` to -limit the admissible timestep also respecting diffusive restrictions. +One can additionally supply a parabolic CFL number `cfl_parabolic` to +limit the admissible timestep also respecting parabolic restrictions. This is only applicable for semidiscretizations of type [`SemidiscretizationHyperbolicParabolic`](@ref). -To enable checking for diffusive timestep restrictions, provide a value greater than zero for `cfl_diffusive`. -By default, `cfl_diffusive` is set to zero which means that only the advective/convective CFL number is considered. -The keyword argument `cfl_diffusive` must be either a `Real` number, corresponding to a constant -diffusive CFL number, or a function of time `t` returning a `Real` number. +To enable checking for parabolic timestep restrictions, provide a value greater than zero for `cfl_parabolic`. +By default, `cfl_parabolic` is set to zero which means that only the hyperbolic CFL number is considered. +The keyword argument `cfl_parabolic` must be either a `Real` number, corresponding to a constant +parabolic CFL number, or a function of time `t` returning a `Real` number. By default, the timestep will be adjusted at every step. For different values of `interval`, the timestep will be adjusted every `interval` steps. """ -struct StepsizeCallback{CflAdvectiveType, CflDiffusiveType} - cfl_advective::CflAdvectiveType - cfl_diffusive::CflDiffusiveType +struct StepsizeCallback{CflHyperbolicType, CflParabolicType} + cfl_hyperbolic::CflHyperbolicType + cfl_parabolic::CflParabolicType interval::Int end @@ -37,10 +37,10 @@ function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:StepsizeCallback}) @nospecialize cb # reduce precompilation time stepsize_callback = cb.affect! - @unpack cfl_advective, cfl_diffusive, interval = stepsize_callback + @unpack cfl_hyperbolic, cfl_parabolic, interval = stepsize_callback print(io, "StepsizeCallback(", - "cfl_advective=", cfl_advective, ", ", - "cfl_diffusive=", cfl_diffusive, ", ", + "cfl_hyperbolic=", cfl_hyperbolic, ", ", + "cfl_parabolic=", cfl_parabolic, ", ", "interval=", interval, ")") return nothing end @@ -55,22 +55,22 @@ function Base.show(io::IO, ::MIME"text/plain", stepsize_callback = cb.affect! setup = [ - "CFL Advective" => stepsize_callback.cfl_advective, - "CFL Diffusive" => stepsize_callback.cfl_diffusive, + "CFL Hyperbolic" => stepsize_callback.cfl_hyperbolic, + "CFL Parabolic" => stepsize_callback.cfl_parabolic, "Interval" => stepsize_callback.interval ] summary_box(io, "StepsizeCallback", setup) end end -function StepsizeCallback(; cfl = 1.0, cfl_diffusive = 0.0, +function StepsizeCallback(; cfl = 1.0, cfl_parabolic = 0.0, interval = 1) # Convert plain real numbers to functions for unified treatment - cfl_conv = isa(cfl, Real) ? Returns(cfl) : cfl - cfl_diff = isa(cfl_diffusive, Real) ? Returns(cfl_diffusive) : cfl_diffusive - stepsize_callback = StepsizeCallback{typeof(cfl_conv), typeof(cfl_diff)}(cfl_conv, - cfl_diff, - interval) + cfl_hyp = isa(cfl, Real) ? Returns(cfl) : cfl + cfl_para = isa(cfl_parabolic, Real) ? Returns(cfl_parabolic) : cfl_parabolic + stepsize_callback = StepsizeCallback{typeof(cfl_hyp), typeof(cfl_para)}(cfl_hyp, + cfl_para, + interval) return DiscreteCallback(stepsize_callback, stepsize_callback, # the first one is the condition, the second the affect! save_positions = (false, false), @@ -78,9 +78,9 @@ function StepsizeCallback(; cfl = 1.0, cfl_diffusive = 0.0, end # Compatibility constructor used in `EulerAcousticsCouplingCallback` -function StepsizeCallback(cfl_advective) - RealT = typeof(cfl_advective) - return StepsizeCallback{RealT, RealT}(cfl_advective, zero(RealT), 1) +function StepsizeCallback(cfl_hyperbolic) + RealT = typeof(cfl_hyperbolic) + return StepsizeCallback{RealT, RealT}(cfl_hyperbolic, zero(RealT), 1) end function initialize!(cb::DiscreteCallback{Condition, Affect!}, u, t, @@ -106,11 +106,11 @@ end t = integrator.t u_ode = integrator.u semi = integrator.p - @unpack cfl_advective, cfl_diffusive = stepsize_callback + @unpack cfl_hyperbolic, cfl_parabolic = stepsize_callback # Dispatch based on semidiscretization - dt = @trixi_timeit timer() "calculate dt" calculate_dt(u_ode, t, cfl_advective, - cfl_diffusive, semi) + dt = @trixi_timeit timer() "calculate dt" calculate_dt(u_ode, t, cfl_hyperbolic, + cfl_parabolic, semi) set_proposed_dt!(integrator, dt) integrator.opts.dtmax = dt @@ -130,57 +130,57 @@ function (cb::DiscreteCallback{Condition, Affect!})(ode::ODEProblem) where {Cond StepsizeCallback } stepsize_callback = cb.affect! - @unpack cfl_advective, cfl_diffusive = stepsize_callback + @unpack cfl_hyperbolic, cfl_parabolic = stepsize_callback u_ode = ode.u0 t = first(ode.tspan) semi = ode.p - return calculate_dt(u_ode, t, cfl_advective, cfl_diffusive, semi) + return calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, semi) end # General case for an abstract single (i.e., non-coupled) semidiscretization -function calculate_dt(u_ode, t, cfl_advective, cfl_diffusive, +function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, semi::AbstractSemidiscretization) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) u = wrap_array(u_ode, mesh, equations, solver, cache) - return cfl_advective(t) * max_dt(u, t, mesh, + return cfl_hyperbolic(t) * max_dt(u, t, mesh, have_constant_speed(equations), equations, solver, cache) end # For Euler-Acoustic simulations with `EulerAcousticsCouplingCallback` -function calculate_dt(u_ode, t, cfl_advective::Real, cfl_diffusive::Real, +function calculate_dt(u_ode, t, cfl_hyperbolic::Real, cfl_parabolic::Real, semi::AbstractSemidiscretization) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) u = wrap_array(u_ode, mesh, equations, solver, cache) - return cfl_advective * max_dt(u, t, mesh, + return cfl_hyperbolic * max_dt(u, t, mesh, have_constant_speed(equations), equations, solver, cache) end # Case for a hyperbolic-parabolic semidiscretization -function calculate_dt(u_ode, t, cfl_advective, cfl_diffusive, +function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, semi::SemidiscretizationHyperbolicParabolic) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) equations_parabolic = semi.equations_parabolic u = wrap_array(u_ode, mesh, equations, solver, cache) - dt_advective = cfl_advective(t) * max_dt(u, t, mesh, - have_constant_speed(equations), equations, - solver, cache) + dt_hyperbolic = cfl_hyperbolic(t) * max_dt(u, t, mesh, + have_constant_speed(equations), equations, + solver, cache) - cfl_diff = cfl_diffusive(t) - if cfl_diff > 0 # Check if diffusive CFL should be considered - dt_diffusive = cfl_diff * max_dt(u, t, mesh, + cfl_para = cfl_parabolic(t) + if cfl_para > 0 # Check if parabolic CFL should be considered + dt_parabolic = cfl_para * max_dt(u, t, mesh, have_constant_diffusivity(equations_parabolic), equations, equations_parabolic, solver, cache) - return min(dt_advective, dt_diffusive) + return min(dt_hyperbolic, dt_parabolic) else - return dt_advective + return dt_hyperbolic end end diff --git a/src/equations/compressible_navier_stokes.jl b/src/equations/compressible_navier_stokes.jl index 3cfeaaf5018..e15c11f3a02 100644 --- a/src/equations/compressible_navier_stokes.jl +++ b/src/equations/compressible_navier_stokes.jl @@ -115,7 +115,7 @@ dynamic_viscosity(u, mu::T, equations) where {T} = mu(u, equations) # Returns - `False()` -Used in diffusive CFL condition computation (see [`StepsizeCallback`](@ref)) to indicate that the +Used in parabolic CFL condition computation (see [`StepsizeCallback`](@ref)) to indicate that the diffusivity is not constant in space and that [`max_diffusivity`](@ref) needs to be computed at every node in every element. diff --git a/src/equations/compressible_navier_stokes_1d.jl b/src/equations/compressible_navier_stokes_1d.jl index f6af3611042..3802c23afd8 100644 --- a/src/equations/compressible_navier_stokes_1d.jl +++ b/src/equations/compressible_navier_stokes_1d.jl @@ -96,7 +96,7 @@ struct CompressibleNavierStokesDiffusion1D{GradientVariables, RealT <: Real, Mu, mu::Mu # viscosity Pr::RealT # Prandtl number kappa::RealT # thermal diffusivity for Fick's law - max_1_kappa::RealT # max(1, kappa) used for diffusive CFL => `max_diffusivity` + max_1_kappa::RealT # max(1, kappa) used for parabolic CFL => `max_diffusivity` equations_hyperbolic::E # CompressibleEulerEquations1D gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl index 125c94cba32..2e7625838da 100644 --- a/src/equations/compressible_navier_stokes_2d.jl +++ b/src/equations/compressible_navier_stokes_2d.jl @@ -96,7 +96,7 @@ struct CompressibleNavierStokesDiffusion2D{GradientVariables, RealT <: Real, Mu, mu::Mu # viscosity Pr::RealT # Prandtl number kappa::RealT # thermal diffusivity for Fick's law - max_4over3_kappa::RealT # max(4/3, kappa) used for diffusive CFL => `max_diffusivity` + max_4over3_kappa::RealT # max(4/3, kappa) used for parabolic CFL => `max_diffusivity` equations_hyperbolic::E # CompressibleEulerEquations2D gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy diff --git a/src/equations/compressible_navier_stokes_3d.jl b/src/equations/compressible_navier_stokes_3d.jl index fe4cc365e2a..874408e1dc8 100644 --- a/src/equations/compressible_navier_stokes_3d.jl +++ b/src/equations/compressible_navier_stokes_3d.jl @@ -96,7 +96,7 @@ struct CompressibleNavierStokesDiffusion3D{GradientVariables, RealT <: Real, Mu, mu::Mu # viscosity Pr::RealT # Prandtl number kappa::RealT # thermal diffusivity for Fick's law - max_4over3_kappa::RealT # max(4/3, kappa) used for diffusive CFL => `max_diffusivity` + max_4over3_kappa::RealT # max(4/3, kappa) used for parabolic CFL => `max_diffusivity` equations_hyperbolic::E # CompressibleEulerEquations3D gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl index bfd33cfae78..716aba0ef2a 100644 --- a/src/equations/equations_parabolic.jl +++ b/src/equations/equations_parabolic.jl @@ -17,7 +17,7 @@ abstract type AbstractLaplaceDiffusion{NDIMS, NVARS} <: # Returns - `True()` -Used in diffusive CFL condition computation (see [`StepsizeCallback`](@ref)) to indicate that the +Used in parabolic CFL condition computation (see [`StepsizeCallback`](@ref)) to indicate that the diffusivity is constant in space and that [`max_diffusivity`](@ref) needs **not** to be re-computed at every node in every element. @@ -32,7 +32,7 @@ if the diffusion term is linear in the variables/constant. # Returns - `equations_parabolic.diffusivity` -Returns isotropic diffusion coefficient for use in diffusive CFL condition computation, +Returns isotropic diffusion coefficient for use in parabolic CFL condition computation, see [`StepsizeCallback`](@ref). """ @inline function max_diffusivity(equations_parabolic::AbstractLaplaceDiffusion) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 01315331bdd..dc86a8bac84 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -357,11 +357,12 @@ end ### StepsizeCallback ################################################################################ # In case of coupled system, use minimum timestep over all systems -function calculate_dt(u_ode, t, cfl_advective, cfl_diffusive, +function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, semi::SemidiscretizationCoupled) dt = minimum(eachsystem(semi)) do i u_ode_slice = get_system_u_ode(u_ode, i, semi) - return calculate_dt(u_ode_slice, t, cfl_advective, cfl_diffusive, semi.semis[i]) + return calculate_dt(u_ode_slice, t, cfl_hyperbolic, cfl_parabolic, + semi.semis[i]) end return dt diff --git a/src/semidiscretization/semidiscretization_coupled_p4est.jl b/src/semidiscretization/semidiscretization_coupled_p4est.jl index 18fc21c8f50..9de383d5b1c 100644 --- a/src/semidiscretization/semidiscretization_coupled_p4est.jl +++ b/src/semidiscretization/semidiscretization_coupled_p4est.jl @@ -323,11 +323,11 @@ end # In case of coupled system, use minimum timestep over all systems # Case for constant `cfl_number`. -function calculate_dt(u_ode, t, cfl_advective, cfl_diffusive, +function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, semi::SemidiscretizationCoupledP4est) dt = minimum(eachsystem(semi)) do i u_ode_slice = get_system_u_ode(u_ode, i, semi) - calculate_dt(u_ode_slice, t, cfl_advective, cfl_diffusive, semi.semis[i]) + calculate_dt(u_ode_slice, t, cfl_hyperbolic, cfl_parabolic, semi.semis[i]) end return dt diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 839d7e4ccdd..9fef07fed7a 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -487,7 +487,7 @@ end callbacks=CallbackSet(summary_callback, alive_callback, analysis_callback, StepsizeCallback(cfl = 0.5, - cfl_diffusive = 0.1)), + cfl_parabolic = 0.1)), adaptive=false, l2=[ 3.804624387087144e-5, diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index b3652929172..1c1d79775c5 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -705,7 +705,7 @@ end solver=DGSEM(polydeg = 3, surface_flux = flux_hlle, basis_type = GaussLegendreBasis), solver_parabolic=ParabolicFormulationLocalDG(), - cfl_diffusive=0.04, + cfl_parabolic=0.04, l2=[ 6.599006355897759e-6, 4.514805201434994e-6, @@ -786,14 +786,14 @@ end @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) end -@trixi_testset "P4estMesh2D: elixir_advection_diffusion_nonperiodic_amr.jl (Diffusive CFL)" begin +@trixi_testset "P4estMesh2D: elixir_advection_diffusion_nonperiodic_amr.jl (Parabolic CFL)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_2d_dgsem", "elixir_advection_diffusion_nonperiodic_amr.jl"), initial_refinement_level=2, callbacks=CallbackSet(summary_callback, analysis_callback, alive_callback, StepsizeCallback(cfl = 1.6, - cfl_diffusive = 0.2)), + cfl_parabolic = 0.2)), ode_alg=CarpenterKennedy2N54(williamson_condition = false), dt=1.0, # will be overwritten l2=[0.00010850375815619432], @@ -1145,7 +1145,7 @@ end callbacks=CallbackSet(summary_callback, analysis_callback, alive_callback, StepsizeCallback(cfl = 2.3, - cfl_diffusive = 1.0)), + cfl_parabolic = 1.0)), adaptive=false, # respect CFL ode_alg=CKLLSRK95_4S(), l2=[ diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 92187aa0433..062209beecc 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -408,7 +408,7 @@ end @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) end -@trixi_testset "P4estMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Diffusive CFL)" begin +@trixi_testset "P4estMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Parabolic CFL)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"), tspan=(0.0, 0.1), @@ -416,7 +416,7 @@ end callbacks=CallbackSet(summary_callback, analysis_callback, alive_callback, StepsizeCallback(cfl = 2.3, - cfl_diffusive = 0.4)), + cfl_parabolic = 0.4)), adaptive=false, # respect CFL ode_alg=CKLLSRK95_4S(), l2=[ @@ -526,7 +526,7 @@ end @test_trixi_include(joinpath(EXAMPLES_DIR, "p4est_3d_dgsem", "elixir_advection_diffusion_nonperiodic.jl"), solver_parabolic=ParabolicFormulationLocalDG(), - cfl_diffusive=0.07, + cfl_parabolic=0.07, l2=[0.0041854757843498725], linf=[0.05166356737492643]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) From 1e231fdf16e4c938031e11fecc5539eb2f2b380f Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 19 Mar 2026 13:56:55 +0100 Subject: [PATCH 06/68] revert NEWS.md --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2f053cd66ca..711bad308be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ for human readability. #### Added -- It is now possible to use `ParabolicFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. +- It is now possible to use `ViscousFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. This is useful for (locally) diffusion-dominated problems. This enables in particular adaptive mesh refinement for that solver-mesh combination ([#2712]). - Added functionality to `ScalarPlotData2D` allowing visualization a field provided by a user-defined scalar function ([#2796]). @@ -160,7 +160,7 @@ This enables in particular adaptive mesh refinement for that solver-mesh combina - Added symmetry plane/reflective wall velocity+stress boundary conditions for the compressible Navier-Stokes equations in 2D and 3D. Currently available only for the `P4estMesh` mesh type, `GradientVariablesPrimitive`, and `Adiabatic` heat boundary condition ([#2416]). - Added `LaplaceDiffusionEntropyVariables1D`, `LaplaceDiffusionEntropyVariables2D`, and `LaplaceDiffusionEntropyVariables3D`. These add scalar diffusion to each - equation of a system, but apply diffusion in terms of the entropy variables, which symmetrizes the parabolic formulation and ensures semi-discrete entropy dissipation ([#2406]). + equation of a system, but apply diffusion in terms of the entropy variables, which symmetrizes the viscous formulation and ensures semi-discrete entropy dissipation ([#2406]). - Added the three-dimensional multi-ion magneto-hydrodynamics (MHD) equations with a generalized Lagrange multipliers (GLM) divergence cleaning technique ([#2215]). - New time integrator `PairedExplicitRK4`, implementing the fourth-order From 52fef8569b3b80e88e4fce56fd1d1d2147afa55b Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 19 Mar 2026 14:44:58 +0100 Subject: [PATCH 07/68] add change to NEWS.md --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 711bad308be..79e449247bf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,7 +8,7 @@ for human readability. ## Changes in the v0.15 lifecycle #### Added - +- The word "viscous" is now used only where it refers specifically to fluid viscosity. The word "parabolic" is used in more general contexts. For example, `ViscousFormulationLocalDG()` is now `ParabolicFormulationLocalDG()`. For consistency, `cfl_advective` and `cfl_diffusive` have also been renamed `cfl_hyperbolic` and `cfl_parabolic` ([#2868]). - It is now possible to use `ViscousFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. This is useful for (locally) diffusion-dominated problems. This enables in particular adaptive mesh refinement for that solver-mesh combination ([#2712]). From 20bdc28563c86d20dbe3e5577db438112422eca5 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:15:34 +0100 Subject: [PATCH 08/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- NEWS.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 79e449247bf..b260d3af7ce 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,7 +8,11 @@ for human readability. ## Changes in the v0.15 lifecycle #### Added -- The word "viscous" is now used only where it refers specifically to fluid viscosity. The word "parabolic" is used in more general contexts. For example, `ViscousFormulationLocalDG()` is now `ParabolicFormulationLocalDG()`. For consistency, `cfl_advective` and `cfl_diffusive` have also been renamed `cfl_hyperbolic` and `cfl_parabolic` ([#2868]). +- The word "viscous" is now used only where it refers specifically to fluid viscosity. +The word "parabolic" is used in more general contexts. +For example, `ViscousFormulationLocalDG()` is now `ParabolicFormulationLocalDG()`. +In particular, viscosity is no longer used as a proxy for any parabolic/diffusive process such as heat conduction. +For consistency, `cfl_advective` and `cfl_diffusive` have also been renamed `cfl_hyperbolic` and `cfl_parabolic` ([#2868]). - It is now possible to use `ViscousFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. This is useful for (locally) diffusion-dominated problems. This enables in particular adaptive mesh refinement for that solver-mesh combination ([#2712]). From a4538422d3fba6009478daf12590acc49d6bba90 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:15:47 +0100 Subject: [PATCH 09/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- docs/literate/src/files/adding_new_parabolic_terms.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/literate/src/files/adding_new_parabolic_terms.jl b/docs/literate/src/files/adding_new_parabolic_terms.jl index ca471a6a70d..1b252c89785 100644 --- a/docs/literate/src/files/adding_new_parabolic_terms.jl +++ b/docs/literate/src/files/adding_new_parabolic_terms.jl @@ -66,7 +66,7 @@ end # \begin{aligned} # \bm{q} &= \nabla u \\ # \bm{\sigma} &= \begin{pmatrix} g_1(u, \bm{q}) \\ g_2(u, \bm{q}) \end{pmatrix} \\ -# \text{parabolic contribution } &= \nabla \cdot \bm{\sigma} +# \text{parabolic contribution} &= \nabla \cdot \bm{\sigma} # \end{aligned} # ``` # From 96234c7c16b911ba9e3fa20a1f5c44ba10a23a4e Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:15:58 +0100 Subject: [PATCH 10/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index f731a31c66b..752ac07ae59 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -558,13 +558,16 @@ function prolong2mortars_divergence!(cache, flux_parabolic, element) for v in eachvariable(equations_parabolic) - flux_parabolic = SVector(flux_parabolic_x[v, i_large, j_large, + flux_parabolic = SVector(flux_parabolic_x[v, i_large, + j_large, k_large, element], - flux_parabolic_y[v, i_large, j_large, + flux_parabolic_y[v, i_large, + j_large, k_large, element], - flux_parabolic_z[v, i_large, j_large, + flux_parabolic_z[v, i_large, + j_large, k_large, element]) From d7f03f2401c3599aaffe854f24869567aceca534 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:16:12 +0100 Subject: [PATCH 11/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 28a1134cf77..b72e223a514 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -506,8 +506,7 @@ function calc_parabolic_fluxes!(flux_parabolic, # Calculate parabolic flux and store each component for later use flux_parabolic_node_x = flux(u_node, (gradients_1_node, gradients_2_node), - 1, - equations_parabolic) + 1, equations_parabolic) flux_parabolic_node_y = flux(u_node, (gradients_1_node, gradients_2_node), 2, equations_parabolic) From b98459afc1ef15e79d20f99a1c49d28ccf672b84 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:16:42 +0100 Subject: [PATCH 12/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index b72e223a514..57c1579b0d0 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -508,8 +508,7 @@ function calc_parabolic_fluxes!(flux_parabolic, flux_parabolic_node_x = flux(u_node, (gradients_1_node, gradients_2_node), 1, equations_parabolic) flux_parabolic_node_y = flux(u_node, (gradients_1_node, gradients_2_node), - 2, - equations_parabolic) + 2, equations_parabolic) set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, dg, i, j, element) From ae219456471609f03fa8e35fa664da62401b03fb Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:16:51 +0100 Subject: [PATCH 13/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 57c1579b0d0..933f95e5355 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -509,8 +509,8 @@ function calc_parabolic_fluxes!(flux_parabolic, 1, equations_parabolic) flux_parabolic_node_y = flux(u_node, (gradients_1_node, gradients_2_node), 2, equations_parabolic) - set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, - dg, + set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, + equations_parabolic, dg, i, j, element) set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, dg, From e3eda0d50abb8d779e48c3bac14d5526f7756549 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:17:01 +0100 Subject: [PATCH 14/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 933f95e5355..cdfedeff436 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -512,8 +512,8 @@ function calc_parabolic_fluxes!(flux_parabolic, set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, dg, i, j, element) - set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, - dg, + set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, + equations_parabolic, dg, i, j, element) end end From ff5d77d1ab015edaa86076028e17118468c98825 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:17:11 +0100 Subject: [PATCH 15/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 3226e9bad59..3a454a83799 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -318,8 +318,8 @@ function calc_parabolic_fluxes!(flux_parabolic, flux_parabolic_node_z = flux(u_node, (gradients_1_node, gradients_2_node, gradients_3_node), 3, equations_parabolic) - set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, - dg, + set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, + equations_parabolic, dg, i, j, k, element) set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, dg, From 9f8f0b768c7cf0b276b8620492d961c1cc62bfaf Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:17:21 +0100 Subject: [PATCH 16/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 3a454a83799..b6236cbac54 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -321,8 +321,8 @@ function calc_parabolic_fluxes!(flux_parabolic, set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, dg, i, j, k, element) - set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, - dg, + set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, + equations_parabolic, dg, i, j, k, element) set_node_vars!(flux_parabolic_z, flux_parabolic_node_z, equations_parabolic, dg, From 64a16c6652e193651b143e1fd9e4ad72c6fd5ef1 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:17:37 +0100 Subject: [PATCH 17/68] Apply suggestion from code review Co-authored-by: Daniel Doehring --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index b6236cbac54..286a3297a98 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -324,8 +324,8 @@ function calc_parabolic_fluxes!(flux_parabolic, set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, dg, i, j, k, element) - set_node_vars!(flux_parabolic_z, flux_parabolic_node_z, equations_parabolic, - dg, + set_node_vars!(flux_parabolic_z, flux_parabolic_node_z, + equations_parabolic, dg, i, j, k, element) end end From c2e5371464453564895e29862ff58bef264d1f40 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 11:18:27 +0100 Subject: [PATCH 18/68] Apply formatter suggestion Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 286a3297a98..986778085ec 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -321,7 +321,7 @@ function calc_parabolic_fluxes!(flux_parabolic, set_node_vars!(flux_parabolic_x, flux_parabolic_node_x, equations_parabolic, dg, i, j, k, element) - set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, + set_node_vars!(flux_parabolic_y, flux_parabolic_node_y, equations_parabolic, dg, i, j, k, element) set_node_vars!(flux_parabolic_z, flux_parabolic_node_z, From 023c7b09bc84bc67e9eb9b2482df46650886babf Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 12:56:29 +0100 Subject: [PATCH 19/68] remove hyperbolic stuff from parabolic solver --- ...lixir_diffusion_mixed_dirichlet_neumann.jl | 7 ++- src/callbacks_step/stepsize.jl | 19 +++++- src/equations/equations_parabolic.jl | 12 ++++ .../semidiscretization_parabolic.jl | 61 ++++++++----------- 4 files changed, 61 insertions(+), 38 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl index 707b97eeb16..18921e01a2f 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl @@ -42,11 +42,14 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval, alive_callback = AliveCallback(analysis_interval = analysis_interval) -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) + +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, + stepsize_callback) ############################################################################### # run the simulation sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); - dt = 5.0e-5, adaptive = false, + dt = stepsize_callback(ode), adaptive = false, ode_default_options()..., callback = callbacks) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 269f6ee5198..c0893144e27 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -18,7 +18,8 @@ a ramp-up of the time step. One can additionally supply a parabolic CFL number `cfl_parabolic` to limit the admissible timestep also respecting parabolic restrictions. -This is only applicable for semidiscretizations of type [`SemidiscretizationHyperbolicParabolic`](@ref). +This is only applicable for semidiscretizations of type +[`SemidiscretizationHyperbolicParabolic`](@ref) and [`SemidiscretizationParabolic`](@ref). To enable checking for parabolic timestep restrictions, provide a value greater than zero for `cfl_parabolic`. By default, `cfl_parabolic` is set to zero which means that only the hyperbolic CFL number is considered. The keyword argument `cfl_parabolic` must be either a `Real` number, corresponding to a constant @@ -149,6 +150,22 @@ function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, solver, cache) end +# Case for a purely parabolic semidiscretization +function calculate_dt(u_ode, t, _cfl_hyperbolic, cfl_parabolic, + semi::SemidiscretizationParabolic) + mesh, equations, solver, cache = mesh_equations_solver_cache(semi) + u = wrap_array(u_ode, mesh, equations, solver, cache) + + cfl_para = cfl_parabolic(t) + if !(cfl_para > 0) + throw(ArgumentError("For `SemidiscretizationParabolic`, set `cfl_parabolic > 0` in `StepsizeCallback`.")) + end + + return cfl_para * max_dt(u, t, mesh, + have_constant_diffusivity(equations), equations, + equations, solver, cache) +end + # For Euler-Acoustic simulations with `EulerAcousticsCouplingCallback` function calculate_dt(u_ode, t, cfl_hyperbolic::Real, cfl_parabolic::Real, semi::AbstractSemidiscretization) diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl index 142ecf2bac5..298a6d55123 100644 --- a/src/equations/equations_parabolic.jl +++ b/src/equations/equations_parabolic.jl @@ -26,6 +26,18 @@ if the diffusion term is linear in the variables/constant. """ @inline have_constant_diffusivity(::AbstractLaplaceDiffusion) = True() +""" + have_constant_speed(equations_parabolic::AbstractEquationsParabolic) + +Return whether the parabolic part is linear with constant diffusivity. + +This enables generic utilities such as [`linear_structure`](@ref) to treat +purely parabolic semidiscretizations analogously to linear hyperbolic ones. +""" +@inline function have_constant_speed(equations_parabolic::AbstractEquationsParabolic) + return have_constant_diffusivity(equations_parabolic) +end + """ max_diffusivity(equations_parabolic::AbstractLaplaceDiffusion) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index a208f78a172..4a89988b39a 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -13,31 +13,20 @@ of a purely parabolic conservation law. """ mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition, BoundaryConditions, SourceTerms, - Solver, Cache, ViscousCache} <: + Solver, Cache} <: AbstractSemidiscretization mesh::Mesh equations::Equations - # This guy is a bit messy since we abuse it as some kind of "exact solution" - # although this doesn't really exist... const initial_condition::InitialCondition - const boundary_conditions::BoundaryConditions const source_terms::SourceTerms - const solver::Solver cache::Cache - cache_viscous::ViscousCache performance_counter::PerformanceCounter end -# We assume some properties of the fields of the semidiscretization, e.g., -# the `equations` and the `mesh` should have the same dimension. We check these -# properties in the outer constructor defined below. While we could ensure -# them even better in an inner constructor, we do not use this approach to -# simplify the integration with Adapt.jl for GPU usage, see -# https://github.com/trixi-framework/Trixi.jl/pull/2677#issuecomment-3591789921 """ SemidiscretizationParabolic(mesh, equations, initial_condition, solver; @@ -60,13 +49,18 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic RealT = real(solver), uEltype = RealT) @assert ndims(mesh) == ndims(equations) - cache = create_cache(mesh, equations, solver, RealT, uEltype) + # Base cache containing data used for both parabolic and hyperbolic solvers + cache_base = create_cache(mesh, equations, solver, RealT, uEltype) _boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver, - cache) + cache_base) check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions) - cache_viscous = create_cache_parabolic(mesh, equations, solver, - nelements(solver, cache), uEltype) + # Parabolic cache containing data only used for parabolic solvers + cache_parabolic = create_cache_parabolic(mesh, equations, solver, + nelements(solver, cache_base), uEltype) + + # Combine caches into a single cache struct for the semidiscretization + cache = (; base = cache_base, parabolic = cache_parabolic) performance_counter = PerformanceCounter() @@ -75,25 +69,19 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic typeof(_boundary_conditions), typeof(source_terms), typeof(solver), - typeof(cache), typeof(cache_viscous)}(mesh, - equations, - initial_condition, - _boundary_conditions, - source_terms, - solver, - cache, - cache_viscous, - performance_counter) + typeof(cache)}(mesh, equations, + initial_condition, + _boundary_conditions, + source_terms, + solver, + cache, + performance_counter) end # @eval due to @muladd @eval Adapt.@adapt_structure(SemidiscretizationParabolic) # Create a new semidiscretization but change some parameters compared to the input. -# `Base.similar` follows a related concept but would require us to `copy` the `mesh`, -# which would impact the performance. Instead, `SciMLBase.remake` has exactly the -# semantics we want to use here. In particular, it allows us to re-use mutable parts, -# e.g. `remake(semi).mesh === semi.mesh`. function remake(semi::SemidiscretizationParabolic; uEltype = real(semi.solver), mesh = semi.mesh, equations = semi.equations, @@ -135,7 +123,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationParabolic summary_header(io, "SemidiscretizationParabolic") summary_line(io, "#spatial dimensions", ndims(semi.equations)) summary_line(io, "mesh", semi.mesh) - summary_line(io, "parabolic equations", semi.equations |> typeof |> nameof) + summary_line(io, "equations", semi.equations |> typeof |> nameof) summary_line(io, "initial condition", semi.initial_condition) summary_line(io, "source terms", semi.source_terms) summary_line(io, "solver", semi.solver |> typeof |> nameof) @@ -153,13 +141,15 @@ end @inline Base.real(semi::SemidiscretizationParabolic) = real(semi.solver) @inline function mesh_equations_solver_cache(semi::SemidiscretizationParabolic) - @unpack mesh, equations, solver, cache = semi + @unpack mesh, equations, solver = semi + cache = semi.cache.base return mesh, equations, solver, cache end function calc_error_norms(func, u_ode, t, analyzer, semi::SemidiscretizationParabolic, cache_analysis) - @unpack mesh, equations, initial_condition, solver, cache = semi + @unpack mesh, equations, initial_condition, solver = semi + cache = semi.cache.base u = wrap_array(u_ode, mesh, equations, solver, cache) return calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition, @@ -176,8 +166,9 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) end function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) - @unpack mesh, equations, boundary_conditions, source_terms, solver, - cache, cache_viscous = semi + @unpack mesh, equations, boundary_conditions, source_terms, solver = semi + cache = semi.cache.base + cache_parabolic = semi.cache.parabolic u = wrap_array(u_ode, mesh, equations, solver, cache) du = wrap_array(du_ode, mesh, equations, solver, cache) @@ -188,7 +179,7 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) @trixi_timeit timer() "rhs!" rhs_parabolic!(du, u, t, mesh, equations, boundary_conditions, source_terms, solver, viscous_formulation, cache, - cache_viscous) + cache_parabolic) runtime = time_ns() - time_start put!(semi.performance_counter, runtime) From 4c07db8977771d1a53192f51923b02f27c0d691c Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 20 Mar 2026 13:18:06 +0100 Subject: [PATCH 20/68] allow specifying parabolic solver --- ..._diffusion_mixed_dirichlet_neumann_br1.jl} | 4 +- .../elixir_diffusion_pure_dirichlet_ldg.jl | 56 +++++++++++++++++++ src/callbacks_step/save_solution.jl | 5 +- .../semidiscretization_parabolic.jl | 17 ++++-- test/test_parabolic_1d.jl | 8 ++- 5 files changed, 81 insertions(+), 9 deletions(-) rename examples/tree_1d_dgsem/{elixir_diffusion_mixed_dirichlet_neumann.jl => elixir_diffusion_mixed_dirichlet_neumann_br1.jl} (91%) create mode 100644 examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl similarity index 91% rename from examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl rename to examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl index 18921e01a2f..eacb580bf23 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl @@ -11,6 +11,7 @@ wave_number = 0.5 * pi equations = LinearDiffusionEquation1D(diffusivity) solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver_parabolic = ParabolicFormulationBassiRebay1() mesh = TreeMesh((0.0,), (1.0,), initial_refinement_level = 3, @@ -26,6 +27,7 @@ boundary_conditions = (; x_neg = boundary_condition_dirichlet, x_pos = boundary_condition_neumann) semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, boundary_conditions = boundary_conditions) ############################################################################### @@ -42,7 +44,7 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval, alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.1) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl new file mode 100644 index 00000000000..62c86abf6b7 --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl @@ -0,0 +1,56 @@ +using OrdinaryDiffEqLowStorageRK +using Trixi + +############################################################################### +# semidiscretization of the pure diffusion equation with pure Dirichlet BCs (LDG) + +diffusivity = 0.5 +amplitude = 0.4 +wave_number = pi + +equations = LinearDiffusionEquation1D(diffusivity) + +solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver_parabolic = ParabolicFormulationLocalDG() + +mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 3, + periodicity = false, + n_cells_max = 30_000) + +initial_condition = (x, t, equations) -> SVector(amplitude * exp(-diffusivity * wave_number^2 * t) * sin(wave_number * x[1])) + +boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) + +boundary_conditions = (; x_neg = boundary_condition_dirichlet, + x_pos = boundary_condition_dirichlet) + +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.1) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 200 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + analysis_integrals = ()) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.1) + +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); + dt = stepsize_callback(ode), adaptive = false, + ode_default_options()..., callback = callbacks) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 6e002d2eb23..be4324aa5d5 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -25,7 +25,7 @@ and the second parameter is the equation struct. Additional nodal variables such as vorticity or the Mach number can be saved by passing a tuple of symbols to `extra_node_variables`, e.g., `extra_node_variables = (:vorticity, :mach)`. In that case the function `get_node_variable` must be defined for each symbol in the tuple. -The expected signature of the function for (purely) hyperbolic equations is: +For purely hyperbolic and purely parabolic equations, the expected signature is: ```julia function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache) # Implementation goes here @@ -34,7 +34,8 @@ end and must return an array of dimension `(ntuple(_ -> n_nodes, ndims(mesh))..., n_elements)`. -For parabolic-hyperbolic equations `equations_parabolic` and `cache_parabolic` must be added: +For hyperbolic-parabolic equations, `equations_parabolic` and `cache_parabolic` +must be added: ```julia function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache, equations_parabolic, cache_parabolic) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 4a89988b39a..5e569022fc4 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -13,7 +13,7 @@ of a purely parabolic conservation law. """ mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition, BoundaryConditions, SourceTerms, - Solver, Cache} <: + Solver, SolverParabolic, Cache} <: AbstractSemidiscretization mesh::Mesh equations::Equations @@ -22,6 +22,7 @@ mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition, const boundary_conditions::BoundaryConditions const source_terms::SourceTerms const solver::Solver + const solver_parabolic::SolverParabolic cache::Cache @@ -30,6 +31,7 @@ end """ SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic=default_parabolic_solver(), source_terms=nothing, boundary_conditions, RealT=real(solver), @@ -42,6 +44,7 @@ single boundary condition that gets applied to all boundaries. """ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic, initial_condition, solver; + solver_parabolic = default_parabolic_solver(), source_terms = nothing, boundary_conditions, # `RealT` is used as real type for node locations etc. @@ -69,11 +72,13 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic typeof(_boundary_conditions), typeof(source_terms), typeof(solver), + typeof(solver_parabolic), typeof(cache)}(mesh, equations, initial_condition, _boundary_conditions, source_terms, solver, + solver_parabolic, cache, performance_counter) end @@ -87,9 +92,11 @@ function remake(semi::SemidiscretizationParabolic; uEltype = real(semi.solver), equations = semi.equations, initial_condition = semi.initial_condition, solver = semi.solver, + solver_parabolic = semi.solver_parabolic, source_terms = semi.source_terms, boundary_conditions = semi.boundary_conditions) return SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, source_terms = source_terms, boundary_conditions = boundary_conditions, uEltype = uEltype) @@ -105,6 +112,7 @@ function Base.show(io::IO, semi::SemidiscretizationParabolic) print(io, ", ", semi.boundary_conditions) print(io, ", ", semi.source_terms) print(io, ", ", semi.solver) + print(io, ", ", semi.solver_parabolic) print(io, ", cache(") for (idx, key) in enumerate(keys(semi.cache)) idx > 1 && print(io, " ") @@ -127,6 +135,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationParabolic summary_line(io, "initial condition", semi.initial_condition) summary_line(io, "source terms", semi.source_terms) summary_line(io, "solver", semi.solver |> typeof |> nameof) + summary_line(io, "parabolic solver", semi.solver_parabolic |> typeof |> nameof) summary_line(io, "total #DOFs per field", ndofsglobal(semi)) summary_footer(io) end @@ -166,19 +175,17 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) end function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) - @unpack mesh, equations, boundary_conditions, source_terms, solver = semi + @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic = semi cache = semi.cache.base cache_parabolic = semi.cache.parabolic u = wrap_array(u_ode, mesh, equations, solver, cache) du = wrap_array(du_ode, mesh, equations, solver, cache) - viscous_formulation = default_parabolic_solver() - time_start = time_ns() @trixi_timeit timer() "rhs!" rhs_parabolic!(du, u, t, mesh, equations, boundary_conditions, source_terms, - solver, viscous_formulation, cache, + solver, solver_parabolic, cache, cache_parabolic) runtime = time_ns() - time_start put!(semi.performance_counter, runtime) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 450b8504fc0..5eb5d40762c 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -35,7 +35,7 @@ isdir(outdir) && rm(outdir, recursive = true) @test semi isa SemidiscretizationParabolic @test semi.solver isa DGSEM - @test !hasproperty(semi, :solver_parabolic) + @test semi.solver_parabolic isa ParabolicFormulationBassiRebay1 @test nvariables(semi) == 1 @test ndims(semi) == 1 @test real(semi) == real(solver) @@ -57,6 +57,12 @@ end tspan = (0.0, 0.05)) end +@trixi_testset "TreeMesh1D: elixir_diffusion_pure_dirichlet_ldg.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_pure_dirichlet_ldg.jl"), + tspan = (0.0, 0.05)) +end + @trixi_testset "TreeMesh1D: elixir_advection_diffusion.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_advection_diffusion.jl"), From ef9af66d5f5926cedbbff5d4fd3f18e8d3905959 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 21 Mar 2026 09:25:00 +0100 Subject: [PATCH 21/68] revise tests --- ...r_diffusion_mixed_dirichlet_neumann_br1.jl | 8 ++- .../elixir_diffusion_pure_dirichlet_ldg.jl | 7 ++- src/callbacks_step/stepsize.jl | 4 +- src/equations/linear_diffusion_equation_1d.jl | 1 - .../semidiscretization_parabolic.jl | 9 ++-- test/test_parabolic_1d.jl | 53 ++++--------------- 6 files changed, 29 insertions(+), 53 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl index eacb580bf23..b0e3a1d74ce 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl @@ -18,7 +18,11 @@ mesh = TreeMesh((0.0,), (1.0,), periodicity = false, n_cells_max = 30_000) -initial_condition = (x, t, equations) -> SVector(1.0 + amplitude * exp(-diffusivity * wave_number^2 * t) * sin(wave_number * x[1])) +# Initial condition consistent with mixed Dirichlet-Neumann BCs and exact solution +initial_condition = (x, t, equations) -> SVector(1.0 + + amplitude * + exp(-diffusivity * wave_number^2 * t) * + sin(wave_number * x[1])) boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(1.0)) boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) @@ -44,7 +48,7 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval, alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl_parabolic = 0.1) +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl index 62c86abf6b7..d1b2ab942ba 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl @@ -18,7 +18,10 @@ mesh = TreeMesh((0.0,), (1.0,), periodicity = false, n_cells_max = 30_000) -initial_condition = (x, t, equations) -> SVector(amplitude * exp(-diffusivity * wave_number^2 * t) * sin(wave_number * x[1])) +# Initial condition consistent with Dirichlet BCs and exact solution +initial_condition = (x, t, equations) -> SVector(amplitude * + exp(-diffusivity * wave_number^2 * t) * + sin(wave_number * x[1])) boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) @@ -43,7 +46,7 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval, alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl_parabolic = 0.1) +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index c0893144e27..9ce2e46ef2c 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -162,8 +162,8 @@ function calculate_dt(u_ode, t, _cfl_hyperbolic, cfl_parabolic, end return cfl_para * max_dt(u, t, mesh, - have_constant_diffusivity(equations), equations, - equations, solver, cache) + have_constant_diffusivity(equations), equations, + equations, solver, cache) end # For Euler-Acoustic simulations with `EulerAcousticsCouplingCallback` diff --git a/src/equations/linear_diffusion_equation_1d.jl b/src/equations/linear_diffusion_equation_1d.jl index 09b20768efb..ad782a5e880 100644 --- a/src/equations/linear_diffusion_equation_1d.jl +++ b/src/equations/linear_diffusion_equation_1d.jl @@ -28,5 +28,4 @@ varnames(::typeof(cons2prim), ::LinearDiffusionEquation1D) = ("scalar",) # orientation == 1 return equations.diffusivity * dudx end - end # @muladd diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 5e569022fc4..6ab3138af8a 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -5,11 +5,11 @@ @muladd begin #! format: noindent -""" +@doc raw""" SemidiscretizationParabolic -A struct containing everything needed to describe a spatial semidiscretization -of a purely parabolic conservation law. +A struct containing everything needed to describe a spatial semidiscretization of a purely +parabolic PDE. """ mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition, BoundaryConditions, SourceTerms, @@ -174,6 +174,8 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) return compute_coefficients!(u_ode, semi.initial_condition, t, semi) end +# Method for `rhs!` that only computes the parabolic right-hand side with no hyperbolic +# contribution. function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic = semi cache = semi.cache.base @@ -192,5 +194,4 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) return nothing end - end # @muladd diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 5eb5d40762c..b2f7dcecfa1 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -11,56 +11,25 @@ EXAMPLES_DIR = examples_dir() outdir = "out" isdir(outdir) && rm(outdir, recursive = true) -@testset "SemidiscretizationHyperbolicParabolic (1D)" begin +@testset "SemidiscretizationHyperbolicParabolic and Semi (1D)" begin #! format: noindent -@trixi_testset "TreeMesh1D: SemidiscretizationParabolic (purely parabolic)" begin - solver = DGSEM(polydeg = 2, surface_flux = flux_central) - mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 2, - n_cells_max = 30_000, - periodicity = false) - - equations_parabolic = LinearDiffusionEquation1D(0.1) - - initial_condition(x, t, equations) = SVector(sinpi(x[1])) - boundary_conditions = (; x_neg = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)), - x_pos = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0))) - - semi = SemidiscretizationParabolic(mesh, equations_parabolic, initial_condition, solver; - boundary_conditions = boundary_conditions) - - @trixi_test_nowarn show(stdout, semi) - @trixi_test_nowarn show(stdout, MIME"text/plain"(), semi) - - @test semi isa SemidiscretizationParabolic - @test semi.solver isa DGSEM - @test semi.solver_parabolic isa ParabolicFormulationBassiRebay1 - @test nvariables(semi) == 1 - @test ndims(semi) == 1 - @test real(semi) == real(solver) - - ode = semidiscretize(semi, (0.0, 0.01)) - u0 = copy(ode.u0) - du0 = similar(u0) - Trixi.rhs!(du0, u0, semi, 0.0) - @test all(isfinite, du0) - - u0_check = similar(ode.u0) - Trixi.compute_coefficients!(u0_check, 0.0, semi) - @test u0_check ≈ ode.u0 -end - -@trixi_testset "TreeMesh1D: elixir_diffusion_mixed_dirichlet_neumann.jl" begin +@trixi_testset "TreeMesh1D: elixir_diffusion_mixed_dirichlet_neumann_br1.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_mixed_dirichlet_neumann.jl"), - tspan = (0.0, 0.05)) + "elixir_diffusion_mixed_dirichlet_neumann_br1.jl"), + l2=[4.906306967386223e-7], linf=[1.8404898263213454e-6]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) end @trixi_testset "TreeMesh1D: elixir_diffusion_pure_dirichlet_ldg.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_pure_dirichlet_ldg.jl"), - tspan = (0.0, 0.05)) + l2=[1.063561640989342e-5], linf=[7.870919430864876e-5]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) end @trixi_testset "TreeMesh1D: elixir_advection_diffusion.jl" begin From bfda3a78b51828ae9e781b586dd1be769a2733aa Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 21 Mar 2026 09:41:59 +0100 Subject: [PATCH 22/68] rename elixirs --- ...dirichlet_ldg.jl => elixir_diffusion_dirichlet_ldg.jl} | 2 +- ...n_br1.jl => elixir_diffusion_dirichlet_neumann_br1.jl} | 0 test/test_parabolic_1d.jl | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename examples/tree_1d_dgsem/{elixir_diffusion_pure_dirichlet_ldg.jl => elixir_diffusion_dirichlet_ldg.jl} (96%) rename examples/tree_1d_dgsem/{elixir_diffusion_mixed_dirichlet_neumann_br1.jl => elixir_diffusion_dirichlet_neumann_br1.jl} (100%) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl similarity index 96% rename from examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl rename to examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl index d1b2ab942ba..bbeeda43e47 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_pure_dirichlet_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl @@ -2,7 +2,7 @@ using OrdinaryDiffEqLowStorageRK using Trixi ############################################################################### -# semidiscretization of the pure diffusion equation with pure Dirichlet BCs (LDG) +# semidiscretization of the pure diffusion equation with Dirichlet BCs (LDG) diffusivity = 0.5 amplitude = 0.4 diff --git a/examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl similarity index 100% rename from examples/tree_1d_dgsem/elixir_diffusion_mixed_dirichlet_neumann_br1.jl rename to examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index b2f7dcecfa1..82ae9a9f4c7 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -14,18 +14,18 @@ isdir(outdir) && rm(outdir, recursive = true) @testset "SemidiscretizationHyperbolicParabolic and Semi (1D)" begin #! format: noindent -@trixi_testset "TreeMesh1D: elixir_diffusion_mixed_dirichlet_neumann_br1.jl" begin +@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_neumann_br1.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_mixed_dirichlet_neumann_br1.jl"), + "elixir_diffusion_dirichlet_neumann_br1.jl"), l2=[4.906306967386223e-7], linf=[1.8404898263213454e-6]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) end -@trixi_testset "TreeMesh1D: elixir_diffusion_pure_dirichlet_ldg.jl" begin +@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_ldg.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_pure_dirichlet_ldg.jl"), + "elixir_diffusion_dirichlet_ldg.jl"), l2=[1.063561640989342e-5], linf=[7.870919430864876e-5]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) From 96cd60dcfc59fb94a3ae39b0b0d910fd98d23f88 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 24 Mar 2026 10:14:01 +0100 Subject: [PATCH 23/68] Apply suggestions from code review Co-authored-by: Hendrik Ranocha --- NEWS.md | 3 ++- src/callbacks_step/stepsize.jl | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index b260d3af7ce..f84a6da11dc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,8 +10,9 @@ for human readability. #### Added - The word "viscous" is now used only where it refers specifically to fluid viscosity. The word "parabolic" is used in more general contexts. -For example, `ViscousFormulationLocalDG()` is now `ParabolicFormulationLocalDG()`. In particular, viscosity is no longer used as a proxy for any parabolic/diffusive process such as heat conduction. +For example, `ViscousFormulationLocalDG` is now `ParabolicFormulationLocalDG` and +`ViscousFormulationBassiRebay1` is now `ParabolicFormulationBassiRebay1`. For consistency, `cfl_advective` and `cfl_diffusive` have also been renamed `cfl_hyperbolic` and `cfl_parabolic` ([#2868]). - It is now possible to use `ViscousFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. This is useful for (locally) diffusion-dominated problems. diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 269f6ee5198..ff86146d51b 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -11,7 +11,7 @@ Set the time step size according to a CFL condition with hyperbolic CFL number `cfl` if the time integration method isn't adaptive itself. -The hyperbolic CFL number must be either a `Real` number, corresponding to a constant +The hyperbolic CFL number `cfl` must be either a `Real` number, corresponding to a constant CFL number, or a function of time `t` returning a `Real` number. The latter approach allows for variable CFL numbers that can be used to realize, e.g., a ramp-up of the time step. @@ -20,7 +20,7 @@ One can additionally supply a parabolic CFL number `cfl_parabolic` to limit the admissible timestep also respecting parabolic restrictions. This is only applicable for semidiscretizations of type [`SemidiscretizationHyperbolicParabolic`](@ref). To enable checking for parabolic timestep restrictions, provide a value greater than zero for `cfl_parabolic`. -By default, `cfl_parabolic` is set to zero which means that only the hyperbolic CFL number is considered. +By default, `cfl_parabolic` is set to zero which means that only the hyperbolic CFL number `cfl` is considered. The keyword argument `cfl_parabolic` must be either a `Real` number, corresponding to a constant parabolic CFL number, or a function of time `t` returning a `Real` number. From e928cc29ab2db6c20bba75ba1025c66695acc794 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 24 Mar 2026 10:16:13 +0100 Subject: [PATCH 24/68] Apply formatting changes Co-authored-by: Hendrik Ranocha --- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 752ac07ae59..21dbdc61f47 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -298,13 +298,19 @@ function prolong2interfaces!(cache, flux_parabolic::Tuple, for v in eachvariable(equations_parabolic) # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*! - flux_parabolic = SVector(flux_parabolic_x[v, i_primary, j_primary, + flux_parabolic = SVector(flux_parabolic_x[v, + i_primary, + j_primary, k_primary, primary_element], - flux_parabolic_y[v, i_primary, j_primary, + flux_parabolic_y[v, + i_primary, + j_primary, k_primary, primary_element], - flux_parabolic_z[v, i_primary, j_primary, + flux_parabolic_z[v, + i_primary, + j_primary, k_primary, primary_element]) From c57779ddc5480cb659991c938242fc3674d6ae2a Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 24 Mar 2026 10:17:27 +0100 Subject: [PATCH 25/68] Apply formatting changes Co-authored-by: Hendrik Ranocha --- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 21dbdc61f47..a6b97d05330 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -355,15 +355,18 @@ function prolong2interfaces!(cache, flux_parabolic::Tuple, for v in eachvariable(equations_parabolic) # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*! - flux_parabolic = SVector(flux_parabolic_x[v, i_secondary, + flux_parabolic = SVector(flux_parabolic_x[v, + i_secondary, j_secondary, k_secondary, secondary_element], - flux_parabolic_y[v, i_secondary, + flux_parabolic_y[v, + i_secondary, j_secondary, k_secondary, secondary_element], - flux_parabolic_z[v, i_secondary, + flux_parabolic_z[v, + i_secondary, j_secondary, k_secondary, secondary_element]) From 805b65b7fb226cc758812e51eca65b952775840f Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 24 Mar 2026 10:17:40 +0100 Subject: [PATCH 26/68] Apply formatting changes Co-authored-by: Hendrik Ranocha --- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index a6b97d05330..4dd650e5c4f 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -567,15 +567,18 @@ function prolong2mortars_divergence!(cache, flux_parabolic, element) for v in eachvariable(equations_parabolic) - flux_parabolic = SVector(flux_parabolic_x[v, i_large, + flux_parabolic = SVector(flux_parabolic_x[v, + i_large, j_large, k_large, element], - flux_parabolic_y[v, i_large, + flux_parabolic_y[v, + i_large, j_large, k_large, element], - flux_parabolic_z[v, i_large, + flux_parabolic_z[v, + i_large, j_large, k_large, element]) From d18b43f4d9b603b9c81330af3ba231d5d35ba018 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 24 Mar 2026 10:31:32 +0100 Subject: [PATCH 27/68] Update NEWS.md --- NEWS.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index f84a6da11dc..d3604ac5340 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,15 +5,22 @@ Trixi.jl follows the interpretation of used in the Julia ecosystem. Notable changes will be documented in this file for human readability. -## Changes in the v0.15 lifecycle +## Changes when updating to v0.16 from v0.15.x + +#### Changed -#### Added - The word "viscous" is now used only where it refers specifically to fluid viscosity. The word "parabolic" is used in more general contexts. In particular, viscosity is no longer used as a proxy for any parabolic/diffusive process such as heat conduction. For example, `ViscousFormulationLocalDG` is now `ParabolicFormulationLocalDG` and `ViscousFormulationBassiRebay1` is now `ParabolicFormulationBassiRebay1`. For consistency, `cfl_advective` and `cfl_diffusive` have also been renamed `cfl_hyperbolic` and `cfl_parabolic` ([#2868]). +Moreover, some internal functions have been renamed accordingly, including the results shown by the timer outputs after running a simulation. + +## Changes in the v0.15 lifecycle + +#### Added + - It is now possible to use `ViscousFormulationLocalDG()` as the `solver_parabolic` for non-conforming `P4estMesh`es. This is useful for (locally) diffusion-dominated problems. This enables in particular adaptive mesh refinement for that solver-mesh combination ([#2712]). From 13162f861cf774ca7b3287d5b566b1beb708eb90 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 24 Mar 2026 12:14:06 +0100 Subject: [PATCH 28/68] add amr --- .../elixir_diffusion_dirichlet_ldg.jl | 6 +- .../elixir_diffusion_dirichlet_neumann_br1.jl | 9 +-- .../tree_1d_dgsem/elixir_diffusion_ldg_amr.jl | 76 +++++++++++++++++++ ...elixir_diffusion_ldg_amr_boundary_layer.jl | 76 +++++++++++++++++++ src/callbacks_step/amr.jl | 26 ++++++- test/test_parabolic_1d.jl | 52 ++++++++----- 6 files changed, 216 insertions(+), 29 deletions(-) create mode 100644 examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl create mode 100644 examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl index bbeeda43e47..895bd0217c7 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl @@ -4,8 +4,8 @@ using Trixi ############################################################################### # semidiscretization of the pure diffusion equation with Dirichlet BCs (LDG) -diffusivity = 0.5 -amplitude = 0.4 +diffusivity = 0.25 +amplitude = 1.0 wave_number = pi equations = LinearDiffusionEquation1D(diffusivity) @@ -35,7 +35,7 @@ semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; ############################################################################### # ODE solvers, callbacks etc. -tspan = (0.0, 0.1) +tspan = (0.0, 1.0) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl index b0e3a1d74ce..c28e2d1b0e6 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl @@ -4,8 +4,8 @@ using Trixi ############################################################################### # semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs -diffusivity = 0.5 -amplitude = 0.4 +diffusivity = 0.25 +amplitude = 1.0 wave_number = 0.5 * pi equations = LinearDiffusionEquation1D(diffusivity) @@ -19,12 +19,11 @@ mesh = TreeMesh((0.0,), (1.0,), n_cells_max = 30_000) # Initial condition consistent with mixed Dirichlet-Neumann BCs and exact solution -initial_condition = (x, t, equations) -> SVector(1.0 + - amplitude * +initial_condition = (x, t, equations) -> SVector(amplitude * exp(-diffusivity * wave_number^2 * t) * sin(wave_number * x[1])) -boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(1.0)) +boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) boundary_conditions = (; x_neg = boundary_condition_dirichlet, diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl new file mode 100644 index 00000000000..2426218abca --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl @@ -0,0 +1,76 @@ +using OrdinaryDiffEqLowStorageRK +using Trixi + +############################################################################### +# semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs +# (BR1 + AMR), configured to test AMR redistribution as a steep boundary layer diffuses + +diffusivity = 0.25 +amplitude = 1.0 +boundary_layer_thickness = 0.01 + +equations = LinearDiffusionEquation1D(diffusivity) + +solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver_parabolic = ParabolicFormulationBassiRebay1() + +mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 2, + periodicity = false, + n_cells_max = 30_000) + +# Initial condition: steep boundary layer profile only (no wave packet). +function initial_condition_boundary_layer(x, t, equations) + x_local = x[1] + + base_mode = amplitude * (1 - exp(-x_local / boundary_layer_thickness)) / + (1 - exp(-1 / boundary_layer_thickness)) + return SVector(base_mode) +end + +initial_condition = initial_condition_boundary_layer +boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) +boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) + +boundary_conditions = (; x_neg = boundary_condition_dirichlet, + x_pos = boundary_condition_neumann) + +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_callback = AnalysisCallback(semi, interval = 200, + analysis_integrals = ()) + +alive_callback = AliveCallback(analysis_interval = 200) + +amr_indicator = IndicatorLöhner(semi, variable = first) +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level = 2, + med_level = -1, + med_threshold = 0.01, + max_level = 6, max_threshold = 0.1) +amr_callback = AMRCallback(semi, amr_controller, + interval = 200, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) + +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) + +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, + amr_callback, stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); + dt = stepsize_callback(ode), adaptive = false, + ode_default_options()..., callback = callbacks, maxiters = 500_000) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl new file mode 100644 index 00000000000..d3dea9fe959 --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -0,0 +1,76 @@ +using OrdinaryDiffEqLowStorageRK +using Trixi + +############################################################################### +# semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs +# (LDG + AMR), configured to test AMR redistribution as a steep boundary layer diffuses + +diffusivity = 0.25 +amplitude = 1.0 +boundary_layer_thickness = 0.01 + +equations = LinearDiffusionEquation1D(diffusivity) + +solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver_parabolic = ParabolicFormulationLocalDG() + +mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 2, + periodicity = false, + n_cells_max = 30_000) + +# Initial condition: steep boundary layer profile only (no wave packet). +function initial_condition_boundary_layer(x, t, equations) + x_local = x[1] + + base_mode = amplitude * (1 - exp(-x_local / boundary_layer_thickness)) / + (1 - exp(-1 / boundary_layer_thickness)) + return SVector(base_mode) +end + +initial_condition = initial_condition_boundary_layer +boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) +boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) + +boundary_conditions = (; x_neg = boundary_condition_dirichlet, + x_pos = boundary_condition_neumann) + +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_callback = AnalysisCallback(semi, interval = 200, + analysis_integrals = ()) + +alive_callback = AliveCallback(analysis_interval = 200) + +amr_indicator = IndicatorLöhner(semi, variable = first) +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level = 2, + med_level = -1, + med_threshold = 0.01, + max_level = 6, max_threshold = 0.1) +amr_callback = AMRCallback(semi, amr_controller, + interval = 200, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) + +stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) + +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, + amr_callback, stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); + dt = stepsize_callback(ode), adaptive = false, + ode_default_options()..., callback = callbacks, maxiters = 500_000) diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 5d28cdacf10..ec721603c33 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -223,6 +223,27 @@ end semi, t, iter; kwargs...) end +@inline function (amr_callback::AMRCallback)(u_ode::AbstractVector, + semi::SemidiscretizationParabolic, + t, iter; + kwargs...) + # Note that we don't `wrap_array` the vector `u_ode` to be able to `resize!` + # it when doing AMR while still dispatching on the `mesh` etc. + mesh, equations, dg, cache = mesh_equations_solver_cache(semi) + + if !(mesh isa TreeMesh{1}) + throw(ArgumentError("Purely parabolic AMR is currently implemented for `TreeMesh{1}` only.")) + end + + if mpi_isparallel() + throw(ArgumentError("Purely parabolic AMR is currently implemented for serial runs only.")) + end + + return amr_callback(u_ode, mesh, equations, dg, cache, + semi.cache.parabolic, + semi, t, iter; kwargs...) +end + # `passive_args` is currently used for Euler with self-gravity to adapt the gravity solver # passively without querying its indicator, based on the assumption that both solvers use # the same mesh. That's a hack and should be improved in the future once we have more examples @@ -381,13 +402,14 @@ end function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, equations, dg::DG, cache, cache_parabolic, - semi::SemidiscretizationHyperbolicParabolic, + semi::Union{SemidiscretizationHyperbolicParabolic, + SemidiscretizationParabolic}, t, iter; only_refine = false, only_coarsen = false) @unpack controller, adaptor = amr_callback u = wrap_array(u_ode, mesh, equations, dg, cache) - # Indicator kept based on hyperbolic variables + # Evaluate the indicator from the current semidiscrete solution. lambda = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, t = t, iter = iter) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 82ae9a9f4c7..37ad7aff81b 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -11,27 +11,9 @@ EXAMPLES_DIR = examples_dir() outdir = "out" isdir(outdir) && rm(outdir, recursive = true) -@testset "SemidiscretizationHyperbolicParabolic and Semi (1D)" begin +@testset "SemidiscretizationHyperbolicParabolic (1D)" begin #! format: noindent -@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_neumann_br1.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_dirichlet_neumann_br1.jl"), - l2=[4.906306967386223e-7], linf=[1.8404898263213454e-6]) - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - -@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_ldg.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_dirichlet_ldg.jl"), - l2=[1.063561640989342e-5], linf=[7.870919430864876e-5]) - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - @trixi_testset "TreeMesh1D: elixir_advection_diffusion.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_advection_diffusion.jl"), @@ -544,6 +526,38 @@ end end end +@testset "SemidiscretizationParabolic (1D)" begin +#! format: noindent + +@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_neumann_br1.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_dirichlet_neumann_br1.jl"), + l2=[4.906306967386223e-7], linf=[1.8404898263213454e-6]) + @trixi_test_nowarn show(stdout, semi) # not tested elsewhere + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end + +@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_ldg.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_dirichlet_ldg.jl"), + l2=[1.063561640989342e-5], linf=[7.870919430864876e-5]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end + +@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg_amr.jl"), + l2=[0.5880773398133277], linf=[0.9400128760232058]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end +end + # Clean up afterwards: delete Trixi output directory @test_nowarn isdir(outdir) && rm(outdir, recursive = true) From eb9914c15011fca4d51f617a94313d22deb2d0fe Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 25 Mar 2026 08:23:48 +0100 Subject: [PATCH 29/68] fix amr examples/tests --- .../elixir_diffusion_dirichlet_ldg.jl | 13 +--- .../elixir_diffusion_dirichlet_neumann_br1.jl | 13 +--- .../tree_1d_dgsem/elixir_diffusion_ldg_amr.jl | 76 ------------------- ...elixir_diffusion_ldg_amr_boundary_layer.jl | 29 +++---- test/test_parabolic_1d.jl | 6 +- 5 files changed, 23 insertions(+), 114 deletions(-) delete mode 100644 examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl index 895bd0217c7..91c682dd3ea 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl @@ -10,13 +10,10 @@ wave_number = pi equations = LinearDiffusionEquation1D(diffusivity) -solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationLocalDG() -mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 3, - periodicity = false, - n_cells_max = 30_000) +mesh = TreeMesh((0.0,), (1.0,), initial_refinement_level = 3, periodicity = false) # Initial condition consistent with Dirichlet BCs and exact solution initial_condition = (x, t, equations) -> SVector(amplitude * @@ -40,11 +37,9 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() -analysis_interval = 200 -analysis_callback = AnalysisCallback(semi, interval = analysis_interval, - analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi, interval = 200, analysis_integrals = ()) -alive_callback = AliveCallback(analysis_interval = analysis_interval) +alive_callback = AliveCallback(analysis_interval = 200) stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl index c28e2d1b0e6..ee8739f1577 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl @@ -10,13 +10,10 @@ wave_number = 0.5 * pi equations = LinearDiffusionEquation1D(diffusivity) -solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationBassiRebay1() -mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 3, - periodicity = false, - n_cells_max = 30_000) +mesh = TreeMesh((0.0,), (1.0,), initial_refinement_level = 3, periodicity = false) # Initial condition consistent with mixed Dirichlet-Neumann BCs and exact solution initial_condition = (x, t, equations) -> SVector(amplitude * @@ -41,11 +38,9 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() -analysis_interval = 200 -analysis_callback = AnalysisCallback(semi, interval = analysis_interval, - analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi, interval = 200, analysis_integrals = ()) -alive_callback = AliveCallback(analysis_interval = analysis_interval) +alive_callback = AliveCallback(analysis_interval = 200) stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl deleted file mode 100644 index 2426218abca..00000000000 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr.jl +++ /dev/null @@ -1,76 +0,0 @@ -using OrdinaryDiffEqLowStorageRK -using Trixi - -############################################################################### -# semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs -# (BR1 + AMR), configured to test AMR redistribution as a steep boundary layer diffuses - -diffusivity = 0.25 -amplitude = 1.0 -boundary_layer_thickness = 0.01 - -equations = LinearDiffusionEquation1D(diffusivity) - -solver = DGSEM(polydeg = 3, surface_flux = flux_central) -solver_parabolic = ParabolicFormulationBassiRebay1() - -mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 2, - periodicity = false, - n_cells_max = 30_000) - -# Initial condition: steep boundary layer profile only (no wave packet). -function initial_condition_boundary_layer(x, t, equations) - x_local = x[1] - - base_mode = amplitude * (1 - exp(-x_local / boundary_layer_thickness)) / - (1 - exp(-1 / boundary_layer_thickness)) - return SVector(base_mode) -end - -initial_condition = initial_condition_boundary_layer -boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) -boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) - -boundary_conditions = (; x_neg = boundary_condition_dirichlet, - x_pos = boundary_condition_neumann) - -semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; - solver_parabolic = solver_parabolic, - boundary_conditions = boundary_conditions) - -############################################################################### -# ODE solvers, callbacks etc. - -tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan) - -summary_callback = SummaryCallback() - -analysis_callback = AnalysisCallback(semi, interval = 200, - analysis_integrals = ()) - -alive_callback = AliveCallback(analysis_interval = 200) - -amr_indicator = IndicatorLöhner(semi, variable = first) -amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level = 2, - med_level = -1, - med_threshold = 0.01, - max_level = 6, max_threshold = 0.1) -amr_callback = AMRCallback(semi, amr_controller, - interval = 200, - adapt_initial_condition = true, - adapt_initial_condition_only_refine = true) - -stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) - -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - amr_callback, stepsize_callback) - -############################################################################### -# run the simulation - -sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); - dt = stepsize_callback(ode), adaptive = false, - ode_default_options()..., callback = callbacks, maxiters = 500_000) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index d3dea9fe959..34b978c70e5 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -2,8 +2,8 @@ using OrdinaryDiffEqLowStorageRK using Trixi ############################################################################### -# semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs -# (LDG + AMR), configured to test AMR redistribution as a steep boundary layer diffuses +# Adaptive semidiscretization of the pure diffusion equation with mixed +# Dirichlet-Neumann BCs and an initial condition with a boundary layer. diffusivity = 0.25 amplitude = 1.0 @@ -11,21 +11,17 @@ boundary_layer_thickness = 0.01 equations = LinearDiffusionEquation1D(diffusivity) -solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationLocalDG() mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 2, + initial_refinement_level = 1, periodicity = false, n_cells_max = 30_000) -# Initial condition: steep boundary layer profile only (no wave packet). function initial_condition_boundary_layer(x, t, equations) - x_local = x[1] - - base_mode = amplitude * (1 - exp(-x_local / boundary_layer_thickness)) / - (1 - exp(-1 / boundary_layer_thickness)) - return SVector(base_mode) + return SVector(amplitude * (1 - exp(-x[1] / boundary_layer_thickness)) / + (1 - exp(-1 / boundary_layer_thickness))) end initial_condition = initial_condition_boundary_layer @@ -47,21 +43,20 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() -analysis_callback = AnalysisCallback(semi, interval = 200, - analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi, interval = 200, analysis_integrals = ()) alive_callback = AliveCallback(analysis_interval = 200) amr_indicator = IndicatorLöhner(semi, variable = first) amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level = 2, + base_level = 1, med_level = -1, - med_threshold = 0.01, - max_level = 6, max_threshold = 0.1) + med_threshold = 0.005, + max_level = 6, max_threshold = 0.025) amr_callback = AMRCallback(semi, amr_controller, - interval = 200, + interval = 100, adapt_initial_condition = true, - adapt_initial_condition_only_refine = true) + adapt_initial_condition_only_refine = false) stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 37ad7aff81b..82b02495126 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -548,10 +548,10 @@ end @test_allocations(Trixi.rhs!, semi, sol, 1000) end -@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr.jl" begin +@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_ldg_amr.jl"), - l2=[0.5880773398133277], linf=[0.9400128760232058]) + "elixir_diffusion_ldg_amr_boundary_layer.jl"), + l2=[0.588077335170678], linf=[0.9400281609880542]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) From e731e81e86ce680acf690278a458d56a9a0d0c39 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 25 Mar 2026 14:38:14 +0100 Subject: [PATCH 30/68] make old fake diffusion tests use real diffusion equation --- .../elixir_diffusion_dirichlet_ldg.jl | 5 +++- .../elixir_diffusion_dirichlet_neumann_br1.jl | 5 +++- .../tree_1d_dgsem/elixir_diffusion_ldg.jl | 20 +++++---------- .../elixir_diffusion_ldg_newton_krylov.jl | 16 +++++------- ...lixir_diffusion_steady_state_linear_map.jl | 25 +++++++------------ test/test_parabolic_1d.jl | 3 +-- test/test_parabolic_2d.jl | 3 +-- 7 files changed, 31 insertions(+), 46 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl index 91c682dd3ea..c6fe3398d5d 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl @@ -13,7 +13,10 @@ equations = LinearDiffusionEquation1D(diffusivity) solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationLocalDG() -mesh = TreeMesh((0.0,), (1.0,), initial_refinement_level = 3, periodicity = false) +mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 3, + periodicity = false, + n_cells_max = 30_000) # Initial condition consistent with Dirichlet BCs and exact solution initial_condition = (x, t, equations) -> SVector(amplitude * diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl index ee8739f1577..79e611bc970 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl @@ -13,7 +13,10 @@ equations = LinearDiffusionEquation1D(diffusivity) solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationBassiRebay1() -mesh = TreeMesh((0.0,), (1.0,), initial_refinement_level = 3, periodicity = false) +mesh = TreeMesh((0.0,), (1.0,), + initial_refinement_level = 3, + periodicity = false, + n_cells_max = 30_000) # Initial condition consistent with mixed Dirichlet-Neumann BCs and exact solution initial_condition = (x, t, equations) -> SVector(amplitude * diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl index 8f79de2d3c4..b6d38477a5e 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl @@ -2,12 +2,10 @@ using OrdinaryDiffEqLowStorageRK using Trixi ############################################################################### -# semidiscretization of the linear (advection) diffusion equation +# semidiscretization of the pure diffusion equation -advection_velocity = 0.0 # Note: This renders the equation mathematically purely parabolic -equations = LinearScalarAdvectionEquation1D(advection_velocity) diffusivity() = 0.5 -equations_parabolic = LaplaceDiffusion1D(diffusivity(), equations) +equations = LinearDiffusionEquation1D(diffusivity()) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) @@ -36,17 +34,11 @@ function initial_condition_pure_diffusion_1d_convergence_test(x, t, end initial_condition = initial_condition_pure_diffusion_1d_convergence_test -# define periodic boundary conditions everywhere -boundary_conditions = boundary_condition_periodic -boundary_conditions_parabolic = boundary_condition_periodic - # A semidiscretization collects data structures and functions for the spatial discretization solver_parabolic = ParabolicFormulationLocalDG() -semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), - initial_condition, - solver; solver_parabolic, - boundary_conditions = (boundary_conditions, - boundary_conditions_parabolic)) +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + boundary_conditions = boundary_condition_periodic) ############################################################################### # ODE solvers, callbacks etc. @@ -60,7 +52,7 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval = 100) +analysis_callback = AnalysisCallback(semi, interval = 100, analysis_integrals = ()) # The AliveCallback prints short status information in regular intervals alive_callback = AliveCallback(analysis_interval = 100) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl index f52b62738cd..7c842767a5b 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl @@ -5,12 +5,10 @@ using LinearSolve # For Jacobian-free Newton-Krylov (GMRES) solver using ADTypes # For automatic differentiation via finite differences ############################################################################### -# semidiscretization of the linear (advection) diffusion equation +# semidiscretization of the pure diffusion equation -advection_velocity = 0.0 # Note: This renders the equation mathematically purely parabolic -equations = LinearScalarAdvectionEquation1D(advection_velocity) diffusivity() = 0.5 -equations_parabolic = LaplaceDiffusion1D(diffusivity(), equations) +equations = LinearDiffusionEquation1D(diffusivity()) # surface flux does not matter for pure diffusion problem solver = DGSEM(polydeg = 3, surface_flux = flux_central) @@ -34,11 +32,9 @@ end initial_condition = initial_condition_pure_diffusion_1d_convergence_test solver_parabolic = ParabolicFormulationLocalDG() -semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), - initial_condition, - solver; solver_parabolic, - boundary_conditions = (boundary_condition_periodic, - boundary_condition_periodic)) +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + boundary_conditions = boundary_condition_periodic) ############################################################################### # ODE solvers, callbacks etc. @@ -47,7 +43,7 @@ tspan = (0.0, 2.0) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() -analysis_callback = AnalysisCallback(semi, interval = 10) +analysis_callback = AnalysisCallback(semi, interval = 10, analysis_integrals = ()) alive_callback = AliveCallback(alive_interval = 1) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) diff --git a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl index 51530f3286f..475c68393f3 100644 --- a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl +++ b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl @@ -3,14 +3,11 @@ using Trixi ############################################################################### # Build pure diffusion (Laplace) operator -advection_velocity = (0, 0) -equations = LinearScalarAdvectionEquation2D(advection_velocity) +# `LaplaceDiffusion2D` needs an auxiliary hyperbolic equation to determine variable layout. +equations_hyperbolic = LinearScalarAdvectionEquation2D((0, 0)) diffusivity() = 1 -equations_parabolic = LaplaceDiffusion2D(diffusivity(), equations) - -# The hyperbolic flux does not matter for this example since -# the hyperbolic part is zero. -solver = DGSEM(polydeg = 5, surface_flux = flux_central) +equations = LaplaceDiffusion2D(diffusivity(), equations_hyperbolic) +solver = DGSEM(polydeg = 5) coordinates_min = (0.0, 0.0) coordinates_max = (1.0, 1.0) @@ -38,19 +35,15 @@ function bc_sin(x, t, equations) end bc_sin_dirichlet = BoundaryConditionDirichlet(bc_sin) -# Same boundary conditions for hyperbolic and parabolic part boundary_conditions = (; x_neg = bc_homogeneous_dirichlet, y_neg = bc_sin_dirichlet, y_pos = bc_sin_dirichlet, x_pos = bc_homogeneous_dirichlet) -# `solver_parabolic = ParabolicFormulationLocalDG()` strictly required for elliptic/diffusion-dominated problem -semi = SemidiscretizationHyperbolicParabolic(mesh, - (equations, equations_parabolic), - initial_condition, solver; - solver_parabolic = ParabolicFormulationLocalDG(), - boundary_conditions = (boundary_conditions, - boundary_conditions)) +# `solver_parabolic = ParabolicFormulationLocalDG()` is strictly required for elliptic/diffusion-dominated problems. +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = ParabolicFormulationLocalDG(), + boundary_conditions = boundary_conditions) # Note that `linear_structure` does not access the `initial_condition`/steady-state solution A_map, b = linear_structure(semi) @@ -82,7 +75,7 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() # Analysis callback quantifies discretization/interpolation error of the exact solution -analysis_callback = AnalysisCallback(semi) +analysis_callback = AnalysisCallback(semi, analysis_integrals = ()) callbacks = CallbackSet(summary_callback, analysis_callback) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 82b02495126..7bfa9308d40 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -76,11 +76,10 @@ end "elixir_diffusion_ldg_newton_krylov.jl"), atol_lin_solve=1e-11, rtol_lin_solve=1e-10, atol_ode_solve=1e-10, rtol_ode_solve=1e-9, - l2=[4.14999791227157e-6], linf=[2.424658410971059e-5]) + l2=[4.14999791227157e-6], linf=[2.424658336047658e-5]) # 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 "TreeMesh1D: elixir_advection_diffusion_restart.jl" begin diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 1c1d79775c5..8fc81688a74 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -396,13 +396,12 @@ end @trixi_testset "TreeMesh2D: elixir_diffusion_steady_state_linear_map.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_diffusion_steady_state_linear_map.jl"), - l2=[2.9029827892716424e-5], linf=[0.0003022506331279151], + l2=[2.9029783114293355e-5], linf=[0.00030225018027482675], # Relax error tols to avoid stochastic CI failures atol=1e-10) # 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 "TreeMesh2D: elixir_navierstokes_convergence.jl" begin From 6be1261c35122e43cd073f301291261535276333 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 25 Mar 2026 15:21:42 +0100 Subject: [PATCH 31/68] consolidate tests/examples and add LinearDiffusionEquation2D --- .../elixir_diffusion_dirichlet_ldg.jl | 57 ------------------ .../elixir_diffusion_dirichlet_neumann_br1.jl | 58 ------------------- .../tree_1d_dgsem/elixir_diffusion_ldg.jl | 2 +- ...elixir_diffusion_ldg_amr_boundary_layer.jl | 2 +- .../elixir_diffusion_ldg_newton_krylov.jl | 2 +- ...lixir_diffusion_steady_state_linear_map.jl | 4 +- src/Trixi.jl | 2 +- src/equations/equations_parabolic.jl | 1 + src/equations/linear_diffusion_equation_1d.jl | 5 ++ src/equations/linear_diffusion_equation_2d.jl | 41 +++++++++++++ test/test_parabolic_1d.jl | 20 +------ 11 files changed, 53 insertions(+), 141 deletions(-) delete mode 100644 examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl delete mode 100644 examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl create mode 100644 src/equations/linear_diffusion_equation_2d.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl deleted file mode 100644 index c6fe3398d5d..00000000000 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_ldg.jl +++ /dev/null @@ -1,57 +0,0 @@ -using OrdinaryDiffEqLowStorageRK -using Trixi - -############################################################################### -# semidiscretization of the pure diffusion equation with Dirichlet BCs (LDG) - -diffusivity = 0.25 -amplitude = 1.0 -wave_number = pi - -equations = LinearDiffusionEquation1D(diffusivity) - -solver = DGSEM(polydeg = 3) -solver_parabolic = ParabolicFormulationLocalDG() - -mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 3, - periodicity = false, - n_cells_max = 30_000) - -# Initial condition consistent with Dirichlet BCs and exact solution -initial_condition = (x, t, equations) -> SVector(amplitude * - exp(-diffusivity * wave_number^2 * t) * - sin(wave_number * x[1])) - -boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) - -boundary_conditions = (; x_neg = boundary_condition_dirichlet, - x_pos = boundary_condition_dirichlet) - -semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; - solver_parabolic = solver_parabolic, - boundary_conditions = boundary_conditions) - -############################################################################### -# ODE solvers, callbacks etc. - -tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan) - -summary_callback = SummaryCallback() - -analysis_callback = AnalysisCallback(semi, interval = 200, analysis_integrals = ()) - -alive_callback = AliveCallback(analysis_interval = 200) - -stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) - -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - stepsize_callback) - -############################################################################### -# run the simulation - -sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); - dt = stepsize_callback(ode), adaptive = false, - ode_default_options()..., callback = callbacks) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl b/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl deleted file mode 100644 index 79e611bc970..00000000000 --- a/examples/tree_1d_dgsem/elixir_diffusion_dirichlet_neumann_br1.jl +++ /dev/null @@ -1,58 +0,0 @@ -using OrdinaryDiffEqLowStorageRK -using Trixi - -############################################################################### -# semidiscretization of the pure diffusion equation with mixed Dirichlet-Neumann BCs - -diffusivity = 0.25 -amplitude = 1.0 -wave_number = 0.5 * pi - -equations = LinearDiffusionEquation1D(diffusivity) - -solver = DGSEM(polydeg = 3) -solver_parabolic = ParabolicFormulationBassiRebay1() - -mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 3, - periodicity = false, - n_cells_max = 30_000) - -# Initial condition consistent with mixed Dirichlet-Neumann BCs and exact solution -initial_condition = (x, t, equations) -> SVector(amplitude * - exp(-diffusivity * wave_number^2 * t) * - sin(wave_number * x[1])) - -boundary_condition_dirichlet = BoundaryConditionDirichlet((x, t, equations) -> SVector(0.0)) -boundary_condition_neumann = BoundaryConditionNeumann((x, t, equations) -> SVector(0.0)) - -boundary_conditions = (; x_neg = boundary_condition_dirichlet, - x_pos = boundary_condition_neumann) - -semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; - solver_parabolic = solver_parabolic, - boundary_conditions = boundary_conditions) - -############################################################################### -# ODE solvers, callbacks etc. - -tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan) - -summary_callback = SummaryCallback() - -analysis_callback = AnalysisCallback(semi, interval = 200, analysis_integrals = ()) - -alive_callback = AliveCallback(analysis_interval = 200) - -stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) - -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - stepsize_callback) - -############################################################################### -# run the simulation - -sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false); - dt = stepsize_callback(ode), adaptive = false, - ode_default_options()..., callback = callbacks) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl index b6d38477a5e..4464d2e037f 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl @@ -52,7 +52,7 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval = 100, analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi, interval = 100) # The AliveCallback prints short status information in regular intervals alive_callback = AliveCallback(analysis_interval = 100) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index 34b978c70e5..5e6a2e82de3 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -43,7 +43,7 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() -analysis_callback = AnalysisCallback(semi, interval = 200, analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi, interval = 200) alive_callback = AliveCallback(analysis_interval = 200) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl index 7c842767a5b..97c14620026 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl @@ -43,7 +43,7 @@ tspan = (0.0, 2.0) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() -analysis_callback = AnalysisCallback(semi, interval = 10, analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi, interval = 10) alive_callback = AliveCallback(alive_interval = 1) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) diff --git a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl index 475c68393f3..6e075bb7e0e 100644 --- a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl +++ b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl @@ -3,10 +3,8 @@ using Trixi ############################################################################### # Build pure diffusion (Laplace) operator -# `LaplaceDiffusion2D` needs an auxiliary hyperbolic equation to determine variable layout. -equations_hyperbolic = LinearScalarAdvectionEquation2D((0, 0)) diffusivity() = 1 -equations = LaplaceDiffusion2D(diffusivity(), equations_hyperbolic) +equations = LinearDiffusionEquation2D(diffusivity()) solver = DGSEM(polydeg = 5) coordinates_min = (0.0, 0.0) diff --git a/src/Trixi.jl b/src/Trixi.jl index 6bc99e06fe8..c88dcb84083 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -186,7 +186,7 @@ export AcousticPerturbationEquations2D, export NonIdealCompressibleEulerEquations1D, NonIdealCompressibleEulerEquations2D export IdealGas, VanDerWaals, PengRobinson -export LinearDiffusionEquation1D, +export LinearDiffusionEquation1D, LinearDiffusionEquation2D, LaplaceDiffusion1D, LaplaceDiffusion2D, LaplaceDiffusion3D, LaplaceDiffusionEntropyVariables1D, LaplaceDiffusionEntropyVariables2D, LaplaceDiffusionEntropyVariables3D, diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl index 298a6d55123..f42b1becae1 100644 --- a/src/equations/equations_parabolic.jl +++ b/src/equations/equations_parabolic.jl @@ -52,6 +52,7 @@ see [`StepsizeCallback`](@ref). end include("linear_diffusion_equation_1d.jl") +include("linear_diffusion_equation_2d.jl") include("laplace_diffusion_1d.jl") include("laplace_diffusion_2d.jl") include("laplace_diffusion_3d.jl") diff --git a/src/equations/linear_diffusion_equation_1d.jl b/src/equations/linear_diffusion_equation_1d.jl index ad782a5e880..f8db9184ce4 100644 --- a/src/equations/linear_diffusion_equation_1d.jl +++ b/src/equations/linear_diffusion_equation_1d.jl @@ -19,8 +19,13 @@ end varnames(::typeof(cons2cons), ::LinearDiffusionEquation1D) = ("scalar",) varnames(::typeof(cons2prim), ::LinearDiffusionEquation1D) = ("scalar",) +varnames(::typeof(cons2entropy), ::LinearDiffusionEquation1D) = ("scalar",) @inline cons2prim(u, equations::LinearDiffusionEquation1D) = u +@inline cons2entropy(u, equations::LinearDiffusionEquation1D) = u + +@inline entropy(u::Real, ::LinearDiffusionEquation1D) = 0.5f0 * u^2 +@inline entropy(u, equations::LinearDiffusionEquation1D) = entropy(u[1], equations) @inline function flux(u, gradients, orientation::Integer, equations::LinearDiffusionEquation1D) diff --git a/src/equations/linear_diffusion_equation_2d.jl b/src/equations/linear_diffusion_equation_2d.jl new file mode 100644 index 00000000000..a899878efc7 --- /dev/null +++ b/src/equations/linear_diffusion_equation_2d.jl @@ -0,0 +1,41 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +@doc raw""" + LinearDiffusionEquation2D(diffusivity) + +The linear diffusion equation (or heat equation) in two space dimensions with constant +diffusivity `\kappa`: +```math +\partial_t u = \partial_1 \left( \kappa \partial_1 u \right) + + \partial_2 \left( \kappa \partial_2 u \right) +``` +""" +struct LinearDiffusionEquation2D{RealT <: Real} <: AbstractLaplaceDiffusion{2, 1} + diffusivity::RealT +end + +varnames(::typeof(cons2cons), ::LinearDiffusionEquation2D) = ("scalar",) +varnames(::typeof(cons2prim), ::LinearDiffusionEquation2D) = ("scalar",) +varnames(::typeof(cons2entropy), ::LinearDiffusionEquation2D) = ("scalar",) + +@inline cons2prim(u, equations::LinearDiffusionEquation2D) = u +@inline cons2entropy(u, equations::LinearDiffusionEquation2D) = u + +@inline entropy(u::Real, ::LinearDiffusionEquation2D) = 0.5f0 * u^2 +@inline entropy(u, equations::LinearDiffusionEquation2D) = entropy(u[1], equations) + +@inline function flux(u, gradients, orientation::Integer, + equations::LinearDiffusionEquation2D) + dudx, dudy = gradients + if orientation == 1 + return SVector(equations.diffusivity * dudx) + else # if orientation == 2 + return SVector(equations.diffusivity * dudy) + end +end +end # @muladd diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 7bfa9308d40..923c1f5215c 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -528,29 +528,11 @@ end @testset "SemidiscretizationParabolic (1D)" begin #! format: noindent -@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_neumann_br1.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_dirichlet_neumann_br1.jl"), - l2=[4.906306967386223e-7], linf=[1.8404898263213454e-6]) - @trixi_test_nowarn show(stdout, semi) # not tested elsewhere - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - -@trixi_testset "TreeMesh1D: elixir_diffusion_dirichlet_ldg.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_dirichlet_ldg.jl"), - l2=[1.063561640989342e-5], linf=[7.870919430864876e-5]) - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - @trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_amr_boundary_layer.jl"), l2=[0.588077335170678], linf=[0.9400281609880542]) + @trixi_test_nowarn show(stdout, semi) # not tested elsewhere # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) From 6aa8f34045fb9aa220a8cb12441a8a5c0a628738 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 25 Mar 2026 20:42:22 +0100 Subject: [PATCH 32/68] update comments/relax tols --- .../tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl | 2 +- test/test_parabolic_2d.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl index 6e075bb7e0e..524d949cbba 100644 --- a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl +++ b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl @@ -38,7 +38,7 @@ boundary_conditions = (; x_neg = bc_homogeneous_dirichlet, y_pos = bc_sin_dirichlet, x_pos = bc_homogeneous_dirichlet) -# `solver_parabolic = ParabolicFormulationLocalDG()` is strictly required for elliptic/diffusion-dominated problems. +# Build the parabolic semidiscretization with a local DG formulation for diffusion semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; solver_parabolic = ParabolicFormulationLocalDG(), boundary_conditions = boundary_conditions) diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 8fc81688a74..c1989111d31 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -398,7 +398,7 @@ end "elixir_diffusion_steady_state_linear_map.jl"), l2=[2.9029783114293355e-5], linf=[0.00030225018027482675], # Relax error tols to avoid stochastic CI failures - atol=1e-10) + atol=1e-9) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) From 6aabdb043bc112cefe5bdc0ef4d564d027c0865b Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 26 Mar 2026 18:47:49 +0100 Subject: [PATCH 33/68] add myself to authors --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index eb56a6aa0ba..b54b8c45f41 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -38,6 +38,7 @@ are listed in alphabetical order: * Lucas Gemein * Sven Goldberg * Joshua Lampert +* Tristan Montoya * Julia Odenthal * Sigrun Ortleb * Hendrik Ranocha From 906ede549fcb6f3e831bf062d46dddfbe282e386 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 26 Mar 2026 19:52:36 +0100 Subject: [PATCH 34/68] add diffusion equation to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 096efbfd016..c59276106c0 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ installation and postprocessing procedures. Its features include: * Hyperbolic diffusion equations for elliptic problems * Lattice-Boltzmann equations (D2Q9 and D3Q27 schemes) * Shallow water equations via [TrixiShallowWater.jl](https://github.com/trixi-framework/TrixiShallowWater.jl) - * Several scalar conservation laws (e.g., linear advection, Burgers' equation, LWR traffic flow) + * Several scalar conservation laws (e.g., linear advection, diffusion/heat equation, Burgers' equation, LWR traffic flow) * Multi-physics simulations * [Self-gravitating gas dynamics](https://github.com/trixi-framework/paper-self-gravitating-gas-dynamics) * Shared-memory parallelization via multithreading From 09a0e18aa5d7e3375103efd37e2828691f0f5bff Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 26 Mar 2026 21:36:24 +0100 Subject: [PATCH 35/68] add consistency check --- .../tree_1d_dgsem/elixir_diffusion_ldg.jl | 4 +- .../elixir_diffusion_ldg_dirichlet.jl | 60 +++++++++++++++++++ .../elixir_diffusion_ldg_newton_krylov.jl | 3 +- test/test_parabolic_1d.jl | 31 ++++++++++ 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl index 4464d2e037f..8f2398f470c 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl @@ -7,8 +7,8 @@ using Trixi diffusivity() = 0.5 equations = LinearDiffusionEquation1D(diffusivity()) -# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) +# Create DG solver with polynomial degree = 3 +solver = DGSEM(polydeg = 3) coordinates_min = -convert(Float64, pi) # minimum coordinate coordinates_max = convert(Float64, pi) # maximum coordinate diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl new file mode 100644 index 00000000000..dc8533ee33a --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl @@ -0,0 +1,60 @@ +using OrdinaryDiffEqLowStorageRK +using Trixi + +############################################################################### +# semidiscretization of the pure diffusion equation + +diffusivity() = 0.5 +equations = LinearDiffusionEquation1D(diffusivity()) + +# Create DG solver with polynomial degree = 3 +solver = DGSEM(polydeg = 3) +solver_parabolic = ParabolicFormulationLocalDG() + +# Create a uniformly refined mesh with nonperiodic boundaries +mesh = TreeMesh(0.0, 1.0, + initial_refinement_level = 4, + n_cells_max = 30_000, # set maximum capacity of tree data structure + periodicity = false) + +function analytical_solution(x, t, equations) + scalar = sinpi(x[1]) * exp(-diffusivity() * pi^2 * t) + return SVector(scalar) +end +initial_condition = analytical_solution + +boundary_conditions = (; x_neg = BoundaryConditionDirichlet(initial_condition), + x_pos = BoundaryConditionDirichlet(initial_condition)) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; + solver_parabolic = solver_parabolic, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 0.1 +tspan = (0.0, 0.1) +ode = semidiscretize(semi, tspan) + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval = 100) + +# The AliveCallback prints short status information in regular intervals +alive_callback = AliveCallback(analysis_interval = 100) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +# For CI purposes, we use fixed time-stepping for this elixir. +sol = solve(ode, RDPK3SpFSAL35(); dt = 1.0e-4, adaptive = false, + ode_default_options()..., callback = callbacks) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl index 97c14620026..5270c9e4dbe 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl @@ -10,8 +10,7 @@ using ADTypes # For automatic differentiation via finite differences diffusivity() = 0.5 equations = LinearDiffusionEquation1D(diffusivity()) -# surface flux does not matter for pure diffusion problem -solver = DGSEM(polydeg = 3, surface_flux = flux_central) +solver = DGSEM(polydeg = 3) coordinates_min = -convert(Float64, pi) coordinates_max = convert(Float64, pi) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 923c1f5215c..92edc676658 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -528,6 +528,37 @@ end @testset "SemidiscretizationParabolic (1D)" begin #! format: noindent +@trixi_testset "TreeMesh1D consistency check: elixir_diffusion_ldg_dirichlet.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg_dirichlet.jl"), + l2=[2.3481439150004898e-6], + linf=[2.4576876189230656e-5]) + @test semi isa SemidiscretizationParabolic + + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) + + # Consistency check: compare to advection-diffusion equation with zero velocity + reference_solution = copy(sol.u[end]) # store reference solution for comparison + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg_dirichlet.jl"), + equations=LinearScalarAdvectionEquation1D(0.0), + semi=SemidiscretizationHyperbolicParabolic(mesh, + (equations, + LaplaceDiffusion1D(diffusivity(), + equations)), + initial_condition, + solver; + solver_parabolic = solver_parabolic, + boundary_conditions = (boundary_conditions, + boundary_conditions))) + @test semi isa SemidiscretizationHyperbolicParabolic + + # Use the same Float64 tolerance defaults as `@test_trixi_include` in TrixiTest.jl + @test sol.u[end]≈reference_solution atol=500 * eps(Float64) rtol=sqrt(eps(Float64)) +end + @trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_amr_boundary_layer.jl"), From 243601f023d07bc695d871a2ceb26f8b29aef2da Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 15:31:16 -0400 Subject: [PATCH 36/68] update tests --- test/test_parabolic_1d.jl | 85 ++++++++++++++++++++------------------ test/test_parabolic_2d.jl | 32 +++++++++----- test/test_visualization.jl | 19 +++++++++ 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 92edc676658..0510e9d255a 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -61,27 +61,6 @@ end @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) end -@trixi_testset "TreeMesh1D: elixir_diffusion_ldg.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_ldg.jl"), - initial_refinement_level=4, tspan=(0.0, 0.4), polydeg=3, - l2=[9.235894939144276e-6], linf=[5.402550135213957e-5]) - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - -@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_newton_krylov.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_ldg_newton_krylov.jl"), - atol_lin_solve=1e-11, rtol_lin_solve=1e-10, - atol_ode_solve=1e-10, rtol_ode_solve=1e-9, - l2=[4.14999791227157e-6], linf=[2.424658336047658e-5]) - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - @trixi_testset "TreeMesh1D: elixir_advection_diffusion_restart.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_advection_diffusion_restart.jl"), @@ -528,19 +507,55 @@ end @testset "SemidiscretizationParabolic (1D)" begin #! format: noindent +@trixi_testset "TreeMesh1D: elixir_diffusion_ldg.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg.jl"), + initial_refinement_level=4, tspan=(0.0, 0.4), polydeg=3, + l2=[9.235894939144276e-6], linf=[5.402550135213957e-5]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end + +@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_newton_krylov.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg_newton_krylov.jl"), + atol_lin_solve=1e-11, rtol_lin_solve=1e-10, + atol_ode_solve=1e-10, rtol_ode_solve=1e-9, + l2=[4.14999791227157e-6], linf=[2.424658336047658e-5]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end + +@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg_amr_boundary_layer.jl"), + l2=[0.588077335170678], linf=[0.9400281609880542]) + @trixi_test_nowarn show(stdout, semi) # not tested elsewhere + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end + @trixi_testset "TreeMesh1D consistency check: elixir_diffusion_ldg_dirichlet.jl" begin + # Run the Dirichlet-Dirichlet elixir (uses `SemidiscretizationParabolic`) @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_dirichlet.jl"), - l2=[2.3481439150004898e-6], - linf=[2.4576876189230656e-5]) - @test semi isa SemidiscretizationParabolic - + analysis_callback=AnalysisCallback(semi, + interval = 100, + extra_analysis_errors = (:l2_error_primitive, + :linf_error_primitive), + extra_analysis_integrals = (entropy,)), + l2=[2.3481439150004898e-6], linf=[2.4576876189230656e-5]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) - # Consistency check: compare to advection-diffusion equation with zero velocity - reference_solution = copy(sol.u[end]) # store reference solution for comparison + # Store reference solution for comparison + reference_solution = copy(sol.u[end]) + + # Run again using an advection-diffusion equation with advection velocity zero @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_dirichlet.jl"), equations=LinearScalarAdvectionEquation1D(0.0), @@ -553,21 +568,11 @@ end solver_parabolic = solver_parabolic, boundary_conditions = (boundary_conditions, boundary_conditions))) - @test semi isa SemidiscretizationHyperbolicParabolic - - # Use the same Float64 tolerance defaults as `@test_trixi_include` in TrixiTest.jl + # Check if the solutions for `SemidiscretizationParabolic` match those from + # `SemidiscretizationHyperbolicParabolic` using the same Float64 tolerance defaults as + # `@test_trixi_include` in TrixiTest.jl. @test sol.u[end]≈reference_solution atol=500 * eps(Float64) rtol=sqrt(eps(Float64)) end - -@trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_ldg_amr_boundary_layer.jl"), - l2=[0.588077335170678], linf=[0.9400281609880542]) - @trixi_test_nowarn show(stdout, semi) # not tested elsewhere - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end end # Clean up afterwards: delete Trixi output directory diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 2c299f6dbad..e57a15fa017 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -393,17 +393,6 @@ end @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) end -@trixi_testset "TreeMesh2D: elixir_diffusion_steady_state_linear_map.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", - "elixir_diffusion_steady_state_linear_map.jl"), - l2=[2.9029783114293355e-5], linf=[0.00030225018027482675], - # Relax error tols to avoid stochastic CI failures - atol=1e-9) - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - @test_allocations(Trixi.rhs!, semi, sol, 1000) -end - @trixi_testset "TreeMesh2D: elixir_navierstokes_convergence.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_navierstokes_convergence.jl"), @@ -1333,6 +1322,27 @@ end end end +@testset "SemidiscretizationParabolic (2D)" begin +#! format: noindent + +@trixi_testset "TreeMesh2D: elixir_diffusion_steady_state_linear_map.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", + "elixir_diffusion_steady_state_linear_map.jl"), + tspan=(0.0, 1.0e-4), + analysis_callback=AnalysisCallback(semi, + interval = 1, + extra_analysis_errors = (:l2_error_primitive, + :linf_error_primitive), + extra_analysis_integrals = (entropy,)), + l2=[2.9029783114293355e-5], linf=[0.00030225018027482675], + # Relax error tols to avoid stochastic CI failures + atol=1e-9) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + @test_allocations(Trixi.rhs!, semi, sol, 1000) +end +end + # Clean up afterwards: delete Trixi.jl output directory @test_nowarn isdir(outdir) && rm(outdir, recursive = true) diff --git a/test/test_visualization.jl b/test/test_visualization.jl index 367021928fa..4e30cabcefe 100644 --- a/test/test_visualization.jl +++ b/test/test_visualization.jl @@ -169,6 +169,15 @@ test_examples_2d = Dict("TreeMesh" => ("tree_2d_dgsem", end end +@trixi_testset "PlotData2D scalar diffusion variables" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", + "elixir_diffusion_steady_state_linear_map.jl")) + pd = PlotData2D(sol; solution_variables = cons2entropy) + + @test pd["scalar"] == Trixi.PlotDataSeries(pd, 1) + @test keys(pd) == ("scalar",) +end + @timed_testset "PlotData1D, PlotDataSeries, PlotMesh" begin # Run Trixi.jl @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", @@ -254,6 +263,16 @@ end end end +@trixi_testset "PlotData1D scalar diffusion variables" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", + "elixir_diffusion_ldg_dirichlet.jl"), + tspan=(0.0, 0.0)) + pd = PlotData1D(sol; solution_variables = cons2entropy) + + @test pd["scalar"] == Trixi.PlotDataSeries(pd, 1) + @test keys(pd) == ("scalar",) +end + @timed_testset "1D plot from 2D solution" begin @trixi_testset "Create 1D plot along curve" begin using OrdinaryDiffEqSSPRK From 54597db51898ef508734169d8b0e3462ed8dc133 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 16:09:16 -0400 Subject: [PATCH 37/68] separate caches --- src/callbacks_step/amr.jl | 2 +- .../semidiscretization_parabolic.jl | 52 ++++++++----------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index ec721603c33..4299e0ce4d7 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -240,7 +240,7 @@ end end return amr_callback(u_ode, mesh, equations, dg, cache, - semi.cache.parabolic, + semi.cache_parabolic, semi, t, iter; kwargs...) end diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 6ab3138af8a..3f27210a234 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -5,26 +5,30 @@ @muladd begin #! format: noindent -@doc raw""" +""" SemidiscretizationParabolic -A struct containing everything needed to describe a spatial semidiscretization of a purely +A struct containing everything needed to describe a spatial semidiscretization of a purely parabolic PDE. """ mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition, BoundaryConditions, SourceTerms, - Solver, SolverParabolic, Cache} <: + Solver, SolverParabolic, + Cache, CacheParabolic} <: AbstractSemidiscretization mesh::Mesh equations::Equations const initial_condition::InitialCondition + const boundary_conditions::BoundaryConditions const source_terms::SourceTerms + const solver::Solver const solver_parabolic::SolverParabolic cache::Cache + cache_parabolic::CacheParabolic performance_counter::PerformanceCounter end @@ -52,18 +56,13 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic RealT = real(solver), uEltype = RealT) @assert ndims(mesh) == ndims(equations) - # Base cache containing data used for both parabolic and hyperbolic solvers - cache_base = create_cache(mesh, equations, solver, RealT, uEltype) + cache = create_cache(mesh, equations, solver, RealT, uEltype) _boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver, - cache_base) + cache) check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions) - # Parabolic cache containing data only used for parabolic solvers cache_parabolic = create_cache_parabolic(mesh, equations, solver, - nelements(solver, cache_base), uEltype) - - # Combine caches into a single cache struct for the semidiscretization - cache = (; base = cache_base, parabolic = cache_parabolic) + nelements(solver, cache), uEltype) performance_counter = PerformanceCounter() @@ -73,14 +72,16 @@ function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic typeof(source_terms), typeof(solver), typeof(solver_parabolic), - typeof(cache)}(mesh, equations, - initial_condition, - _boundary_conditions, - source_terms, - solver, - solver_parabolic, - cache, - performance_counter) + typeof(cache), + typeof(cache_parabolic)}(mesh, equations, + initial_condition, + _boundary_conditions, + source_terms, + solver, + solver_parabolic, + cache, + cache_parabolic, + performance_counter) end # @eval due to @muladd @@ -150,15 +151,13 @@ end @inline Base.real(semi::SemidiscretizationParabolic) = real(semi.solver) @inline function mesh_equations_solver_cache(semi::SemidiscretizationParabolic) - @unpack mesh, equations, solver = semi - cache = semi.cache.base + @unpack mesh, equations, solver, cache = semi return mesh, equations, solver, cache end function calc_error_norms(func, u_ode, t, analyzer, semi::SemidiscretizationParabolic, cache_analysis) - @unpack mesh, equations, initial_condition, solver = semi - cache = semi.cache.base + @unpack mesh, equations, initial_condition, solver, cache = semi u = wrap_array(u_ode, mesh, equations, solver, cache) return calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition, @@ -166,7 +165,6 @@ function calc_error_norms(func, u_ode, t, analyzer, end function compute_coefficients(t, semi::SemidiscretizationParabolic) - # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` return compute_coefficients(semi.initial_condition, t, semi) end @@ -174,12 +172,8 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) return compute_coefficients!(u_ode, semi.initial_condition, t, semi) end -# Method for `rhs!` that only computes the parabolic right-hand side with no hyperbolic -# contribution. function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) - @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic = semi - cache = semi.cache.base - cache_parabolic = semi.cache.parabolic + @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi u = wrap_array(u_ode, mesh, equations, solver, cache) du = wrap_array(du_ode, mesh, equations, solver, cache) From e8ad24042c8cb1a5c97691819a1e196dbdccb6c3 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 17:32:07 -0400 Subject: [PATCH 38/68] dispatch for parabolic linear structure --- ...elixir_diffusion_ldg_amr_boundary_layer.jl | 12 ++-- src/equations/equations_parabolic.jl | 12 ---- src/semidiscretization/semidiscretization.jl | 53 +++++++++------ ...semidiscretization_hyperbolic_parabolic.jl | 64 ++++--------------- .../semidiscretization_parabolic.jl | 15 +++++ test/test_parabolic_1d.jl | 2 +- 6 files changed, 69 insertions(+), 89 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index 5e6a2e82de3..8d85de55c6f 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -15,7 +15,7 @@ solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationLocalDG() mesh = TreeMesh((0.0,), (1.0,), - initial_refinement_level = 1, + initial_refinement_level = 0, periodicity = false, n_cells_max = 30_000) @@ -49,12 +49,12 @@ alive_callback = AliveCallback(analysis_interval = 200) amr_indicator = IndicatorLöhner(semi, variable = first) amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level = 1, - med_level = -1, - med_threshold = 0.005, - max_level = 6, max_threshold = 0.025) + base_level = 0, + med_level = 3, + med_threshold = 0.01, + max_level = 6, max_threshold = 0.175) amr_callback = AMRCallback(semi, amr_controller, - interval = 100, + interval = 200, adapt_initial_condition = true, adapt_initial_condition_only_refine = false) diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl index f42b1becae1..d2642153e85 100644 --- a/src/equations/equations_parabolic.jl +++ b/src/equations/equations_parabolic.jl @@ -26,18 +26,6 @@ if the diffusion term is linear in the variables/constant. """ @inline have_constant_diffusivity(::AbstractLaplaceDiffusion) = True() -""" - have_constant_speed(equations_parabolic::AbstractEquationsParabolic) - -Return whether the parabolic part is linear with constant diffusivity. - -This enables generic utilities such as [`linear_structure`](@ref) to treat -purely parabolic semidiscretizations analogously to linear hyperbolic ones. -""" -@inline function have_constant_speed(equations_parabolic::AbstractEquationsParabolic) - return have_constant_diffusivity(equations_parabolic) -end - """ max_diffusivity(equations_parabolic::AbstractLaplaceDiffusion) diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index 65425213327..49bc8859b66 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -236,6 +236,34 @@ function compute_coefficients!(u_ode, func, t, semi::AbstractSemidiscretization) mesh_equations_solver_cache(semi)...) end +function _linear_structure_from_rhs(semi::AbstractSemidiscretization, make_apply_rhs!; + t0 = zero(real(semi))) + # allocate memory + u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) + du_ode = similar(u_ode) + + apply_rhs! = make_apply_rhs!(u_ode, du_ode) + + # get the right hand side from boundary conditions and optional source terms + u_ode .= zero(eltype(u_ode)) + apply_rhs!(du_ode, u_ode) + b = -du_ode + + # Create a copy of `b` used internally to extract the linear part of `semi`. + # This is necessary to get everything correct when the user updates the + # returned vector `b`. + b_tmp = copy(b) + + # wrap the linear operator + A = LinearMap(length(u_ode), ismutating = true) do dest, src + apply_rhs!(dest, src) + @. dest += b_tmp + return dest + end + + return A, b +end + """ linear_structure(semi::AbstractSemidiscretization; t0 = zero(real(semi))) @@ -270,28 +298,13 @@ function linear_structure(semi::AbstractSemidiscretization; throw(ArgumentError("`linear_structure` expects linear equations.")) end - # allocate memory - u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) - du_ode = similar(u_ode) - - # get the right hand side from boundary conditions and optional source terms - u_ode .= zero(eltype(u_ode)) - rhs!(du_ode, u_ode, semi, t0) - b = -du_ode - - # Create a copy of `b` used internally to extract the linear part of `semi`. - # This is necessary to get everything correct when the user updates the - # returned vector `b`. - b_tmp = copy(b) - - # wrap the linear operator - A = LinearMap(length(u_ode), ismutating = true) do dest, src - rhs!(dest, src, semi, t0) - @. dest += b_tmp - return dest + make_apply_rhs! = function (_, _) + return function (dest, src) + return rhs!(dest, src, semi, t0) + end end - return A, b + return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) end """ diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index bb6487d46f4..56430e641ed 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -427,37 +427,17 @@ function linear_structure(semi::SemidiscretizationHyperbolicParabolic; throw(ArgumentError("`linear_structure` expects linear equations.")) end - # allocate memory - u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) - du_ode = similar(u_ode) - - # get the right hand side from boundary conditions and optional source terms - u_ode .= zero(eltype(u_ode)) - rhs!(du_ode, u_ode, semi, t0) - b = -du_ode - - # Repeat for parabolic part - rhs_parabolic!(du_ode, u_ode, semi, t0) - @. b -= du_ode - - # Create a copy of `b` used internally to extract the linear part of `semi`. - # This is necessary to get everything correct when the user updates the - # returned vector `b`. - b_tmp = copy(b) - - # additional storage for parabolic part - dest_para = similar(du_ode) - - # wrap the linear operator - A = LinearMap(length(u_ode), ismutating = true) do dest, src - rhs!(dest, src, semi, t0) - rhs_parabolic!(dest_para, src, semi, t0) - - @. dest += dest_para + b_tmp - return dest + make_apply_rhs! = function (_, du_ode) + dest_para = similar(du_ode) + return function (dest, src) + rhs!(dest, src, semi, t0) + rhs_parabolic!(dest_para, src, semi, t0) + @. dest += dest_para + return dest + end end - return A, b + return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) end function _jacobian_ad_forward(semi::SemidiscretizationHyperbolicParabolic, t0, u0_ode, @@ -514,29 +494,13 @@ function linear_structure_parabolic(semi::SemidiscretizationHyperbolicParabolic; throw(ArgumentError("`linear_structure_parabolic` expects equations with constant diffusive terms.")) end - # allocate memory - u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) - du_ode = similar(u_ode) - - # get the parabolic right hand side from boundary conditions and optional source terms - u_ode .= zero(eltype(u_ode)) - rhs_parabolic!(du_ode, u_ode, semi, t0) - b = -du_ode - - # Create a copy of `b` used internally to extract the linear part of `semi`. - # This is necessary to get everything correct when the user updates the - # returned vector `b`. - b_tmp = copy(b) - - # wrap the linear operator - A = LinearMap(length(u_ode), ismutating = true) do dest, src - rhs_parabolic!(dest, src, semi, t0) - - @. dest += b_tmp - return dest + make_apply_rhs! = function (_, _) + return function (dest, src) + return rhs_parabolic!(dest, src, semi, t0) + end end - return A, b + return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) end """ diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 3f27210a234..0e171333ddb 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -172,6 +172,21 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) return compute_coefficients!(u_ode, semi.initial_condition, t, semi) end +function linear_structure(semi::SemidiscretizationParabolic; + t0 = zero(real(semi))) + if have_constant_diffusivity(semi.equations) == False() + throw(ArgumentError("`linear_structure` expects equations with constant diffusive terms.")) + end + + make_apply_rhs! = function (_, _) + return function (dest, src) + return rhs!(dest, src, semi, t0) + end + end + + return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) +end + function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 0510e9d255a..d376781b21b 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -531,7 +531,7 @@ end @trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_amr_boundary_layer.jl"), - l2=[0.588077335170678], linf=[0.9400281609880542]) + l2=[0.5881457102264551], linf=[0.9302621795999283]) @trixi_test_nowarn show(stdout, semi) # not tested elsewhere # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) From bcf02c0e07479ce7a2b7b90a45615ec7364f2ae7 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 18:45:53 -0400 Subject: [PATCH 39/68] specialize extra node variables and update tests --- ...elixir_diffusion_ldg_amr_boundary_layer.jl | 16 +++++++++++++++- src/callbacks_step/save_solution.jl | 10 +++++++++- .../semidiscretization_parabolic.jl | 9 +++++++++ src/solvers/dg.jl | 17 ++++++++++++++++- test/test_parabolic_1d.jl | 2 ++ test/test_type.jl | 14 ++++++++++++++ test/test_visualization.jl | 19 ------------------- 7 files changed, 65 insertions(+), 22 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index 8d85de55c6f..ff80fb039ac 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -60,8 +60,22 @@ amr_callback = AMRCallback(semi, amr_controller, stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) +# specify extra node variables to be saved in the `SaveSolutionCallback` +extra_node_variables = (:dudx,) +function Trixi.get_node_variable(::Val{:dudx}, u, mesh, equations, dg, cache, + cache_parabolic) + return copy(@view cache_parabolic.parabolic_container.gradients[1, :, :]) +end + +# save the solution at the last time step, including the extra node variables +save_solution = SaveSolutionCallback(dt = 2.0, + save_initial_solution = false, + save_final_solution = true, + solution_variables = cons2cons, + extra_node_variables = extra_node_variables) + callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - amr_callback, stepsize_callback) + save_solution, amr_callback, stepsize_callback) ############################################################################### # run the simulation diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 23e165984cc..a8a22bb7d87 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -25,7 +25,7 @@ and the second parameter is the equation struct. Additional nodal variables such as vorticity or the Mach number can be saved by passing a tuple of symbols to `extra_node_variables`, e.g., `extra_node_variables = (:vorticity, :mach)`. In that case the function `get_node_variable` must be defined for each symbol in the tuple. -For purely hyperbolic and purely parabolic equations, the expected signature is: +For purely hyperbolic equations, the expected signature is: ```julia function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache) # Implementation goes here @@ -34,6 +34,14 @@ end and must return an array of dimension `(ntuple(_ -> n_nodes, ndims(mesh))..., n_elements)`. +For purely parabolic equations, `cache_parabolic` must be added: +```julia +function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache, + cache_parabolic) + # Implementation goes here +end +``` + For hyperbolic-parabolic equations, `equations_parabolic` and `cache_parabolic` must be added: ```julia diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 0e171333ddb..442f4791b2e 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -172,6 +172,15 @@ function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic) return compute_coefficients!(u_ode, semi.initial_condition, t, semi) end +# Required for storing `extra_node_variables` in the `SaveSolutionCallback`. +# Not to be confused with `get_node_vars` which returns the variables of the simulated equation. +function get_node_variables!(node_variables, u_ode, + semi::SemidiscretizationParabolic) + return get_node_variables!(node_variables, u_ode, + mesh_equations_solver_cache(semi)..., + semi.cache_parabolic) +end + function linear_structure(semi::SemidiscretizationParabolic; t0 = zero(real(semi))) if have_constant_diffusivity(semi.equations) == False() diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index b2404041e1b..1666ffe4de1 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -38,7 +38,22 @@ function get_node_variables!(node_variables, u_ode, mesh, equations, return nothing end -# Version for parabolic-extended equations + +# Version for purely parabolic equations (adds cache_parabolic). +function get_node_variables!(node_variables, u_ode, mesh, equations, + dg, cache, cache_parabolic) + if !isempty(node_variables) + u = wrap_array(u_ode, mesh, equations, dg, cache) + for var in keys(node_variables) + node_variables[var] = get_node_variable(Val(var), u, mesh, equations, + dg, cache, cache_parabolic) + end + end + + return nothing +end + +# Version for hyperbolic-parabolic equations (adds equations_parabolic and cache_parabolic). function get_node_variables!(node_variables, u_ode, mesh, equations, dg, cache, equations_parabolic, cache_parabolic) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index d376781b21b..c11496231eb 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -529,6 +529,7 @@ end end @trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin + @test_nowarn isdir(outdir) && rm(outdir, recursive = true) @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_amr_boundary_layer.jl"), l2=[0.5881457102264551], linf=[0.9302621795999283]) @@ -536,6 +537,7 @@ end # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) + @test_nowarn isdir(outdir) && rm(outdir, recursive = true) end @trixi_testset "TreeMesh1D consistency check: elixir_diffusion_ldg_dirichlet.jl" begin diff --git a/test/test_type.jl b/test/test_type.jl index 06d9c4b1192..ef4d7453612 100644 --- a/test/test_type.jl +++ b/test/test_type.jl @@ -1863,6 +1863,20 @@ isdir(outdir) && rm(outdir, recursive = true) end end + @timed_testset "Linear Diffusion Equation" begin + for RealT in (Float32, Float64) + u = SVector(one(RealT)) + + equations_1d = LinearDiffusionEquation1D(RealT(0.1)) + @test eltype(@inferred cons2prim(u, equations_1d)) == RealT + @test eltype(@inferred cons2entropy(u, equations_1d)) == RealT + + equations_2d = LinearDiffusionEquation2D(RealT(0.1)) + @test eltype(@inferred cons2prim(u, equations_2d)) == RealT + @test eltype(@inferred cons2entropy(u, equations_2d)) == RealT + end + end + @timed_testset "Laplace Diffusion 2D" begin for RealT in (Float32, Float64) equations = LinearScalarAdvectionEquation2D(RealT(1), RealT(1)) diff --git a/test/test_visualization.jl b/test/test_visualization.jl index 4e30cabcefe..367021928fa 100644 --- a/test/test_visualization.jl +++ b/test/test_visualization.jl @@ -169,15 +169,6 @@ test_examples_2d = Dict("TreeMesh" => ("tree_2d_dgsem", end end -@trixi_testset "PlotData2D scalar diffusion variables" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_2d_dgsem", - "elixir_diffusion_steady_state_linear_map.jl")) - pd = PlotData2D(sol; solution_variables = cons2entropy) - - @test pd["scalar"] == Trixi.PlotDataSeries(pd, 1) - @test keys(pd) == ("scalar",) -end - @timed_testset "PlotData1D, PlotDataSeries, PlotMesh" begin # Run Trixi.jl @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", @@ -263,16 +254,6 @@ end end end -@trixi_testset "PlotData1D scalar diffusion variables" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", - "elixir_diffusion_ldg_dirichlet.jl"), - tspan=(0.0, 0.0)) - pd = PlotData1D(sol; solution_variables = cons2entropy) - - @test pd["scalar"] == Trixi.PlotDataSeries(pd, 1) - @test keys(pd) == ("scalar",) -end - @timed_testset "1D plot from 2D solution" begin @trixi_testset "Create 1D plot along curve" begin using OrdinaryDiffEqSSPRK From 2e65bb53560c8ae9f9afdaae666ca23df5830774 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 21:42:19 -0400 Subject: [PATCH 40/68] fix outdir issue in tests --- test/test_parabolic_1d.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index c11496231eb..d376781b21b 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -529,7 +529,6 @@ end end @trixi_testset "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin - @test_nowarn isdir(outdir) && rm(outdir, recursive = true) @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_amr_boundary_layer.jl"), l2=[0.5881457102264551], linf=[0.9302621795999283]) @@ -537,7 +536,6 @@ end # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) - @test_nowarn isdir(outdir) && rm(outdir, recursive = true) end @trixi_testset "TreeMesh1D consistency check: elixir_diffusion_ldg_dirichlet.jl" begin From 20a067d1f096e63baa2845ec7950d0a57af36bdf Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 22:08:48 -0400 Subject: [PATCH 41/68] remove check + variable naming consistency in stepsize.jl --- src/callbacks_step/stepsize.jl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 726a9b22dc8..a6639967dc4 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -151,17 +151,12 @@ function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, end # Case for a purely parabolic semidiscretization -function calculate_dt(u_ode, t, _cfl_hyperbolic, cfl_parabolic, +function calculate_dt(u_ode, t, cfl_hyperbolic, cfl_parabolic, semi::SemidiscretizationParabolic) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) u = wrap_array(u_ode, mesh, equations, solver, cache) - cfl_para = cfl_parabolic(t) - if !(cfl_para > 0) - throw(ArgumentError("For `SemidiscretizationParabolic`, set `cfl_parabolic > 0` in `StepsizeCallback`.")) - end - - return cfl_para * max_dt(u, t, mesh, + return cfl_parabolic(t) * max_dt(u, t, mesh, have_constant_diffusivity(equations), equations, equations, solver, cache) end From c38e0aa35f6fd96ff103f40ee917a1f1e3075f94 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 22:44:46 -0400 Subject: [PATCH 42/68] remove unneeded restrictions, add missing dispatches, and update tests --- .../elixir_diffusion_ldg_dirichlet.jl | 4 ++-- src/callbacks_step/amr.jl | 24 ++----------------- test/test_parabolic_1d.jl | 19 ++++++++++++++- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl index dc8533ee33a..bb273463c12 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_dirichlet.jl @@ -34,8 +34,8 @@ semi = SemidiscretizationParabolic(mesh, equations, initial_condition, solver; ############################################################################### # ODE solvers, callbacks etc. -# Create ODE problem with time span from 0.0 to 0.1 -tspan = (0.0, 0.1) +# Create ODE problem with time span from 0.0 to 1.0 +tspan = (0.0, 1.0) ode = semidiscretize(semi, tspan) # At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 4299e0ce4d7..612422adcc0 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -213,7 +213,8 @@ end end @inline function (amr_callback::AMRCallback)(u_ode::AbstractVector, - semi::SemidiscretizationHyperbolicParabolic, + semi::Union{SemidiscretizationHyperbolicParabolic, + SemidiscretizationParabolic}, t, iter; kwargs...) # Note that we don't `wrap_array` the vector `u_ode` to be able to `resize!` @@ -223,27 +224,6 @@ end semi, t, iter; kwargs...) end -@inline function (amr_callback::AMRCallback)(u_ode::AbstractVector, - semi::SemidiscretizationParabolic, - t, iter; - kwargs...) - # Note that we don't `wrap_array` the vector `u_ode` to be able to `resize!` - # it when doing AMR while still dispatching on the `mesh` etc. - mesh, equations, dg, cache = mesh_equations_solver_cache(semi) - - if !(mesh isa TreeMesh{1}) - throw(ArgumentError("Purely parabolic AMR is currently implemented for `TreeMesh{1}` only.")) - end - - if mpi_isparallel() - throw(ArgumentError("Purely parabolic AMR is currently implemented for serial runs only.")) - end - - return amr_callback(u_ode, mesh, equations, dg, cache, - semi.cache_parabolic, - semi, t, iter; kwargs...) -end - # `passive_args` is currently used for Euler with self-gravity to adapt the gravity solver # passively without querying its indicator, based on the assumption that both solvers use # the same mesh. That's a hack and should be improved in the future once we have more examples diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index d376781b21b..0a1d7b4745a 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -532,16 +532,32 @@ end @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_amr_boundary_layer.jl"), l2=[0.5881457102264551], linf=[0.9302621795999283]) - @trixi_test_nowarn show(stdout, semi) # not tested elsewhere # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs!, semi, sol, 1000) + + # Test `show` method not exercised in elixirs + @trixi_test_nowarn show(IOContext(stdout, :compact => true), MIME"text/plain"(), + semi) + + # Test basic semidiscretization dispatches + @test ndims(semi) == ndims(semi.mesh) + @test nvariables(semi) == nvariables(semi.equations) + @test real(semi) == real(semi.solver) + + # Test that `remake` works for `SemidiscretizationParabolic` + semi_remade = remake(semi) + @test semi_remade isa SemidiscretizationParabolic + @test semi_remade !== semi + @test semi_remade.mesh === semi.mesh + @test ndofsglobal(semi_remade) == ndofsglobal(semi) end @trixi_testset "TreeMesh1D consistency check: elixir_diffusion_ldg_dirichlet.jl" begin # Run the Dirichlet-Dirichlet elixir (uses `SemidiscretizationParabolic`) @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_dirichlet.jl"), + tspan=(0.0, 0.1), analysis_callback=AnalysisCallback(semi, interval = 100, extra_analysis_errors = (:l2_error_primitive, @@ -558,6 +574,7 @@ end # Run again using an advection-diffusion equation with advection velocity zero @test_trixi_include(joinpath(EXAMPLES_DIR, "tree_1d_dgsem", "elixir_diffusion_ldg_dirichlet.jl"), + tspan=(0.0, 0.1), equations=LinearScalarAdvectionEquation1D(0.0), semi=SemidiscretizationHyperbolicParabolic(mesh, (equations, From 95abb83056905c2ff89406f2535aa653ec71c9ef Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 23:05:36 -0400 Subject: [PATCH 43/68] cleanup --- src/callbacks_step/amr.jl | 2 +- src/callbacks_step/save_solution.jl | 2 +- src/semidiscretization/semidiscretization.jl | 23 ++++++++------- ...semidiscretization_hyperbolic_parabolic.jl | 28 +++++++++---------- .../semidiscretization_parabolic.jl | 8 ++---- 5 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 612422adcc0..34d89bf3e3f 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -389,7 +389,7 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, @unpack controller, adaptor = amr_callback u = wrap_array(u_ode, mesh, equations, dg, cache) - # Evaluate the indicator from the current semidiscrete solution. + lambda = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, t = t, iter = iter) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index a8a22bb7d87..a7302361daa 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -25,7 +25,7 @@ and the second parameter is the equation struct. Additional nodal variables such as vorticity or the Mach number can be saved by passing a tuple of symbols to `extra_node_variables`, e.g., `extra_node_variables = (:vorticity, :mach)`. In that case the function `get_node_variable` must be defined for each symbol in the tuple. -For purely hyperbolic equations, the expected signature is: +The expected signature of the function for (purely) hyperbolic equations is: ```julia function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache) # Implementation goes here diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index 49bc8859b66..9079dfaa2d6 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -236,14 +236,15 @@ function compute_coefficients!(u_ode, func, t, semi::AbstractSemidiscretization) mesh_equations_solver_cache(semi)...) end -function _linear_structure_from_rhs(semi::AbstractSemidiscretization, make_apply_rhs!; - t0 = zero(real(semi))) +function _linear_structure_from_rhs(semi::AbstractSemidiscretization, apply_rhs!) # allocate memory u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) du_ode = similar(u_ode) - apply_rhs! = make_apply_rhs!(u_ode, du_ode) + return _linear_structure_from_rhs(u_ode, du_ode, apply_rhs!) +end +function _linear_structure_from_rhs(u_ode, du_ode, apply_rhs!) # get the right hand side from boundary conditions and optional source terms u_ode .= zero(eltype(u_ode)) apply_rhs!(du_ode, u_ode) @@ -255,10 +256,10 @@ function _linear_structure_from_rhs(semi::AbstractSemidiscretization, make_apply b_tmp = copy(b) # wrap the linear operator - A = LinearMap(length(u_ode), ismutating = true) do dest, src - apply_rhs!(dest, src) - @. dest += b_tmp - return dest + A = LinearMap(length(u_ode), ismutating = true) do du_ode, u_ode + apply_rhs!(du_ode, u_ode) + @. du_ode += b_tmp + return du_ode end return A, b @@ -298,13 +299,11 @@ function linear_structure(semi::AbstractSemidiscretization; throw(ArgumentError("`linear_structure` expects linear equations.")) end - make_apply_rhs! = function (_, _) - return function (dest, src) - return rhs!(dest, src, semi, t0) - end + apply_rhs! = function (du_ode, u_ode) + return rhs!(du_ode, u_ode, semi, t0) end - return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) + return _linear_structure_from_rhs(semi, apply_rhs!) end """ diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index 56430e641ed..a05a9c8e87e 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -427,17 +427,19 @@ function linear_structure(semi::SemidiscretizationHyperbolicParabolic; throw(ArgumentError("`linear_structure` expects linear equations.")) end - make_apply_rhs! = function (_, du_ode) - dest_para = similar(du_ode) - return function (dest, src) - rhs!(dest, src, semi, t0) - rhs_parabolic!(dest_para, src, semi, t0) - @. dest += dest_para - return dest - end + # Preallocate once to avoid repeated temporary allocations in `apply_rhs!`. + u_ode_template = allocate_coefficients(mesh_equations_solver_cache(semi)...) + du_ode_template = similar(u_ode_template) + du_ode_parabolic = similar(du_ode_template) + + apply_rhs! = function (du_ode, u_ode) + rhs!(du_ode, u_ode, semi, t0) + rhs_parabolic!(du_ode_parabolic, u_ode, semi, t0) + @. du_ode += du_ode_parabolic + return du_ode end - return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) + return _linear_structure_from_rhs(u_ode_template, du_ode_template, apply_rhs!) end function _jacobian_ad_forward(semi::SemidiscretizationHyperbolicParabolic, t0, u0_ode, @@ -494,13 +496,11 @@ function linear_structure_parabolic(semi::SemidiscretizationHyperbolicParabolic; throw(ArgumentError("`linear_structure_parabolic` expects equations with constant diffusive terms.")) end - make_apply_rhs! = function (_, _) - return function (dest, src) - return rhs_parabolic!(dest, src, semi, t0) - end + apply_rhs! = function (du_ode, u_ode) + return rhs_parabolic!(du_ode, u_ode, semi, t0) end - return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) + return _linear_structure_from_rhs(semi, apply_rhs!) end """ diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 442f4791b2e..543b8a8c3fd 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -187,13 +187,11 @@ function linear_structure(semi::SemidiscretizationParabolic; throw(ArgumentError("`linear_structure` expects equations with constant diffusive terms.")) end - make_apply_rhs! = function (_, _) - return function (dest, src) - return rhs!(dest, src, semi, t0) - end + apply_rhs! = function (du_ode, u_ode) + return rhs!(du_ode, u_ode, semi, t0) end - return _linear_structure_from_rhs(semi, make_apply_rhs!; t0 = t0) + return _linear_structure_from_rhs(semi, apply_rhs!) end function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) From 6905853bed87c6fbb776fbd242964fe3f371588f Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 23:06:57 -0400 Subject: [PATCH 44/68] spacing --- src/callbacks_step/amr.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 34d89bf3e3f..967d682d8dc 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -389,7 +389,6 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, @unpack controller, adaptor = amr_callback u = wrap_array(u_ode, mesh, equations, dg, cache) - lambda = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, t = t, iter = iter) From 6b1463f99cb7d8faa5c7b24865a5d98cadc3fc60 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 23:09:48 -0400 Subject: [PATCH 45/68] line breaks --- src/callbacks_step/save_solution.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index a7302361daa..62c1973d169 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -42,8 +42,8 @@ function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache, end ``` -For hyperbolic-parabolic equations, `equations_parabolic` and `cache_parabolic` -must be added: +For hyperbolic-parabolic equations, `equations_parabolic` and `cache_parabolic` must be +added: ```julia function get_node_variable(::Val{symbol}, u, mesh, equations, dg, cache, equations_parabolic, cache_parabolic) From 3f514be3a8b1489c2cd29b251ea4ac66eb36d53f Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 23:48:42 -0400 Subject: [PATCH 46/68] consistency --- ...lixir_diffusion_steady_state_linear_map.jl | 2 +- src/semidiscretization/semidiscretization.jl | 16 +++++-------- ...semidiscretization_hyperbolic_parabolic.jl | 24 +++++++++---------- .../semidiscretization_parabolic.jl | 4 ++-- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl index 524d949cbba..af3c12aeab4 100644 --- a/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl +++ b/examples/tree_2d_dgsem/elixir_diffusion_steady_state_linear_map.jl @@ -73,7 +73,7 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() # Analysis callback quantifies discretization/interpolation error of the exact solution -analysis_callback = AnalysisCallback(semi, analysis_integrals = ()) +analysis_callback = AnalysisCallback(semi) callbacks = CallbackSet(summary_callback, analysis_callback) diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index 9079dfaa2d6..99c2c47a925 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -241,10 +241,6 @@ function _linear_structure_from_rhs(semi::AbstractSemidiscretization, apply_rhs! u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) du_ode = similar(u_ode) - return _linear_structure_from_rhs(u_ode, du_ode, apply_rhs!) -end - -function _linear_structure_from_rhs(u_ode, du_ode, apply_rhs!) # get the right hand side from boundary conditions and optional source terms u_ode .= zero(eltype(u_ode)) apply_rhs!(du_ode, u_ode) @@ -256,10 +252,10 @@ function _linear_structure_from_rhs(u_ode, du_ode, apply_rhs!) b_tmp = copy(b) # wrap the linear operator - A = LinearMap(length(u_ode), ismutating = true) do du_ode, u_ode - apply_rhs!(du_ode, u_ode) - @. du_ode += b_tmp - return du_ode + A = LinearMap(length(u_ode), ismutating = true) do dest, src + apply_rhs!(dest, src) + @. dest += b_tmp + return dest end return A, b @@ -299,8 +295,8 @@ function linear_structure(semi::AbstractSemidiscretization; throw(ArgumentError("`linear_structure` expects linear equations.")) end - apply_rhs! = function (du_ode, u_ode) - return rhs!(du_ode, u_ode, semi, t0) + apply_rhs! = function (dest, src) + return rhs!(dest, src, semi, t0) end return _linear_structure_from_rhs(semi, apply_rhs!) diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index a05a9c8e87e..ddd07a6cae1 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -427,19 +427,17 @@ function linear_structure(semi::SemidiscretizationHyperbolicParabolic; throw(ArgumentError("`linear_structure` expects linear equations.")) end - # Preallocate once to avoid repeated temporary allocations in `apply_rhs!`. - u_ode_template = allocate_coefficients(mesh_equations_solver_cache(semi)...) - du_ode_template = similar(u_ode_template) - du_ode_parabolic = similar(du_ode_template) - - apply_rhs! = function (du_ode, u_ode) - rhs!(du_ode, u_ode, semi, t0) - rhs_parabolic!(du_ode_parabolic, u_ode, semi, t0) - @. du_ode += du_ode_parabolic - return du_ode + # additional storage for parabolic part + dest_para = allocate_coefficients(mesh_equations_solver_cache(semi)...) + + apply_rhs! = function (dest, src) + rhs!(dest, src, semi, t0) + rhs_parabolic!(dest_para, src, semi, t0) + @. dest += dest_para + return dest end - return _linear_structure_from_rhs(u_ode_template, du_ode_template, apply_rhs!) + return _linear_structure_from_rhs(semi, apply_rhs!) end function _jacobian_ad_forward(semi::SemidiscretizationHyperbolicParabolic, t0, u0_ode, @@ -496,8 +494,8 @@ function linear_structure_parabolic(semi::SemidiscretizationHyperbolicParabolic; throw(ArgumentError("`linear_structure_parabolic` expects equations with constant diffusive terms.")) end - apply_rhs! = function (du_ode, u_ode) - return rhs_parabolic!(du_ode, u_ode, semi, t0) + apply_rhs! = function (dest, src) + return rhs_parabolic!(dest, src, semi, t0) end return _linear_structure_from_rhs(semi, apply_rhs!) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 543b8a8c3fd..fa68eb293ee 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -187,8 +187,8 @@ function linear_structure(semi::SemidiscretizationParabolic; throw(ArgumentError("`linear_structure` expects equations with constant diffusive terms.")) end - apply_rhs! = function (du_ode, u_ode) - return rhs!(du_ode, u_ode, semi, t0) + apply_rhs! = function (dest, src) + return rhs!(dest, src, semi, t0) end return _linear_structure_from_rhs(semi, apply_rhs!) From 8ea291ac8448798e41acdd626a77d98c521a530d Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sat, 28 Mar 2026 23:51:46 -0400 Subject: [PATCH 47/68] add comment --- src/semidiscretization/semidiscretization.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index 99c2c47a925..4f77943287f 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -236,6 +236,7 @@ function compute_coefficients!(u_ode, func, t, semi::AbstractSemidiscretization) mesh_equations_solver_cache(semi)...) end +# Helper function to compute linear structure for a given semidiscretization and `rhs!` function _linear_structure_from_rhs(semi::AbstractSemidiscretization, apply_rhs!) # allocate memory u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) From 145466f6f38c1faaf06edca720d56610a730fb1c Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 29 Mar 2026 00:35:32 -0400 Subject: [PATCH 48/68] Trixi.ndofsglobal needed in test --- test/test_parabolic_1d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 0a1d7b4745a..57925284c01 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -550,7 +550,7 @@ end @test semi_remade isa SemidiscretizationParabolic @test semi_remade !== semi @test semi_remade.mesh === semi.mesh - @test ndofsglobal(semi_remade) == ndofsglobal(semi) + @test Trixi.ndofsglobal(semi_remade) == Trixi.ndofsglobal(semi) end @trixi_testset "TreeMesh1D consistency check: elixir_diffusion_ldg_dirichlet.jl" begin From d51be88baac8afc9a6e8284516f5d70e1823081d Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 29 Mar 2026 09:38:15 -0400 Subject: [PATCH 49/68] clarify standalone diffusion equations in docstrings --- src/equations/linear_diffusion_equation_1d.jl | 7 +++++-- src/equations/linear_diffusion_equation_2d.jl | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/equations/linear_diffusion_equation_1d.jl b/src/equations/linear_diffusion_equation_1d.jl index f8db9184ce4..288709decc4 100644 --- a/src/equations/linear_diffusion_equation_1d.jl +++ b/src/equations/linear_diffusion_equation_1d.jl @@ -8,10 +8,13 @@ @doc raw""" LinearDiffusionEquation1D(diffusivity) -The linear diffusion equation (or heat equation) in one space dimension with constant diffusivity `\kappa`: +The linear diffusion equation (or heat equation) in one space dimension with constant `diffusivity` ``\kappa``: ```math -\partial_t u = \partial_1 \left( \kappa \partial_1 u \right) +\partial_t u = \partial_1 \left( \kappa \partial_1 u \right). ``` +Unlike [`LaplaceDiffusion1D`](@ref), which represents the parabolic part of a +hyperbolic-parabolic equation, `LinearDiffusionEquation1D` represents a purely parabolic +equation without any hyperbolic part. """ struct LinearDiffusionEquation1D{RealT <: Real} <: AbstractLaplaceDiffusion{1, 1} diffusivity::RealT diff --git a/src/equations/linear_diffusion_equation_2d.jl b/src/equations/linear_diffusion_equation_2d.jl index a899878efc7..31bec47bc0c 100644 --- a/src/equations/linear_diffusion_equation_2d.jl +++ b/src/equations/linear_diffusion_equation_2d.jl @@ -8,12 +8,14 @@ @doc raw""" LinearDiffusionEquation2D(diffusivity) -The linear diffusion equation (or heat equation) in two space dimensions with constant -diffusivity `\kappa`: +The linear diffusion equation (or heat equation) in two space dimensions with constant `diffusivity` ``\kappa``: ```math \partial_t u = \partial_1 \left( \kappa \partial_1 u \right) - + \partial_2 \left( \kappa \partial_2 u \right) + + \partial_2 \left( \kappa \partial_2 u \right). ``` +Unlike [`LaplaceDiffusion2D`](@ref), which represents the parabolic part of a +hyperbolic-parabolic equation, `LinearDiffusionEquation2D` represents a purely parabolic +equation without any hyperbolic part. """ struct LinearDiffusionEquation2D{RealT <: Real} <: AbstractLaplaceDiffusion{2, 1} diffusivity::RealT From 6dc51f6cae360148081576bfe9101f959044e69d Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 29 Mar 2026 09:41:19 -0400 Subject: [PATCH 50/68] Apply suggestions from code review Co-authored-by: Hendrik Ranocha --- examples/tree_1d_dgsem/elixir_diffusion_ldg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl index 8f2398f470c..3f3b060838e 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg.jl @@ -2,7 +2,7 @@ using OrdinaryDiffEqLowStorageRK using Trixi ############################################################################### -# semidiscretization of the pure diffusion equation +# semidiscretization of the (pure) linear diffusion equation diffusivity() = 0.5 equations = LinearDiffusionEquation1D(diffusivity()) From 970181318ec60b6544c0a19d5d5047d3936b284d Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 29 Mar 2026 09:57:21 -0400 Subject: [PATCH 51/68] add parabolic linear_structure docstring --- .../semidiscretization_parabolic.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index fa68eb293ee..967288ec331 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -181,6 +181,20 @@ function get_node_variables!(node_variables, u_ode, semi.cache_parabolic) end +""" + linear_structure(semi::SemidiscretizationParabolic; + t0 = zero(real(semi))) + +Wraps the right-hand side operator of the parabolic semidiscretization `semi` +at time `t0` as an affine-linear operator given by a linear operator `A` +and a vector `b`: +```math +\\partial_t u(t) = A u(t) - b. +``` +Works only for equations for which `have_constant_diffusivity(equations) == True()`. +As in [`linear_structure(semi::AbstractSemidiscretization)`](@ref), returns `A` and `b`, +with `A` represented as a matrix-free `LinearMap`. +""" function linear_structure(semi::SemidiscretizationParabolic; t0 = zero(real(semi))) if have_constant_diffusivity(semi.equations) == False() From 1ffcff2e19ee3ac3459792fbe0ba1afe52da4c49 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 29 Mar 2026 23:20:09 -0400 Subject: [PATCH 52/68] add note about non-public API --- .../tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index ff80fb039ac..26a7887be22 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -62,6 +62,9 @@ stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) # specify extra node variables to be saved in the `SaveSolutionCallback` extra_node_variables = (:dudx,) + +# note that `get_node_variable` requires the use of Trixi.jl internals that are not part +# of the public API, so this specific usage is not guaranteed to be stable across releases function Trixi.get_node_variable(::Val{:dudx}, u, mesh, equations, dg, cache, cache_parabolic) return copy(@view cache_parabolic.parabolic_container.gradients[1, :, :]) From cf28a3373e5b0823d9e5776788712fc0b525373b Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 29 Mar 2026 23:41:27 -0400 Subject: [PATCH 53/68] clarify non-public API note --- .../tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index 26a7887be22..f5fff3bc00e 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -63,8 +63,9 @@ stepsize_callback = StepsizeCallback(cfl_parabolic = 0.05) # specify extra node variables to be saved in the `SaveSolutionCallback` extra_node_variables = (:dudx,) -# note that `get_node_variable` requires the use of Trixi.jl internals that are not part -# of the public API, so this specific usage is not guaranteed to be stable across releases +# note that using `get_node_variable` to access the gradient exposes Trixi.jl internals +# that are not part of the public API, so this usage is not guaranteed to be stable across +# releases function Trixi.get_node_variable(::Val{:dudx}, u, mesh, equations, dg, cache, cache_parabolic) return copy(@view cache_parabolic.parabolic_container.gradients[1, :, :]) From 4bb1888a959c53fa6970ae28f096937cfeea5cb1 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 31 Mar 2026 15:03:21 -0400 Subject: [PATCH 54/68] Update NEWS.md --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index fc572df5bf1..52875fd62f6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,8 @@ for human readability. ## Changes in the v0.16 lifecycle #### Added +- A new semidiscretization type `SemidiscretizationParabolic` has been added to support purely parabolic equations with no hyperbolic part. +The new equation types `LinearDiffusionEquation1D` and `LinearDiffusionEquation2D` have been implemented to demonstrate this functionality. - GPU support extended to include AMD GPU with a buildkite workflow using `TRIXI_TEST=AMDGPU` ([#2834]). ## Changes when updating to v0.16 from v0.15.x From 377889a3f7d6f402fc7bc264678e18b763c1550c Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Tue, 31 Mar 2026 16:48:33 -0400 Subject: [PATCH 55/68] Add PR reference to NEWS.md --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 52875fd62f6..c2791fc3e67 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ for human readability. #### Added - A new semidiscretization type `SemidiscretizationParabolic` has been added to support purely parabolic equations with no hyperbolic part. -The new equation types `LinearDiffusionEquation1D` and `LinearDiffusionEquation2D` have been implemented to demonstrate this functionality. +The new equation types `LinearDiffusionEquation1D` and `LinearDiffusionEquation2D` have been implemented to demonstrate this functionality ([#2874]). - GPU support extended to include AMD GPU with a buildkite workflow using `TRIXI_TEST=AMDGPU` ([#2834]). ## Changes when updating to v0.16 from v0.15.x From ab926a5b4023731785d8893be1b09b024d6701cd Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 2 Apr 2026 10:26:02 -0400 Subject: [PATCH 56/68] Update examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl Co-authored-by: Hendrik Ranocha --- .../elixir_diffusion_ldg_amr_boundary_layer.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl index f5fff3bc00e..3fa9683d7c8 100644 --- a/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl +++ b/examples/tree_1d_dgsem/elixir_diffusion_ldg_amr_boundary_layer.jl @@ -5,11 +5,11 @@ using Trixi # Adaptive semidiscretization of the pure diffusion equation with mixed # Dirichlet-Neumann BCs and an initial condition with a boundary layer. -diffusivity = 0.25 -amplitude = 1.0 -boundary_layer_thickness = 0.01 +diffusivity() = 0.25 +amplitude() = 1.0 +boundary_layer_thickness() = 0.01 -equations = LinearDiffusionEquation1D(diffusivity) +equations = LinearDiffusionEquation1D(diffusivity()) solver = DGSEM(polydeg = 3) solver_parabolic = ParabolicFormulationLocalDG() @@ -20,8 +20,8 @@ mesh = TreeMesh((0.0,), (1.0,), n_cells_max = 30_000) function initial_condition_boundary_layer(x, t, equations) - return SVector(amplitude * (1 - exp(-x[1] / boundary_layer_thickness)) / - (1 - exp(-1 / boundary_layer_thickness))) + return SVector(amplitude() * (1 - exp(-x[1] / boundary_layer_thickness())) / + (1 - exp(-1 / boundary_layer_thickness()))) end initial_condition = initial_condition_boundary_layer From f39f03cb51d1e968b517cfc1149ae10f99b78b3e Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 3 Apr 2026 18:55:36 -0400 Subject: [PATCH 57/68] update docstrings for LaplaceDiffusion1D/2D --- src/equations/laplace_diffusion_1d.jl | 4 ++++ src/equations/laplace_diffusion_2d.jl | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/equations/laplace_diffusion_1d.jl b/src/equations/laplace_diffusion_1d.jl index c340c673a2e..aed45016459 100644 --- a/src/equations/laplace_diffusion_1d.jl +++ b/src/equations/laplace_diffusion_1d.jl @@ -3,6 +3,10 @@ `LaplaceDiffusion1D` represents a scalar diffusion term ``\nabla \cdot (\kappa\nabla u))`` with diffusivity ``\kappa`` applied to each solution component defined by `equations`. +This is intended for use as the parabolic part of a hyperbolic-parabolic partial +differential equation, where the hyperbolic part is defined by `equations`. For a purely +parabolic diffusion equation without any hyperbolic part, see +[`LinearDiffusionEquation1D`](@ref). """ struct LaplaceDiffusion1D{E, N, T} <: AbstractLaplaceDiffusion{1, N} diffusivity::T diff --git a/src/equations/laplace_diffusion_2d.jl b/src/equations/laplace_diffusion_2d.jl index c243f3de364..a740cbda31b 100644 --- a/src/equations/laplace_diffusion_2d.jl +++ b/src/equations/laplace_diffusion_2d.jl @@ -3,6 +3,9 @@ `LaplaceDiffusion2D` represents a scalar diffusion term ``\nabla \cdot (\kappa\nabla u))`` with diffusivity ``\kappa`` applied to each solution component defined by `equations`. +This is intended for use as the parabolic part of a hyperbolic-parabolic system, where the +hyperbolic part is defined by `equations`. For a purely parabolic diffusion equation +without any hyperbolic part, see [`LinearDiffusionEquation2D`](@ref). """ struct LaplaceDiffusion2D{E, N, T} <: AbstractLaplaceDiffusion{2, N} diffusivity::T From 8b52028a488b156411895aa2fe307c138c05bd6f Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 3 Apr 2026 18:58:26 -0400 Subject: [PATCH 58/68] separate bullet point in README.md for diffusion --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c59276106c0..9d929b4969c 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ installation and postprocessing procedures. Its features include: * Hyperbolic diffusion equations for elliptic problems * Lattice-Boltzmann equations (D2Q9 and D3Q27 schemes) * Shallow water equations via [TrixiShallowWater.jl](https://github.com/trixi-framework/TrixiShallowWater.jl) - * Several scalar conservation laws (e.g., linear advection, diffusion/heat equation, Burgers' equation, LWR traffic flow) + * Several scalar conservation laws (e.g., linear advection, Burgers' equation, LWR traffic flow) + * Linear diffusion/heat equation * Multi-physics simulations * [Self-gravitating gas dynamics](https://github.com/trixi-framework/paper-self-gravitating-gas-dynamics) * Shared-memory parallelization via multithreading From d8ca7ebbe401a1c4b159892c809a77c4aec84173 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 3 Apr 2026 19:05:23 -0400 Subject: [PATCH 59/68] make laplace_diffusion 1d docstring consistent with 2d --- src/equations/laplace_diffusion_1d.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/equations/laplace_diffusion_1d.jl b/src/equations/laplace_diffusion_1d.jl index aed45016459..007d080097d 100644 --- a/src/equations/laplace_diffusion_1d.jl +++ b/src/equations/laplace_diffusion_1d.jl @@ -3,10 +3,9 @@ `LaplaceDiffusion1D` represents a scalar diffusion term ``\nabla \cdot (\kappa\nabla u))`` with diffusivity ``\kappa`` applied to each solution component defined by `equations`. -This is intended for use as the parabolic part of a hyperbolic-parabolic partial -differential equation, where the hyperbolic part is defined by `equations`. For a purely -parabolic diffusion equation without any hyperbolic part, see -[`LinearDiffusionEquation1D`](@ref). +This is intended for use as the parabolic part of a hyperbolic-parabolic system, where the +hyperbolic part is defined by `equations`. For a purely parabolic diffusion equation +without any hyperbolic part, see [`LinearDiffusionEquation1D`](@ref). """ struct LaplaceDiffusion1D{E, N, T} <: AbstractLaplaceDiffusion{1, N} diffusivity::T From ce750f546b81b52474764cb1c162b50748b1c081 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 5 Apr 2026 22:39:35 -0400 Subject: [PATCH 60/68] dispatch rhs! to rhs_parabolic --- src/semidiscretization/semidiscretization_parabolic.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 967288ec331..a498d3e2156 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -208,7 +208,7 @@ function linear_structure(semi::SemidiscretizationParabolic; return _linear_structure_from_rhs(semi, apply_rhs!) end -function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) +function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi u = wrap_array(u_ode, mesh, equations, solver, cache) @@ -224,4 +224,8 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) return nothing end + +@inline function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) + return rhs_parabolic!(du_ode, u_ode, semi, t) +end end # @muladd From 45e8a4e9ee2b432defb6545b136903ccfb610420 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Sun, 5 Apr 2026 22:46:00 -0400 Subject: [PATCH 61/68] update docs to include heat equation --- docs/src/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/index.md b/docs/src/index.md index 4792e97dc57..1a047c23fb4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -59,6 +59,7 @@ installation and postprocessing procedures. Its features include: * Lattice-Boltzmann equations (D2Q9 and D3Q27 schemes) * Shallow water equations via [TrixiShallowWater.jl](https://github.com/trixi-framework/TrixiShallowWater.jl) * Several scalar conservation laws (e.g., linear advection, Burgers' equation, LWR traffic flow) + * Linear diffusion/heat equation * Multi-physics simulations * [Self-gravitating gas dynamics](https://github.com/trixi-framework/paper-self-gravitating-gas-dynamics) * Shared-memory parallelization via multithreading From c1f8d155d627b9b86c980dc41f86203cf1f52d0a Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Mon, 6 Apr 2026 00:34:06 -0400 Subject: [PATCH 62/68] add proper dispatch using default_rhs(semi) --- src/semidiscretization/semidiscretization.jl | 44 ++++++++++++------- .../semidiscretization_parabolic.jl | 20 +++++---- test/test_parabolic_1d.jl | 8 ++-- test/test_parabolic_2d.jl | 2 +- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index 4f77943287f..97dbe524fb1 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -65,6 +65,9 @@ function integrate(u_ode, semi::AbstractSemidiscretization; normalize = true) return integrate(cons2cons, u_ode, semi; normalize = normalize) end +# Select the right-hand side function corresponding to the semidiscretization `semi`. +@inline default_rhs(::AbstractSemidiscretization) = rhs! + """ calc_error_norms([func=(u_node,equations)->u_node,] u_ode, t, analyzer, semi::AbstractSemidiscretization, cache_analysis) @@ -123,18 +126,19 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan; end u0_ode = compute_coefficients(first(tspan), semi) # Invoke initial condition + rhs_semi! = default_rhs(semi) # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using # mpi_isparallel() && MPI.Barrier(mpi_comm()) # See https://github.com/trixi-framework/Trixi.jl/issues/328 - iip = true # is-inplace, i.e., we modify a vector when calling rhs! - specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + iip = true # is-inplace, i.e., we modify a vector when calling `rhs_semi!` + specialize = SciMLBase.FullSpecialize # specialize on `rhs_semi!` and parameters (semi) # Check if Jacobian prototype is provided for sparse Jacobian if jac_prototype !== nothing # Convert `jac_prototype` to real type, as seen here: # https://docs.sciml.ai/DiffEqDocs/stable/tutorials/advanced_ode_example/#Declaring-a-Sparse-Jacobian-with-Automatic-Sparsity-Detection - ode = SciMLBase.ODEFunction(rhs!, + ode = SciMLBase.ODEFunction(rhs_semi!, jac_prototype = convert.(eltype(u0_ode), jac_prototype), colorvec = colorvec) # coloring vector is optional @@ -142,9 +146,9 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan; return ODEProblem{iip, specialize}(ode, u0_ode, tspan, semi) else # We could also construct an `ODEFunction` explicitly without the Jacobian here, - # but we stick to the lean direct in-place function `rhs!` and + # but we stick to the lean direct in-place function `rhs_semi!` and # let OrdinaryDiffEq.jl handle the rest - return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) + return ODEProblem{iip, specialize}(rhs_semi!, u0_ode, tspan, semi) end end @@ -177,18 +181,19 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan, end u0_ode = load_restart_file(semi, restart_file) # Load initial condition from restart file + rhs_semi! = default_rhs(semi) # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using # mpi_isparallel() && MPI.Barrier(mpi_comm()) # See https://github.com/trixi-framework/Trixi.jl/issues/328 - iip = true # is-inplace, i.e., we modify a vector when calling rhs! - specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + iip = true # is-inplace, i.e., we modify a vector when calling `rhs_semi!` + specialize = SciMLBase.FullSpecialize # specialize on `rhs_semi!` and parameters (semi) # Check if Jacobian prototype is provided for sparse Jacobian if jac_prototype !== nothing # Convert `jac_prototype` to real type, as seen here: # https://docs.sciml.ai/DiffEqDocs/stable/tutorials/advanced_ode_example/#Declaring-a-Sparse-Jacobian-with-Automatic-Sparsity-Detection - ode = SciMLBase.ODEFunction(rhs!, + ode = SciMLBase.ODEFunction(rhs_semi!, jac_prototype = convert.(eltype(u0_ode), jac_prototype), colorvec = colorvec) # coloring vector is optional @@ -196,9 +201,9 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan, return ODEProblem{iip, specialize}(ode, u0_ode, tspan, semi) else # We could also construct an `ODEFunction` explicitly without the Jacobian here, - # but we stick to the lean direct in-place function `rhs!` and + # but we stick to the lean direct in-place function `rhs_semi!` and # let OrdinaryDiffEq.jl handle the rest - return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) + return ODEProblem{iip, specialize}(rhs_semi!, u0_ode, tspan, semi) end end @@ -236,7 +241,8 @@ function compute_coefficients!(u_ode, func, t, semi::AbstractSemidiscretization) mesh_equations_solver_cache(semi)...) end -# Helper function to compute linear structure for a given semidiscretization and `rhs!` +# Helper function to compute linear structure for a given semidiscretization +# `semi` and applied RHS `apply_rhs!` function _linear_structure_from_rhs(semi::AbstractSemidiscretization, apply_rhs!) # allocate memory u_ode = allocate_coefficients(mesh_equations_solver_cache(semi)...) @@ -320,9 +326,10 @@ function jacobian_fd(semi::AbstractSemidiscretization; du0_ode = similar(u_ode) dup_ode = similar(u_ode) dum_ode = similar(u_ode) + rhs_semi! = default_rhs(semi) # compute residual of linearization state - rhs!(du0_ode, u_ode, semi, t0) + rhs_semi!(du0_ode, u_ode, semi, t0) # initialize Jacobian matrix J = zeros(eltype(u_ode), length(u_ode), length(u_ode)) @@ -340,11 +347,11 @@ function jacobian_fd(semi::AbstractSemidiscretization; # plus fluctuation u_ode[idx] = u0_ode[idx] + epsilon - rhs!(dup_ode, u_ode, semi, t0) + rhs_semi!(dup_ode, u_ode, semi, t0) # minus fluctuation u_ode[idx] = u0_ode[idx] - epsilon - rhs!(dum_ode, u_ode, semi, t0) + rhs_semi!(dum_ode, u_ode, semi, t0) # restore linearization state u_ode[idx] = u0_ode[idx] @@ -383,11 +390,12 @@ end function _jacobian_ad_forward(semi, t0, u0_ode, du_ode, config) new_semi = remake(semi, uEltype = eltype(config)) + rhs_semi! = default_rhs(new_semi) # Create anonymous function passed as first argument to `ForwardDiff.jacobian` to match # `ForwardDiff.jacobian(f!, y::AbstractArray, x::AbstractArray, # cfg::JacobianConfig = JacobianConfig(f!, y, x), check=Val{true}())` J = ForwardDiff.jacobian(du_ode, u0_ode, config) do du_ode, u_ode - return Trixi.rhs!(du_ode, u_ode, new_semi, t0) + return rhs_semi!(du_ode, u_ode, new_semi, t0) end return J @@ -418,6 +426,7 @@ end function _jacobian_ad_forward_structarrays(semi, t0, u0_ode_plain, du_ode_plain, config) new_semi = remake(semi, uEltype = eltype(config)) + rhs_semi! = default_rhs(new_semi) # Create anonymous function passed as first argument to `ForwardDiff.jacobian` to match # `ForwardDiff.jacobian(f!, y::AbstractArray, x::AbstractArray, # cfg::JacobianConfig = JacobianConfig(f!, y, x), check=Val{true}())` @@ -433,7 +442,7 @@ function _jacobian_ad_forward_structarrays(semi, t0, u0_ode_plain, du_ode_plain, :, v), nvariables(semi))) - return Trixi.rhs!(du_ode, u_ode, new_semi, t0) + return rhs_semi!(du_ode, u_ode, new_semi, t0) end return J @@ -456,11 +465,12 @@ end function _jacobian_ad_forward_staticarrays(semi, t0, u0_ode_plain, du_ode_plain, config) new_semi = remake(semi, uEltype = eltype(config)) + rhs_semi! = default_rhs(new_semi) J = ForwardDiff.jacobian(du_ode_plain, u0_ode_plain, config) do du_ode_plain, u_ode_plain u_ode = reinterpret(SVector{nvariables(semi), eltype(config)}, u_ode_plain) du_ode = reinterpret(SVector{nvariables(semi), eltype(config)}, du_ode_plain) - return Trixi.rhs!(du_ode, u_ode, new_semi, t0) + return rhs_semi!(du_ode, u_ode, new_semi, t0) end return J diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index a498d3e2156..7913a7d69cf 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -202,12 +202,16 @@ function linear_structure(semi::SemidiscretizationParabolic; end apply_rhs! = function (dest, src) - return rhs!(dest, src, semi, t0) + return rhs_parabolic!(dest, src, semi, t0) end return _linear_structure_from_rhs(semi, apply_rhs!) end +# For a purely parabolic semidiscretization, the right-hand side is `rhs_parabolic!` +# instead of the default `rhs!`. +@inline default_rhs(::SemidiscretizationParabolic) = rhs_parabolic! + function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) @unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi @@ -215,17 +219,15 @@ function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) du = wrap_array(du_ode, mesh, equations, solver, cache) time_start = time_ns() - @trixi_timeit timer() "rhs!" rhs_parabolic!(du, u, t, mesh, equations, - boundary_conditions, source_terms, - solver, solver_parabolic, cache, - cache_parabolic) + @trixi_timeit timer() "rhs_parabolic!" rhs_parabolic!(du, u, t, mesh, equations, + boundary_conditions, + source_terms, + solver, solver_parabolic, + cache, + cache_parabolic) runtime = time_ns() - time_start put!(semi.performance_counter, runtime) return nothing end - -@inline function rhs!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) - return rhs_parabolic!(du_ode, u_ode, semi, t) -end end # @muladd diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 57925284c01..40b26960ba0 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -514,7 +514,7 @@ end l2=[9.235894939144276e-6], linf=[5.402550135213957e-5]) # 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 "TreeMesh1D: elixir_diffusion_ldg_newton_krylov.jl" begin @@ -525,7 +525,7 @@ end l2=[4.14999791227157e-6], linf=[2.424658336047658e-5]) # 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 "TreeMesh1D: elixir_diffusion_ldg_amr_boundary_layer.jl" begin @@ -534,7 +534,7 @@ end l2=[0.5881457102264551], linf=[0.9302621795999283]) # 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) # Test `show` method not exercised in elixirs @trixi_test_nowarn show(IOContext(stdout, :compact => true), MIME"text/plain"(), @@ -566,7 +566,7 @@ end l2=[2.3481439150004898e-6], linf=[2.4576876189230656e-5]) # 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) # Store reference solution for comparison reference_solution = copy(sol.u[end]) diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 6d830856a27..16bdc57a157 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -1372,7 +1372,7 @@ end atol=1e-9) # 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 end From 27c638b2aa4f5040ad7c12166f3434c713044db0 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Mon, 6 Apr 2026 01:23:41 -0400 Subject: [PATCH 63/68] make timer output consistent with other use of parabolic rhs --- src/semidiscretization/semidiscretization_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 7913a7d69cf..715db0a112f 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -219,7 +219,7 @@ function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) du = wrap_array(du_ode, mesh, equations, solver, cache) time_start = time_ns() - @trixi_timeit timer() "rhs_parabolic!" rhs_parabolic!(du, u, t, mesh, equations, + @trixi_timeit timer() "parabolic rhs!" rhs_parabolic!(du, u, t, mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, From 5a416d3860813122cebf6d65630fc32fcef10396 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Mon, 6 Apr 2026 01:26:26 -0400 Subject: [PATCH 64/68] use @trixi_timeit_ext backend --- .../semidiscretization_parabolic.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/semidiscretization/semidiscretization_parabolic.jl b/src/semidiscretization/semidiscretization_parabolic.jl index 715db0a112f..4c663c70381 100644 --- a/src/semidiscretization/semidiscretization_parabolic.jl +++ b/src/semidiscretization/semidiscretization_parabolic.jl @@ -217,14 +217,17 @@ function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationParabolic, t) u = wrap_array(u_ode, mesh, equations, solver, cache) du = wrap_array(du_ode, mesh, equations, solver, cache) + backend = trixi_backend(u) time_start = time_ns() - @trixi_timeit timer() "parabolic rhs!" rhs_parabolic!(du, u, t, mesh, equations, - boundary_conditions, - source_terms, - solver, solver_parabolic, - cache, - cache_parabolic) + @trixi_timeit_ext backend timer() "parabolic rhs!" rhs_parabolic!(du, u, t, mesh, + equations, + boundary_conditions, + source_terms, + solver, + solver_parabolic, + cache, + cache_parabolic) runtime = time_ns() - time_start put!(semi.performance_counter, runtime) From 0ecb3b5c591c180247d26789d063ef38a4b42ab5 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 8 Apr 2026 12:39:02 -0400 Subject: [PATCH 65/68] revert test values --- test/test_parabolic_1d.jl | 2 +- test/test_parabolic_2d.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 40b26960ba0..4d1961dd0d8 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -522,7 +522,7 @@ end "elixir_diffusion_ldg_newton_krylov.jl"), atol_lin_solve=1e-11, rtol_lin_solve=1e-10, atol_ode_solve=1e-10, rtol_ode_solve=1e-9, - l2=[4.14999791227157e-6], linf=[2.424658336047658e-5]) + l2=[4.14999791227157e-6], linf=[2.424658410971059e-5]) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 16bdc57a157..ac99445476e 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -1367,9 +1367,9 @@ end extra_analysis_errors = (:l2_error_primitive, :linf_error_primitive), extra_analysis_integrals = (entropy,)), - l2=[2.9029783114293355e-5], linf=[0.00030225018027482675], + l2=[2.9029827892716424e-5], linf=[0.0003022506331279151], # Relax error tols to avoid stochastic CI failures - atol=1e-9) + atol=1e-10) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) From 4e4da24891f3458ccfaf0a1879a525ffdda404c4 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 8 Apr 2026 16:06:48 -0400 Subject: [PATCH 66/68] slightly relax tol on elixir_diffusion_ldg_newton_krylov --- test/test_parabolic_1d.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 4d1961dd0d8..f16cd664326 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -522,7 +522,9 @@ end "elixir_diffusion_ldg_newton_krylov.jl"), atol_lin_solve=1e-11, rtol_lin_solve=1e-10, atol_ode_solve=1e-10, rtol_ode_solve=1e-9, - l2=[4.14999791227157e-6], linf=[2.424658410971059e-5]) + l2=[4.14999791227157e-6], linf=[2.424658410971059e-5], + # Relax error tols to avoid stochastic CI failures + atol=1e-12) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) @test_allocations(Trixi.rhs_parabolic!, semi, sol, 1000) From 9f483c404ce4b0e7ed94898226a084d175bced25 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Wed, 8 Apr 2026 16:14:05 -0400 Subject: [PATCH 67/68] add intermediate linear_diffusion_equation.jl file --- src/equations/equations_parabolic.jl | 4 ++-- src/equations/linear_diffusion_equation.jl | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 src/equations/linear_diffusion_equation.jl diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl index de2f7b28a69..f57ed103247 100644 --- a/src/equations/equations_parabolic.jl +++ b/src/equations/equations_parabolic.jl @@ -8,9 +8,9 @@ gradient_variable_transformation(::AbstractEquationsParabolic) = cons2cons struct GradientVariablesConservative end include("laplace_diffusion.jl") -include("linear_diffusion_equation_1d.jl") -include("linear_diffusion_equation_2d.jl") include("laplace_diffusion_entropy_variables.jl") +include("linear_diffusion_equation.jl") + include("compressible_navier_stokes.jl") diff --git a/src/equations/linear_diffusion_equation.jl b/src/equations/linear_diffusion_equation.jl new file mode 100644 index 00000000000..41550c3628c --- /dev/null +++ b/src/equations/linear_diffusion_equation.jl @@ -0,0 +1,2 @@ +include("linear_diffusion_equation_1d.jl") +include("linear_diffusion_equation_2d.jl") From b5107df96821b00101d31cf86d71939f3efac46d Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Thu, 9 Apr 2026 13:25:01 -0400 Subject: [PATCH 68/68] retrigger CI