Skip to content

Commit d9ddf59

Browse files
taimoorsohailnavidcysimone-silvestri
authored
Add Bathymetry metadata to allow using diverse bathymetry datasets (#518)
* Cleaning up directory tree for metadata of datasets and introducing bathymetry metadata * New files * Some more changes to get the metadata toi work * Moved everything into DataWrangling * Moved around the necessary files * Making the ETOPO Metadata align with other datasets; and using same download_dataset() function * Moved Bathymetry.jl out of DataWrangling * Adding the ETOPO Metadata * Trying to create a native grid based on ETOPO data * Clean up * Added the missing imports to ETOPO?? * Fixed native_grid issue * Tried to get size(metadata) to work... * Apply suggestions from code review * Finalised ETOPO implementation * drop local dir references * bit of cleanup * don't import if not extend * bump minor release * undo bump minor; bump patch release instead * add default regrid_bathymetry w ETOPO * add ETOPOBathymetry -> ETOPO2022 * update regrid_bathymetry(target_grid::DistributedGrid,...) * Trying to figure out regrid_bathymetry * Update src/DataWrangling/ETOPO/ETOPO.jl * regrid_bathymetry downloads if needed + few tweaks * Update src/Bathymetry.jl * fix docstrings * cleanup + add @root in download_dataset * Metadatum only has one parameter * clean imports * better comment * import relative * cleanup; we need <:Any, <:Any... why? * fix import * add ETOPO2022 in test_downloading * fix test * delete * test both methods * add 'hidden' _regrid_bathymetry that does not download * fix tests * easier way to trigger download of ETOPO bathymetry * only import what's needed * Apply suggestions from code review Co-authored-by: Simone Silvestri <[email protected]> * distributed might need to download * homogenise info msg * debug * debug * debug * comment out MultiYearJRA55; data is not available * fix distributed_utils tests to use new topography metadata * uncomment tests * cleanup * cleanup * cleaner * can't have nested @root * ensure the new_grid uses the halos passed on to it * less intepolation_passes * better comment --------- Co-authored-by: Navid C. Constantinou <[email protected]> Co-authored-by: Simone Silvestri <[email protected]>
1 parent 6c9bba3 commit d9ddf59

File tree

14 files changed

+256
-251
lines changed

14 files changed

+256
-251
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name = "ClimaOcean"
22
uuid = "0376089a-ecfe-4b0e-a64f-9c555d74d754"
33
license = "MIT"
44
authors = ["Climate Modeling Alliance and contributors"]
5-
version = "0.6.8"
5+
version = "0.6.9"
66

77
[deps]
88
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
@@ -31,8 +31,8 @@ Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"
3131
ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"
3232

3333
[weakdeps]
34-
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
3534
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
35+
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
3636
Reactant = "3c362404-f566-11ee-1572-e11a4b42c853"
3737

3838
[extensions]

src/Bathymetry.jl

Lines changed: 93 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,36 @@
11
module Bathymetry
22

3-
export regrid_bathymetry, retrieve_bathymetry, download_bathymetry
3+
export regrid_bathymetry
44

5+
using Downloads
56
using ImageMorphology
6-
using ..DataWrangling: download_progress
7-
using Oceananigans.DistributedComputations
8-
7+
using KernelAbstractions: @kernel, @index
98
using Oceananigans
109
using Oceananigans.Architectures: architecture, on_architecture
11-
using Oceananigans.DistributedComputations: DistributedGrid, reconstruct_global_grid, barrier!, all_reduce
12-
using Oceananigans.Grids: halo_size, λnodes, φnodes, x_domain, y_domain, topology
13-
using Oceananigans.Utils: pretty_filesize, launch!
14-
using Oceananigans.Fields: interpolate!
1510
using Oceananigans.BoundaryConditions
16-
using KernelAbstractions: @kernel, @index
17-
using JLD2
18-
11+
using Oceananigans.DistributedComputations
12+
using Oceananigans.DistributedComputations: DistributedGrid, reconstruct_global_grid, all_reduce
13+
using Oceananigans.Fields: interpolate!
14+
using Oceananigans.Grids: x_domain, y_domain, topology
15+
using Oceananigans.Utils: launch!
1916
using OffsetArrays
20-
using ClimaOcean
21-
2217
using NCDatasets
23-
using Downloads
2418
using Printf
2519
using Scratch
2620

27-
download_bathymetry_cache::String = ""
28-
function __init__()
29-
global download_bathymetry_cache = @get_scratch!("Bathymetry")
30-
end
31-
32-
# etopo_url = "https://www.ngdc.noaa.gov/thredds/fileServer/global/ETOPO2022/60s/60s_surface_elev_netcdf/" *
33-
# "ETOPO_2022_v1_60s_N90W180_surface.nc"
34-
35-
etopo_url = "https://www.dropbox.com/scl/fi/6pwalcuuzgtpanysn4h6f/" *
36-
"ETOPO_2022_v1_60s_N90W180_surface.nc?rlkey=2t7890ruyk4nd5t5eov5768lt&st=yfxsy1lu&dl=0"
37-
38-
"""
39-
download_bathymetry(; dir = download_bathymetry_cache,
40-
url = etopo_url,
41-
filename = "ETOPO_2022_v1_60s_N90W180_surface.nc")
42-
43-
Download the bathymetry from `url` and saves it under `filename` in the directory `dir` and
44-
return the full filepath where the bathymetry is saved.
45-
"""
46-
function download_bathymetry(; url = etopo_url,
47-
dir = download_bathymetry_cache,
48-
filename = "ETOPO_2022_v1_60s_N90W180_surface.nc")
49-
50-
filepath = joinpath(dir, filename)
51-
52-
#TODO: embed this into a @root macro; see failed attempts in https://github.com/CliMA/ClimaOcean.jl/pull/391
53-
if !isfile(filepath)
54-
Downloads.download(url, filepath; progress=download_progress)
55-
end
21+
using ..DataWrangling: Metadatum, native_grid, metadata_path, download_dataset
22+
using ..DataWrangling.ETOPO: ETOPO2022
5623

57-
return filepath
58-
end
24+
# methods specific to bathymetric datasets live within dataset modules
5925

6026
"""
61-
regrid_bathymetry(target_grid;
27+
regrid_bathymetry(target_grid, metadata;
6228
height_above_water = nothing,
6329
minimum_depth = 0,
64-
dir = download_bathymetry_cache,
65-
url = ClimaOcean.Bathymetry.etopo_url,
66-
filename = "ETOPO_2022_v1_60s_N90W180_surface.nc",
67-
interpolation_passes = 1,
68-
major_basins = 1)
30+
major_basins = 1
31+
interpolation_passes = 1)
6932
70-
Return bathymetry associated with the NetCDF file at `path = joinpath(dir, filename)` regridded onto `target_grid`.
71-
If `path` does not exist, then a download is attempted from `joinpath(url, filename)`.
33+
Return bathymetry that corresponds to `metadata` onto `target_grid`.
7234
7335
Arguments
7436
=========
@@ -84,13 +46,6 @@ Keyword Arguments
8446
- `minimum_depth`: minimum depth for the shallow regions, defined as a positive value.
8547
`h > - minimum_depth` is considered land. Default: 0.
8648
87-
- `dir`: directory of the bathymetry-containing file. Default: `download_bathymetry_cache`.
88-
89-
- `filename`: file containing bathymetric data. Must be netCDF with fields:
90-
1. `lat` vector of latitude nodes
91-
2. `lon` vector of longitude nodes
92-
3. `z` matrix of depth values
93-
9449
- `interpolation_passes`: regridding/interpolation passes. The bathymetry is interpolated in
9550
`interpolation_passes - 1` intermediate steps. The more the interpolation
9651
steps the smoother the final bathymetry becomes.
@@ -115,15 +70,27 @@ Keyword Arguments
11570
the smallest basins are removed first. `major_basins = 1` retains only the largest basin.
11671
If `Inf` then no basins are removed. Default: 1.
11772
"""
118-
function regrid_bathymetry(target_grid;
73+
function regrid_bathymetry(target_grid, metadata;
11974
height_above_water = nothing,
12075
minimum_depth = 0,
121-
dir = download_bathymetry_cache,
122-
url = etopo_url,
123-
filename = "ETOPO_2022_v1_60s_N90W180_surface.nc",
12476
interpolation_passes = 1,
12577
major_basins = 1) # Allow an `Inf` number of "lakes"
12678

79+
download_dataset(metadata)
80+
81+
return _regrid_bathymetry(target_grid, metadata;
82+
height_above_water,
83+
minimum_depth,
84+
interpolation_passes,
85+
major_basins)
86+
end
87+
88+
# regrid the bathymetry assuming the data is already downloaded
89+
function _regrid_bathymetry(target_grid, metadata;
90+
height_above_water,
91+
minimum_depth,
92+
interpolation_passes,
93+
major_basins)
12794
if isinteger(interpolation_passes)
12895
interpolation_passes = convert(Int, interpolation_passes)
12996
end
@@ -132,27 +99,17 @@ function regrid_bathymetry(target_grid;
13299
return throw(ArgumentError("interpolation_passes has to be an integer ≥ 1"))
133100
end
134101

135-
filepath = download_bathymetry(; url, dir, filename)
136-
dataset = Dataset(filepath, "r")
102+
arch = architecture(target_grid)
137103

104+
bathymetry_native_grid = native_grid(metadata, arch; halo = (10, 10, 1))
138105
FT = eltype(target_grid)
139106

140-
φ_data = dataset["lat"][:]
141-
λ_data = dataset["lon"][:]
142-
z_data = convert(Array{FT}, dataset["z"][:, :])
143-
144-
# Convert longitude from (-180, 180) to (0, 360)
145-
λ_data .+= 180
146-
Nhx = size(z_data, 1)
147-
z_data = circshift(z_data, (Nhx ÷ 2, 0))
107+
filepath = metadata_path(metadata)
108+
dataset = Dataset(filepath, "r")
148109

110+
z_data = convert(Array{FT}, dataset["z"][:, :])
149111
close(dataset)
150112

151-
# Diagnose target grid information
152-
arch = architecture(target_grid)
153-
λ_data = λ_data |> Array{BigFloat}
154-
φ_data = φ_data |> Array{BigFloat}
155-
156113
if !isnothing(height_above_water)
157114
# Overwrite the height of cells above water.
158115
# This has an impact on reconstruction. Greater height_above_water reduces total
@@ -161,27 +118,7 @@ function regrid_bathymetry(target_grid;
161118
z_data[land] .= height_above_water
162119
end
163120

164-
# Infer the "native grid" of the bathymetry data and make a bathymetry field.
165-
Δλ = λ_data[2] - λ_data[1]
166-
Δφ = φ_data[2] - φ_data[1]
167-
168-
λ₁_data = convert(Float64, λ_data[1] - Δλ / 2)
169-
λ₂_data = convert(Float64, λ_data[end] + Δλ / 2)
170-
φ₁_data = convert(Float64, φ_data[1] - Δφ / 2)
171-
φ₂_data = convert(Float64, φ_data[end] + Δφ / 2)
172-
173-
Nxn = length(λ_data)
174-
Nyn = length(φ_data)
175-
Nzn = 1
176-
177-
native_grid = LatitudeLongitudeGrid(arch, Float32;
178-
size = (Nxn, Nyn, Nzn),
179-
latitude = (φ₁_data, φ₂_data),
180-
longitude = (λ₁_data, λ₂_data),
181-
z = (0, 1),
182-
halo = (10, 10, 1))
183-
184-
native_z = Field{Center, Center, Nothing}(native_grid)
121+
native_z = Field{Center, Center, Nothing}(bathymetry_native_grid)
185122
set!(native_z, z_data)
186123
fill_halo_regions!(native_z)
187124

@@ -201,6 +138,57 @@ function regrid_bathymetry(target_grid;
201138
return target_z
202139
end
203140

141+
"""
142+
regrid_bathymetry(target_grid; dataset=ETOPO2022(), kw...)
143+
144+
Regrid bathymetry from `dataset` onto `target_grid`. Default: `dataset = ETOPO2022()`.
145+
"""
146+
function regrid_bathymetry(target_grid; dataset = ETOPO2022(), kw...)
147+
metadatum = Metadatum(:bottom_height; dataset)
148+
return regrid_bathymetry(target_grid, metadatum; kw...)
149+
end
150+
151+
# Regridding bathymetry for distributed grids, we handle the whole process
152+
# on just one rank, and share the results with the other processors.
153+
function regrid_bathymetry(target_grid::DistributedGrid, metadata;
154+
height_above_water = nothing,
155+
minimum_depth = 0,
156+
interpolation_passes = 1,
157+
major_basins = 1)
158+
159+
download_dataset(metadata)
160+
161+
global_grid = reconstruct_global_grid(target_grid)
162+
global_grid = on_architecture(CPU(), global_grid)
163+
arch = architecture(target_grid)
164+
Nx, Ny, _ = size(global_grid)
165+
166+
# If all ranks open a gigantic bathymetry and the memory is
167+
# shared, we could easily have OOM errors.
168+
# We perform the reconstruction only on rank 0 and share the result.
169+
bottom_height = if arch.local_rank == 0
170+
# use regrid method that assumes data is downloaded
171+
bottom_field = _regrid_bathymetry(global_grid, metadata;
172+
height_above_water, minimum_depth, interpolation_passes, major_basins)
173+
bottom_field.data[1:Nx, 1:Ny, 1]
174+
else
175+
zeros(Nx, Ny)
176+
end
177+
178+
# Synchronize
179+
Oceananigans.DistributedComputations.global_barrier(arch.communicator)
180+
181+
# Share the result (can we share SubArrays?)
182+
bottom_height = all_reduce(+, bottom_height, arch)
183+
184+
# Partition the result
185+
local_bottom_height = Field{Center, Center, Nothing}(target_grid)
186+
set!(local_bottom_height, bottom_height)
187+
fill_halo_regions!(local_bottom_height)
188+
189+
return local_bottom_height
190+
end
191+
204192
@kernel function _enforce_minimum_depth!(target_z, minimum_depth)
205193
i, j = @index(Global, NTuple)
206194
z = @inbounds target_z[i, j, 1]
@@ -240,17 +228,20 @@ function interpolate_bathymetry_in_passes(native_z, target_grid;
240228
old_z = native_z
241229
TX, TY = topology(target_grid)
242230

231+
Hx, Hy, Hz = Oceananigans.halo_size(native_z.grid)
232+
243233
@info "Interpolation passes of bathymetry size $(size(old_z)) onto a $gridtype target grid of size $Nt:"
244234
for pass = 1:passes - 1
245235
new_size = (Nλ[pass], Nφ[pass], 1)
246-
@info " Performing pass $pass to size $new_size."
236+
@info " pass $pass to size $new_size"
247237

248238
new_grid = LatitudeLongitudeGrid(architecture(target_grid), Float32,
249239
size = new_size,
250240
latitude = (latitude[1], latitude[2]),
251241
longitude = (longitude[1], longitude[2]),
252242
z = (0, 1),
253-
topology = (TX, TY, Bounded))
243+
topology = (TX, TY, Bounded),
244+
halo = (Hx, Hy, Hz))
254245

255246
new_z = Field{Center, Center, Nothing}(new_grid)
256247

@@ -266,39 +257,6 @@ function interpolate_bathymetry_in_passes(native_z, target_grid;
266257
return target_z
267258
end
268259

269-
# Regridding bathymetry for distributed grids, we handle the whole process
270-
# on just one rank, and share the results with the other processors.
271-
function regrid_bathymetry(target_grid::DistributedGrid; kw...)
272-
global_grid = reconstruct_global_grid(target_grid)
273-
global_grid = on_architecture(CPU(), global_grid)
274-
arch = architecture(target_grid)
275-
Nx, Ny, _ = size(global_grid)
276-
277-
# If all ranks open a gigantic bathymetry and the memory is
278-
# shared, we could easily have OOM errors.
279-
# We perform the reconstruction only on rank 0 and share the result.
280-
bottom_height = if arch.local_rank == 0
281-
bottom_field = regrid_bathymetry(global_grid; kw...)
282-
bottom_field.data[1:Nx, 1:Ny, 1]
283-
else
284-
zeros(Nx, Ny)
285-
end
286-
287-
# Synchronize
288-
Oceananigans.DistributedComputations.global_barrier(arch.communicator)
289-
290-
# Share the result (can we share SubArrays?)
291-
bottom_height = all_reduce(+, bottom_height, arch)
292-
293-
# Partition the result
294-
local_bottom_height = Field{Center, Center, Nothing}(target_grid)
295-
set!(local_bottom_height, bottom_height)
296-
fill_halo_regions!(local_bottom_height)
297-
298-
return local_bottom_height
299-
end
300-
301-
302260
"""
303261
remove_minor_basins!(z_data, keep_major_basins)
304262
@@ -402,39 +360,4 @@ function remove_minor_basins!(zb, keep_major_basins)
402360
return nothing
403361
end
404362

405-
"""
406-
retrieve_bathymetry(grid, filename; kw...)
407-
408-
Retrieve the bathymetry data from a file or generate it using a grid and save it to a file.
409-
410-
Arguments
411-
=========
412-
413-
- `grid`: The grid used to generate the bathymetry data.
414-
- `filename`: The name of the file to read or save the bathymetry data.
415-
- `kw...`: Additional keyword arguments.
416-
417-
Returns
418-
=======
419-
420-
- `bottom_height`: The retrieved or generated bathymetry data.
421-
422-
If the specified file exists, the function reads the bathymetry data from the file.
423-
Otherwise, it generates the bathymetry data using the provided grid and saves it to the file before returning it.
424-
"""
425-
function retrieve_bathymetry(grid, filename; kw...)
426-
427-
if isfile(filename)
428-
bottom_height = jldopen(filename)["bathymetry"]
429-
else
430-
bottom_height = regrid_bathymetry(grid; kw...)
431-
jldsave(filename, bathymetry = Array(interior(bottom_height)))
432-
end
433-
434-
return bottom_height
435-
end
436-
437-
retrieve_bathymetry(grid, ::Nothing; kw...) = regrid_bathymetry(grid; kw...)
438-
retrieve_bathymetry(grid; kw...) = regrid_bathymetry(grid; kw...)
439-
440363
end # module

0 commit comments

Comments
 (0)