Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maximum-principle-satisfying WENO scheme #2557

Merged
merged 21 commits into from
May 21, 2022

Conversation

simone-silvestri
Copy link
Collaborator

@simone-silvestri simone-silvestri commented May 16, 2022

Following https://royalsocietypublishing.org/doi/epdf/10.1098/rspa.2011.0153 (section 5)

At the moment this is just a scheme check, so I have implemented it in 1D (and only in the horizontal direction)

For the moment this script

using Oceananigans

arch = CPU()
grid = RectilinearGrid(arch, size = (100, 100, 1), x = (0, 2π), y = (0, 2π), z=(0, 1), topology = (Periodic, Periodic, Bounded))

free_surface = ImplicitFreeSurface()

model = HydrostaticFreeSurfaceModel(; grid, free_surface,
                                    momentum_advection = WENO5(),
                                    tracer_advection = WENO5(bounds = (0, 1)),
                                    buoyancy = nothing,
                                    tracers = :c)

@inline function linear_u_profile(x, y, z) 
    if x < π/2 || x > 3 * π/2
        return 0.0
    else
        if y > 7/4*π || y < π/4
            return 0.0
        elseif y < 5/4 * π && y > 3/4 * π
            return π/2
        elseif y >= π/4 && y <= 3/4 * π
            return y - π/4
        else
            return π/2 - (y - 5/4 * π)
        end
    end
end

@inline function rectangle_c(x, y, z)
    if x > 2/3 * π && x < 4/3 * π
        if x > 2/3 * π && x < 4/3 * π
            return 1.0
        else
            return 0.0
        end
    else
        return 0.0
    end
end

set!(model, u = linear_u_profile, c = rectangle_c)

Δt = grid.Lx / grid.Nx / maximum(abs, model.velocities.u) * 0.2

for step in 1:10000
    time_step!(model, Δt)
    @info "step $step, time $(model.clock.time)"
end

returns

julia> model.tracers.c
100×100×1 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 100×100×1 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│   └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: ZeroFlux, top: ZeroFlux, immersed: ZeroFlux
└── data: 106×106×7 OffsetArray(::Array{Float64, 3}, -2:103, -2:103, -2:4) with eltype Float64 with indices -2:103×-2:103×-2:4
    └── max=1.03919, min=-0.0214752, mean=0.351983

for usual WENO5
and

julia> model.tracers.c
100×100×1 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 100×100×1 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│   └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: ZeroFlux, top: ZeroFlux, immersed: ZeroFlux
└── data: 106×106×7 OffsetArray(::Array{Float64, 3}, -2:103, -2:103, -2:4) with eltype Float64 with indices -2:103×-2:103×-2:4
    └── max=0.999906, min=1.09505e-5, mean=0.352073

for WENO5(bounds = (0, 1))

So it seems like this scheme, even in its 1D formulation, might help with our tracer bound issues.

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented May 16, 2022

This is a heatmap of the tracer after 10000 time steps (the initial condition of the tracer is 0 <= c <= 1). The top is WENO5, the bottom WENO5(bounds=(0, 1)). The out-of-bounds cells are substituted with a NaN

positivity_preserving

@simone-silvestri
Copy link
Collaborator Author

Same plot obtained with a second order centered scheme for tracers

positivity_preserving-2

@francispoulin
Copy link
Collaborator

This is great to see!

I imagine we could use this for the height field in ShallowWaterModel to allow for wetting and drying for a single layer. And also to prevent layer collapse in the case of multiple layers.

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented May 17, 2022

yeah, I guess in that case you don't want to set an upper bound so we can use WENO5(bounds = (0, Inf))

@simone-silvestri simone-silvestri changed the title [WIP] Maximum-principle-satisfying WENO scheme Maximum-principle-satisfying WENO scheme May 17, 2022
@glwagner
Copy link
Member

Just a small plea to make this implementation compatible with / moves us towards the refactor envisioned in #2454

return 1/Vᶜᶜᶜ(i, j, k, grid) * (div_x + div_y + div_z)
end

@inline function bounded_tracer_flux_divergence_x(i, j, k, grid, advection::BoundPreservingScheme, u, c)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! It seems like these three funcionts are almost identical except for u -> v -> w. Is that true?

If yes, I wonder if there is away to merge these to one, rather than having three functions that do the same thing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indices, areas and interpolations differ. I can try merging it with some metaprogramming

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary. This is maybe cleaner but thought I would make the observation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer having separate functions over metaprogramming in this case.

@francispoulin the difference between x, y, z is that they depend on indices i, j, k respectively. So we also have advective_tracer_flux_x, etc.

@@ -26,7 +30,7 @@ Weighted Essentially Non-Oscillatory (WENO) fifth-order advection scheme.

