11module Bathymetry
22
3- export regrid_bathymetry, retrieve_bathymetry, download_bathymetry
3+ export regrid_bathymetry
44
5+ using Downloads
56using ImageMorphology
6- using .. DataWrangling: download_progress
7- using Oceananigans. DistributedComputations
8-
7+ using KernelAbstractions: @kernel , @index
98using Oceananigans
109using 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!
1510using 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!
1916using OffsetArrays
20- using ClimaOcean
21-
2217using NCDatasets
23- using Downloads
2418using Printf
2519using 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
7335Arguments
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
202139end
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
267258end
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
403361end
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-
440363end # module
0 commit comments