-
Notifications
You must be signed in to change notification settings - Fork 186
/
partial_cell_bottom.jl
148 lines (111 loc) · 5.65 KB
/
partial_cell_bottom.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using Oceananigans.Utils: prettysummary
using Oceananigans.Fields: fill_halo_regions!
using Printf
#####
##### PartialCellBottom
#####
struct PartialCellBottom{H, E} <: AbstractGridFittedBottom{H}
bottom_height :: H
minimum_fractional_cell_height :: E
end
const PCBIBG = ImmersedBoundaryGrid{<:Any, <:Any, <:Any, <:Any, <:Any, <:PartialCellBottom}
function Base.summary(ib::PartialCellBottom)
hmax = maximum(parent(ib.bottom_height))
hmin = minimum(parent(ib.bottom_height))
hmean = mean(parent(ib.bottom_height))
summary1 = "PartialCellBottom("
summary2 = string("mean(z)=", prettysummary(hmean),
", min(z)=", prettysummary(hmin),
", max(z)=", prettysummary(hmax),
", ϵ=", prettysummary(ib.minimum_fractional_cell_height))
summary3 = ")"
return summary1 * summary2 * summary3
end
Base.summary(ib::PartialCellBottom{<:Function}) = @sprintf("PartialCellBottom(%s, ϵ=%.1f)",
prettysummary(ib.bottom_height, false),
prettysummary(ib.minimum_fractional_cell_height))
function Base.show(io::IO, ib::PartialCellBottom)
print(io, summary(ib), '\n')
print(io, "├── bottom_height: ", prettysummary(ib.bottom_height), '\n')
print(io, "└── minimum_fractional_cell_height: ", prettysummary(ib.minimum_fractional_cell_height))
end
"""
PartialCellBottom(bottom_height; minimum_fractional_cell_height=0.2)
Return `PartialCellBottom` representing an immersed boundary with "partial"
bottom cells. That is, the height of the bottommost cell in each column is reduced
to fit the provided `bottom_height`, which may be a `Field`, `Array`, or function
of `(x, y)`.
The height of partial bottom cells is greater than
```
minimum_fractional_cell_height * Δz,
```
where `Δz` is the original height of the bottom cell underlying grid.
"""
function PartialCellBottom(bottom_height; minimum_fractional_cell_height=0.2)
return PartialCellBottom(bottom_height, minimum_fractional_cell_height)
end
function ImmersedBoundaryGrid(grid, ib::PartialCellBottom)
bottom_field = Field{Center, Center, Nothing}(grid)
set!(bottom_field, ib.bottom_height)
fill_halo_regions!(bottom_field)
new_ib = PartialCellBottom(bottom_field, ib.minimum_fractional_cell_height)
TX, TY, TZ = topology(grid)
return ImmersedBoundaryGrid{TX, TY, TZ}(grid, new_ib)
end
function on_architecture(arch, ib::PartialCellBottom{<:Field})
architecture(ib.bottom_height) == arch && return ib
arch_grid = on_architecture(arch, ib.bottom_height.grid)
new_bottom_height = Field{Center, Center, Nothing}(arch_grid)
copyto!(parent(new_bottom_height), parent(ib.bottom_height))
return PartialCellBottom(new_bottom_height, ib.minimum_fractional_cell_height)
end
Adapt.adapt_structure(to, ib::PartialCellBottom) = PartialCellBottom(adapt(to, ib.bottom_height.data),
ib.minimum_fractional_cell_height)
on_architecture(to, ib::PartialCellBottom) = PartialCellBottom(on_architecture(to, ib.bottom_height.data),
on_architecture(to, ib.minimum_fractional_cell_height))
"""
--x--
∘ k+1
k+1 --x-- ↑ <- node z
∘ k | Δz
k --x-- ↓
Criterion is h ≥ z - ϵ Δz
"""
@inline function _immersed_cell(i, j, k, underlying_grid, ib::PartialCellBottom)
# Face node above current cell
z = znode(i, j, k+1, underlying_grid, c, c, f)
h = @inbounds ib.bottom_height[i, j, 1]
return z ≤ h
end
@inline bottom_cell(i, j, k, ibg::PCBIBG) = !immersed_cell(i, j, k, ibg.underlying_grid, ibg.immersed_boundary) &
immersed_cell(i, j, k-1, ibg.underlying_grid, ibg.immersed_boundary)
@inline function Δzᶜᶜᶜ(i, j, k, ibg::PCBIBG)
underlying_grid = ibg.underlying_grid
ib = ibg.immersed_boundary
# Get node at face above and defining nodes on c,c,f
z = znode(i, j, k+1, underlying_grid, c, c, f)
# Get bottom height and fractional Δz parameter
h = @inbounds ib.bottom_height[i, j, 1]
ϵ = ibg.immersed_boundary.minimum_fractional_cell_height
# Are we in a bottom cell?
at_the_bottom = bottom_cell(i, j, k, ibg)
full_Δz = Δzᶜᶜᶜ(i, j, k, ibg.underlying_grid)
partial_Δz = max(ϵ * full_Δz, z - h)
return ifelse(at_the_bottom, partial_Δz, full_Δz)
end
@inline function Δzᶜᶜᶠ(i, j, k, ibg::PCBIBG)
just_above_bottom = bottom_cell(i, j, k-1, ibg)
zc = znode(i, j, k, ibg.underlying_grid, c, c, c)
zf = znode(i, j, k, ibg.underlying_grid, c, c, f)
full_Δz = Δzᶜᶜᶠ(i, j, k, ibg.underlying_grid)
partial_Δz = zc - zf + Δzᶜᶜᶜ(i, j, k-1, ibg) / 2
Δz = ifelse(just_above_bottom, partial_Δz, full_Δz)
return Δz
end
@inline Δzᶠᶜᶜ(i, j, k, ibg::PCBIBG) = min(Δzᶜᶜᶜ(i-1, j, k, ibg), Δzᶜᶜᶜ(i, j, k, ibg))
@inline Δzᶜᶠᶜ(i, j, k, ibg::PCBIBG) = min(Δzᶜᶜᶜ(i, j-1, k, ibg), Δzᶜᶜᶜ(i, j, k, ibg))
@inline Δzᶠᶠᶜ(i, j, k, ibg::PCBIBG) = min(Δzᶠᶜᶜ(i, j-1, k, ibg), Δzᶠᶜᶜ(i, j, k, ibg))
@inline Δzᶠᶜᶠ(i, j, k, ibg::PCBIBG) = min(Δzᶜᶜᶠ(i-1, j, k, ibg), Δzᶜᶜᶠ(i, j, k, ibg))
@inline Δzᶜᶠᶠ(i, j, k, ibg::PCBIBG) = min(Δzᶜᶜᶠ(i, j-1, k, ibg), Δzᶜᶜᶠ(i, j, k, ibg))
@inline Δzᶠᶠᶠ(i, j, k, ibg::PCBIBG) = min(Δzᶠᶜᶠ(i, j-1, k, ibg), Δzᶠᶜᶠ(i, j, k, ibg))
@inline z_bottom(i, j, ibg::PCBIBG) = @inbounds ibg.immersed_boundary.bottom_height[i, j, 1]