Skip to content

Commit e75025f

Browse files
Implement and test vertical mass borrowing limiters
1 parent 76dac21 commit e75025f

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

src/Limiters/Limiters.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ abstract type AbstractLimiter end
1919

2020
# implementations
2121
include("quasimonotone.jl")
22+
include("vertical_mass_borrowing_limiter.jl")
2223

2324
end # end module
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#=
2+
3+
From E3SM:
4+
https://github.com/E3SM-Project/E3SM/blob/2c377c5ec9a5585170524b366ad85074ab1b1a5c/components/eam/src/physics/cam/massborrow.F90
5+
Described in Appendix B of
6+
https://gmd.copernicus.org/articles/11/1971/2018/gmd-11-1971-2018.pdf
7+
8+
=#
9+
10+
import .DataLayouts as DL
11+
12+
struct VerticalMassBorrowingLimiter{F, T}
13+
bmass::F
14+
ic::F
15+
qmin::T
16+
end
17+
function VerticalMassBorrowingLimiter(f::Fields.Field, qmin)
18+
bmass = similar(Spaces.level(f, 1))
19+
ic = similar(Spaces.level(f, 1))
20+
return VerticalMassBorrowingLimiter(bmass, ic, qmin)
21+
end
22+
23+
apply_limiter!(
24+
ρq::Fields.Field,
25+
ρ::Fields.Field,
26+
lim::VerticalMassBorrowingLimiter,
27+
) = apply_limiter!(ρq, ρ, lim, ClimaComms.device(axes(ρ)))
28+
29+
function apply_limiter!(
30+
ρq::Fields.Field,
31+
ρ::Fields.Field,
32+
lim::VerticalMassBorrowingLimiter,
33+
device::ClimaComms.AbstractCPUDevice,
34+
)
35+
Fields.bycolumn(axes(ρ)) do colidx
36+
cache =
37+
(; bmass = lim.bmass[colidx], ic = lim.ic[colidx], qmin = lim.qmin)
38+
columnwise_massborrow_cpu(ρq[colidx], ρ[colidx], cache)
39+
end
40+
end
41+
42+
function columnwise_massborrow_cpu(ρq::Fields.Field, ρ::Fields.Field, cache) # column fields
43+
(; bmass, ic, qmin) = cache
44+
45+
pdel = Fields.field_values(Fields.Δz_field(ρq))
46+
# loop over tracers
47+
nlevels = Spaces.nlevels(axes(ρq))
48+
@. ic = 0
49+
@. bmass = 0
50+
ρq_vals = Fields.field_values(ρq)
51+
# top to bottom
52+
for f in 1:DataLayouts.ncomponents(ρq_vals)
53+
for v in 1:nlevels
54+
CI = CartesianIndex(1, 1, f, v, 1)
55+
# new mass in the current layer
56+
pdel_v = DL.getindex_field(pdel, CI)
57+
nmass = DL.getindex_field(ρq_vals, CI) + bmass[] / pdel_v
58+
if nmass > qmin[f]
59+
# if new mass in the current layer is positive, don't borrow mass any more
60+
DL.setindex_field!(ρq_vals, nmass, CI)
61+
bmass[] = 0
62+
else
63+
# set mass to qmin in the current layer, and save bmass
64+
bmass[] = (nmass - qmin[f]) * pdel_v
65+
DL.setindex_field!(ρq_vals, qmin[f], CI)
66+
ic[] = ic[] + 1
67+
end
68+
end
69+
70+
# bottom to top
71+
for v in nlevels:-1:1
72+
CI = CartesianIndex(1, 1, f, v, 1)
73+
# if the surface layer still needs to borrow mass
74+
if bmass[] < 0
75+
pdel_v = DL.getindex_field(pdel, CI)
76+
# new mass in the current layer
77+
nmass = DL.getindex_field(ρq_vals, CI) + bmass[] / pdel_v
78+
if nmass > qmin[f]
79+
# if new mass in the current layer is positive, don't borrow mass any more
80+
DL.setindex_field!(ρq_vals, nmass, CI)
81+
bmass[] = 0
82+
else
83+
# if new mass in the current layer is negative, continue to borrow mass
84+
bmass[] = (nmass - qmin[f]) * pdel_v
85+
DL.setindex_field!(ρq_vals, qmin[f], CI)
86+
end
87+
end
88+
end
89+
end
90+
91+
return nothing
92+
end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#=
2+
julia --project
3+
using Revise; include(joinpath("test", "Limiters", "vertical_mass_borrowing_limiter.jl"))
4+
=#
5+
using ClimaComms
6+
ClimaComms.@import_required_backends
7+
using ClimaCore: Fields, Spaces, Limiters
8+
using ClimaCore.RecursiveApply
9+
using ClimaCore.Grids
10+
using ClimaCore.CommonGrids
11+
using Test
12+
13+
@testset "Vertical mass borrowing limiter" begin
14+
15+
FT = Float64
16+
grid = ExtrudedCubedSphereGrid(;
17+
z_elem = 10,
18+
z_min = 0,
19+
z_max = 1,
20+
radius = 10,
21+
h_elem = 10,
22+
n_quad_points = 4,
23+
)
24+
cspace = Spaces.ExtrudedFiniteDifferenceSpace(grid, Grids.CellCenter())
25+
26+
# Initialize fields
27+
coords = Fields.coordinate_field(cspace)
28+
ρ = map(coord -> 1.0, coords)
29+
x_scale = FT(1.2)
30+
y_scale = FT(1.5)
31+
q = similar(ρ)
32+
ρq = ρ .⊠ q
33+
q_ref = similar(ρ)
34+
ρq_ref = ρ .⊠ q_ref
35+
36+
limiter = Limiters.VerticalMassBorrowingLimiter(ρq, (0.0,))
37+
38+
# Limiters.compute_bounds!(limiter, ρq_ref, ρ)
39+
Limiters.apply_limiter!(ρq, ρ, limiter)
40+
q = RecursiveApply.rdiv.(ρq, ρ)
41+
42+
# @test
43+
end

0 commit comments

Comments
 (0)