|
1 | 1 | # Vertical grids |
2 | 2 |
|
3 | | -A few vertical grids are implemented within the [Grid Utilities](@ref ClimaOcean.GridUtils) module. |
4 | | - |
5 | | -### Exponential spacing |
6 | | - |
7 | | -The [`ExponentialCoordinate`](@ref) method returns a coordinate with interfaces that lie on an exponential profile. |
8 | | -By that, we mean that a uniformly discretized domain in the range ``[a, b]`` is mapped back onto itself via either |
9 | | - |
10 | | -```math |
11 | | -z \mapsto w(z) = b - (b - a) \frac{\exp{[(b - z) / h]} - 1}{\exp{[(b - a) / h]} - 1} \quad \text{(right biased)} |
12 | | -``` |
13 | | - |
14 | | -or |
15 | | - |
16 | | -```math |
17 | | -z \mapsto w(z) = a + (b - a) \frac{\exp{[(z - a) / h]} - 1}{\exp{[(b - a) / h]} - 1} \quad \text{(left biased)} |
18 | | -``` |
19 | | - |
20 | | -The exponential mappings above have an e-folding controlled by scale ``h``. |
21 | | -It worths noting that the exponential maps imply that the cell widths (distances between interfaces) grow linearly at a rate inversely proportional to ``h / (b - a)``. |
22 | | - |
23 | | -The right-biased map biases the interfaces being closer towards ``b``; the left-biased map biases the interfaces towards ``a``. |
24 | | - |
25 | | -At the limit ``h / (b - a) \to \infty`` both mappings reduce to identity (``w \to z``) and thus the grid becomes uniformly spaced. |
26 | | - |
27 | | - |
28 | | -!!! note "Oceanography-related bias" |
29 | | - For oceanographic purposes, the right-biased exponential mapping is usually more relevant as it implies finer vertical resolution closer to the ocean's surface. |
30 | | - |
31 | | -```@setup vgrids |
32 | | -using CairoMakie |
33 | | -set_theme!(Theme(Lines = (linewidth = 3,))) |
34 | | -CairoMakie.activate!(type="svg") |
35 | | -``` |
36 | | - |
37 | | -```@example vgrids |
38 | | -using ClimaOcean |
39 | | -using ClimaOcean.GridUtils: rightbiased_exponential_mapping, leftbiased_exponential_mapping |
40 | | -
|
41 | | -using CairoMakie |
42 | | -
|
43 | | -depth = 1000 |
44 | | -
|
45 | | -zb = - depth # left |
46 | | -zt = 0 # right |
47 | | -z = range(zb, stop=zt, length=501) |
48 | | -zp = range(zb, stop=zt, length=6) # coarser for plotting |
49 | | -
|
50 | | -axis_labels = (xlabel="uniform coordinate z / (b-a)", |
51 | | - ylabel="mapped coordinate w / (b-a)") |
52 | | -
|
53 | | -fig = Figure(size=(800, 350)) |
54 | | -axl = Axis(fig[1, 1]; title="left-biased map", axis_labels...) |
55 | | -axr = Axis(fig[1, 2]; title="right-biased map", axis_labels...) |
56 | | -
|
57 | | -for scale in [depth/20, depth/5, depth/2, 1e12*depth] |
58 | | - label = "h / (b-a) = $(scale / depth)" |
59 | | - lines!(axl, z / depth, leftbiased_exponential_mapping.(z, zb, zt, scale) / depth; label) |
60 | | - scatter!(axl, zp / depth, leftbiased_exponential_mapping.(zp, zb, zt, scale) / depth) |
61 | | -
|
62 | | - lines!(axr, z / depth, rightbiased_exponential_mapping.(z, zb, zt, scale) / depth; label) |
63 | | - scatter!(axr, zp / depth, rightbiased_exponential_mapping.(zp, zb, zt, scale) / depth) |
64 | | -end |
65 | | -
|
66 | | -Legend(fig[2, :], axl, orientation = :horizontal) |
67 | | -
|
68 | | -fig |
69 | | -``` |
70 | | - |
71 | | -Note that the smallest the ratio ``h / (b-a)`` is, the more finely-packed are the mapped points towards the left or right side of the domain. |
72 | | - |
73 | | - |
74 | | -Let's see to use [`ExponentialCoordinate`](@ref) works. Here we construct a vertical grid with 10 cells that goes down to 1000 meters depth. |
75 | | - |
76 | | -```@example vgrids |
77 | | -using ClimaOcean |
78 | | -
|
79 | | -Nz = 10 |
80 | | -depth = 1000 |
81 | | -left = zb = -depth |
82 | | -right = 0 |
83 | | -
|
84 | | -z = ExponentialCoordinate(Nz, left, right) |
85 | | -``` |
86 | | - |
87 | | -By default, the oceanographically-relevant right-biased map was used. |
88 | | -Also note above, that the default e-folding scale (`scale = (right - left) / 5`) was used. |
89 | | -If we don't prescribe a value for `right` then its default oceanographically-appropriate value of 0 is used. |
90 | | - |
91 | | -We can inspect the interfaces of the coordinate via |
92 | | - |
93 | | -```@example vgrids |
94 | | -[z(k) for k in 1:Nz+1] |
95 | | -``` |
96 | | - |
97 | | -To demonstrate how the scale ``h`` affects the coordinate, we construct below two such exponential |
98 | | -coordinates: the first with ``h / (b - a) = 1/5`` and the second with ``h / (b - a) = 1/2``. |
99 | | - |
100 | | -```@example vgrids |
101 | | -using ClimaOcean |
102 | | -using Oceananigans |
103 | | -
|
104 | | -Nz = 10 |
105 | | -depth = 1000 |
106 | | -
|
107 | | -
|
108 | | -using CairoMakie |
109 | | -
|
110 | | -fig = Figure() |
111 | | -
|
112 | | -scale = depth / 5 |
113 | | -z = ExponentialCoordinate(Nz, -depth; scale) |
114 | | -grid = RectilinearGrid(; size=Nz, z, topology=(Flat, Flat, Bounded)) |
115 | | -zf = znodes(grid, Face()) |
116 | | -zc = znodes(grid, Center()) |
117 | | -Δz = zspacings(grid, Center())[1, 1, 1:Nz] |
118 | | -
|
119 | | -
|
120 | | -axΔz1 = Axis(fig[1, 1]; xlabel = "z-spacing (m)", ylabel = "z (m)", title = "scale = depth / 5") |
121 | | -axz1 = Axis(fig[1, 2]) |
122 | | -linkaxes!(axΔz1, axz1) |
123 | | -
|
124 | | -lΔz = lines!(axΔz1, - zf * (depth/scale) / Nz, zf, color=(:purple, 0.3)) |
125 | | -scatter!(axΔz1, Δz, zc) |
126 | | -hidespines!(axΔz1, :t, :r) |
127 | | -
|
128 | | -lines!(axz1, [0, 0], [-depth, 0], color=:gray) |
129 | | -scatter!(axz1, 0 * zf, zf, marker=:hline, color=:gray, markersize=20) |
130 | | -scatter!(axz1, 0 * zc, zc) |
131 | | -hidedecorations!(axz1) |
132 | | -hidespines!(axz1) |
133 | | -
|
134 | | -
|
135 | | -scale = depth / 2 |
136 | | -z = ExponentialCoordinate(Nz, -depth; scale) |
137 | | -grid = RectilinearGrid(; size=Nz, z, topology=(Flat, Flat, Bounded)) |
138 | | -zf = znodes(grid, Face()) |
139 | | -zc = znodes(grid, Center()) |
140 | | -Δz = zspacings(grid, Center())[1, 1, 1:Nz] |
141 | | -
|
142 | | -axΔz2 = Axis(fig[1, 3]; xlabel = "z-spacing (m)", ylabel = "z (m)", title = "scale = depth / 2") |
143 | | -axz2 = Axis(fig[1, 4]) |
144 | | -linkaxes!(axΔz2, axz2) |
145 | | -
|
146 | | -lΔz = lines!(axΔz2, - zf * (depth/scale) / Nz, zf, color=(:purple, 0.3)) |
147 | | -scatter!(axΔz2, Δz, zc) |
148 | | -hidespines!(axΔz2, :t, :r) |
149 | | -
|
150 | | -lines!(axz2, [0, 0], [-depth, 0], color=:gray) |
151 | | -scatter!(axz2, 0 * zf, zf, marker=:hline, color=:gray, markersize=20) |
152 | | -scatter!(axz2, 0 * zc, zc) |
153 | | -hidedecorations!(axz2) |
154 | | -hidespines!(axz2) |
155 | | -
|
156 | | -
|
157 | | -colsize!(fig.layout, 2, Relative(0.1)) |
158 | | -colsize!(fig.layout, 4, Relative(0.1)) |
159 | | -
|
160 | | -legend = Legend(fig[2, :], [lΔz], ["slope = (depth / scale) / Nz"], orientation = :horizontal) |
161 | | -
|
162 | | -fig |
163 | | -``` |
164 | | - |
165 | | -For both grids, the spacings grow linearly with depth and sum up to the total depth. |
166 | | -But with the larger ``h / L`` is, the smaller the rate is that the spacings increase with depth. |
167 | | - |
168 | | -A ridiculously large value of ``h / L`` (approximating infinity) gives a uniform grid: |
169 | | - |
170 | | -```@example vgrids |
171 | | -z = ExponentialCoordinate(Nz, depth, scale = 1e12*depth) |
172 | | -[z(k) for k in 1:Nz+1] |
173 | | -``` |
174 | | - |
175 | | -A downside of [`ExponentialCoordinate`](@ref) is that we don't have tight control on the minimum |
176 | | -spacing at the surface. |
177 | | -To prescribe the surface spacing we need to play around with the scale ``h`` and the number of vertical cells ``N_z``. |
178 | | - |
179 | | -### Stretched ``z`` faces |
180 | | - |
181 | | -The [`StretchedCoordinate`](@ref) method allows a tighter control on the vertical spacing at the surface. |
182 | | -That is, we can prescribe a constant spacing over the top `surface_layer_height` below which the grid spacing |
183 | | -increases following a prescribed stretching law. |
184 | | -The downside here is that neither the final grid depth nor the total number of vertical cells can be prescribed. |
185 | | -The final depth we get is greater or equal from what we prescribe via the keyword argument `depth`. |
186 | | -Also, the total number of cells we end up with depends on the stretching law. |
187 | | - |
188 | | -The three grids below have constant 20-meter spacing for the top 120 meters. |
189 | | -We prescribe to all three a `depth = 750` meters and we apply power-law stretching for depths below 120 meters. |
190 | | -The bigger the power-law stretching factor is, the further the last interface goes beyond the prescribed depth and/or with less total number of cells. |
191 | | - |
192 | | -```@example vgrids |
193 | | -depth = 750 |
194 | | -surface_layer_Δz = 20 |
195 | | -surface_layer_height = 120 |
196 | | -
|
197 | | -z = StretchedCoordinate(; depth, |
198 | | - surface_layer_Δz, |
199 | | - surface_layer_height, |
200 | | - stretching = PowerLawStretching(1.08)) |
201 | | -grid = RectilinearGrid(; size=length(z), z, topology=(Flat, Flat, Bounded)) |
202 | | -zf = znodes(grid, Face()) |
203 | | -zc = znodes(grid, Center()) |
204 | | -Δz = zspacings(grid, Center()) |
205 | | -Δz = view(Δz, 1, 1, :) # for plotting |
206 | | -
|
207 | | -fig = Figure(size=(800, 550)) |
208 | | -
|
209 | | -axΔz1 = Axis(fig[1, 1]; |
210 | | - xlabel = "z-spacing (m)", |
211 | | - ylabel = "z (m)", |
212 | | - title = "PowerLawStretching(1.09)\n $(length(zf)-1) cells\n bottom interface at z = $(zf[1]) m\n ") |
213 | | -
|
214 | | -axz1 = Axis(fig[1, 2]) |
215 | | -
|
216 | | -ldepth = hlines!(axΔz1, -depth, color = :salmon, linestyle=:dash) |
217 | | -lzbottom = hlines!(axΔz1, zf[1], color = :grey) |
218 | | -scatter!(axΔz1, Δz, zc) |
219 | | -hidespines!(axΔz1, :t, :r) |
220 | | -
|
221 | | -lines!(axz1, [0, 0], [zf[1], 0], color=:gray) |
222 | | -scatter!(axz1, 0 * zf, zf, marker=:hline, color=:gray, markersize=20) |
223 | | -scatter!(axz1, 0 * zc, zc) |
224 | | -hidedecorations!(axz1) |
225 | | -hidespines!(axz1) |
226 | | -
|
227 | | -
|
228 | | -z = StretchedCoordinate(; depth, |
229 | | - surface_layer_Δz, |
230 | | - surface_layer_height, |
231 | | - stretching = PowerLawStretching(1.04)) |
232 | | -grid = RectilinearGrid(; size=length(z), z, topology=(Flat, Flat, Bounded)) |
233 | | -zf = znodes(grid, Face()) |
234 | | -zc = znodes(grid, Center()) |
235 | | -Δz = zspacings(grid, Center()) |
236 | | -Δz = view(Δz, 1, 1, :) # for plotting |
237 | | -
|
238 | | -axΔz2 = Axis(fig[1, 3]; |
239 | | - xlabel = "z-spacing (m)", |
240 | | - ylabel = "z (m)", |
241 | | - title = "PowerLawStretching(1.04)\n $(length(zf)-1) cells\n bottom interface at z = $(zf[1]) m\n ") |
242 | | -axz2 = Axis(fig[1, 4]) |
243 | | -
|
244 | | -ldepth = hlines!(axΔz2, -depth, color = :salmon, linestyle=:dash) |
245 | | -lzbottom = hlines!(axΔz2, zf[1], color = :grey) |
246 | | -scatter!(axΔz2, Δz, zc) |
247 | | -hidespines!(axΔz2, :t, :r) |
248 | | -
|
249 | | -lines!(axz2, [0, 0], [zf[1], 0], color=:gray) |
250 | | -scatter!(axz2, 0 * zf, zf, marker=:hline, color=:gray, markersize=20) |
251 | | -scatter!(axz2, 0 * zc, zc) |
252 | | -hidedecorations!(axz2) |
253 | | -hidespines!(axz2) |
254 | | -
|
255 | | -
|
256 | | -z = StretchedCoordinate(; depth, |
257 | | - surface_layer_Δz, |
258 | | - surface_layer_height, |
259 | | - stretching = PowerLawStretching(1.04), |
260 | | - constant_bottom_spacing_depth = 500) |
261 | | -grid = RectilinearGrid(; size=length(z), z, topology=(Flat, Flat, Bounded)) |
262 | | -zf = znodes(grid, Face()) |
263 | | -zc = znodes(grid, Center()) |
264 | | -Δz = zspacings(grid, Center()) |
265 | | -Δz = view(Δz, 1, 1, :) # for plotting |
266 | | -
|
267 | | -axΔz3 = Axis(fig[1, 5]; |
268 | | - xlabel = "z-spacing (m)", |
269 | | - ylabel = "z (m)", |
270 | | - title = "PowerLawStretching(1.04)\n $(length(zf)-1) cells\n bottom interface at z = $(zf[1]) m\n constant spacing below 500 m") |
271 | | -axz3 = Axis(fig[1, 6]) |
272 | | -
|
273 | | -ldepth = hlines!(axΔz3, -depth, color = :salmon, linestyle=:dash) |
274 | | -lzbottom = hlines!(axΔz3, zf[1], color = :grey) |
275 | | -scatter!(axΔz3, Δz, zc) |
276 | | -
|
277 | | -hidespines!(axΔz3, :t, :r) |
278 | | -
|
279 | | -lines!(axz3, [0, 0], [zf[1], 0], color=:gray) |
280 | | -scatter!(axz3, 0 * zf, zf, marker=:hline, color=:gray, markersize=20) |
281 | | -scatter!(axz3, 0 * zc, zc) |
282 | | -hidedecorations!(axz3) |
283 | | -hidespines!(axz3) |
284 | | -
|
285 | | -
|
286 | | -linkaxes!(axΔz1, axz1, axΔz2, axz2, axΔz3, axz3) |
287 | | -
|
288 | | -Legend(fig[2, :], [ldepth, lzbottom], ["prescribed depth", "bottom-most z interface"], orientation = :horizontal) |
289 | | -
|
290 | | -colsize!(fig.layout, 2, Relative(0.1)) |
291 | | -colsize!(fig.layout, 4, Relative(0.1)) |
292 | | -colsize!(fig.layout, 6, Relative(0.1)) |
293 | | -
|
294 | | -fig |
295 | | -``` |
| 3 | +We construct vertical coordinates using `Oceananigans.ExponentialCoordinate` and |
| 4 | +`Oceananigans.ConstantToStretchedCoordinate`. The |
| 5 | +[Grids section](https://clima.github.io/OceananigansDocumentation/dev/grids/#Coordinate-helper-utilities) |
| 6 | +in the Oceananigans Documentation includes a mini-tutorial on how the above-mentioned methods |
| 7 | +can be used to generate a coordinate of your liking. |
0 commit comments