Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utility for regridding ETOPO bathymetry to any grid #35

Merged
merged 15 commits into from
Oct 11, 2023

Conversation

glwagner
Copy link
Member

@glwagner glwagner commented Jul 10, 2023

This PR primarily adds a utility called regrid_bathymetry(target_grid) designed to "regrid" ETOPO bathymetry to any latitude-longitude grid.

The bathymetry file is downloaded from:

https://www.ngdc.noaa.gov/thredds/fileServer/global/ETOPO2022/60s/60s_surface_elev_netcdf/ETOPO_2022_v1_60s_N90
W180_surface.nc

into the directory /data. (If the file is already found there, it is not re-downloaded). The url and filename can be changed via keyword arguments.

The basic usage is demonstrated by examples/generate_bathymetry.jl:

grid = LatitudeLongitudeGrid(CPU();
                             size = (360, 160, 1),
                             latitude = (-80, 80),
                             longitude = (-180, 180),
                             z = (0, 1),
                             halo = (4, 4, 4))

one_degree_h = regrid_bathymetry(grid, height_above_water=1, minimum_depth=5)

regrid_bathymetry performs the following steps:

  1. Load ETOPO bathymetry from file (possibly downloading first).
  2. Setting the height of land ("above water") to 1 meter. (The default is nothing, which does not change land height.)
  3. Reduce the minimum ocean depth to 5 meters (default: 0).
  4. Create the ETOPO "native grid", and then regrid the modified ETOPO bathymetry to the target grid (above, a grid with one-degree resolution).

The regridding is done by area averaging. Thus by adjusting the two keywords height_above_water and minimum_depth, users can tweak the position of coastlines. For example, larger height_above_water will yield a coarse bathymetry with more land.

Eventually I would also like to add the kwarg fill_inland_seas.

We may also have additional functions like quarter_degree_bathymetry(), which use regrid_bathymetry to generate an initial bathymetry estimate, and then perform further modifications based on bespoke knowledge for quarter degree grids (such as widening the Strait of Gibraltar).

Closes #32

@glwagner
Copy link
Member Author

Here's an example of the utility in action for a quarter degree grid:

image

@codecov
Copy link

codecov bot commented Jul 10, 2023

Codecov Report

Attention: 119 lines in your changes are missing coverage. Please review.

Comparison is base (c760cf7) 2.03% compared to head (badf930) 7.81%.

Additional details and impacted files
@@           Coverage Diff            @@
##            main     #35      +/-   ##
========================================
+ Coverage   2.03%   7.81%   +5.78%     
========================================
  Files          9      12       +3     
  Lines        591     678      +87     
========================================
+ Hits          12      53      +41     
- Misses       579     625      +46     
Files Coverage Δ
src/ClimaOcean.jl 73.33% <ø> (+46.06%) ⬆️
src/DataWrangling.jl 0.00% <0.00%> (ø)
src/InitialConditions.jl 0.00% <0.00%> (ø)
src/Bathymetry.jl 0.00% <0.00%> (ø)

... and 3 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@glwagner
Copy link
Member Author

50th degree bathymetry for the mediterranean:

image

@simone-silvestri
Copy link
Collaborator

simone-silvestri commented Aug 1, 2023

You'll need to remove connected regions, otherwise, when prescribing fluxes it might be that temperature (or salinity) grow beyond physical values because there is no way to mix isolated cells.

I found a very simple way to do it using the scikit package in pyhton

using PyCall

const ABOVE_SEA_LEVEL = 0.01
const scikitimage = PyNULL()
copy!(scikitimage, pyimport_conda("skimage.measure", "scikit-image"))


function remove_connected_regions(bat)

    bathymetry = deepcopy(bat)
    batneg     = deepcopy(bathymetry)

    batneg[batneg.>0] .= 0
    batneg[batneg.<0] .= 1

    labels = scikitimage.label(batneg, connectivity = 1)
    total_elements = zeros(maximum(labels))

    for i in 1:lastindex(total_elements)
        total_elements[i] = sum(labels[labels.==i])
    end

    ocean_idx = findfirst(x -> x == maximum(x), total_elements)
    second_maximum = maximum(filter((x) -> x != total_elements[ocean_idx], total_elements))

    # the bering sea is disconnected from the pacific/atlantic ocean if we go below 80 degree latitude
    bering_sea_idx = findfirst(x -> x == second_maximum, total_elements)

    labels = Float64.(labels)
    labels[labels.==0] .= NaN

    for i in 1:length(total_elements)
        if (i != ocean_idx) && (i != bering_sea_idx)
            labels[labels.==i] .= NaN
        end
    end

    bathymetry .+= labels
    bathymetry[isnan.(bathymetry)] .= ABOVE_SEA_LEVEL

    return bathymetry
end

@glwagner
Copy link
Member Author

glwagner commented Aug 1, 2023

Thanks @simone-silvestri . In the original post I called his

Eventually I would also like to add the kwarg fill_inland_seas.

By "filling inland seas", I mean the same thing you mean "remove connected regions" (I think). An inland sea is a region that isn't connected to the ocean.

As for the solution, I think we want to avoid depending on python packages in ClimaOcean if we can --- is there any other way?

@simone-silvestri
Copy link
Collaborator

Hmmm, I haven't found any simple way like this in Julia, but there might be an equivalent package to scikit-image

@navidcy
Copy link
Collaborator

navidcy commented Sep 20, 2023

Sorry @glwagner, this slipped my head long now!

@simone-silvestri
Copy link
Collaborator

I heuristically found that smoother bathymetry allows larger time steps and a lower bathymetry noise.
It might be that a conservative regridding leads to a very unsmooth and jagged bathymetry with spurious grid-scale features.
In my personal experience, I found out that a Gaussian interpolation (akin to repeated bilinear interpolations in passes on progressively coarser grids) is great for retaining the large-scale features that can be represented while eliminating the small-scale features that just show up on the grid as grid-scale noise

@glwagner
Copy link
Member Author

@simone-silvestri have you done many simulations at 1 and 2 degree? I think the averaging becomes more important as coarse resolutions (ie to preserve basic properties like ocean volume and connectivity).

I don't think we need to have "only one" way to generate bathymetry either. But it's good to know that we may want other different utilities.

I do think we have to interpolate in general (because we don't have a general regridder, and we will often want to do simulations on non-latitude-longitude grids). So I was thinking that we could consider a two step generation: first conservatively map from 1/60th to a resolution a bit closer to what we are targeting. The second step is to interpolate the remapped bathymetry to a finer grid.

@glwagner glwagner merged commit 8e6fedd into main Oct 11, 2023
8 of 9 checks passed
@glwagner glwagner deleted the glw/bathymetry-utils branch October 11, 2023 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bathymetry generation for OMIP (+ global simulations)
3 participants