diff --git a/.gitignore b/.gitignore index ffeb3567..9b8f968b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ out*/ run run/* **/*.code-workspace +.vscode/ diff --git a/NEWS.md b/NEWS.md index b816ac38..6a09fae1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,11 @@ used in the Julia ecosystem. Notable changes will be documented in this file for human readability. +## Changes in the v0.4 lifecycle + +#### Added +- Add `BBMEquation1D` ([#150]). + ## Changes when updating to v0.5 from v0.4.x #### Changed diff --git a/README.md b/README.md index c0327188..a8e5762e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ **DispersiveShallowWater.jl** is a [Julia](https://julialang.org/) package that implements structure-preserving numerical methods for dispersive shallow water models. To date, it provides provably conservative, entropy-conserving and well-balanced numerical schemes for some dispersive shallow water models: +* the [Benjamin-Bona-Mahony (BBM) equation, also known as regularized long-wave equation](https://doi.org/10.4208/cicp.OA-2020-0119), * the [BBM-BBM equations with varying bottom topography](https://iopscience.iop.org/article/10.1088/1361-6544/ac3c29), * the [dispersive shallow water model proposed by Magnus Svärd and Henrik Kalisch](https://arxiv.org/abs/2302.09924), * the [Serre-Green-Naghdi equations](https://arxiv.org/abs/2408.02665). diff --git a/docs/src/index.md b/docs/src/index.md index c7a1c8ab..9fd925ed 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -12,6 +12,7 @@ [**DispersiveShallowWater.jl**](https://github.com/JoshuaLampert/DispersiveShallowWater.jl) is a [Julia](https://julialang.org/) package that implements structure-preserving numerical methods for dispersive shallow water models. To date, it provides provably conservative, entropy-conserving and well-balanced numerical schemes for some dispersive shallow water models: +* the [Benjamin-Bona-Mahony (BBM) equation, also known as regularized long-wave equation](https://doi.org/10.4208/cicp.OA-2020-0119), * the [BBM-BBM equations with varying bottom topography](https://iopscience.iop.org/article/10.1088/1361-6544/ac3c29), * the [dispersive shallow water model proposed by Magnus Svärd and Henrik Kalisch](https://arxiv.org/abs/2302.09924), * the [Serre-Green-Naghdi equations](https://arxiv.org/abs/2408.02665). diff --git a/examples/bbm_1d/bbm_1d_basic.jl b/examples/bbm_1d/bbm_1d_basic.jl new file mode 100644 index 00000000..5c871351 --- /dev/null +++ b/examples/bbm_1d/bbm_1d_basic.jl @@ -0,0 +1,40 @@ +using OrdinaryDiffEq +using DispersiveShallowWater + +############################################################################### +# Semidiscretization of the BBM equation (conserves the quadratic invariant) + +equations = BBMEquation1D(gravity_constant = 9.81, split_form = true) + +initial_condition = initial_condition_convergence_test +boundary_conditions = boundary_condition_periodic + +# create homogeneous mesh +coordinates_min = -90.0 +coordinates_max = 90.0 +N = 512 +mesh = Mesh1D(coordinates_min, coordinates_max, N) + +# create solver with periodic SBP operators of accuracy order 4 +accuracy_order = 4 +solver = Solver(mesh, accuracy_order) + +# semidiscretization holds all the necessary data structures for the spatial discretization +semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# Create `ODEProblem` and run the simulation +tspan = (0.0, 1000.0) +ode = semidiscretize(semi, tspan) +summary_callback = SummaryCallback() +analysis_callback = AnalysisCallback(semi; interval = 10, + extra_analysis_errors = (:conservation_error,), + extra_analysis_integrals = (waterheight_total, + entropy_modified, + hamiltonian)) +callbacks = CallbackSet(analysis_callback, summary_callback) + +saveat = range(tspan..., length = 100) +sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) diff --git a/examples/bbm_1d/bbm_1d_fourier.jl b/examples/bbm_1d/bbm_1d_fourier.jl new file mode 100644 index 00000000..58082d34 --- /dev/null +++ b/examples/bbm_1d/bbm_1d_fourier.jl @@ -0,0 +1,42 @@ +using OrdinaryDiffEq +using DispersiveShallowWater +using SummationByPartsOperators: fourier_derivative_operator + +############################################################################### +# Semidiscretization of the BBM equation + +equations = BBMEquation1D(gravity_constant = 9.81) + +initial_condition = initial_condition_convergence_test +boundary_conditions = boundary_condition_periodic + +# create homogeneous mesh +coordinates_min = -90.0 +coordinates_max = 90.0 +N = 512 +mesh = Mesh1D(coordinates_min, coordinates_max, N) + +# create solver with Fourier SBP operators +D1 = fourier_derivative_operator(xmin(mesh), xmax(mesh), nnodes(mesh)) +D2 = D1^2 +solver = Solver(D1, D2) + +# semidiscretization holds all the necessary data structures for the spatial discretization +semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# Create `ODEProblem` and run the simulation +tspan = (0.0, 1000.0) +ode = semidiscretize(semi, tspan) +summary_callback = SummaryCallback() +analysis_callback = AnalysisCallback(semi; interval = 10, + extra_analysis_errors = (:conservation_error,), + extra_analysis_integrals = (waterheight_total, + entropy_modified, + hamiltonian)) +callbacks = CallbackSet(analysis_callback, summary_callback) + +saveat = range(tspan..., length = 100) +sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) diff --git a/examples/bbm_1d/bbm_1d_hamiltonian_relaxation.jl b/examples/bbm_1d/bbm_1d_hamiltonian_relaxation.jl new file mode 100644 index 00000000..ccfabe0d --- /dev/null +++ b/examples/bbm_1d/bbm_1d_hamiltonian_relaxation.jl @@ -0,0 +1,43 @@ +using OrdinaryDiffEq +using DispersiveShallowWater + +############################################################################### +# Semidiscretization of the BBM equation (conserves the cubic Hamiltonian) + +equations = BBMEquation1D(gravity_constant = 9.81, split_form = false) + +initial_condition = initial_condition_convergence_test +boundary_conditions = boundary_condition_periodic + +# create homogeneous mesh +coordinates_min = -90.0 +coordinates_max = 90.0 +N = 512 +mesh = Mesh1D(coordinates_min, coordinates_max, N) + +# create solver with periodic SBP operators of accuracy order 4 +accuracy_order = 4 +solver = Solver(mesh, accuracy_order) + +# semidiscretization holds all the necessary data structures for the spatial discretization +semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# Create `ODEProblem` and run the simulation +tspan = (0.0, 1000.0) +ode = semidiscretize(semi, tspan) +summary_callback = SummaryCallback() +analysis_callback = AnalysisCallback(semi; interval = 10, + extra_analysis_errors = (:conservation_error,), + extra_analysis_integrals = (waterheight_total, + entropy_modified, + hamiltonian)) + +relaxation_callback = RelaxationCallback(invariant = hamiltonian) +# Always put relaxation_callback before analysis_callback to guarantee conservation of the invariant +callbacks = CallbackSet(relaxation_callback, analysis_callback, summary_callback) + +saveat = range(tspan..., length = 100) +sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) diff --git a/examples/bbm_1d/bbm_1d_manufactured.jl b/examples/bbm_1d/bbm_1d_manufactured.jl new file mode 100644 index 00000000..a738a8cd --- /dev/null +++ b/examples/bbm_1d/bbm_1d_manufactured.jl @@ -0,0 +1,40 @@ +using OrdinaryDiffEq +using DispersiveShallowWater + +############################################################################### +# Semidiscretization of the BBM equation + +equations = BBMEquation1D(gravity_constant = 9.81, D = 2.0) + +initial_condition = initial_condition_manufactured +source_terms = source_terms_manufactured +boundary_conditions = boundary_condition_periodic + +# create homogeneous mesh +coordinates_min = -1.0 +coordinates_max = 1.0 +N = 512 +mesh = Mesh1D(coordinates_min, coordinates_max, N) + +# create solver with periodic SBP operators of accuracy order 4 +accuracy_order = 4 +solver = Solver(mesh, accuracy_order) + +# semidiscretization holds all the necessary data structures for the spatial discretization +semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions, + source_terms = source_terms) + +############################################################################### +# Create `ODEProblem` and run the simulation +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) +summary_callback = SummaryCallback() +analysis_callback = AnalysisCallback(semi; interval = 10, + extra_analysis_errors = (:conservation_error,), + extra_analysis_integrals = (waterheight_total,)) +callbacks = CallbackSet(analysis_callback, summary_callback) + +saveat = range(tspan..., length = 100) +sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) diff --git a/examples/bbm_1d/bbm_1d_relaxation.jl b/examples/bbm_1d/bbm_1d_relaxation.jl new file mode 100644 index 00000000..706b9788 --- /dev/null +++ b/examples/bbm_1d/bbm_1d_relaxation.jl @@ -0,0 +1,43 @@ +using OrdinaryDiffEq +using DispersiveShallowWater + +############################################################################### +# Semidiscretization of the BBM equation + +equations = BBMEquation1D(gravity_constant = 9.81) + +initial_condition = initial_condition_convergence_test +boundary_conditions = boundary_condition_periodic + +# create homogeneous mesh +coordinates_min = -90.0 +coordinates_max = 90.0 +N = 512 +mesh = Mesh1D(coordinates_min, coordinates_max, N) + +# create solver with periodic SBP operators of accuracy order 4 +accuracy_order = 4 +solver = Solver(mesh, accuracy_order) + +# semidiscretization holds all the necessary data structures for the spatial discretization +semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# Create `ODEProblem` and run the simulation +tspan = (0.0, 1000.0) +ode = semidiscretize(semi, tspan) +summary_callback = SummaryCallback() +analysis_callback = AnalysisCallback(semi; interval = 10, + extra_analysis_errors = (:conservation_error,), + extra_analysis_integrals = (waterheight_total, + entropy_modified, + hamiltonian)) + +relaxation_callback = RelaxationCallback(invariant = entropy_modified) +# Always put relaxation_callback before analysis_callback to guarantee conservation of the invariant +callbacks = CallbackSet(relaxation_callback, analysis_callback, summary_callback) + +saveat = range(tspan..., length = 100) +sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) diff --git a/src/DispersiveShallowWater.jl b/src/DispersiveShallowWater.jl index 3e2ef6a4..54633bf0 100644 --- a/src/DispersiveShallowWater.jl +++ b/src/DispersiveShallowWater.jl @@ -61,7 +61,7 @@ include("util.jl") export examples_dir, get_examples, default_example, convergence_test export AbstractShallowWaterEquations, - BBMBBMEquations1D, + BBMEquation1D, BBMBBMEquations1D, SvärdKalischEquations1D, SvaerdKalischEquations1D, SerreGreenNaghdiEquations1D, HyperbolicSerreGreenNaghdiEquations1D @@ -71,7 +71,8 @@ export prim2prim, prim2cons, cons2prim, prim2phys, gravity_constant, bathymetry, still_water_surface, energy_total, entropy, lake_at_rest_error, - energy_total_modified, entropy_modified + energy_total_modified, entropy_modified, + hamiltonian export Mesh1D, xmin, xmax, nnodes diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index c0eee27a..d0bd1432 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -339,3 +339,4 @@ pretty_form_utf(::typeof(energy_total)) = "∫e_total" pretty_form_utf(::typeof(entropy_modified)) = "∫U_modified" pretty_form_utf(::typeof(energy_total_modified)) = "∫e_modified" pretty_form_utf(::typeof(lake_at_rest_error)) = "∫|η-η₀|" +pretty_form_utf(::typeof(hamiltonian)) = "∫H" diff --git a/src/equations/bbm_1d.jl b/src/equations/bbm_1d.jl new file mode 100644 index 00000000..f23b8a09 --- /dev/null +++ b/src/equations/bbm_1d.jl @@ -0,0 +1,245 @@ +@doc raw""" + BBMEquation1D(; gravity_constant, D = 1.0, eta0 = 0.0, split_form = true) + +BBM (Benjamin–Bona–Mahony) equation in one spatial dimension. +The equation is given by +```math +\begin{aligned} + \eta_t + \sqrt{gD}\eta_x + \frac{3}{2}\sqrt{\frac{g}{D}}\eta\eta_x - \frac{1}{6}D^2\eta_{xxt} &= 0. +\end{aligned} +``` + +The unknown quantity of the BBM equation is the total water height ``\eta``. +The gravitational constant is denoted by `g` and the constant bottom topography (bathymetry) ``b = \eta_0 - D``, +where ``\eta_0`` is the constant still-water surface and ``D`` the still-water depth. The water height above +the bathymetry is therefore given by ``h = \eta - \eta_0 + D``. The BBM equation is only implemented for ``\eta_0 = 0``. + +The equations only support a flat bathymetry. + +The BBM equation is first described in Benjamin, Bona, and Mahony (1972). +The semidiscretization implemented here is developed in Ranocha, Mitsotakis, and Ketcheson (2020) +for `split_form = true` and in Linders, Ranocha, and Birken (2023) for `split_form = false`. +If `split_form` is `true`, a split form in the semidiscretization is used, which conserves +- the total water mass (integral of ``h``) as a linear invariant +- a quadratic invariant (integral of ``1/2\eta(\eta - 1/6D^2\eta_{xx})`` or for periodic boundary conditions + equivalently ``1/2(\eta^2 + 1/6D^2\eta_x^2)``), which is called here [`energy_total_modified`](@ref) + (and [`entropy_modified`](@ref)) because it contains derivatives of the solution + +for periodic boundary conditions. If `split_form` is `false` the semidiscretization conserves +- the total water mass (integral of ``h``) as a linear invariant +- the Hamiltonian (integral of ``1/4\sqrt{g/D}\eta^3 + 1/2\sqrt{gD}\eta^2``) (see [`hamiltonian`](@ref)) + +for periodic boundary conditions. + +- Thomas B. Benjamin, Jerry L. Bona and John J. Mahony (1972) + Model equations for long waves in nonlinear dispersive systems + [DOI: 10.1098/rsta.1972.0032](https://doi.org/10.1098/rsta.1972.0032) +- Hendrik Ranocha, Dimitrios Mitsotakis and David I. Ketcheson (2020) + A Broad Class of Conservative Numerical Methods for Dispersive Wave Equations + [DOI: 10.4208/cicp.OA-2020-0119](https://doi.org/10.4208/cicp.OA-2020-0119) +- Viktor Linders, Hendrik Ranocha and Philipp Birken (2023) + Resolving entropy growth from iterative methods + [DOI: 10.1007/s10543-023-00992-w](https://doi.org/10.1007/s10543-023-00992-w) +""" +struct BBMEquation1D{RealT <: Real} <: AbstractBBMEquation{1, 1} + gravity::RealT # gravitational constant + D::RealT # still-water depth + eta0::RealT # constant still-water surface + split_form::Bool # whether to use a split-form or not +end + +function BBMEquation1D(; gravity_constant, D = 1.0, eta0 = 0.0, split_form = true) + BBMEquation1D(gravity_constant, D, eta0, split_form) +end + +""" + initial_condition_convergence_test(x, t, equations::BBMEquation1D, mesh) + +A travelling-wave solution used for convergence tests in a periodic domain, here generalized +for dimensional variables. + +See section 4.1.3 in (there is an error in paper: it should be `sech^2` instead of `cosh`): +- Hendrik Ranocha, Dimitrios Mitsotakis and David I. Ketcheson (2020) + A Broad Class of Conservative Numerical Methods for Dispersive Wave Equations + [DOI: 10.4208/cicp.OA-2020-0119](https://doi.org/10.4208/cicp.OA-2020-0119) +""" +function initial_condition_convergence_test(x, t, equations::BBMEquation1D, mesh) + g = gravity_constant(equations) + D = equations.D + alpha = sqrt(g * D) + beta = 3 / 2 * sqrt(g / D) + gamma = 1 / 6 * D^2 + c = 1.2 * alpha + A = 3 * (c - alpha) / beta + K = 0.5 * sqrt(1 / gamma * (1 - alpha / c)) + x_t = mod(x - c * t - xmin(mesh), xmax(mesh) - xmin(mesh)) + xmin(mesh) + eta = A * sech(K * x_t)^2 + return SVector(eta) +end + +""" + initial_condition_manufactured(x, t, equations::BBMEquation1D, mesh) + +A smooth manufactured solution in combination with [`source_terms_manufactured`](@ref). +""" +function initial_condition_manufactured(x, t, + equations::BBMEquation1D, + mesh) + eta = exp(t / 2) * sinpi(2 * (x - t / 2)) + return SVector(eta) +end + +""" + source_terms_manufactured(q, x, t, equations::BBMEquation1D, mesh) + +A smooth manufactured solution in combination with [`initial_condition_manufactured`](@ref). +""" +function source_terms_manufactured(q, x, t, equations::BBMEquation1D) + g = gravity_constant(equations) + D = still_waterdepth(q, equations) + a1 = sqrt(g * D) + a2 = sqrt(g / D) + a3 = cospi(t - 2 * x) + a4 = sinpi(t - 2 * x) + a5 = sinpi(2 * t - 4 * x) + a6 = exp(t / 2) + dq1 = -pi^2 * D^2 * (a4 + 2 * pi * a3) * a6 / 3 - 3 * pi * a2 * exp(t) * a5 / 2 + + 2 * pi * a1 * a6 * a3 - a6 * a4 / 2 - pi * a6 * a3 + + return SVector(dq1) +end + +function create_cache(mesh, equations::BBMEquation1D, + solver, initial_condition, + ::BoundaryConditionPeriodic, + RealT, uEltype) + D = equations.D + invImD2 = lu(I - 1 / 6 * D^2 * sparse(solver.D2)) + eta2 = zeros(RealT, nnodes(mesh)) + eta2_x = zero(eta2) + eta_x = zero(eta2) + etaeta_x = zero(eta2) + eta_xx = zero(eta2) + g = gravity_constant(equations) + c_0 = sqrt(g * D) + c_1 = sqrt(g / D) + cache = (; invImD2, eta2, eta2_x, eta_x, etaeta_x, eta_xx, c_0, c_1, + solver.D1, solver.D2) + if solver.D1 isa PeriodicUpwindOperators + eta_x_upwind = zero(eta2) + cache = (; cache..., eta_x_upwind) + end + return cache +end + +# For `equations.split_form == true`, the discretization conserves the mass for eta and the +# modified energy for periodic boundary conditions, see +# - Hendrik Ranocha, Dimitrios Mitsotakis and David I. Ketcheson (2020) +# A Broad Class of Conservative Numerical Methods for Dispersive Wave Equations +# [DOI: 10.4208/cicp.OA-2020-0119](https://doi.org/10.4208/cicp.OA-2020-0119) +# For `equations.split_form == false`, the discretization conserves the mass for eta and the +# Hamiltonian for periodic boundary conditions, see +# - Viktor Linders, Hendrik Ranocha and Philipp Birken (2023) +# Resolving entropy growth from iterative methods +# [DOI: 10.1007/s10543-023-00992-w](https://doi.org/10.1007/s10543-023-00992-w) +function rhs!(dq, q, t, mesh, equations::BBMEquation1D, initial_condition, + ::BoundaryConditionPeriodic, source_terms, solver, cache) + (; invImD2, eta2, eta2_x, eta_x, etaeta_x, c_0, c_1) = cache + if solver.D1 isa PeriodicUpwindOperators + (; eta_x_upwind) = cache + end + + eta, = q.x + deta, = dq.x + + @trixi_timeit timer() "hyperbolic" begin + @.. eta2 = eta^2 + if solver.D1 isa PeriodicDerivativeOperator || + solver.D1 isa UniformPeriodicCoupledOperator || + solver.D1 isa FourierDerivativeOperator + mul!(eta2_x, solver.D1, eta2) + mul!(eta_x, solver.D1, eta) + if equations.split_form + @.. etaeta_x = eta * eta_x + @.. deta = -(0.5 * c_1 * (eta2_x + etaeta_x) + c_0 * eta_x) + else + @.. deta = -(0.75 * c_1 * eta2_x + c_0 * eta_x) + end + elseif solver.D1 isa PeriodicUpwindOperators + mul!(eta2_x, solver.D1.central, eta2) + mul!(eta_x_upwind, solver.D1.minus, eta) + mul!(eta_x, solver.D1.central, eta) + if equations.split_form + @.. etaeta_x = eta * eta_x + @.. deta = -(0.5 * c_1 * (eta2_x + etaeta_x) + c_0 * eta_x_upwind) + else + @.. deta = -(0.75 * c_1 * eta2_x + c_0 * eta_x_upwind) + end + else + @error "unknown type of first-derivative operator: $(typeof(solver.D1))" + end + end + + @trixi_timeit timer() "source terms" calc_sources!(dq, q, t, source_terms, equations, + solver) + + @trixi_timeit timer() "elliptic" begin + solve_system_matrix!(deta, invImD2, equations) + end + return nothing +end + +""" + energy_total_modified!(e, q_global, equations::BBMEquation1D, cache) + +Return the modified total energy `e` of the primitive variables `q_global` for the +[`BBMEquation1D`](@ref). The `energy_total_modified` +is a conserved quantity (for periodic boundary conditions). + +It is given by +```math +\\frac{1}{2} \\eta(\\eta - \\frac{1}{6}D^2\\eta_{xx}). +``` + +`q_global` is a vector of the primitive variables at ALL nodes. +`cache` needs to hold the SBP operators used by the `solver`. + +See also [`energy_total_modified`](@ref). +""" +function energy_total_modified!(e, q_global, equations::BBMEquation1D, cache) + eta, = q_global.x + D = equations.D + + (; D1, D2, eta_xx, tmp1) = cache + if D1 isa PeriodicUpwindOperators + mul!(tmp1, D1.minus, eta) + mul!(eta_xx, D1.plus, tmp1) + else + mul!(eta_xx, D2, eta) + end + + @.. e = 0.5 * eta * (eta - 1 / 6 * D^2 * eta_xx) + return e +end + +""" + hamiltonian!(H, q_global, equations::BBMEquation1D, cache) + +Return the Hamiltonian `H` of the primitive variables `q_global` for the +[`BBMEquation1D`](@ref). The Hamiltonian is given by +```math +\\frac{1}{4}\\sqrt{\\frac{g}{D}}\\eta^3 + \\frac{1}{2}\\sqrt{gD}\\eta^2. +``` + +`q_global` is a vector of the primitive variables at ALL nodes. + +See also [`hamiltonian`](@ref). +""" +function hamiltonian!(H, q_global, equations::BBMEquation1D, cache) + eta, = q_global.x + (; c_0, c_1, eta2, tmp1) = cache + @.. eta2 = c_0 * eta^2 + @.. tmp1 = c_1 * eta^3 + @.. H = 1 / 4 * tmp1 + 1 / 2 * eta2 + return H +end diff --git a/src/equations/bbm_bbm_1d.jl b/src/equations/bbm_bbm_1d.jl index 6465e1f2..238b4a7d 100644 --- a/src/equations/bbm_bbm_1d.jl +++ b/src/equations/bbm_bbm_1d.jl @@ -13,7 +13,7 @@ The unknown quantities of the BBM-BBM equations are the total water height ``\et The gravitational constant is denoted by `g` and the constant bottom topography (bathymetry) ``b = \eta_0 - D``. The water height above the bathymetry is therefore given by ``h = \eta - \eta_0 + D``. The BBM-BBM equations are only implemented for ``\eta_0 = 0``. -Two types of variable `bathymetry_type` are supported: +Two types of `bathymetry_type` are supported: - [`bathymetry_flat`](@ref): flat bathymetry (typically ``b = 0`` everywhere) - [`bathymetry_variable`](@ref): general variable bathymetry @@ -29,14 +29,14 @@ One reference for the BBM-BBM system can be found in Bona et al. (1998). The semidiscretization implemented here was developed for flat bathymetry in Ranocha et al. (2020) and generalized for a variable bathymetry in Lampert and Ranocha (2024). It conserves -- the total water (integral of ``h``) as a linear invariant +- the total water mass (integral of ``h``) as a linear invariant - the total velocity (integral of ``v``) as a linear invariant for flat bathymetry - the total energy for periodic boundary conditions (see Lampert, Ranocha). For reflecting boundary conditions, the semidiscretization conserves - the total water (integral of ``h``) as a linear invariant -- the total energy +- the total energy. Additionally, it is well-balanced for the lake-at-rest stationary solution, see Lampert and Ranocha (2024). @@ -398,10 +398,6 @@ function create_cache(mesh, equations::BBMBBMEquations1D, return (; invImD2d, invImD2n, etav, Dv, v2, tmp2) end -function solve_system_matrix!(dv, system_matrix, ::BBMBBMEquations1D) - ldiv!(system_matrix, dv) -end - # Discretization that conserves # - the total water (integral of ``h``) as a linear invariant # - the total momentum (integral of ``v``) as a linear invariant for flat bathymetry diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 7d51fe1b..8cb78ed0 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -81,6 +81,7 @@ it is also as useful. @inline prim2prim(q, ::AbstractEquations) = q varnames(::typeof(prim2prim), ::AbstractShallowWaterEquations) = ("η", "v", "D") +varnames(::typeof(prim2prim), ::AbstractEquations{1, 1}) = ("η",) """ prim2cons(q, equations) @@ -100,7 +101,13 @@ The inverse conversion is performed by [`cons2prim`](@ref). return SVector(h, hv, b) end -varnames(::typeof(prim2cons), ::AbstractShallowWaterEquations) = ("h", "hv", "hv") +function prim2cons(q, equations::AbstractEquations{1, 1}) + h = waterheight(q, equations) + return SVector(h) +end + +varnames(::typeof(prim2cons), ::AbstractShallowWaterEquations) = ("h", "hv", "b") +varnames(::typeof(prim2cons), ::AbstractEquations{1, 1}) = ("h",) """ cons2prim(u, equations) @@ -121,6 +128,15 @@ The inverse conversion is performed by [`prim2cons`](@ref). return SVector(eta, v, D) end +function cons2prim(u, equations::AbstractEquations{1, 1}) + h, = u + eta0 = equations.eta0 + D = equations.D + b = eta0 - D + eta = h + b + return SVector(eta) +end + """ waterheight_total(q, equations) @@ -131,14 +147,14 @@ Return the total waterheight of the primitive variables `q` for a given set of `q` is a vector of the primitive variables at a single node, i.e., a vector of the correct length `nvariables(equations)`. """ -@inline function waterheight_total(q, ::AbstractShallowWaterEquations) +@inline function waterheight_total(q, ::AbstractEquations) return q[1] end varnames(::typeof(waterheight_total), equations) = ("η",) """ - waterheight(q, equations::AbstractShallowWaterEquations) + waterheight(q, equations) Return the waterheight of the primitive variables `q` for a given set of `equations`, i.e., the waterheight ``h`` above the bathymetry ``b``. @@ -148,7 +164,7 @@ of the correct length `nvariables(equations)`. See also [`waterheight_total`](@ref), [`bathymetry`](@ref). """ -@inline function waterheight(q, equations::AbstractShallowWaterEquations) +@inline function waterheight(q, equations::AbstractEquations) return waterheight_total(q, equations) - bathymetry(q, equations) end @@ -194,12 +210,12 @@ See [`momentum`](@ref). varnames(::typeof(discharge), equations) = ("P",) """ - still_water_surface(q, equations::AbstractShallowWaterEquations) + still_water_surface(q, equations) Return the still water surface ``\\eta_0`` (lake at rest) for a given set of `equations`. """ -@inline function still_water_surface(q, equations::AbstractShallowWaterEquations) +@inline function still_water_surface(q, equations::AbstractEquations) return equations.eta0 end @@ -207,6 +223,8 @@ end return q[3] end +still_waterdepth(q, equations::AbstractEquations{1, 1}) = equations.D + """ bathymetry(q, equations) @@ -216,33 +234,32 @@ Return the bathymetry of the primitive variables `q` for a given set of `q` is a vector of the primitive variables at a single node, i.e., a vector of the correct length `nvariables(equations)`. """ -@inline function bathymetry(q, equations::AbstractShallowWaterEquations) - D = q[3] +@inline function bathymetry(q, equations::AbstractEquations) + D = still_waterdepth(q, equations) eta0 = still_water_surface(q, equations) return eta0 - D end """ - lake_at_rest_error(q, equations::AbstractShallowWaterEquations) + lake_at_rest_error(q, equations) Calculate the error for the "lake-at-rest" test case where the [`waterheight_total`](@ref) ``\\eta = h + b`` should be a constant value over time (given by the value ``\\eta_0`` passed to the `equations` when constructing them). """ -@inline function lake_at_rest_error(q, equations::AbstractShallowWaterEquations) +@inline function lake_at_rest_error(q, equations::AbstractEquations) eta = waterheight_total(q, equations) eta0 = still_water_surface(q, equations) return abs(eta0 - eta) end """ - gravity_constant(equations::AbstractShallowWaterEquations) + gravity_constant(equations) Return the gravity constant ``g`` for a given set of `equations`. -See also [`AbstractShallowWaterEquations`](@ref). """ -@inline function gravity_constant(equations::AbstractShallowWaterEquations) +@inline function gravity_constant(equations::AbstractEquations) return equations.gravity end @@ -290,7 +307,7 @@ varnames(::typeof(entropy), equations) = ("U",) # The modified entropy/total energy takes the whole `q_global` for every point in space """ - energy_total_modified(q_global, equations::AbstractShallowWaterEquations, cache) + energy_total_modified(q_global, equations, cache) Return the modified total energy of the primitive variables `q_global` for the `equations`. This modified total energy is a conserved quantity and can @@ -298,7 +315,11 @@ contain additional terms compared to the usual [`energy_total`](@ref). For example, for the [`SvaerdKalischEquations1D`](@ref) and the [`SerreGreenNaghdiEquations1D`](@ref), it contains additional terms depending on the derivative of the velocity ``v_x`` modeling non-hydrostatic -contributions. +contributions. For equations which are not `AbstractShallowWaterEquations`, +the modified total energy does not have to be an extension of the usual +[`energy_total`](@ref) and does not have to be related to a physical energy. +However, it is still a conserved quantity for appropriate boundary conditions +and may contain derivatives of the solution. `q_global` is a vector of the primitive variables at ALL nodes. `cache` needs to hold the SBP operators used by the `solver` if non-hydrostatic @@ -307,17 +328,17 @@ terms are present. Internally, this function allocates a vector for the output and calls [`DispersiveShallowWater.energy_total_modified!`](@ref). """ -function energy_total_modified(q_global, equations::AbstractShallowWaterEquations, cache) +function energy_total_modified(q_global, equations::AbstractEquations, cache) e = similar(q_global.x[begin]) return energy_total_modified!(e, q_global, equations, cache) end """ - energy_total_modified!(e, q_global, equations::AbstractShallowWaterEquations, cache) + energy_total_modified!(e, q_global, equations, cache) In-place version of [`energy_total_modified`](@ref). """ -function energy_total_modified!(e, q_global, equations::AbstractShallowWaterEquations, +function energy_total_modified!(e, q_global, equations::AbstractEquations, cache) # `q_global` is an `ArrayPartition` of the primitive variables at all nodes @assert nvariables(equations) == length(q_global.x) @@ -332,17 +353,17 @@ end varnames(::typeof(energy_total_modified), equations) = ("e_modified",) """ - entropy_modified(q_global, equations::AbstractShallowWaterEquations, cache) + entropy_modified(q_global, equations, cache) Alias for [`energy_total_modified`](@ref). """ -@inline function entropy_modified(q_global, equations::AbstractShallowWaterEquations, cache) +@inline function entropy_modified(q_global, equations::AbstractEquations, cache) e = similar(q_global.x[begin]) return entropy_modified!(e, q_global, equations, cache) end """ - entropy_modified!(e, q_global, equations::AbstractShallowWaterEquations, cache) + entropy_modified!(e, q_global, equations, cache) In-place version of [`entropy_modified`](@ref). """ @@ -353,6 +374,33 @@ In-place version of [`entropy_modified`](@ref). varnames(::typeof(entropy_modified), equations) = ("U_modified",) +# The Hamiltonian takes the whole `q_global` for every point in space +""" + hamiltonian(q_global, equations, cache) + +Return the Hamiltonian of the primitive variables `q_global` for the +`equations`. The Hamiltonian is a conserved quantity and may contain +derivatives of the solution. + +`q_global` is a vector of the primitive variables at ALL nodes. +`cache` needs to hold the SBP operators used by the `solver` if non-hydrostatic +terms are present. + +Internally, this function allocates a vector for the output and +calls [`DispersiveShallowWater.hamiltonian!`](@ref). + +!!! note + This function is not necessarily implemented for all equations. + See [`DispersiveShallowWater.hamiltonian!`](@ref) for further details + of equations supporting it. +""" +function hamiltonian(q_global, equations::AbstractEquations, cache) + H = similar(q_global.x[begin]) + return hamiltonian!(H, q_global, equations, cache) +end + +varnames(::typeof(hamiltonian), equations) = ("H",) + # Add methods to show some information on systems of equations. function Base.show(io::IO, equations::AbstractEquations) # Since this is not performance-critical, we can use `@nospecialize` to reduce latency. @@ -488,6 +536,11 @@ See also [`bathymetry_flat`](@ref) and [`bathymetry_mild_slope`](@ref). """ const bathymetry_variable = BathymetryVariable() +# BBM equation +abstract type AbstractBBMEquation{NDIMS, NVARS} <: + AbstractEquations{NDIMS, NVARS} end +include("bbm_1d.jl") + # BBM-BBM equations abstract type AbstractBBMBBMEquations{NDIMS, NVARS} <: AbstractShallowWaterEquations{NDIMS, NVARS} end @@ -501,7 +554,6 @@ include("svaerd_kalisch_1d.jl") # Serre-Green-Naghdi equations abstract type AbstractSerreGreenNaghdiEquations{NDIMS, NVARS} <: AbstractShallowWaterEquations{NDIMS, NVARS} end -const AbstractSerreGreenNaghdiEquations1D = AbstractSerreGreenNaghdiEquations{1} include("serre_green_naghdi_1d.jl") include("hyperbolic_serre_green_naghdi_1d.jl") @@ -529,6 +581,10 @@ function solve_system_matrix!(dv, system_matrix, rhs, return nothing end +function solve_system_matrix!(dv, system_matrix, ::Union{BBMEquation1D, BBMBBMEquations1D}) + ldiv!(system_matrix, dv) +end + """ initial_condition_dingemans(x, t, equations::AbstractShallowWaterEquations, mesh) diff --git a/src/equations/hyperbolic_serre_green_naghdi_1d.jl b/src/equations/hyperbolic_serre_green_naghdi_1d.jl index d550f154..1dbd9eb6 100644 --- a/src/equations/hyperbolic_serre_green_naghdi_1d.jl +++ b/src/equations/hyperbolic_serre_green_naghdi_1d.jl @@ -46,7 +46,7 @@ to the original [`SerreGreenNaghdiEquations1D`](@ref). However, the wave speeds of the hyperbolic system increase with increasing ``\lambda``, so that explicit time integration methods become more expensive. -Two types of variable `bathymetry_type` are supported: +Two types of `bathymetry_type` are supported: - [`bathymetry_flat`](@ref): flat bathymetry (typically ``b = 0`` everywhere) - [`bathymetry_mild_slope`](@ref): variable bathymetry with mild-slope approximation @@ -146,7 +146,7 @@ function set_approximation_variables!(q, mesh, return nothing end -# TODO: There is name clash. For the SerreGreenNaghdiEquations1D, +# TODO: There is a name clash. For the SerreGreenNaghdiEquations1D, # the corresponding function is called initial_condition_convergence_test # However, we cannot use that name since it's not an analytical solution. # How shall we handle this? diff --git a/src/equations/serre_green_naghdi_1d.jl b/src/equations/serre_green_naghdi_1d.jl index 97a7b879..7c4d1984 100644 --- a/src/equations/serre_green_naghdi_1d.jl +++ b/src/equations/serre_green_naghdi_1d.jl @@ -18,7 +18,7 @@ The gravitational constant is denoted by `g` and the bottom topography is therefore given by ``h = \eta - \eta_0 + D``. The total water height is therefore given by ``\eta = h + b``. -Three types of variable `bathymetry_type` are supported: +Three types of `bathymetry_type` are supported: - [`bathymetry_flat`](@ref): flat bathymetry (typically ``b = 0`` everywhere) - [`bathymetry_mild_slope`](@ref): variable bathymetry with mild-slope approximation - [`bathymetry_variable`](@ref): general variable bathymetry diff --git a/src/equations/svaerd_kalisch_1d.jl b/src/equations/svaerd_kalisch_1d.jl index 30df899f..07a95d98 100644 --- a/src/equations/svaerd_kalisch_1d.jl +++ b/src/equations/svaerd_kalisch_1d.jl @@ -32,7 +32,7 @@ Currently, the equations only support a general variable bathymetry, see [`bathy The equations by Svärd and Kalisch are presented and analyzed in Svärd and Kalisch (2023). The semidiscretization implemented here conserves -- the total water (integral of ``h``) as a linear invariant +- the total water mass (integral of ``h``) as a linear invariant - the total momentum (integral of ``h v``) as a nonlinear invariant for flat bathymetry - the total modified energy diff --git a/src/semidiscretization.jl b/src/semidiscretization.jl index b26247fd..aba79a95 100644 --- a/src/semidiscretization.jl +++ b/src/semidiscretization.jl @@ -147,12 +147,13 @@ end # Obtain the function, which has an additional `!` appended to the name inplace_version(f) = getfield(@__MODULE__, Symbol(string(nameof(f)) * "!")) -# The entropy/energy of the Svärd-Kalisch and Serre-Green-Naghdi equations -# takes the whole `q` for every point in space since it requires +# The modified entropy/energy and the Hamiltonian +# take the whole `q` for every point in space since it requires # the derivative of the velocity `v_x`. function integrate_quantity!(quantity, func::Union{typeof(energy_total_modified), - typeof(entropy_modified)}, q, + typeof(entropy_modified), + typeof(hamiltonian)}, q, semi::Semidiscretization) inplace_version(func)(quantity, q, semi.equations, semi.cache) integrate(quantity, semi) @@ -196,7 +197,9 @@ function compute_coefficients!(q, func, t, semi::Semidiscretization) solver) end -function check_bathymetry(equations, q0) +check_bathymetry(equations, q0) = nothing + +function check_bathymetry(equations::AbstractShallowWaterEquations, q0) if equations.bathymetry_type isa BathymetryFlat _, _, D = q0.x value = first(D) diff --git a/test/runtests.jl b/test/runtests.jl index 84983eb5..1526bd47 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,7 @@ using Test include("test_aqua.jl") include("test_unit.jl") include("test_visualization.jl") + include("test_bbm_1d.jl") include("test_bbm_bbm_1d.jl") include("test_svaerd_kalisch_1d.jl") include("test_serre_green_naghdi_1d.jl") diff --git a/test/test_bbm_1d.jl b/test/test_bbm_1d.jl new file mode 100644 index 00000000..a4e7c621 --- /dev/null +++ b/test/test_bbm_1d.jl @@ -0,0 +1,184 @@ +module TestBBM1D + +using Test +using DispersiveShallowWater + +include("test_util.jl") + +EXAMPLES_DIR = joinpath(examples_dir(), "bbm_1d") + +@testset "BBMEquation1D" begin + @trixi_testset "bbm_1d_basic" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_1d_basic.jl"), + tspan=(0.0, 100.0), + l2=[0.020905920116663004], + linf=[0.011361008348288737], + cons_error=[1.3322676295501878e-15], + change_waterheight=1.3322676295501878e-15, + change_entropy_modified=-8.852448858398532e-7, + change_hamiltonian=-4.366246803000351e-6) + + @test_allocations(semi, sol, allocs=5_000) + + # test upwind operators + using SummationByPartsOperators: upwind_operators, periodic_derivative_operator + using SparseArrays: sparse + using OrdinaryDiffEq: solve + D1 = upwind_operators(periodic_derivative_operator; derivative_order = 1, + accuracy_order = accuracy_order, xmin = mesh.xmin, + xmax = mesh.xmax, + N = mesh.N) + D2 = sparse(D1.minus) * sparse(D1.plus) + solver = Solver(D1, D2) + semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + ode = semidiscretize(semi, (0.0, 100.0)) + sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) + atol = 1e-12 + rtol = 1e-12 + errs = errors(analysis_callback) + l2 = [0.11730278305145693] + l2_measured = errs.l2_error[:, end] + for (l2_expected, l2_actual) in zip(l2, l2_measured) + @test isapprox(l2_expected, l2_actual, atol = atol, rtol = rtol) + end + linf = [0.06433115916307008] + linf_measured = errs.linf_error[:, end] + for (linf_expected, linf_actual) in zip(linf, linf_measured) + @test isapprox(linf_expected, linf_actual, atol = atol, rtol = rtol) + end + + @test_allocations(semi, sol, allocs=5_000) + end + + @trixi_testset "bbm_1d_basic with split_form = false" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_1d_basic.jl"), + tspan=(0.0, 100.0), + split_form=false, + l2=[0.018773238618175637], + linf=[0.0102691348638429], + cons_error=[4.218847493575595e-15], + change_waterheight=4.218847493575595e-15, + change_entropy_modified=-5.764463673341158e-7, + change_hamiltonian=-3.3263833076890847e-6) + + @test_allocations(semi, sol, allocs=5_000) + + # test upwind operators + using SummationByPartsOperators: upwind_operators, periodic_derivative_operator + using SparseArrays: sparse + using OrdinaryDiffEq: solve + D1 = upwind_operators(periodic_derivative_operator; derivative_order = 1, + accuracy_order = accuracy_order, xmin = mesh.xmin, + xmax = mesh.xmax, + N = mesh.N) + D2 = sparse(D1.minus) * sparse(D1.plus) + solver = Solver(D1, D2) + semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + ode = semidiscretize(semi, (0.0, 100.0)) + sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) + atol = 1e-12 + rtol = 1e-12 + errs = errors(analysis_callback) + l2 = [0.11994249550267427] + l2_measured = errs.l2_error[:, end] + for (l2_expected, l2_actual) in zip(l2, l2_measured) + @test isapprox(l2_expected, l2_actual, atol = atol, rtol = rtol) + end + linf = [0.06576332657307044] + linf_measured = errs.linf_error[:, end] + for (linf_expected, linf_actual) in zip(linf, linf_measured) + @test isapprox(linf_expected, linf_actual, atol = atol, rtol = rtol) + end + + @test_allocations(semi, sol, allocs=5_000) + end + + @trixi_testset "bbm_1d_relaxation" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_1d_relaxation.jl"), + tspan=(0.0, 100.0), + l2=[0.020884321819757214], + linf=[0.011350059432479132], + cons_error=[2.220446049250313e-15], + change_waterheight=2.220446049250313e-15, + change_entropy_modified=2.7755575615628914e-17, + change_hamiltonian=-1.039007974656947e-6) + + @test_allocations(semi, sol, allocs=5_000) + end + + @trixi_testset "bbm_1d_hamiltonian_relaxation" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_1d_hamiltonian_relaxation.jl"), + tspan=(0.0, 100.0), + l2=[0.018751641621494324], + linf=[0.010258179709480952], + cons_error=[3.552713678800501e-15], + change_waterheight=3.552713678800501e-15, + change_entropy_modified=3.085709884831367e-7, + change_hamiltonian=4.440892098500626e-16) + + @test_allocations(semi, sol, allocs=5_000) + end + + @trixi_testset "bbm_1d_fourier" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_1d_fourier.jl"), + tspan=(0.0, 100.0), + l2=[1.812306511686721e-5], + linf=[1.0044426771715909e-5], + cons_error=[6.439293542825908e-15], + change_waterheight=-6.439293542825908e-15, + change_entropy_modified=-8.734545181576792e-7, + change_hamiltonian=-3.282887288680314e-6) + + @test_allocations(semi, sol, allocs=5_000) + end + + @trixi_testset "bbm_1d_manufactured" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_1d_manufactured.jl"), + tspan=(0.0, 1.0), + l2=[6.073756646372025e-9], + linf=[8.869951328982495e-9], + cons_error=[2.60491292165127e-12], + change_waterheight=2.60491292165127e-12, + atol=1e-11) # to make CI pass + + @test_allocations(semi, sol, allocs=5_000) + + # test upwind operators + using SummationByPartsOperators: upwind_operators, periodic_derivative_operator + using SparseArrays: sparse + using OrdinaryDiffEq: solve + D1 = upwind_operators(periodic_derivative_operator; derivative_order = 1, + accuracy_order = accuracy_order, xmin = mesh.xmin, + xmax = mesh.xmax, + N = mesh.N) + D2 = sparse(D1.minus) * sparse(D1.plus) + solver = Solver(D1, D2) + semi = Semidiscretization(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions, + source_terms = source_terms) + ode = semidiscretize(semi, (0.0, 1.0)) + sol = solve(ode, Tsit5(), abstol = 1e-7, reltol = 1e-7, + save_everystep = false, callback = callbacks, saveat = saveat) + atol = 1e-11 # to make CI pass + rtol = 1e-12 + errs = errors(analysis_callback) + l2 = [9.94432411102018e-8] + l2_measured = errs.l2_error[:, end] + for (l2_expected, l2_actual) in zip(l2, l2_measured) + @test isapprox(l2_expected, l2_actual, atol = atol, rtol = rtol) + end + linf = [1.026056097863659e-7] + linf_measured = errs.linf_error[:, end] + for (linf_expected, linf_actual) in zip(linf, linf_measured) + @test isapprox(linf_expected, linf_actual, atol = atol, rtol = rtol) + end + + @test_allocations(semi, sol, allocs=5_000) + end +end + +end # module diff --git a/test/test_bbm_bbm_1d.jl b/test/test_bbm_bbm_1d.jl index 1f96e0c3..747927fd 100644 --- a/test/test_bbm_bbm_1d.jl +++ b/test/test_bbm_bbm_1d.jl @@ -7,7 +7,7 @@ include("test_util.jl") EXAMPLES_DIR = joinpath(examples_dir(), "bbm_bbm_1d") -@testset "BBMBBM1D" begin +@testset "BBMBBMEquations1D" begin @trixi_testset "bbm_bbm_1d_basic" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "bbm_bbm_1d_basic.jl"), tspan=(0.0, 1.0), diff --git a/test/test_svaerd_kalisch_1d.jl b/test/test_svaerd_kalisch_1d.jl index f4c6c83f..85550773 100644 --- a/test/test_svaerd_kalisch_1d.jl +++ b/test/test_svaerd_kalisch_1d.jl @@ -7,7 +7,7 @@ include("test_util.jl") EXAMPLES_DIR = joinpath(examples_dir(), "svaerd_kalisch_1d") -@testset "SvaerdKalisch1D" begin +@testset "SvaerdKalischEquations1D" begin @trixi_testset "svaerd_kalisch_1d_manufactured" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "svaerd_kalisch_1d_manufactured.jl"), diff --git a/test/test_unit.jl b/test/test_unit.jl index 6990343e..32f8211a 100644 --- a/test/test_unit.jl +++ b/test/test_unit.jl @@ -106,6 +106,54 @@ using SparseArrays: sparse, SparseMatrixCSC @test_nowarn display(boundary_conditions) end + @testset "BBMEquation1D" begin + equations = @test_nowarn @inferred BBMEquation1D(gravity_constant = 1.0) + @test_nowarn print(equations) + @test_nowarn display(equations) + conversion_functions = [ + waterheight_total, + waterheight, + entropy, + energy_total, + prim2cons, + prim2prim, + prim2phys, + energy_total_modified, + entropy_modified, + hamiltonian, + ] + for conversion in conversion_functions + @test DispersiveShallowWater.varnames(conversion, equations) isa Tuple + end + q = [42.0] + @test @inferred(prim2prim(q, equations)) == q + @test isapprox(@inferred(cons2prim(prim2cons(q, equations), equations)), q) + @test @inferred(waterheight_total(q, equations)) == 42.0 + @test @inferred(waterheight(q, equations)) == 43.0 + @test @inferred(still_water_surface(q, equations)) == 0.0 + @test @inferred(prim2phys(q, equations)) == @inferred(prim2prim(q, equations)) + @testset "energy_total_modified and hamiltonian" begin + initial_condition = initial_condition_manufactured + boundary_conditions = boundary_condition_periodic + mesh = @inferred Mesh1D(-1.0, 1.0, 10) + solver = Solver(mesh, 4) + semi = @inferred Semidiscretization(mesh, equations, initial_condition, + solver; boundary_conditions) + q = @inferred DispersiveShallowWater.compute_coefficients(initial_condition, + 0.0, semi) + _, _, _, cache = @inferred DispersiveShallowWater.mesh_equations_solver_cache(semi) + e_modified = @inferred energy_total_modified(q, equations, cache) + e_modified_total = @inferred DispersiveShallowWater.integrate(e_modified, semi) + @test isapprox(e_modified_total, 3.710663574870101) + U_modified = @inferred entropy_modified(q, equations, cache) + U_modified_total = @inferred DispersiveShallowWater.integrate(U_modified, semi) + @test isapprox(U_modified_total, e_modified_total) + h = @inferred hamiltonian(q, equations, cache) + h_total = @inferred DispersiveShallowWater.integrate(h, semi) + @test isapprox(h_total, 0.5) + end + end + @testset "BBMBBMEquations1D" begin equations = @test_nowarn @inferred BBMBBMEquations1D(gravity_constant = 9.81) @test_nowarn print(equations) diff --git a/test/test_util.jl b/test/test_util.jl index fa022066..41dbf632 100644 --- a/test/test_util.jl +++ b/test/test_util.jl @@ -8,6 +8,7 @@ using Test: @test change_velocity=nothing, change_entropy=nothing, change_entropy_modified=nothing, + change_hamiltonian=nothing, lake_at_rest=nothing, atol=1e-12, rtol=sqrt(eps()), atol_ints=1e-11, rtol_ints=sqrt(eps())) @@ -17,8 +18,8 @@ By default, only the absence of error output is checked. If `l2`, `linf` or `cons_error` are specified, in addition the resulting L2/Linf/conservation errors are compared approximately against these reference values, using `atol, rtol` as absolute/relative tolerance. -If `change_waterheight`, `change_velocity`, `change_momemtum`, `change_entropy`, `change_entropy_modified` -or `lake_at_rest` are specified, in addition the resulting changes of the different errors are +If `change_waterheight`, `change_velocity`, `change_momemtum`, `change_entropy`, `change_entropy_modified`, +`change_hamiltonian`, or `lake_at_rest` are specified, in addition the resulting changes of the different errors are compared approximately against these reference values, using `atol_ints`, `rtol_ints` as absolute/relative tolerance. """ macro test_trixi_include(example, args...) @@ -30,6 +31,7 @@ macro test_trixi_include(example, args...) local change_momentum = get_kwarg(args, :change_momentum, nothing) local change_entropy = get_kwarg(args, :change_entropy, nothing) local change_entropy_modified = get_kwarg(args, :change_entropy_modified, nothing) + local change_hamiltonian = get_kwarg(args, :change_hamiltonian, nothing) local lake_at_rest = get_kwarg(args, :lake_at_rest, nothing) local atol = get_kwarg(args, :atol, 1e-12) local rtol = get_kwarg(args, :rtol, sqrt(eps())) @@ -41,7 +43,8 @@ macro test_trixi_include(example, args...) if (arg.head == :(=) && !(arg.args[1] in (:l2, :linf, :cons_error, :change_waterheight, :change_velocity, :change_momentum, :change_entropy, - :change_entropy_modified, :lake_at_rest, + :change_entropy_modified, :change_hamiltonian, + :lake_at_rest, :atol, :rtol, :atol_ints, :rtol_ints))) push!(kwargs, Pair(arg.args...)) end @@ -88,7 +91,7 @@ macro test_trixi_include(example, args...) if !isnothing($change_waterheight) || !isnothing($change_velocity) || !isnothing($change_momentum) || !isnothing($change_entropy) || !isnothing($change_entropy_modified) || - !isnothing($lake_at_rest) + !isnothing($change_hamiltonian) || !isnothing($lake_at_rest) ints = integrals(analysis_callback) if !isnothing($change_waterheight) @@ -126,6 +129,14 @@ macro test_trixi_include(example, args...) rtol = $rtol_ints) end + if !isnothing($change_hamiltonian) + change_hamiltonian_change_measured = ints.hamiltonian[end] - + ints.hamiltonian[1] + @test isapprox($change_hamiltonian, change_hamiltonian_change_measured, + atol = $atol_ints, + rtol = $rtol_ints) + end + if !isnothing($lake_at_rest) lake_at_rest_measured = ints.lake_at_rest_error[end] @test isapprox($lake_at_rest, lake_at_rest_measured, atol = $atol_ints,