-
Notifications
You must be signed in to change notification settings - Fork 4
Hydro model channel improvements #156
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
base: develop
Are you sure you want to change the base?
Changes from all commits
95450f3
d51a403
f20a97b
c2d4944
ece5ce1
6cd0d58
3cd22f7
b6581ba
30b2d3d
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 |
|---|---|---|
|
|
@@ -396,18 +396,6 @@ subroutine li_SGH_solve(domain, err) | |
| block => block % next | ||
| end do | ||
|
|
||
|
|
||
| ! ============= | ||
| ! ============= | ||
| ! ============= | ||
| ! subcycle hydro model until master dt is reached | ||
| ! ============= | ||
| ! ============= | ||
| ! ============= | ||
| timecycling: do while (timeLeft > 0.0_RKIND) | ||
| numSubCycles = numSubCycles + 1 | ||
|
|
||
|
|
||
| ! ============= | ||
| ! Calculate time-varying forcing, as needed | ||
| ! ============= | ||
|
|
@@ -436,10 +424,6 @@ subroutine li_SGH_solve(domain, err) | |
| err = ior(err, 1) | ||
| endif | ||
|
|
||
| ! SHMIP forcing will override basalMeltInput. | ||
| call shmip_timevarying_forcing(block, err_tmp) | ||
| err = ior(err, err_tmp) | ||
|
|
||
| block => block % next | ||
| end do | ||
|
|
||
|
|
@@ -453,6 +437,29 @@ subroutine li_SGH_solve(domain, err) | |
| endif | ||
|
|
||
|
|
||
|
|
||
| ! ============= | ||
| ! ============= | ||
| ! ============= | ||
| ! subcycle hydro model until master dt is reached | ||
| ! ============= | ||
| ! ============= | ||
| ! ============= | ||
| timecycling: do while (timeLeft > 0.0_RKIND) | ||
| numSubCycles = numSubCycles + 1 | ||
|
|
||
|
|
||
| ! apply shmip forcing if needed | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
| ! SHMIP forcing will override basalMeltInput from above | ||
| call shmip_timevarying_forcing(block, err_tmp) | ||
| err = ior(err, err_tmp) | ||
|
|
||
| block => block % next | ||
| end do | ||
|
|
||
|
|
||
| ! ============= | ||
| ! Update till water layer thickness | ||
| ! ============= | ||
|
|
@@ -482,6 +489,7 @@ subroutine li_SGH_solve(domain, err) | |
| ! ============= | ||
| ! Calculate edge quantities and advective fluxes | ||
| ! ============= | ||
| call mpas_timer_start("calc edge quantities") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
|
|
||
|
|
@@ -518,12 +526,14 @@ subroutine li_SGH_solve(domain, err) | |
|
|
||
| block => block % next | ||
| end do | ||
| call mpas_timer_stop("calc edge quantities") | ||
|
|
||
|
|
||
| ! ============= | ||
| ! Update Channel fields | ||
| ! ============= | ||
| if (config_SGH_chnl_active) then | ||
| call mpas_timer_start("update channel") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
|
|
||
|
|
@@ -539,13 +549,16 @@ subroutine li_SGH_solve(domain, err) | |
| call mpas_dmpar_field_halo_exch(domain, 'channelMelt') | ||
| call mpas_dmpar_field_halo_exch(domain, 'channelPressureFreeze') | ||
| call mpas_timer_stop("halo updates") | ||
| call mpas_timer_stop("update channel") | ||
| endif | ||
|
|
||
|
|
||
| ! ============= | ||
| ! Calculate adaptive time step | ||
| ! ============= | ||
| call mpas_timer_start("calc SGH dt") | ||
| call check_timestep(domain, timeLeft, numSubCycles, err_tmp) | ||
| call mpas_timer_stop("calc SGH dt") | ||
| err = ior(err, err_tmp) | ||
| if (err > 0) then | ||
| exit timecycling | ||
|
|
@@ -555,6 +568,7 @@ subroutine li_SGH_solve(domain, err) | |
| ! ============= | ||
| ! Compute flux divergence | ||
| ! ============= | ||
| call mpas_timer_start("compute flux div") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
| call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) | ||
|
|
@@ -591,12 +605,14 @@ subroutine li_SGH_solve(domain, err) | |
| call mpas_timer_start("halo updates") | ||
| call mpas_dmpar_field_halo_exch(domain, 'divergence') | ||
| call mpas_timer_stop("halo updates") | ||
| call mpas_timer_stop("compute flux div") | ||
|
|
||
|
|
||
| ! ============= | ||
| ! Update channel area now that we have time step | ||
| ! ============= | ||
| if (config_SGH_chnl_active) then | ||
| call mpas_timer_start("evolve channel") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
|
|
||
|
|
@@ -611,11 +627,13 @@ subroutine li_SGH_solve(domain, err) | |
| call mpas_dmpar_field_halo_exch(domain, 'channelMeltInputCell') | ||
| call mpas_dmpar_field_halo_exch(domain, 'channelArea') | ||
| call mpas_timer_stop("halo updates") | ||
| call mpas_timer_stop("evolve channel") | ||
| endif | ||
|
|
||
| ! ============= | ||
| ! Calculate total grounding line discharges | ||
| ! ============= | ||
| call mpas_timer_start("calc SGH GL flux") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
|
|
||
|
|
@@ -624,10 +642,12 @@ subroutine li_SGH_solve(domain, err) | |
|
|
||
| block => block % next | ||
| end do | ||
| call mpas_timer_stop("calc SGH GL flux") | ||
|
|
||
| ! ============= | ||
| ! Update water layer thickness | ||
| ! ============= | ||
| call mpas_timer_start("update water layer thickness") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
| call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) | ||
|
|
@@ -660,6 +680,7 @@ subroutine li_SGH_solve(domain, err) | |
|
|
||
| block => block % next | ||
| end do | ||
| call mpas_timer_stop("update water layer thickness") | ||
|
|
||
| ! ============= | ||
| ! Calculate pressure field | ||
|
|
@@ -679,6 +700,7 @@ subroutine li_SGH_solve(domain, err) | |
| ! ordering choice is only needed to support channels. Without it, runs with | ||
| ! channels would use an out of date waterThickness in the hydropotential and | ||
| ! do not restart correctly due to the order of operations mismatch. | ||
| call mpas_timer_start("calc pressure") | ||
| block => domain % blocklist | ||
| do while (associated(block)) | ||
|
|
||
|
|
@@ -709,6 +731,7 @@ subroutine li_SGH_solve(domain, err) | |
|
|
||
| block => block % next | ||
| end do | ||
| call mpas_timer_stop("calc pressure") | ||
|
|
||
| deltatSGHaccumulated = deltatSGHaccumulated + deltatSGH | ||
|
|
||
|
|
@@ -879,6 +902,7 @@ subroutine calc_edge_quantities(block, err) | |
| real (kind=RKIND), pointer :: bedRoughMax | ||
| real (kind=RKIND) :: conduc_coeff_wtd | ||
| character (len=StrKIND), pointer :: config_SGH_tangent_slope_calculation | ||
| logical, pointer :: config_SGH_allow_terrestrial_outflow | ||
| real (kind=RKIND), pointer :: config_sea_level | ||
| real (kind=RKIND), pointer :: rhoo | ||
| integer, pointer :: nEdges | ||
|
|
@@ -909,6 +933,7 @@ subroutine calc_edge_quantities(block, err) | |
| call mpas_pool_get_config(liConfigs, 'config_SGH_conduc_coeff_drowned', conduc_coeff_drowned) | ||
| call mpas_pool_get_config(liConfigs, 'config_SGH_bed_roughness_max', bedRoughMax) | ||
| call mpas_pool_get_config(liConfigs, 'config_SGH_tangent_slope_calculation', config_SGH_tangent_slope_calculation) | ||
| call mpas_pool_get_config(liConfigs, 'config_SGH_allow_terrestrial_outflow', config_SGH_allow_terrestrial_outflow) | ||
| call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) | ||
| call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhoo) | ||
|
|
||
|
|
@@ -972,19 +997,35 @@ subroutine calc_edge_quantities(block, err) | |
| ! This one-sided implementation also creates outflowing conditions at terrestrial boundary | ||
| do iEdge = 1, nEdges | ||
| if (hydroTerrestrialMarginMask(iEdge) == 1) then | ||
| cell1 = cellsOnEdge(1, iEdge) | ||
| cell2 = cellsOnEdge(2, iEdge) | ||
| if (li_mask_is_grounded_ice(cellMask(cell1))) then ! cell2 is the icefree cell - replace phi there with cell1 Phig | ||
|
|
||
| hydropotentialBaseSlopeNormal(iEdge) = - waterPressure(cell1) / dcEdge(iEdge) | ||
| hydropotentialSlopeNormal(iEdge) = - (rho_water * gravity * waterThickness(cell1) + waterPressure(cell1)) / dcEdge(iEdge) | ||
|
|
||
| else ! cell1 is the icefree cell - replace phi there with cell2 Phig | ||
|
|
||
| hydropotentialBaseSlopeNormal(iEdge) = waterPressure(cell2) / dcEdge(iEdge) | ||
| hydropotentialSlopeNormal(iEdge) = (rho_water * gravity * waterThickness(cell2) + waterPressure(cell2)) / dcEdge(iEdge) | ||
|
|
||
| endif ! which cell is icefree | ||
| if (config_SGH_allow_terrestrial_outflow) then | ||
| cell1 = cellsOnEdge(1, iEdge) | ||
| cell2 = cellsOnEdge(2, iEdge) | ||
| if (li_mask_is_grounded_ice(cellMask(cell1))) then ! cell2 is the icefree cell - replace phi there with cell1 Phig | ||
| if (bedTopography(cell2) < bedTopography(cell1)) then | ||
| hydropotentialBaseSlopeNormal(iEdge) = - waterPressure(cell1) / dcEdge(iEdge) | ||
| hydropotentialSlopeNormal(iEdge) = - (rho_water * gravity * waterThickness(cell1) + waterPressure(cell1)) / dcEdge(iEdge) | ||
| else | ||
| ! If bare ground elevation is higher than elevation beneath the ice, no flux | ||
| hydropotentialBaseSlopeNormal(iEdge) = 0.0_RKIND | ||
| hydropotentialSlopeNormal(iEdge) = 0.0_RKIND | ||
| waterPressureSlopeNormal(iEdge) = 0.0_RKIND | ||
| endif | ||
| else ! cell1 is the icefree cell - replace phi there with cell2 Phig | ||
| if (bedTopography(cell1) < bedTopography(cell2)) then | ||
| hydropotentialBaseSlopeNormal(iEdge) = waterPressure(cell2) / dcEdge(iEdge) | ||
| hydropotentialSlopeNormal(iEdge) = (rho_water * gravity * waterThickness(cell2) + waterPressure(cell2)) / dcEdge(iEdge) | ||
| else | ||
| ! If bare ground elevation is higher than elevation beneath the ice, no flux | ||
| hydropotentialBaseSlopeNormal(iEdge) = 0.0_RKIND | ||
| hydropotentialSlopeNormal(iEdge) = 0.0_RKIND | ||
| waterPressureSlopeNormal(iEdge) = 0.0_RKIND | ||
| endif | ||
| endif ! which cell is icefree | ||
| else ! if disallow terrestrial outflow | ||
| hydropotentialBaseSlopeNormal(iEdge) = 0.0_RKIND | ||
| hydropotentialSlopeNormal(iEdge) = 0.0_RKIND | ||
| waterPressureSlopeNormal(iEdge) = 0.0_RKIND | ||
| endif ! if allow terrestrial outflow | ||
| endif ! if edge of grounded ice | ||
| end do | ||
|
|
||
|
|
@@ -1844,6 +1885,7 @@ subroutine update_channel(block, err) | |
| real (kind=RKIND), pointer :: creep_coeff | ||
| real (kind=RKIND), pointer :: rhoi | ||
| real (kind=RKIND), pointer :: config_SGH_incipient_channel_width | ||
| real (kind=RKIND), pointer :: bedRoughMax | ||
| logical, pointer :: config_SGH_include_pressure_melt | ||
| real (kind=RKIND), dimension(:), pointer :: channelArea | ||
| real (kind=RKIND), dimension(:), pointer :: channelMelt | ||
|
|
@@ -1862,6 +1904,7 @@ subroutine update_channel(block, err) | |
| real (kind=RKIND), dimension(:), pointer :: effectivePressureSGH | ||
| real (kind=RKIND), dimension(:), pointer :: channelDiffusivity | ||
| real (kind=RKIND), dimension(:), pointer :: waterThicknessEdgeUpwind | ||
| real (kind=RKIND), dimension(:), pointer :: waterThickness | ||
| integer, dimension(:), pointer :: waterFluxMask | ||
| integer, dimension(:), pointer :: hydroMarineMarginMask | ||
| integer, dimension(:), pointer :: edgeMask | ||
|
|
@@ -1885,6 +1928,7 @@ subroutine update_channel(block, err) | |
| call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_creep_coefficient', creep_coeff) | ||
| call mpas_pool_get_config(liConfigs, 'config_SGH_incipient_channel_width', config_SGH_incipient_channel_width) | ||
| call mpas_pool_get_config(liConfigs, 'config_SGH_include_pressure_melt', config_SGH_include_pressure_melt) | ||
| call mpas_pool_get_config(liConfigs, 'config_SGH_bed_roughness_max', bedRoughMax) | ||
| call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) | ||
| call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) | ||
| call mpas_pool_get_array(hydroPool, 'channelArea', channelArea) | ||
|
|
@@ -1908,6 +1952,7 @@ subroutine update_channel(block, err) | |
| call mpas_pool_get_array(hydroPool, 'hydroMarineMarginMask', hydroMarineMarginMask) | ||
| call mpas_pool_get_array(hydroPool, 'channelDiffusivity', channelDiffusivity) | ||
| call mpas_pool_get_array(hydroPool, 'waterThicknessEdgeUpwind', waterThicknessEdgeUpwind) | ||
| call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) | ||
| call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) | ||
| ! Calculate terms needed for opening (melt) rate | ||
|
|
||
|
|
@@ -2029,9 +2074,11 @@ subroutine evolve_channel(block, err) | |
| integer, dimension(:,:), pointer :: edgeSignOnCell | ||
| real (kind=RKIND), dimension(:), pointer :: areaCell | ||
| real (kind=RKIND), dimension(:), pointer :: dcEdge | ||
| real (kind=RKIND), pointer :: config_SGH_chnl_max_area | ||
| integer, pointer :: nCellsSolve | ||
| integer :: iCell, iEdgeOnCell, iEdge | ||
|
|
||
| call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_max_area', config_SGH_chnl_max_area) | ||
| call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) | ||
| call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) | ||
| call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) | ||
|
|
@@ -2077,6 +2124,7 @@ subroutine evolve_channel(block, err) | |
| channelArea = channelChangeRate * deltatSGH + channelArea | ||
| ! If sheet dissipation contributes to channel, there should be no need for a minimum channel size | ||
| channelArea = max(1.0e-8_RKIND, channelArea) ! make some tiny value when it goes negative | ||
| channelArea = min(config_SGH_chnl_max_area, channelArea) ! limit channel area to config-specified max value | ||
|
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. Would it be better to just limit channelMelt and channelPressureFreeze when channelArea exceeds config_SGH_chnl_max_area? That way the channel still wouldn't grow past the max value but we also wouldn't violate mass conservation if there was no actual unaccounted melting. Probably doesn't affect the outcome but could be cleaner to not have a cell where channelMelt far exceeds the channelArea
Author
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. @alexolinhager , that's a good suggestion. I'll look into how best to make the adjustment. |
||
|
|
||
| !-------------------------------------------------------------------- | ||
| end subroutine evolve_channel | ||
|
|
||
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 think we will eventually need to port your MPAS-Tools ice surface alteration code into MALI if we want to run with evolving dynamics. The primary reason for iceThicknessHydro was the development if single-cell surface depressions that formed when running coupled simulations. This was mostly an issue when running with the higher order velocity solver, which created speckled ice thicknesses along the domain edges as the run progressed.
It seems like the MPAS-Tools code should fix that issue, so I think we should try replacing the iceThicknessHydro code with what you wrote there. Would it be better to make that it's own PR, or tack it onto this one?
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.
Thanks for reminding me about the noise along the boundaries of regional domains with the higher-order advection. I think change to iceThicknessHydro should occur in a separate PR. The depression-filling algorithm in my standalone script will be a decent amount of work to port from python to fortran, I think, because it currently relies on some python libraries for a lot of the work. Alternatively, we might be able to keep the existing iceThicknessHydro but disable modifications near the GL. And maybe there is a fix to the HO advection scheme to ensure it reduces to first-order near the boundaries of regional domains (cc: @trhille). With any of those options, I think we should tackle them separately from this PR.