$(TYPEDFIELDS)
"""
struct WENO5{FT, XT, YT, ZT, XS, YS, ZS, VI, WF} <: AbstractUpwindBiasedAdvectionScheme{2}
struct WENO5{FT, XT, YT, ZT, XS, YS, ZS, VI, WF, PP} <: AbstractUpwindBiasedAdvectionScheme{2}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the design we will want when we do #2454 ? If we can avoid digging ourselves into a deeper hole we should, but maybe it's not possible without a lot of effort...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not still completely sure how #2454 will go along, this change only modifies the div_Uc so I guess it might be easy to adapt in the future

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #2454 we will disentangle vector invariant from WENO5, so VI will go away. It might still make sense to blend WENO5 with flux-limited WENO5, however. It's just that if we have nth order WENO, we would also have to figure out how to do nth order flux limited WENO if they are entangled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the limiter will be independent on the order of the original scheme. You always want to blend your scheme into a first order upwind (when crossing bounds)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, great!

@glwagner
Copy link
Member

glwagner commented May 17, 2022

To support different bounds for different tracers, let's allow the advection scheme to be a NamedTuple. Then pass the advection scheme specific to each tracer into to the tracer tendency kernel. This moves us towards the design we want for #2454 and isn't any more difficult than a different solution.

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented May 17, 2022

I think it is already like this for the HydrostaticFreeSurfaceModel,
we can specify

c_adv = WENO5(bounds = (0, 1))
T_adv = WENO5()
model = HydrostaticFreeSurfaceModel(tracer_advection = (c = c_adv, T = T_adv), tracers = (:c, :T))

(I just found out)
I ll change the nonhydrostatic and shallow water model to have the same feature

@simone-silvestri
Copy link
Collaborator Author

I guess it is better to change the ShallowWaterModel in #2522 as a lot is changing already

@glwagner
Copy link
Member

For NonhydrostaticModel you'll have to come up with an API / syntax for momentum versus tracers.

Definitely not a bad idea, but we don't want to abuse people with too many syntax changes so we may want to discuss what that would look like.

@glwagner
Copy link
Member

I think it is already like this for the HydrostaticFreeSurfaceModel, we can specify

c_adv = WENO5(bounds = (0, 1))
T_adv = WENO5()
model = HydrostaticFreeSurfaceModel(tracer_advection = (c = c_adv, T = T_adv), tracers = (:c, :T))

(I just found out) I ll change the nonhydrostatic and shallow water model to have the same feature

Heh, I probably wrote that but then forgot! Good rediscovery. That seems like a decent API --- what do you think?

@simone-silvestri
Copy link
Collaborator Author

simone-silvestri commented May 18, 2022

I like it! I changed the NonhydrostaticModel to be the same.

A way to facilitate users is that we leave the option to specify a global advection (so that we keep the keyword) or two separate momentum_advection and tracer_advection.
I would rather just change it to be consistent with the HydrostaticFreeSurfaceModel directly.

what do you guys think?

@glwagner
Copy link
Member

glwagner commented May 18, 2022

I like it! I changed the NonhydrostaticModel to be the same.

A way to facilitate users is that we leave the option to specify a global advection (so that we keep the keyword) or two separate momentum_advection and tracer_advection. I would rather just change it to be consistent with the HydrostaticFreeSurfaceModel directly.

what do you guys think?

I think we should do that in a different PR because it's a major, major breaking change to the most popular model!

As for the syntax I'd prefer --- I would prefer one keyword argument advection. We need that for the changes proposed in #2454 --- because the schemes for different components can depend on one another. So it's easier to put it in one object.

I also feel it's better to just specify grid once to the advection constructor. But that would be harder to implement now.

@@ -50,7 +55,8 @@ end
"""
NonhydrostaticModel(; grid,
clock = Clock{eltype(grid)}(0, 0, 1),
advection = CenteredSecondOrder(),
momentum_advection = CenteredSecondOrder(),
tracer_advection = CenteredSecondOrder(),
Copy link
Member

@glwagner glwagner May 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer one kwarg advection --- and we should do this in a different PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, then we can change it in a different PR (for the moment then separate bounds can be used only in the HydrostaticFreeSurfaceModel)

p̃ = (cᵢⱼ - ω̂₁ * c₋ᴿ - ω̂ₙ * c₊ᴸ) / (1 - 2ω̂₁)
M = max(p̃, c₊ᴸ, c₋ᴿ)
m = min(p̃, c₊ᴸ, c₋ᴿ)
θ = min(abs((upper_limit - cᵢⱼ)/(M - cᵢⱼ + ε₂)), abs((lower_limit - cᵢⱼ)/(m - cᵢⱼ + ε₂)), 1.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
θ = min(abs((upper_limit - cᵢⱼ)/(M - cᵢⱼ + ε₂)), abs((lower_limit - cᵢⱼ)/(m - cᵢⱼ + ε₂)), 1.0)
θ = min(abs((upper_limit - cᵢⱼ)/(M - cᵢⱼ + ε₂)), abs((lower_limit - cᵢⱼ)/(m - cᵢⱼ + ε₂)), one(grid))

@simone-silvestri simone-silvestri merged commit ea9a1a1 into main May 21, 2022
@navidcy navidcy deleted the ss/positivity_preserving_weno branch February 8, 2023 04:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants