-
Notifications
You must be signed in to change notification settings - Fork 0
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
support C-grid connectivity in deseas #39
base: master
Are you sure you want to change the base?
Changes from all commits
1fb330c
a2dc32b
f2dfe61
43b590d
690da6b
806f4df
c176f26
fc7a8a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,8 @@ module topography | |
integer(int32) :: nyt = 0 | ||
! Depth variable and attributes | ||
real(real32), allocatable :: depth(:,:) | ||
character(len=3) :: lakes_removed = "no " | ||
character(len=1) :: grid_type = 'B' | ||
character(len=3) :: lakes_removed = 'no ' | ||
real(real32) :: min_depth = -1.0 | ||
integer :: min_level = 0 | ||
real(real32) :: max_depth = -1.0 | ||
|
@@ -44,9 +45,11 @@ module topography | |
contains | ||
|
||
!------------------------------------------------------------------------- | ||
type(topography_t) function topography_constructor(filename) result(topog) | ||
type(topography_t) function topography_constructor(filename, grid_type) result(topog) | ||
character(len=*), intent(in) :: filename | ||
character(len=1), intent(in), optional :: grid_type | ||
|
||
character(len=1):: file_grid_type | ||
integer(int32) :: ncid, depth_id, frac_id, geolon_id, geolat_id, dids(2), history_len ! NetCDF ids | ||
|
||
write(output_unit,'(3a)') "Reading topography from file '", trim(filename), "'" | ||
|
@@ -72,6 +75,19 @@ type(topography_t) function topography_constructor(filename) result(topog) | |
call handle_error(nf90_get_att(ncid, depth_id, 'maximum_depth', topog%max_depth), isfatal=.false.) | ||
call handle_error(nf90_get_att(ncid, depth_id, 'nonadvective_cells_removed', topog%nonadvective_cells_removed), isfatal=.false.) | ||
|
||
if (present(grid_type)) then | ||
topog%grid_type = grid_type ! grid_type arg overrides value in file | ||
else | ||
call handle_error(nf90_get_att(ncid, depth_id, 'grid_type', file_grid_type), isfatal=.false.) | ||
if ( file_grid_type == 'B' .or. file_grid_type == 'C' ) then | ||
topog%grid_type = file_grid_type | ||
end if | ||
end if | ||
write(output_unit,*) " grid_type = ", topog%grid_type | ||
if (all(topog%grid_type /= ['B', 'C'])) then | ||
call handle_error(nf90_einval, .true., "grid_type must be B or C") | ||
end if | ||
|
||
! Get sea area fraction | ||
call handle_error(nf90_inq_varid(ncid, 'sea_area_fraction', frac_id)) | ||
allocate(topog%frac(topog%nxt, topog%nyt)) | ||
|
@@ -112,6 +128,7 @@ subroutine topography_copy(topog_out, topog_in) | |
topog_out%min_level = topog_in%min_level | ||
topog_out%max_depth = topog_in%max_depth | ||
topog_out%nonadvective_cells_removed = topog_in%nonadvective_cells_removed | ||
topog_out%grid_type = topog_in%grid_type | ||
|
||
! Sea area fraction | ||
allocate(topog_out%frac, source=topog_in%frac) | ||
|
@@ -150,6 +167,7 @@ subroutine topography_write(this, filename) | |
call handle_error(nf90_def_var_fill(ncid, depth_id, 0, MISSING_VALUE)) | ||
call handle_error(nf90_put_att(ncid, depth_id, 'long_name', 'depth')) | ||
call handle_error(nf90_put_att(ncid, depth_id, 'units', 'm')) | ||
call handle_error(nf90_put_att(ncid, depth_id, 'grid_type', this%grid_type)) | ||
call handle_error(nf90_put_att(ncid, depth_id, 'lakes_removed', this%lakes_removed)) | ||
if (this%min_depth > 0.0) then | ||
call handle_error(nf90_put_att(ncid, depth_id, 'minimum_depth', this%min_depth)) | ||
|
@@ -249,7 +267,6 @@ subroutine topography_number_seas(this, sea_number, number_of_seas, silent) | |
silent_ = .false. | ||
end if | ||
|
||
! Do | ||
land = this%nxt + this%nyt + 1 | ||
sea = land | ||
do j = 1, this%nyt | ||
|
@@ -313,20 +330,27 @@ subroutine topography_number_seas(this, sea_number, number_of_seas, silent) | |
im = this%nxt | ||
ip = 2 | ||
if (sea(i, j) < land .and. sea(i, j) > 0) then | ||
sea(i,j) = min(sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
counter = counter + 1 | ||
new_sea = min(sea(im, j), sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
if (sea(i, j) /= new_sea) then | ||
sea(i, j) = new_sea | ||
counter = counter + 1 | ||
end if | ||
end if | ||
do i = 2, this%nxt - 1 | ||
im = i - 1 | ||
ip = i + 1 | ||
if (sea(i, j) < land .and. sea(i, j) > 0) then | ||
!get chokes | ||
choke_east = .not. (any(sea(i:ip, jp) == land) .and. any(sea(i:ip, jm) == land)) | ||
choke_west = .not. (any(sea(im:i, jp) == land) .and. any(sea(im:i, jm) == land)) | ||
choke_south = .not. (any(sea(im, jm:j) == land) .and. any(sea(ip, jm:j) == land)) | ||
choke_north = .not. (any(sea(im, j:jp) == land) .and. any(sea(ip, j:jp) == land)) | ||
new_sea = min(minval([sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)], & | ||
mask=[choke_west, choke_east, choke_south, choke_north]), land) | ||
if ( this%grid_type == 'C' ) then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For cases like this one, I strongly prefer to use the select case construct instead of an if statement, as it's easier to extend and to catch possible mistakes (e.g., right now, if one would have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks, good idea |
||
new_sea = min(sea(i, j), sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
else | ||
! get chokes, assuming B-grid connectivity rules | ||
choke_east = .not. (any(sea(i:ip, jp) == land) .and. any(sea(i:ip, jm) == land)) | ||
choke_west = .not. (any(sea(im:i, jp) == land) .and. any(sea(im:i, jm) == land)) | ||
choke_south = .not. (any(sea(im, jm:j) == land) .and. any(sea(ip, jm:j) == land)) | ||
choke_north = .not. (any(sea(im, j:jp) == land) .and. any(sea(ip, j:jp) == land)) | ||
new_sea = min(sea(i, j), minval([sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)], & | ||
mask=[choke_west, choke_east, choke_south, choke_north])) | ||
end if | ||
if (sea(i, j) /= new_sea) then | ||
sea(i, j) = new_sea | ||
counter = counter + 1 | ||
|
@@ -337,8 +361,11 @@ subroutine topography_number_seas(this, sea_number, number_of_seas, silent) | |
ip = 1 | ||
im = i - 1 | ||
if (sea(i, j) < land .and. sea(i, j) > 0) then | ||
sea(i,j)=min(sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
counter = counter + 1 | ||
new_sea = min(sea(i, j), sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
if (sea(i, j) /= new_sea) then | ||
sea(i, j) = new_sea | ||
counter = counter + 1 | ||
end if | ||
end if | ||
end do | ||
|
||
|
@@ -350,20 +377,27 @@ subroutine topography_number_seas(this, sea_number, number_of_seas, silent) | |
im = this%nxt | ||
ip = 2 | ||
if (sea(i, j) < land .and. sea(i, j) > 0) then | ||
sea(i,j) = min(sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
counter = counter + 1 | ||
new_sea = min(sea(i, j), sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
if (sea(i, j) /= new_sea) then | ||
sea(i, j) = new_sea | ||
counter = counter + 1 | ||
end if | ||
end if | ||
do i = this%nxt - 1, 2, -1 | ||
im = i - 1 | ||
ip = i + 1 | ||
if (sea(i, j) < land .and. sea(i, j) > 0) then | ||
!get chokes | ||
choke_east = .not. (any(sea(i:ip, jp) == land) .and. any(sea(i:ip, jm) == land)) | ||
choke_west = .not. (any(sea(im:i, jp) == land) .and. any(sea(im:i, jm) == land)) | ||
choke_south = .not. (any(sea(im, jm:j) == land) .and. any(sea(ip, jm:j) == land)) | ||
choke_north = .not. (any(sea(im, j:jp) == land) .and. any(sea(ip, j:jp) == land)) | ||
new_sea = min(minval([sea(im, j), sea(ip, j), sea(i, jm), sea(i,jp)], & | ||
mask=[choke_west, choke_east, choke_south, choke_north]), land) | ||
if ( this%grid_type == 'C' ) then | ||
new_sea = min(sea(i, j), sea(i, j), sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
else | ||
! get chokes, assuming B-grid connectivity rules | ||
choke_east = .not. (any(sea(i:ip, jp) == land) .and. any(sea(i:ip, jm) == land)) | ||
choke_west = .not. (any(sea(im:i, jp) == land) .and. any(sea(im:i, jm) == land)) | ||
choke_south = .not. (any(sea(im, jm:j) == land) .and. any(sea(ip, jm:j) == land)) | ||
choke_north = .not. (any(sea(im, j:jp) == land) .and. any(sea(ip, j:jp) == land)) | ||
new_sea = min(sea(i, j), minval([sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)], & | ||
mask=[choke_west, choke_east, choke_south, choke_north])) | ||
end if | ||
if (sea(i, j) /= new_sea) then | ||
sea(i, j) = new_sea | ||
counter = counter + 1 | ||
|
@@ -374,8 +408,11 @@ subroutine topography_number_seas(this, sea_number, number_of_seas, silent) | |
ip = 1 | ||
im = i - 1 | ||
if (sea(i, j) < land .and. sea(i, j) > 0) then | ||
sea(i,j) = min(sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
counter = counter + 1 | ||
new_sea = min(sea(i, j), sea(im, j), sea(ip, j), sea(i, jm), sea(i, jp)) | ||
if (sea(i, j) /= new_sea) then | ||
sea(i, j) = new_sea | ||
counter = counter + 1 | ||
end if | ||
end if | ||
end do | ||
|
||
|
@@ -427,7 +464,7 @@ subroutine topography_deseas(this) | |
this%depth = MISSING_VALUE | ||
this%frac = MISSING_VALUE | ||
end where | ||
this%lakes_removed = "yes" | ||
this%lakes_removed = 'yes' | ||
|
||
deallocate(sea) | ||
|
||
|
@@ -484,7 +521,7 @@ subroutine topography_fill_fraction(this, sea_area_fraction) | |
call this%number_seas(number_of_seas = nseas, silent=.true.) | ||
if (nseas > 1) then | ||
write(output_unit,'(a)') "WARNING: new seas have been created. To fix, rerun deseas again." | ||
this%lakes_removed = 'no' | ||
this%lakes_removed = 'no ' | ||
end if | ||
end if | ||
end if | ||
|
@@ -508,6 +545,10 @@ subroutine topography_nonadvective(this, vgrid_file, vgrid_type, potholes, coast | |
integer(int32) :: im, ip, jm, jp | ||
integer(int32) :: nseas | ||
|
||
if ( this%grid_type /= 'B' ) then | ||
call handle_error(nf90_einval, .true., "nonadvective: grid_type must be B") | ||
end if | ||
|
||
vgrid = vgrid_t(vgrid_file, vgrid_type) | ||
write(output_unit,*) 'Zeta dimensions', 2*vgrid%nlevels + 1, vgrid%nlevels | ||
allocate(zw(0:vgrid%nlevels)) | ||
|
@@ -618,11 +659,11 @@ subroutine topography_nonadvective(this, vgrid_file, vgrid_type, potholes, coast | |
if (fix .and. (coastal .or. potholes)) then | ||
this%nonadvective_cells_removed = 'yes' | ||
if (changes_made .and. this%lakes_removed == 'yes') then | ||
! Check if new lakes were created new lakes | ||
! Check if new lakes were created | ||
call this%number_seas(number_of_seas = nseas, silent=.true.) | ||
if (nseas > 1) then | ||
write(output_unit,'(a)') "WARNING: new seas have been created. To fix, rerun deseas again." | ||
this%lakes_removed = 'no' | ||
this%lakes_removed = 'no ' | ||
end if | ||
end if | ||
end if | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Convert test_topo.png to test_topo.nc to use for testing deseas. | ||
""" | ||
|
||
from PIL import Image | ||
import numpy as np | ||
import xarray as xr | ||
|
||
image = Image.open ("test_topo.png") | ||
data = np.flipud(np.array(image)).astype("float") | ||
|
||
coords = {"ny": range(0, data.shape[0]), | ||
"nx": range(0, data.shape[1])} | ||
da = xr.DataArray(data, dims=[ k for k in coords ], coords=coords, name="depth") | ||
ds = xr.Dataset(data_vars={"depth": da, | ||
"sea_area_fraction": da, # dummy data | ||
"geolon_t": da, # dummy data | ||
"geolat_t": da # dummy data | ||
}) | ||
ds.to_netcdf("test_topo.nc") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to check if the value given in the command line is consistent with the value on the file? Otherwise, if I understand correctly, it's possible to change the grid type on file when running
deseas
, which is a bit weird.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an intentional feature, otherwise there's no way to alter the grid type on file (which otherwise defaults to B)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add some words to that effect in the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main purpose of grid_type in the file is to indicate to fill_fraction, fix_nonadvective and check_nonadvective what connectivity rules to use when checking whether the number of seas has changed, ie to indicate the rules used previously by deseas when lakes_removed was set to 'yes'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. Yes, this definitely needs to be documented, both in the docs and in the code.