diff --git a/src/core_landice/Registry.xml b/src/core_landice/Registry.xml index 1e31339959..b60bb79814 100644 --- a/src/core_landice/Registry.xml +++ b/src/core_landice/Registry.xml @@ -39,6 +39,9 @@ + @@ -70,7 +73,7 @@ possible_values="'from_vertex_barycentric', 'from_vertex_barycentric_kiteareas', 'from_normal_slope'" /> - + + + @@ -107,18 +122,29 @@ possible_values=".true. or .false." /> --> + + + + + - @@ -130,6 +156,14 @@ description="Value of eigencalving parameter if taken as a scalar by option config_calving_eigencalving_parameter_source. (Default value is 1.0e9 m a converted to units used here.)" possible_values="any positive real number" /> + + + + + + + + + + + + + + + + @@ -157,8 +247,8 @@ description="Selection of the method for initializing the ice temperature (as described further below)." possible_values="'sfc_air_temperature', 'linear', 'file'" /> - + + + + + + + + - + + - - - + + + + + + + + + + + + + + + + + + - - - + + + + + + @@ -454,7 +636,7 @@ - + + + + + + + + + + + @@ -491,6 +683,16 @@ + + + + + + + + + + - + + @@ -554,6 +766,19 @@ + + + + + + + + + + + + + @@ -756,6 +981,9 @@ is the value of that variable from the *previous* time level! + @@ -844,8 +1072,14 @@ is the value of that variable from the *previous* time level! + + @@ -855,6 +1089,12 @@ is the value of that variable from the *previous* time level! + + @@ -862,13 +1102,63 @@ is the value of that variable from the *previous* time level! description="proportionality constant K2+- used in eigencalving formulation" /> + + + + + + + + - + + + + + + + + + - + + - + + @@ -892,7 +1193,62 @@ is the value of that variable from the *previous* time level! /> + + + + + + + + + + + /> + + + + + + + /> + + + @@ -932,34 +1288,80 @@ is the value of that variable from the *previous* time level! + + + + + + + - - + - + + + + + + + + - @@ -991,17 +1387,17 @@ is the value of that variable from the *previous* time level! description="X-component of observed surface velocity" /> - - - - - @@ -1012,7 +1408,7 @@ is the value of that variable from the *previous* time level! - + - + - @@ -94,7 +102,7 @@ description="width of sheet beneath/around channel that contributes to melt within the channel" possible_values="positive real number" /> - @@ -152,7 +160,7 @@ - @@ -184,6 +192,8 @@ description="hydropotential without water thickness contribution on vertices. Only used for some choices of config_SGH_tangent_slope_calculation." /> + + + + diff --git a/src/core_landice/analysis_members/Registry_regional_stats.xml b/src/core_landice/analysis_members/Registry_regional_stats.xml index e33203221d..fbea7c7989 100644 --- a/src/core_landice/analysis_members/Registry_regional_stats.xml +++ b/src/core_landice/analysis_members/Registry_regional_stats.xml @@ -60,6 +60,12 @@ + + @@ -84,6 +90,9 @@ + @@ -104,7 +113,7 @@ clobber_mode="truncate" runtime_format="single_file"> - - + + diff --git a/src/core_landice/analysis_members/mpas_li_global_stats.F b/src/core_landice/analysis_members/mpas_li_global_stats.F index d8e8a20d1a..41ddc9aebd 100644 --- a/src/core_landice/analysis_members/mpas_li_global_stats.F +++ b/src/core_landice/analysis_members/mpas_li_global_stats.F @@ -71,7 +71,7 @@ module li_global_stats ! !----------------------------------------------------------------------- - subroutine li_init_global_stats(domain, memberName, err)!{{{ + subroutine li_init_global_stats(domain, memberName, err) !----------------------------------------------------------------- ! @@ -105,7 +105,7 @@ subroutine li_init_global_stats(domain, memberName, err)!{{{ err = 0 - end subroutine li_init_global_stats!}}} + end subroutine li_init_global_stats !*********************************************************************** ! @@ -120,7 +120,7 @@ end subroutine li_init_global_stats!}}} ! !----------------------------------------------------------------------- - subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ + subroutine li_compute_global_stats(domain, memberName, timeLevel, err) !----------------------------------------------------------------- ! @@ -165,27 +165,24 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ type (mpas_pool_type), pointer :: velocityPool ! arrays, vars needed from other pools for calculations here - real (kind=RKIND), pointer :: config_ice_density real (kind=RKIND), pointer :: deltat real (kind=RKIND), dimension(:), pointer :: areaCell real (kind=RKIND), dimension(:), pointer :: dvEdge real (kind=RKIND), dimension(:), pointer :: thickness real (kind=RKIND), dimension(:), pointer :: bedTopography - real (kind=RKIND), dimension(:), pointer :: sfcMassBal + real (kind=RKIND), dimension(:), pointer :: sfcMassBalApplied + real (kind=RKIND), dimension(:), pointer :: groundedSfcMassBalApplied real (kind=RKIND), dimension(:), pointer :: basalMassBal real (kind=RKIND), dimension(:), pointer :: groundedBasalMassBal real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal real (kind=RKIND), dimension(:), pointer :: calvingThickness real (kind=RKIND), dimension(:), pointer :: surfaceSpeed real (kind=RKIND), dimension(:), pointer :: basalSpeed - real (kind=RKIND), dimension(:,:), pointer :: layerNormalVelocity - real (kind=RKIND), dimension(:,:), pointer :: layerThicknessEdge + real (kind=RKIND), dimension(:), pointer :: fluxAcrossGroundingLine + real (kind=RKIND), dimension(:), pointer :: groundedToFloatingThickness integer, dimension(:), pointer :: cellMask - integer, dimension(:), pointer :: edgeMask - integer, dimension(:,:), pointer :: cellsOnEdge integer, pointer :: nCellsSolve integer, pointer :: nEdgesSolve - integer, pointer :: nVertLevels ! config options needed real (kind=RKIND), pointer :: config_sea_level @@ -194,7 +191,6 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ ! Local counters integer :: k, iCell, iEdge - integer :: iCell1, iCell2 ! scalars to be calculated here from global reductions real (kind=RKIND), pointer :: totalIceArea, totalIceVolume @@ -203,12 +199,14 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ real (kind=RKIND), pointer :: floatingIceArea, floatingIceVolume real (kind=RKIND), pointer :: iceThicknessMax, iceThicknessMin, iceThicknessMean real (kind=RKIND), pointer :: totalSfcMassBal, totalBasalMassBal + real (kind=RKIND), pointer :: totalGroundedSfcMassBal, totalFloatingSfcMassBal real (kind=RKIND), pointer :: totalGroundedBasalMassBal, totalFloatingBasalMassBal real (kind=RKIND), pointer :: avgNetAccumulation real (kind=RKIND), pointer :: avgGroundedBasalMelt real (kind=RKIND), pointer :: avgSubshelfMelt real (kind=RKIND), pointer :: totalCalvingFlux real (kind=RKIND), pointer :: groundingLineFlux + real (kind=RKIND), pointer :: groundingLineMigrationFlux real (kind=RKIND), pointer :: surfaceSpeedMax real (kind=RKIND), pointer :: basalSpeedMax @@ -219,17 +217,18 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ real (kind=RKIND) :: blockSumFloatingIceArea, blockSumFloatingIceVolume real (kind=RKIND) :: blockThickMin, blockThickMax real (kind=RKIND) :: blockSumSfcMassBal, blockSumBasalMassBal + real (kind=RKIND) :: blockSumGroundedSfcMassBal, blockSumFloatingSfcMassBal real (kind=RKIND) :: blockSumGroundedBasalMassBal, blockSumFloatingBasalMassBal real (kind=RKIND) :: blockSumCalvingFlux real (kind=RKIND) :: blockMaxSurfaceSpeed real (kind=RKIND) :: blockMaxBasalSpeed real (kind=RKIND) :: blockGLflux + real (kind=RKIND) :: blockGLMigrationFlux ! Local variables for calculations - real (kind=RKIND) :: fluxSign ! variables for processing stats - integer, parameter :: kMaxVariables = 16 ! Increase if number of stats increase + integer, parameter :: kMaxVariables = 24 ! Increase if number of stats increase integer :: nVars real (kind=RKIND), dimension(kMaxVariables) :: reductions, sums, mins, maxes @@ -246,11 +245,14 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ blockSumFloatingIceArea = 0.0_RKIND blockSumFloatingIceVolume = 0.0_RKIND blockSumSfcMassBal = 0.0_RKIND + blockSumGroundedSfcMassBal = 0.0_RKIND + blockSumFloatingSfcMassBal = 0.0_RKIND blockSumBasalMassBal = 0.0_RKIND blockSumGroundedBasalMassBal = 0.0_RKIND blockSumFloatingBasalMassBal = 0.0_RKIND blockSumCalvingFlux = 0.0_RKIND blockGLflux = 0.0_RKIND + blockGLMigrationFlux = 0.0_RKIND ! initialize max to 0, min to large number blockThickMin = 100000.0_RKIND @@ -274,27 +276,24 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) ! get values and arrays from standard pools - call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) - call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_array(meshPool, 'deltat', deltat) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) - call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) - call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'sfcMassBalApplied', sfcMassBalApplied) + call mpas_pool_get_array(geometryPool, 'groundedSfcMassBalApplied', groundedSfcMassBalApplied) call mpas_pool_get_array(geometryPool, 'basalMassBal', basalMassBal) call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) - call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(geometryPool, 'groundedToFloatingThickness', groundedToFloatingThickness) call mpas_pool_get_array(velocityPool, 'surfaceSpeed', surfaceSpeed) call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) - call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + call mpas_pool_get_array(velocityPool, 'fluxAcrossGroundingLine', fluxAcrossGroundingLine) ! loop over cells do iCell = 1,nCellsSolve @@ -306,7 +305,7 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ * areaCell(iCell) * thickness(iCell) blockSumVAF = blockSumVAF + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) * areaCell(iCell) * & - ( thickness(iCell) + (rhow / rhoi) * min(0.0_RKIND, (bedTopography(iCell) - config_sea_level)) ) + ( thickness(iCell) + (rhow / rhoi) * min(0.0_RKIND, (bedTopography(iCell) - config_sea_level)) ) blockSumGroundedIceArea = blockSumGroundedIceArea + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) & * areaCell(iCell) @@ -326,20 +325,22 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ blockThickMin = thickness(iCell) endif - ! sfc and basal mass balance (kg yr^{-1}) - !SFP: These calculations need to be tested still - blockSumSfcMassBal = blockSumSfcMassBal + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & - * areaCell(iCell) * sfcMassBal(iCell) * scyr + ! SMB (kg yr^{-1}) + blockSumSfcMassBal = blockSumSfcMassBal + areaCell(iCell) * sfcMassBalApplied(iCell) * scyr + blockSumGroundedSfcMassBal = blockSumGroundedSfcMassBal + areaCell(iCell) * groundedSfcMassBalApplied(iCell) * scyr + blockSumFloatingSfcMassBal = blockSumFloatingSfcMassBal + & + (sfcMassBalApplied(iCell) - groundedSfcMassBalApplied(iCell)) * areaCell(iCell) * scyr + ! BMB (kg yr-1) blockSumBasalMassBal = blockSumBasalMassBal + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & * areaCell(iCell) * basalMassBal(iCell) * scyr - blockSumGroundedBasalMassBal = blockSumGroundedBasalMassBal + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + blockSumGroundedBasalMassBal = blockSumGroundedBasalMassBal + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND)& * areaCell(iCell) * groundedBasalMassBal(iCell) * scyr - blockSumFloatingBasalMassBal = blockSumFloatingBasalMassBal + real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) & + blockSumFloatingBasalMassBal = blockSumFloatingBasalMassBal + real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND)& * areaCell(iCell) * floatingBasalMassBal(iCell) * scyr ! mass lass due do calving (kg yr^{-1}) blockSumCalvingFlux = blockSumCalvingFlux + calvingThickness(iCell) * & - areaCell(iCell) * config_ice_density / ( deltat / scyr ) + areaCell(iCell) * rhoi / ( deltat / scyr ) ! max surface speed if (surfaceSpeed(iCell) > blockMaxSurfaceSpeed) then @@ -351,29 +352,17 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ blockMaxBasalSpeed = basalSpeed(iCell) endif + ! GL migration flux + blockGLMigrationFlux = blockGLMigrationFlux + groundedToFloatingThickness(iCell) * areaCell(iCell) & + * rhoi / (deltat / scyr) ! convert from m to kg/yr + end do ! end loop over cells ! Loop over edges do iEdge = 1, nEdgesSolve - if (li_mask_is_grounding_line(edgeMask(iEdge))) then - ! Determine sign of this edge relative to GL - ! (+=grounded to floating, -=floating to grounded) - iCell1 = cellsOnEdge(1,iEdge) - iCell2 = cellsOnEdge(2,iEdge) - if (li_mask_is_grounded_ice(cellMask(iCell1))) then - fluxSign = 1.0_RKIND - else - fluxSign = -1.0_RKIND - endif - - ! Loop over levels - do k = 1, nVertLevels - ! Flux across GL, units = kg/yr - blockGLflux = blockGLflux + fluxSign * layerNormalVelocity(k, iEdge) * dvEdge(iEdge) * layerThicknessEdge(k, iEdge) & - * scyr * config_ice_density ! convert from m^3/s to kg/yr - end do ! end loop over levels - end if ! if GL - + ! Flux across GL, units = kg/yr + blockGLflux = blockGLflux + fluxAcrossGroundingLine(iEdge) * dvEdge(iEdge) & + * scyr * rhoi ! convert from m^2/s to kg/yr end do ! end loop over edges block => block % next @@ -412,13 +401,17 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ sums(5) = blockSumFloatingIceArea sums(6) = blockSumFloatingIceVolume sums(7) = blockSumSfcMassBal - sums(8) = blockSumBasalMassBal - sums(9) = blockSumGroundedBasalMassBal - sums(10) = blockSumFloatingBasalMassBal - sums(11) = blockSumCalvingFlux - sums(12) = blockSumVAF - sums(13) = blockGLflux - nVars = 13 + sums(8) = blockSumGroundedSfcMassBal + sums(9) = blockSumFloatingSfcMassBal + + sums(10) = blockSumBasalMassBal + sums(11) = blockSumGroundedBasalMassBal + sums(12) = blockSumFloatingBasalMassBal + sums(13) = blockSumCalvingFlux + sums(14) = blockSumVAF + sums(15) = blockGLflux + sums(16) = blockGLMigrationflux + nVars = 16 call mpas_dmpar_sum_real_array(dminfo, nVars, sums(1:nVars), reductions(1:nVars)) @@ -435,6 +428,8 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ call mpas_pool_get_array(globalStatsAMPool, 'groundedIceVolume', groundedIceVolume) call mpas_pool_get_array(globalStatsAMPool, 'iceThicknessMean', iceThicknessMean) call mpas_pool_get_array(globalStatsAMPool, 'totalSfcMassBal', totalSfcMassBal) + call mpas_pool_get_array(globalStatsAMPool, 'totalGroundedSfcMassBal', totalGroundedSfcMassBal) + call mpas_pool_get_array(globalStatsAMPool, 'totalFloatingSfcMassBal', totalFloatingSfcMassBal) call mpas_pool_get_array(globalStatsAMPool, 'avgNetAccumulation', avgNetAccumulation) call mpas_pool_get_array(globalStatsAMPool, 'totalBasalMassBal', totalBasalMassBal) call mpas_pool_get_array(globalStatsAMPool, 'totalGroundedBasalMassBal', totalGroundedBasalMassBal) @@ -443,6 +438,7 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ call mpas_pool_get_array(globalStatsAMPool, 'avgSubshelfMelt', avgSubshelfMelt) call mpas_pool_get_array(globalStatsAMPool, 'totalCalvingFlux', totalCalvingFlux) call mpas_pool_get_array(globalStatsAMPool, 'groundingLineFlux', groundingLineFlux) + call mpas_pool_get_array(globalStatsAMPool, 'groundingLineMigrationFlux', groundingLineMigrationFlux) totalIceArea = reductions(1) totalIceVolume = reductions(2) @@ -451,17 +447,33 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ floatingIceArea = reductions(5) floatingIceVolume = reductions(6) totalSfcMassBal = reductions(7) - totalBasalMassBal = reductions(8) - totalGroundedBasalMassBal = reductions(9) - totalFloatingBasalMassBal = reductions(10) - totalCalvingFlux = reductions(11) - volumeAboveFloatation = reductions(12) - groundingLineFlux = reductions(13) - - iceThicknessMean = totalIceVolume / totalIceArea - avgNetAccumulation = totalSfcMassBal / totalIceArea / rhoi - avgGroundedBasalMelt = -1.0_RKIND * totalGroundedBasalMassBal / groundedIceArea / rhoi - avgSubshelfMelt = -1.0_RKIND * totalFloatingBasalMassBal / floatingIceArea / rhoi + totalGroundedSfcMassBal = reductions(8) + totalFloatingSfcMassBal = reductions(9) + totalBasalMassBal = reductions(10) + totalGroundedBasalMassBal = reductions(11) + totalFloatingBasalMassBal = reductions(12) + totalCalvingFlux = reductions(13) + volumeAboveFloatation = reductions(14) + groundingLineFlux = reductions(15) + groundingLineMigrationFlux = reductions(16) + + if (totalIceArea > 0.0_RKIND) then + iceThicknessMean = totalIceVolume / totalIceArea + avgNetAccumulation = totalSfcMassBal / totalIceArea / rhoi + else + iceThicknessMean = 0.0_RKIND + avgNetAccumulation = 0.0_RKIND + endif + if (groundedIceArea > 0.0_RKIND) then + avgGroundedBasalMelt = -1.0_RKIND * totalGroundedBasalMassBal / groundedIceArea / rhoi + else + avgGroundedBasalMelt = 0.0_RKIND + endif + if (floatingIceArea > 0.0_RKIND) then + avgSubshelfMelt = -1.0_RKIND * totalFloatingBasalMassBal / floatingIceArea / rhoi + else + avgSubshelfMelt = 0.0_RKIND + endif block => block % next end do @@ -514,7 +526,7 @@ subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ - end subroutine li_compute_global_stats!}}} + end subroutine li_compute_global_stats !*********************************************************************** ! @@ -529,7 +541,7 @@ end subroutine li_compute_global_stats!}}} ! !----------------------------------------------------------------------- - subroutine li_restart_global_stats(domain, memberName, err)!{{{ + subroutine li_restart_global_stats(domain, memberName, err) !----------------------------------------------------------------- ! @@ -563,7 +575,7 @@ subroutine li_restart_global_stats(domain, memberName, err)!{{{ err = 0 - end subroutine li_restart_global_stats!}}} + end subroutine li_restart_global_stats !*********************************************************************** ! @@ -578,7 +590,7 @@ end subroutine li_restart_global_stats!}}} ! !----------------------------------------------------------------------- - subroutine li_finalize_global_stats(domain, memberName, err)!{{{ + subroutine li_finalize_global_stats(domain, memberName, err) !----------------------------------------------------------------- ! @@ -612,7 +624,7 @@ subroutine li_finalize_global_stats(domain, memberName, err)!{{{ err = 0 - end subroutine li_finalize_global_stats!}}} + end subroutine li_finalize_global_stats end module li_global_stats diff --git a/src/core_landice/analysis_members/mpas_li_regional_stats.F b/src/core_landice/analysis_members/mpas_li_regional_stats.F index f83bddd3de..0e0543df57 100644 --- a/src/core_landice/analysis_members/mpas_li_regional_stats.F +++ b/src/core_landice/analysis_members/mpas_li_regional_stats.F @@ -71,7 +71,7 @@ module li_regional_stats ! !----------------------------------------------------------------------- - subroutine li_init_regional_stats(domain, memberName, err)!{{{ + subroutine li_init_regional_stats(domain, memberName, err) !----------------------------------------------------------------- ! @@ -105,7 +105,7 @@ subroutine li_init_regional_stats(domain, memberName, err)!{{{ err = 0 - end subroutine li_init_regional_stats!}}} + end subroutine li_init_regional_stats !*********************************************************************** ! @@ -120,7 +120,7 @@ end subroutine li_init_regional_stats!}}} ! !----------------------------------------------------------------------- - subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ + subroutine li_compute_regional_stats(domain, memberName, timeLevel, err) !----------------------------------------------------------------- ! @@ -169,18 +169,20 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ real (kind=RKIND), pointer :: config_ice_density real (kind=RKIND), pointer :: deltat real (kind=RKIND), dimension(:), pointer :: areaCell - real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: dvEdge real (kind=RKIND), dimension(:), pointer :: thickness real (kind=RKIND), dimension(:), pointer :: bedTopography - real (kind=RKIND), dimension(:), pointer :: sfcMassBal + real (kind=RKIND), dimension(:), pointer :: sfcMassBalApplied + real (kind=RKIND), dimension(:), pointer :: groundedSfcMassBalApplied real (kind=RKIND), dimension(:), pointer :: basalMassBal real (kind=RKIND), dimension(:), pointer :: groundedBasalMassBal real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal real (kind=RKIND), dimension(:), pointer :: calvingThickness - real (kind=RKIND), dimension(:), pointer :: surfaceSpeed + real (kind=RKIND), dimension(:), pointer :: surfaceSpeed real (kind=RKIND), dimension(:), pointer :: basalSpeed - real (kind=RKIND), dimension(:,:), pointer :: layerNormalVelocity - real (kind=RKIND), dimension(:,:), pointer :: layerThicknessEdge + real (kind=RKIND), dimension(:), pointer :: fluxAcrossGroundingLine + real (kind=RKIND), dimension(:), pointer :: groundedToFloatingThickness + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity ! config options needed real (kind=RKIND), pointer :: config_sea_level @@ -193,7 +195,8 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ integer, dimension(:,:), pointer :: cellsOnEdge integer, pointer :: nRegions, nRegionGroups !, maxRegionsInGroup !! maxRegionsInGroup not needed / used yet integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels - integer :: k, iCell, iCell1, iCell2, iEdge + integer :: k, iCell, iEdge + integer :: upwindCell integer :: iRegion, iGroup ! scalars to be calculated here from regional sums @@ -203,14 +206,16 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ real (kind=RKIND), dimension(:), pointer :: regionalFloatingIceArea, regionalFloatingIceVolume real (kind=RKIND), dimension(:), pointer :: regionalIceThicknessMin, regionalIceThicknessMax, regionalIceThicknessMean real (kind=RKIND), dimension(:), pointer :: regionalSumSfcMassBal, regionalSumBasalMassBal + real (kind=RKIND), dimension(:), pointer :: regionalSumGroundedSfcMassBal, regionalSumFloatingSfcMassBal real (kind=RKIND), dimension(:), pointer :: regionalSumGroundedBasalMassBal, regionalSumFloatingBasalMassBal real (kind=RKIND), dimension(:), pointer :: regionalSumCalvingFlux real (kind=RKIND), dimension(:), pointer :: regionalSumGroundingLineFlux + real (kind=RKIND), dimension(:), pointer :: regionalSumGroundingLineMigrationFlux real (kind=RKIND), dimension(:), pointer :: regionalAvgNetAccumulation real (kind=RKIND), dimension(:), pointer :: regionalAvgGroundedBasalMelt real (kind=RKIND), dimension(:), pointer :: regionalAvgSubshelfMelt real (kind=RKIND), dimension(:), pointer :: regionalSurfaceSpeedMax - real (kind=RKIND), dimension(:), pointer :: regionalBasalSpeedMax + real (kind=RKIND), dimension(:), pointer :: regionalBasalSpeedMax ! storage for sums over blocks real (kind=RKIND), dimension(:), allocatable :: blockSumRegionIceArea, blockSumRegionIceVolume @@ -219,9 +224,11 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ real (kind=RKIND), dimension(:), allocatable :: blockSumRegionFloatingIceArea, blockSumRegionFloatingIceVolume real (kind=RKIND), dimension(:), allocatable :: blockRegionThickMin, blockRegionThickMax real (kind=RKIND), dimension(:), allocatable :: blockSumRegionSfcMassBal, blockSumRegionBasalMassBal + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionGroundedSfcMassBal, blockSumRegionFloatingSfcMassBal real (kind=RKIND), dimension(:), allocatable :: blockSumRegionGroundedBasalMassBal, blockSumRegionFloatingBasalMassBal real (kind=RKIND), dimension(:), allocatable :: blockSumRegionCalvingFlux real (kind=RKIND), dimension(:), allocatable :: blockSumRegionGLflux + real (kind=RKIND), dimension(:), allocatable :: blockRegionGLMigrationFlux real (kind=RKIND), dimension(:), allocatable :: blockRegionMaxSurfaceSpeed real (kind=RKIND), dimension(:), allocatable :: blockRegionMaxBasalSpeed @@ -229,7 +236,7 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ real (kind=RKIND) :: fluxSign ! variables for processing stats - integer, parameter :: kMaxVariables = 16 ! Increase if number of stats increase + integer, parameter :: kMaxVariables = 32 ! Increase if number of stats increase integer :: nVars real (kind=RKIND), dimension(kMaxVariables) :: reductions, sums, mins, maxes @@ -270,15 +277,17 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) - call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'sfcMassBalApplied', sfcMassBalApplied) + call mpas_pool_get_array(geometryPool, 'groundedSfcMassBalApplied', groundedSfcMassBalApplied) call mpas_pool_get_array(geometryPool, 'basalMassBal', basalMassBal) call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) - call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(geometryPool, 'groundedToFloatingThickness', groundedToFloatingThickness) call mpas_pool_get_array(velocityPool, 'surfaceSpeed', surfaceSpeed) call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) - call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + call mpas_pool_get_array(velocityPool, 'fluxAcrossGroundingLine', fluxAcrossGroundingLine) + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) ! get region cell masks from regionMasks.nc input file call mpas_pool_get_array(regionsPool, 'regionCellMasks', regionCellMasks) @@ -292,7 +301,10 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ allocate(blockRegionMaxBasalSpeed(nRegions)); allocate(blockRegionMaxSurfaceSpeed(nRegions)) allocate(blockSumRegionSfcMassBal(nRegions)); allocate(blockSumRegionBasalMassBal(nRegions)) allocate(blockSumRegionGroundedBasalMassBal(nRegions)); allocate(blockSumRegionFloatingBasalMassBal(nRegions)) - allocate(blockSumRegionCalvingFlux(nRegions)); allocate(blockSumRegionGLflux(nRegions)) + allocate(blockSumRegionGroundedSfcMassBal(nRegions)); allocate(blockSumRegionFloatingSfcMassBal(nRegions)) + allocate(blockSumRegionCalvingFlux(nRegions)) + allocate(blockSumRegionGLflux(nRegions)) + allocate(blockRegionGLMigrationFlux(nRegions)) blockSumRegionIceArea = 0.0_RKIND; blockSumRegionIceVolume = 0.0_RKIND blockSumRegionVAF = 0.0_RKIND @@ -301,8 +313,11 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ blockRegionThickMin = 10000.0_RKIND; blockRegionThickMax = 0.0_RKIND blockRegionMaxBasalSpeed = 0.0_RKIND; blockRegionMaxSurfaceSpeed = 0.0_RKIND blockSumRegionSfcMassBal = 0.0_RKIND; blockSumRegionBasalMassBal = 0.0_RKIND + blockSumRegionGroundedSfcMassBal = 0.0_RKIND; blockSumRegionFloatingSfcMassBal = 0.0_RKIND blockSumRegionGroundedBasalMassBal = 0.0_RKIND; blockSumRegionFloatingBasalMassBal = 0.0_RKIND - blockSumRegionCalvingFlux = 0.0_RKIND; blockSumRegionGLflux = 0.0_RKIND + blockSumRegionCalvingFlux = 0.0_RKIND + blockSumRegionGLflux = 0.0_RKIND + blockRegionGLMigrationFlux = 0.0_RKIND do iCell = 1,nCellsSolve ! loop over cells ! do iGroup = 1,nRegionGroups ! loop over groups @@ -341,23 +356,33 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ ( real(regionCellMasks(iRegion,iCell),RKIND) * areaCell(iCell) & * thickness(iCell) * real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) ) - ! regional sum of sfc mass balance (kg yr^{-1}) + ! regional sum of sfc mass balance (kg yr^{-1}) blockSumRegionSfcMassBal(iRegion) = blockSumRegionSfcMassBal(iRegion) + ( real(regionCellMasks(iRegion,iCell),RKIND) & - * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) * areaCell(iCell) * sfcMassBal(iCell) * scyr ) + * areaCell(iCell) * sfcMassBalApplied(iCell) * scyr ) + + ! regional sum of sfc mass balance on grounded ice (kg yr^{-1}) + blockSumRegionGroundedSfcMassBal(iRegion) = blockSumRegionGroundedSfcMassBal(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) & + * areaCell(iCell) * groundedSfcMassBalApplied(iCell) * scyr) + + ! regional sum of sfc mass balance on floatingice (kg yr^{-1}) + blockSumRegionFloatingSfcMassBal(iRegion) = blockSumRegionFloatingSfcMassBal(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) & + * areaCell(iCell) * (sfcMassBalApplied(iCell) - groundedSfcMassBalApplied(iCell)) * scyr) ! regional sum of basal mass balance (kg yr^{-1}) blockSumRegionBasalMassBal(iRegion) = blockSumRegionBasalMassBal(iRegion) + & ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & * areaCell(iCell) * basalMassBal(iCell) * scyr ) - ! regional sum of grounded basal mass balance (kg yr^{-1}) + ! regional sum of floating basal mass balance (kg yr^{-1}) blockSumRegionFloatingBasalMassBal(iRegion) = blockSumRegionFloatingBasalMassBal(iRegion) + & ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) & * areaCell(iCell) * floatingBasalMassBal(iCell) * scyr ) - ! regional sum of floating basal mass balance (kg yr^{-1}) + ! regional sum of grounded basal mass balance (kg yr^{-1}) blockSumRegionGroundedBasalMassBal(iRegion) = blockSumRegionGroundedBasalMassBal(iRegion) + & - ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) & * areaCell(iCell) * groundedBasalMassBal(iCell) * scyr ) ! regional sum of mass lass due do calving (kg yr^{-1}) @@ -387,44 +412,35 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ blockRegionMaxBasalSpeed(iRegion) = basalSpeed(iCell) endif + ! GL migration flux + blockRegionGLMigrationFlux(iRegion) = blockRegionGLMigrationFlux(iRegion) + & + groundedToFloatingThickness(iCell) * areaCell(iCell) * rhoi / (deltat / scyr) + end do ! end loop over regions ! end do ! end loop over groups end do ! end loop over cells - do iEdge = 1,nEdgesSolve ! loop over edges - ! do iGroup = 1,nRegionGroups ! loop over groups - do iRegion = 1,nRegions ! loop over regions + do iEdge = 1,nEdgesSolve ! loop over edges if (li_mask_is_grounding_line(edgeMask(iEdge))) then - ! Determine sign of this edge relative to GL - ! (+=grounded to floating, -=floating to grounded) - iCell1 = cellsOnEdge(1,iEdge) - iCell2 = cellsOnEdge(2,iEdge) - if (li_mask_is_grounded_ice(cellMask(iCell1))) then - fluxSign = 1.0_RKIND + ! Determine upwind cell - we are choosing to assign edge quantities to the region of the upwind cell + if (normalVelocity(1,iEdge) > 0.0_RKIND) then ! using surface velo to determine upwind cell + ! upwind cell is cell 1 on this edge + upwindCell = cellsOnEdge(1,iEdge) else - fluxSign = -1.0_RKIND + ! upwind cell is cell 2 on this edge + upwindCell = cellsOnEdge(2,iEdge) endif - ! first need to determine if the edge flux is being calc. on is part of the region of interest - ! Use cellsOnEdge indices from above to index relevant regionCellMasks values and sum - if(regionCellMasks(iRegion,iCell1) + regionCellMasks(iRegion,iCell2) > 0) then - - ! Loop over levels - do k = 1, nVertLevels - ! Flux across GL, units = kg/yr - blockSumRegionGLflux(iRegion) = blockSumRegionGLflux(iRegion) + fluxSign * layerNormalVelocity(k, iEdge) * & - dvEdge(iEdge) * layerThicknessEdge(k, iEdge) & - * scyr * config_ice_density ! convert from m^3/s to kg/yr - end do ! end loop over levels - - end if ! if edge is on cell in region of interest - + do iRegion = 1,nRegions ! find this edge's region(s) + if(regionCellMasks(iRegion, upwindCell) == 1) then + ! assign the GL flux from this edge to the stat + blockSumRegionGLflux(iRegion) = blockSumRegionGLflux(iRegion) + & + fluxAcrossGroundingLine(iEdge) * dvEdge(iEdge) * scyr * config_ice_density ! convert from m^3/s to kg/yr + end if ! if edge is on cell in region of interest + end do ! end loop over regions end if ! if GL - - end do ! end loop over regions - ! end do ! end loop over groups end do ! end loop over edges block => block % next @@ -451,7 +467,7 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ sums = 0.0_RKIND reductions = 0.0_RKIND - + sums(1) = blockSumRegionIceArea(iRegion) sums(2) = blockSumRegionIceVolume(iRegion) sums(3) = blockSumRegionGroundedIceArea(iRegion) @@ -459,13 +475,16 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ sums(5) = blockSumRegionFloatingIceArea(iRegion) sums(6) = blockSumRegionFloatingIceVolume(iRegion) sums(7) = blockSumRegionSfcMassBal(iRegion) - sums(8) = blockSumRegionBasalMassBal(iRegion) - sums(9) = blockSumRegionGroundedBasalMassBal(iRegion) - sums(10) = blockSumRegionFloatingBasalMassBal(iRegion) - sums(11) = blockSumRegionCalvingFlux(iRegion) - sums(12) = blockSumRegionVAF(iRegion) - sums(13) = blockSumRegionGLflux(iRegion) - nVars = 13 + sums(8) = blockSumRegionGroundedSfcMassBal(iRegion) + sums(9) = blockSumRegionFloatingSfcMassBal(iRegion) + sums(10) = blockSumRegionBasalMassBal(iRegion) + sums(11) = blockSumRegionGroundedBasalMassBal(iRegion) + sums(12) = blockSumRegionFloatingBasalMassBal(iRegion) + sums(13) = blockSumRegionCalvingFlux(iRegion) + sums(14) = blockSumRegionVAF(iRegion) + sums(15) = blockSumRegionGLflux(iRegion) + sums(16) = blockRegionGLMigrationFlux(iRegion) + nVars = 16 call mpas_dmpar_sum_real_array(dminfo, nVars, sums(1:nVars), reductions(1:nVars)) @@ -473,7 +492,7 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'regionalStatsAM', regionalStatsAMPool) - + ! get values from regional stats pool call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceArea', regionalIceArea) call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceVolume', regionalIceVolume) @@ -485,6 +504,8 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceThicknessMean', regionalIceThicknessMean) call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumSfcMassBal', regionalSumSfcMassBal) call mpas_pool_get_array(regionalStatsAMPool, 'regionalAvgNetAccumulation', regionalAvgNetAccumulation) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumGroundedSfcMassBal', regionalSumGroundedSfcMassBal) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumFloatingSfcMassBal', regionalSumFloatingSfcMassBal) call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumBasalMassBal', regionalSumBasalMassBal) call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumGroundedBasalMassBal', regionalSumGroundedBasalMassBal) call mpas_pool_get_array(regionalStatsAMPool, 'regionalAvgGroundedBasalMelt', regionalAvgGroundedBasalMelt) @@ -492,7 +513,9 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ call mpas_pool_get_array(regionalStatsAMPool, 'regionalAvgSubshelfMelt', regionalAvgSubshelfMelt) call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumCalvingFlux', regionalSumCalvingFlux) call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumGroundingLineFlux', regionalSumGroundingLineFlux) - + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumGroundingLineMigrationFlux', & + regionalSumGroundingLineMigrationFlux) + regionalIceArea(iRegion) = reductions(1) regionalIceVolume(iRegion) = reductions(2) regionalGroundedIceArea(iRegion) = reductions(3) @@ -500,19 +523,35 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ regionalFloatingIceArea(iRegion) = reductions(5) regionalFloatingIceVolume(iRegion) = reductions(6) regionalSumSfcMassBal(iRegion) = reductions(7) - regionalSumBasalMassBal(iRegion) = reductions(8) - regionalSumGroundedBasalMassBal(iRegion) = reductions(9) - regionalSumFloatingBasalMassBal(iRegion) = reductions(10) - regionalSumCalvingFlux(iRegion) = reductions(11) - regionalVolumeAboveFloatation(iRegion) = reductions(12) - regionalSumGroundingLineFlux(iRegion) = reductions(13) - - regionalIceThicknessMean(iRegion) = regionalIceVolume(iRegion) / regionalIceArea(iRegion) - regionalAvgNetAccumulation(iRegion) = regionalSumSfcMassBal(iRegion) / regionalIceArea(iRegion) / rhoi - regionalAvgGroundedBasalMelt(iRegion) = -1.0_RKIND * regionalSumGroundedBasalMassBal(iRegion) / & - regionalGroundedIceArea(iRegion) / rhoi - regionalAvgSubshelfMelt(iRegion) = -1.0_RKIND * regionalSumFloatingBasalMassBal(iRegion) / & - regionalFloatingIceArea(iRegion) / rhoi + regionalSumGroundedSfcMassBal(iRegion) = reductions(8) + regionalSumFloatingSfcMassBal(iRegion) = reductions(9) + regionalSumBasalMassBal(iRegion) = reductions(10) + regionalSumGroundedBasalMassBal(iRegion) = reductions(11) + regionalSumFloatingBasalMassBal(iRegion) = reductions(12) + regionalSumCalvingFlux(iRegion) = reductions(13) + regionalVolumeAboveFloatation(iRegion) = reductions(14) + regionalSumGroundingLineFlux(iRegion) = reductions(15) + regionalSumGroundingLineMigrationFlux(iRegion) = reductions(16) + + if (regionalIceArea(iRegion) > 0.0_RKIND) then + regionalIceThicknessMean(iRegion) = regionalIceVolume(iRegion) / regionalIceArea(iRegion) + regionalAvgNetAccumulation(iRegion) = regionalSumSfcMassBal(iRegion) / regionalIceArea(iRegion) / rhoi + else + regionalIceThicknessMean(iRegion) = 0.0_RKIND + regionalAvgNetAccumulation(iRegion) = 0.0_RKIND + endif + if (regionalGroundedIceArea(iRegion) > 0.0_RKIND) then + regionalAvgGroundedBasalMelt(iRegion) = -1.0_RKIND * regionalSumGroundedBasalMassBal(iRegion) / & + regionalGroundedIceArea(iRegion) / rhoi + else + regionalAvgGroundedBasalMelt(iRegion) = 0.0_RKIND + endif + if (regionalFloatingIceArea(iRegion) > 0.0_RKIND) then + regionalAvgSubshelfMelt(iRegion) = -1.0_RKIND * regionalSumFloatingBasalMassBal(iRegion) / & + regionalFloatingIceArea(iRegion) / rhoi + else + regionalAvgSubshelfMelt(iRegion) = 0.0_RKIND + endif block => block % next end do @@ -556,7 +595,7 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ regionalBasalSpeedMax(iRegion) = reductions(3) block => block % next end do - + end do ! loop over regions ! deallocate storage for sums over blocks @@ -567,10 +606,13 @@ subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ deallocate(blockRegionThickMin); deallocate(blockRegionThickMax) deallocate(blockRegionMaxBasalSpeed); deallocate(blockRegionMaxSurfaceSpeed) deallocate(blockSumRegionSfcMassBal); deallocate(blockSumRegionBasalMassBal) + deallocate(blockSumRegionGroundedSfcMassBal); deallocate(blockSumRegionFloatingSfcMassBal) deallocate(blockSumRegionGroundedBasalMassBal); deallocate(blockSumRegionFloatingBasalMassBal) - deallocate(blockSumRegionCalvingFlux); deallocate(blockSumRegionGLflux) + deallocate(blockSumRegionCalvingFlux) + deallocate(blockSumRegionGLflux) + deallocate(blockRegionGLMigrationflux) - end subroutine li_compute_regional_stats!}}} + end subroutine li_compute_regional_stats !*********************************************************************** ! @@ -585,7 +627,7 @@ end subroutine li_compute_regional_stats!}}} ! !----------------------------------------------------------------------- - subroutine li_restart_regional_stats(domain, memberName, err)!{{{ + subroutine li_restart_regional_stats(domain, memberName, err) !----------------------------------------------------------------- ! @@ -619,7 +661,7 @@ subroutine li_restart_regional_stats(domain, memberName, err)!{{{ err = 0 - end subroutine li_restart_regional_stats!}}} + end subroutine li_restart_regional_stats !*********************************************************************** ! @@ -634,7 +676,7 @@ end subroutine li_restart_regional_stats!}}} ! !----------------------------------------------------------------------- - subroutine li_finalize_regional_stats(domain, memberName, err)!{{{ + subroutine li_finalize_regional_stats(domain, memberName, err) !----------------------------------------------------------------- ! @@ -668,7 +710,7 @@ subroutine li_finalize_regional_stats(domain, memberName, err)!{{{ err = 0 - end subroutine li_finalize_regional_stats!}}} + end subroutine li_finalize_regional_stats end module li_regional_stats diff --git a/src/core_landice/external_albany_version b/src/core_landice/external_albany_version index 4f00bb26c2..66846f145a 100644 --- a/src/core_landice/external_albany_version +++ b/src/core_landice/external_albany_version @@ -1 +1 @@ -d3968fc9e9b4f825f87d31564ea67d3e4fab656b +b745455e8ec7358d8be887a986c3509cec97cbfb diff --git a/src/core_landice/external_trilinos_version b/src/core_landice/external_trilinos_version index a7fd78f3de..6449871951 100644 --- a/src/core_landice/external_trilinos_version +++ b/src/core_landice/external_trilinos_version @@ -1 +1 @@ -9630bc5b179a0c5e1537923e3450ff79bb3ae78d +c29bb03b7743745f341efad5d82c8fc2dc93b274 diff --git a/src/core_landice/landice.cmake b/src/core_landice/landice.cmake index 0d580d7800..3b550fd340 100644 --- a/src/core_landice/landice.cmake +++ b/src/core_landice/landice.cmake @@ -69,6 +69,7 @@ list(APPEND RAW_SOURCES core_landice/mode_forward/mpas_li_velocity_simple.F core_landice/mode_forward/mpas_li_velocity_external.F core_landice/mode_forward/mpas_li_subglacial_hydro.F + core_landice/mode_forward/mpas_li_bedtopo.F ) if (CPPDEFS MATCHES ".*MPAS_LI_BUILD_INTERFACE.*") diff --git a/src/core_landice/mode_forward/Interface_velocity_solver.cpp b/src/core_landice/mode_forward/Interface_velocity_solver.cpp index 505268788b..3971076ba9 100644 --- a/src/core_landice/mode_forward/Interface_velocity_solver.cpp +++ b/src/core_landice/mode_forward/Interface_velocity_solver.cpp @@ -14,32 +14,30 @@ distributed with this code, or at http://mpas-dev.github.com/license.html #include #include #include +#include +#include #include "Interface_velocity_solver.hpp" -//#include -//#include -//#include // =================================================== //! Namespaces // =================================================== -//typedef std::list exchangeList_Type; +#define changeTrianglesOwnership + // ice_problem pointer int Ordering = 0; //ordering ==0 means that the mesh is extruded layerwise, whereas ordering==1 means that the mesh is extruded columnwise. MPI_Comm comm, reducedComm; bool isDomainEmpty = true; -bool initialize_velocity = true; bool first_time_step = true; int nCells_F, nEdges_F, nVertices_F; int nCellsSolve_F, nEdgesSolve_F, nVerticesSolve_F; -int nVertices, nEdges, nTriangles, nGlobalVertices, nGlobalEdges, - nGlobalTriangles; +int nVertices, nEdges, nTriangles, globalVertexStride, globalEdgeStride,globalTriangleStride; int maxNEdgesOnCell_F; int const *cellsOnEdge_F, *cellsOnVertex_F, *verticesOnCell_F, - *verticesOnEdge_F, *edgesOnCell_F, *indexToCellID_F, *nEdgesOnCells_F, - *verticesMask_F, *cellsMask_F, *dirichletCellsMask_F, *floatingEdgesMask_F; + *verticesOnEdge_F, *edgesOnCell_F, *indexToCellID_F, *indexToEdgeID_F, *indexToVertexID_F, *nEdgesOnCells_F, + *verticesMask_F, *cellsMask_F, *dirichletCellsMask_F; std::vector layersRatio, levelsNormalizedThickness; int nLayers; double const *xCell_F, *yCell_F, *zCell_F, *xVertex_F, *yVertex_F, *zVertex_F, *areaTriangle_F; @@ -48,6 +46,7 @@ const double unit_length = 1000; const double T0 = 273.15; const double secondsInAYear = 31536000.0; // This may vary slightly in MPAS, but this should be close enough for how this is used. double minThickness = 1e-3; //[km] +double thermal_thickness_limit; //[km] const double minBeta = 1e-5; double rho_ice; double rho_ocean; @@ -63,13 +62,13 @@ int original_stderr; // the location of stderr before we captured it int Interface_stdout; // the location of stdout as we use it here //void *phgGrid = 0; -std::vector edgesToReceive, fCellsToReceive, indexToTriangleID, - verticesOnTria, trianglesOnEdge, trianglesPositionsOnEdge, verticesOnEdge, - trianglesProcIds, reduced_ranks; -std::vector indexToVertexID, vertexToFCell, triangleToFVertex, indexToEdgeID, edgeToFEdge, - mask, fVertexToTriangleID, fCellToVertex, floatingEdgesIds, dirichletNodesIDs; -std::vector temperatureOnTetra, dissipationHeatOnTetra, velocityOnVertices, velocityOnCells, - elevationData, thicknessData, betaData, bedTopographyData, temperatureData, smbData, thicknessOnCells; +std::vector indexToTriangleID, + verticesOnTria, trianglesOnEdge, verticesOnEdge, + trianglesProcIds, reduced_ranks; +std::vector indexToVertexID, vertexToFCell, vertexProcIDs, triangleToFVertex, indexToEdgeID, edgeToFEdge, + fVertexToTriangleID, fCellToVertex, iceMarginEdgesLIds, dirichletNodesIDs; +std::vector dissipationHeatOnPrisms, velocityOnVertices, velocityOnCells, + elevationData, thicknessData, betaData, bedTopographyData, stiffnessFactorData, effecPressData, muFrictionData, temperatureDataOnPrisms, smbData, thicknessOnCells, bodyForceOnBasalCell; std::vector isVertexBoundary, isBoundaryEdge; // only needed for creating ASCII mesh @@ -78,6 +77,7 @@ std::vector smbUncertaintyData; std::vector bmbData, bmbUncertaintyData; std::vector observedVeloXData, observedVeloYData, observedVeloUncertaintyData; std::vector observedDHDtData, observedDHDtUncertaintyData; +std::vector surfaceAirTemperatureData, basalHeatFluxData; std::vector indexToCellIDData; int numBoundaryEdges; @@ -86,8 +86,8 @@ double radius; exchangeList_Type const *sendCellsList_F = 0, *recvCellsList_F = 0; exchangeList_Type const *sendEdgesList_F = 0, *recvEdgesList_F = 0; exchangeList_Type const *sendVerticesList_F = 0, *recvVerticesList_F = 0; -exchangeList_Type sendCellsListReversed, recvCellsListReversed, - sendEdgesListReversed, recvEdgesListReversed; +exchangeList_Type sendVerticesListReversed, recvVerticesListReversed, + sendCellsListReversed, recvCellsListReversed; exchange::exchange(int _procID, int const* vec_first, int const* vec_last, int fieldDim) : @@ -106,24 +106,25 @@ int velocity_solver_init_mpi(int* fComm) { // get MPI_Comm from Fortran comm = MPI_Comm_f2c(*fComm); reducedComm = MPI_COMM_NULL; // initialize to null so we can check if set - - return 0; + return velocity_solver_init_mpi__(comm); } void velocity_solver_set_parameters(double const* gravity_F, double const* ice_density_F, double const* ocean_density_F, double const* sea_level_F, double const* flowParamA_F, - double const* enhancementFactor_F, double const* flowLawExponent_F, double const* dynamic_thickness_F, + double const* flowLawExponent_F, double const* dynamic_thickness_F, double const* clausius_clapeyron_coeff, + double const* thermal_thickness_limit_F, int const* li_mask_ValueDynamicIce, int const* li_mask_ValueIce, bool const* use_GLP_F) { // This function sets parameter values used by MPAS on the C/C++ side rho_ice = *ice_density_F; rho_ocean = *ocean_density_F; + thermal_thickness_limit = *thermal_thickness_limit_F / unit_length; // Import with Albany scaling dynamic_ice_bit_value = *li_mask_ValueDynamicIce; ice_present_bit_value = *li_mask_ValueIce; velocity_solver_set_physical_parameters__(*gravity_F, rho_ice, *ocean_density_F, *sea_level_F/unit_length, *flowParamA_F*std::pow(unit_length,4)*secondsInAYear, - *enhancementFactor_F, *flowLawExponent_F, *dynamic_thickness_F/unit_length, *use_GLP_F, *clausius_clapeyron_coeff); + *flowLawExponent_F, *dynamic_thickness_F/unit_length, *use_GLP_F, *clausius_clapeyron_coeff); } @@ -132,11 +133,6 @@ void velocity_solver_export_2d_data(double const* lowerSurface_F, double const* thickness_F, double const* beta_F) { if (isDomainEmpty) return; -#ifdef LIFEV - import2DFields(lowerSurface_F, thickness_F, beta_F, minThickneess); - velocity_solver_export_2d_data__(reducedComm, elevationData, thicknessData, - betaData, indexToVertexID); -#endif } void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, @@ -147,6 +143,8 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, int const* _verticesOnCell_F, int const* _verticesOnEdge_F, int const* _edgesOnCell_F, int const* _nEdgesOnCells_F, int const* _indexToCellID_F, + int const* _indexToEdgeID_F, + int const* _indexToVertexID_F, double const* _xCell_F, double const* _yCell_F, double const* _zCell_F, double const* _xVertex_F, double const* _yVertex_F, double const* _zVertex_F, double const* _areaTriangle_F, @@ -169,7 +167,9 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, verticesOnEdge_F = _verticesOnEdge_F; edgesOnCell_F = _edgesOnCell_F; nEdgesOnCells_F = _nEdgesOnCells_F; + indexToEdgeID_F = _indexToEdgeID_F; indexToCellID_F = _indexToCellID_F; + indexToVertexID_F = _indexToVertexID_F; xCell_F = _xCell_F; yCell_F = _yCell_F; zCell_F = _zCell_F; @@ -177,7 +177,6 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, yVertex_F = _yVertex_F; zVertex_F = _zVertex_F; areaTriangle_F = _areaTriangle_F; - mask.resize(nVertices_F); thicknessOnCells.resize(nCellsSolve_F); @@ -190,9 +189,6 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, recvVerticesList_F = new exchangeList_Type( unpackMpiArray(recvVerticesArray_F)); - trianglesProcIds.resize(nVertices_F); - getProcIds(trianglesProcIds, recvVerticesList_F); - if (radius > 10) { xCellProjected.resize(nCells_F); yCellProjected.resize(nCells_F); @@ -211,23 +207,6 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, } void velocity_solver_init_l1l2(double const* levelsRatio_F) { -#ifdef LIFEV - velocityOnVertices.resize(2 * nVertices * (nLayers + 1), 0.); - velocityOnCells.resize(2 * nCells_F * (nLayers + 1), 0.); - - if (isDomainEmpty) - return; - - layersRatio.resize(nLayers); - // !!Indexing of layers is reversed - for (int i = 0; i < nLayers; i++) - layersRatio[i] = levelsRatio_F[nLayers - 1 - i]; - //std::copy(levelsRatio_F, levelsRatio_F+nLayers, layersRatio.begin()); - mapCellsToVertices(velocityOnCells, velocityOnVertices, 2, nLayers, Ordering); - - velocity_solver_init_l1l2__(layersRatio, velocityOnVertices, initialize_velocity); - initialize_velocity = false; -#endif } @@ -237,82 +216,9 @@ void velocity_solver_solve_l1l2(double const* lowerSurface_F, double const* thickness_F, double const* beta_F, double const* temperature_F, double* const dirichletVelocityXValue, double* const dirichletVelocitYValue, double* u_normal_F, double* xVelocityOnCell, double* yVelocityOnCell) { - -#ifdef LIFEV - - std::fill(u_normal_F, u_normal_F + nEdges_F * (nLayers+1), 0.); - - double localSum(0), sum(0); - - for (int i = 0; i < nCellsSolve_F; i++) { - localSum = std::max(localSum, - std::fabs(thickness_F[i] - thicknessOnCells[i])); - } - - MPI_Allreduce(&localSum, &sum, 1, MPI_DOUBLE, MPI_MAX, comm); - - std::cout << "Thickness change: " << sum << std::endl; - std::copy(thickness_F, thickness_F + nCellsSolve_F, &thicknessOnCells[0]); - - if (!isDomainEmpty) { - std::vector temperatureData(nLayers * nVertices); - - import2DFields(lowerSurface_F, thickness_F, beta_F, minThickness); - - for (int index = 0; index < nVertices; index++) { - int iCell = vertexToFCell[index]; - for (int il = 0; il < nLayers; il++) { - temperatureData[index + il * nVertices] = temperature_F[iCell * nLayers - + (nLayers - il - 1)]; - } - } - - - velocity_solver_solve_l1l2__(elevationData, thicknessData, betaData, - temperatureData, indexToVertexID, velocityOnVertices); - } - - - mapVerticesToCells (velocityOnVertices, &velocityOnCells[0], 2, nLayers, Ordering); - - //computing x, yVelocityOnCell - int sizeVelOnCell = nCells_F * (nLayers + 1); - for(int iCell=0; iCell regulThk(thicknessData); - for (int index = 0; index < nVertices; index++) - regulThk[index] = std::max(1e-4, thicknessData[index]); - - std::vector mpasIndexToVertexID(nVertices); - for (int i = 0; i < nVertices; i++) { - mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; - } -#ifdef LIFEV - velocity_solver_export_l1l2_velocity__(layersRatio, elevationData, regulThk, mpasIndexToVertexID, reducedComm); -#endif -} +void velocity_solver_export_l1l2_velocity() {} void velocity_solver_init_fo(double const *levelsRatio_F) { @@ -326,23 +232,16 @@ void velocity_solver_init_fo(double const *levelsRatio_F) { // !!Indexing of layers is reversed for (int i = 0; i < nLayers; i++) layersRatio[i] = levelsRatio_F[nLayers - 1 - i]; - //std::copy(levelsRatio_F, levelsRatio_F+nLayers, layersRatio.begin()); - mapCellsToVertices(velocityOnCells, velocityOnVertices, 2, nLayers, Ordering); - -#ifdef LIFEV - velocity_solver_init_fo__(layersRatio, velocityOnVertices, indexToVertexID, initialize_velocity); -#endif - // iceProblemPtr->initializeSolverFO(layersRatio, velocityOnVertices, thicknessData, elevationData, indexToVertexID, initialize_velocity); - initialize_velocity = false; } void velocity_solver_solve_fo(double const* bedTopography_F, double const* lowerSurface_F, - double const* thickness_F, double const* beta_F, - double const* smb_F, double const* temperature_F, + double const* thickness_F, double * beta_F, + double const* smb_F, double const* temperature_F, double const* stiffnessFactor_F, + double const* effecPress_F, double const* muFriction_F, double* const dirichletVelocityXValue, double* const dirichletVelocitYValue, - double* u_normal_F, double* dissipation_heat_F, + double* u_normal_F, double* bodyForce_F, double* dissipation_heat_F, double* xVelocityOnCell, double* yVelocityOnCell, double const* deltat, int *error) { @@ -368,52 +267,36 @@ void velocity_solver_solve_fo(double const* bedTopography_F, double const* lower if (!isDomainEmpty) { -#ifdef LIFEV - double localSum(0), sum(0); - - for (int i = 0; i < nCellsSolve_F; i++) { - localSum = std::max(localSum, - std::fabs(thickness_F[i] - thicknessOnCells[i])); - } - - MPI_Allreduce(&localSum, &sum, 1, MPI_DOUBLE, MPI_MAX, comm); - - std::cout << "Thickness change: " << sum << std::endl; - std::copy(thickness_F, thickness_F + nCellsSolve_F, &thicknessOnCells[0]); -#endif - - - - std::map bdExtensionMap; - import2DFields(bdExtensionMap, bedTopography_F, lowerSurface_F, thickness_F, beta_F, temperature_F, smb_F, minThickness); + std::vector > marineBdyExtensionMap; + importFields(marineBdyExtensionMap, bedTopography_F, lowerSurface_F, thickness_F, beta_F, stiffnessFactor_F, effecPress_F, muFriction_F, temperature_F, smb_F, minThickness); std::vector regulThk(thicknessData); for (int index = 0; index < nVertices; index++) regulThk[index] = std::max(1e-4, thicknessData[index]); - importP0Temperature(); - - dissipationHeatOnTetra.resize(3 * nLayers * indexToTriangleID.size()); + dissipationHeatOnPrisms.resize(nLayers * indexToTriangleID.size()); + bodyForceOnBasalCell.resize(indexToTriangleID.size()); std::cout << "\n\nTimeStep: "<< *deltat << "\n\n"<< std::endl; double dt = (*deltat)/secondsInAYear; int albany_error; - velocity_solver_solve_fo__(nLayers, nGlobalVertices, nGlobalTriangles, + velocity_solver_solve_fo__(nLayers, globalVertexStride, globalTriangleStride, Ordering, first_time_step, indexToVertexID, indexToTriangleID, minBeta, regulThk, levelsNormalizedThickness, elevationData, thicknessData, betaData, bedTopographyData, smbData, - temperatureOnTetra, dissipationHeatOnTetra, velocityOnVertices, + stiffnessFactorData, effecPressData, muFrictionData, + temperatureDataOnPrisms, bodyForceOnBasalCell, dissipationHeatOnPrisms, velocityOnVertices, albany_error, dt); *error=albany_error; } - + exportDissipationHeat(dissipation_heat_F); - std::vector mpasIndexToVertexID(nVertices); - for (int i = 0; i < nVertices; i++) { - mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; + if (bodyForce_F!=nullptr) { + exportBodyForce(bodyForce_F); } + exportBeta(beta_F); mapVerticesToCells(velocityOnVertices, &velocityOnCells[0], 2, nLayers, Ordering); @@ -427,81 +310,15 @@ void velocity_solver_solve_fo(double const* bedTopography_F, double const* lower xVelocityOnCell[indexReversed] = velocityOnCells[index]; yVelocityOnCell[indexReversed] = velocityOnCells[index+sizeVelOnCell]; } - - if (!isDomainEmpty) get_prism_velocity_on_FEdges(u_normal_F, velocityOnCells, edgeToFEdge); - + allToAll(u_normal_F, sendEdgesList_F, recvEdgesList_F, nLayers+1); std::vector velOnEdges(nEdges * (nLayers+1)); for (int i = 0; i < nEdges; i++) { for (int il = 0; il < nLayers+1; il++) { velOnEdges[i * (nLayers+1) + il] = u_normal_F[edgeToFEdge[i] * (nLayers+1) + il]; } } - - allToAll(u_normal_F, &sendEdgesListReversed, &recvEdgesListReversed, nLayers+1); - - allToAll(u_normal_F, sendEdgesList_F, recvEdgesList_F, nLayers+1); - first_time_step = false; - -#ifdef LIFEV - - std::vector edgesProcId(nEdges_F), trianglesProcIds(nVertices_F); - getProcIds(edgesProcId, recvEdgesList_F); - getProcIds(trianglesProcIds, recvVerticesList_F); - - int localSumInt(0), sumInt(0); - - for (int i = 0; i < nEdges; i++) { - for (int il = 0; il < 1; il++) { - if (std::fabs( - velOnEdges[i * (nLayers+1) + il] - - u_normal_F[edgeToFEdge[i] * nLayers + il]) > 1e-9) - // if(edgeToFEdge[i]>nEdgesSolve_F) - { - localSumInt++; - int edge = edgeToFEdge[i]; - int gEdge = indexToEdgeID[i]; - ID fVertex0 = verticesOnEdge_F[2 * edge] - 1; - ID fVertex1 = verticesOnEdge_F[2 * edge + 1] - 1; - ID triaId0 = fVertexToTriangleID[fVertex0]; - ID triaId1 = fVertexToTriangleID[fVertex1]; - ID procTria0 = trianglesProcIds[fVertex0]; - ID procTria1 = trianglesProcIds[fVertex1]; - std::cout << "vs( " << velOnEdges[i * (nLayers+1) + il] << ", " - << u_normal_F[edgeToFEdge[i] * nLayers + il] << ") "; - std::cout << "edge: " << edge << ", gEdge: " << gEdge << ", on proc: " - << edgesProcId[edgeToFEdge[i]]; - if (triaId0 != NotAnId) { - std::cout << ". first tria0: " << triaId0 << " on proc: " - << procTria0; - } - if (triaId1 != NotAnId) { - std::cout << ".. second tria0:" << std::endl; - } - if ((triaId0 == NotAnId) || (triaId1 == NotAnId)) { - std::cout << ". and to Tria: " << triaId1 << " on proc: " << procTria1 - << std::endl; - } - - } - - //localSum = std::max(localSum, std::fabs(velOnEdges[i*nLayers+il] - u_normal_F[edgeToFEdge[i]*nLayers+il])); - } - } - - MPI_Allreduce(&localSumInt, &sumInt, 1, MPI_INT, MPI_SUM, comm); - - int localNum(sendEdgesListReversed.size()), num(0); - - MPI_Allreduce(&localNum, &num, 1, MPI_INT, MPI_SUM, comm); - - std::cout << "Edges change: " << sumInt << " " << num << std::endl; - -#endif - - - } @@ -525,7 +342,7 @@ void velocity_solver_finalize() { /*duality: * - * mpas(F) | lifev + * mpas(F) | Albany LandIce (C++) * ---------|--------- * cell | vertex * vertex | triangle @@ -533,14 +350,12 @@ void velocity_solver_finalize() { * */ -void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cellsMask_F, int const* _dirichletCellsMask_F, int const* _floatingEdgesMask_F) { +void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cellsMask_F, int const* _dirichletCellsMask_F) { int numProcs, me; - verticesMask_F = _verticesMask_F; cellsMask_F = _cellsMask_F; verticesMask_F = _verticesMask_F; dirichletCellsMask_F = _dirichletCellsMask_F; - floatingEdgesMask_F = _floatingEdgesMask_F; MPI_Comm_size(comm, &numProcs); MPI_Comm_rank(comm, &me); @@ -548,88 +363,156 @@ void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cel numProcs + 1), globalOffsetVertices(numProcs + 1), globalOffsetEdge( numProcs + 1); + + // First, we compute the FE triangles belonging to this processor. + // If changeTrianglesOwnership is not define, the triangles belonging to this + // processor will be the subset of the triangles (MPAS vertices) owned by this proc + // that contain dynamic ice. + // If changeTrianglesOwnership is define, we rearrange the ownership of the triangles + // to improve the quality of the FE mesh and avoid corner cases (see below). + triangleToFVertex.clear(); triangleToFVertex.reserve(nVertices_F); std::vector fVertexToTriangle(nVertices_F, NotAnId); - bool changed = false; - for (int i(0); i < nVerticesSolve_F; i++) { - if ((verticesMask_F[i] & dynamic_ice_bit_value) && !isGhostTriangle(i)) { - fVertexToTriangle[i] = triangleToFVertex.size(); - triangleToFVertex.push_back(i); - } - changed = changed || (verticesMask_F[i] != mask[i]); - } - for (int i(0); i < nVertices_F; i++) - mask[i] = verticesMask_F[i]; + //vector containing proc ranks for owned and shared FE triangles + trianglesProcIds.assign(nVertices_F,NotAnId); + + //vector containing proc ranks for owned and shared MPAS cells + std::vector fCellsProcIds(nCells_F); + getProcIds(fCellsProcIds, recvCellsList_F); +#ifdef changeTrianglesOwnership + std::vector fVerticesProcIds(nVertices_F); + getProcIds(fVerticesProcIds, recvVerticesList_F); + for (int i(0); i < nVertices_F; i++) { + int cellWithMinID=nCellsSolve_F; + if ((verticesMask_F[i] & dynamic_ice_bit_value)) { + int minCellId = std::numeric_limits::max(); + int minCellIdProc(0); + + int cellProc[3]; + bool invalidCell=false; + for (int j = 0; j < 3; j++) { + int iCell = cellsOnVertex_F[3 * i + j] - 1; + if(iCell >= nCells_F) { + invalidCell = true; + break; + } + int cellID = indexToCellID_F[iCell]; + cellProc[j] = fCellsProcIds[iCell]; + if(cellID < minCellId) { + minCellId = cellID; + cellWithMinID = iCell; + minCellIdProc = cellProc[j]; + } + } + + if(invalidCell) continue; + + // the proc that owns at least 2 nodes of the triangle i. If all nodes belong to different procs, procOwns2Nodes is set to -1 + int procOwns2Nodes = ((cellProc[0] == cellProc[1]) || (cellProc[0] == cellProc[2])) ? cellProc[0] : + (cellProc[1] == cellProc[2]) ? cellProc[1] : -1; + + int vertexProc = fVerticesProcIds[i]; + bool triangleOwnsANode = (cellProc[0] == vertexProc) || (cellProc[1] == vertexProc) || (cellProc[2] == vertexProc); - if (changed) - std::cout << "mask changed!!" << std::endl; + //A triangle will be owned by a proc if: + // 1. the proc owns at least 2 nodes of the triangle associated to that vertex, OR + // 2. all the nodes of the triangle belong to three different procs, and the proc owns the fortran vertex and a node OR + // 3. the three nodes of the triangle and the fortran vertex belong to four different procs, and the proc owns the node with the minimum ID - if ((me == 0) && (triangleToFVertex.size() == 0)) - for (int i(0); i < nVerticesSolve_F; i++) { - if (!isGhostTriangle(i)) { + trianglesProcIds[i] = (procOwns2Nodes != -1) ? procOwns2Nodes : + triangleOwnsANode ? vertexProc : + minCellIdProc; + + if (trianglesProcIds[i] == me) { fVertexToTriangle[i] = triangleToFVertex.size(); triangleToFVertex.push_back(i); - break; - } + } } + } +#else + //in this case we just set the proc ranks for owned and shared FE triangles to the be the same as MPAS owned and shared vertices + getProcIds(trianglesProcIds, recvVerticesList_F); + for (int i(0); i < nVerticesSolve_F; i++) { + if (verticesMask_F[i] & dynamic_ice_bit_value) { + fVertexToTriangle[i] = triangleToFVertex.size(); + triangleToFVertex.push_back(i); + } + } +#endif nTriangles = triangleToFVertex.size(); + //Initialize the ice sheet problem with the number of FE triangles on this prov initialize_iceProblem(nTriangles); - //Compute the global number of triangles, and the localOffset on the local processor, such that a globalID = localOffset + index - int localOffset(0); - nGlobalTriangles = 0; - computeLocalOffset(nTriangles, localOffset, nGlobalTriangles); - - //Communicate the globalIDs, computed locally, to the other processors. - indexToTriangleID.resize(nTriangles); - - //To make local, not used + //Create a list of global IDs for FE triangles, using MPAS vertices IDs fVertexToTriangleID.assign(nVertices_F, NotAnId); - // std::vector fVertexToTriangleID(nVertices_F, NotAnId); - for (int index(0); index < nTriangles; index++) - fVertexToTriangleID[triangleToFVertex[index]] = index + localOffset; - + for (int index(0); index < nTriangles; index++) { + fVertexToTriangleID[triangleToFVertex[index]] = indexToVertexID_F[triangleToFVertex[index]]; + } + +#ifdef changeTrianglesOwnership + // because we change the ownership of some triangles, we need to first communicate back to the processors that used to own those triangles + // the data of the newly owned triangles. We do this by defining "reversed" send and receive lists, communicate back using those lists, and + // then communicate "forward" using the usual send and receive lists. + // We could join these two step in one communication, but for the moment we do that separately + createReverseExchangeLists(sendVerticesListReversed, recvVerticesListReversed, trianglesProcIds, indexToVertexID_F, recvVerticesList_F); + allToAll(fVertexToTriangleID, &sendVerticesListReversed, &recvVerticesListReversed); + allToAll(fVertexToTriangle, &sendVerticesListReversed, &recvVerticesListReversed); + allToAll(trianglesProcIds, sendVerticesList_F, recvVerticesList_F); +#endif allToAll(fVertexToTriangleID, sendVerticesList_F, recvVerticesList_F); + allToAll(fVertexToTriangle, sendVerticesList_F, recvVerticesList_F); - for (int index(0); index < nTriangles; index++) + //we define the vector of global triangles Ids and compute the stride between the largest and the smallest Id globally + //This will be needed by the velocity solver to create the 3D FE mesh. + indexToTriangleID.resize(nTriangles); + int maxTriangleID=std::numeric_limits::min(), minTriangleID=std::numeric_limits::max(), maxGlobalTriangleID, minGlobalTriangleID; + for (int index(0); index < nTriangles; index++) { indexToTriangleID[index] = fVertexToTriangleID[triangleToFVertex[index]]; + maxTriangleID = (indexToTriangleID[index] > maxTriangleID) ? indexToTriangleID[index] : maxTriangleID; + minTriangleID = (indexToTriangleID[index] < minTriangleID) ? indexToTriangleID[index] : minTriangleID; + } - //Compute triangle edges - std::vector fEdgeToEdge(nEdges_F), edgesToSend, trianglesProcIds( - nVertices_F); - getProcIds(trianglesProcIds, recvVerticesList_F); + MPI_Allreduce(&maxTriangleID, &maxGlobalTriangleID, 1, MPI_INT, MPI_MAX, comm); + MPI_Allreduce(&minTriangleID, &minGlobalTriangleID, 1, MPI_INT, MPI_MIN, comm); + globalTriangleStride = maxGlobalTriangleID - minGlobalTriangleID +1; - int interfaceSize(0); + // Second, we compute the FE edges belonging to the FE triangles owned by this processor. + // We first compute boundary edges, and then all the other edges. + std::vector fEdgeToEdge(nEdges_F); std::vector fEdgeToEdgeID(nEdges_F, NotAnId); - edgesToReceive.clear(); edgeToFEdge.clear(); isBoundaryEdge.clear(); trianglesOnEdge.clear(); - edgesToReceive.reserve(nEdges_F - nEdgesSolve_F); edgeToFEdge.reserve(nEdges_F); trianglesOnEdge.reserve(nEdges_F * 2); - edgesToSend.reserve(nEdgesSolve_F); isBoundaryEdge.reserve(nEdges_F); - //first, we compute boundary edges (boundary edges must be the first edges) + //we compute boundary edges (boundary edges must be the first edges) for (int i = 0; i < nEdges_F; i++) { ID fVertex1(verticesOnEdge_F[2 * i] - 1), fVertex2( verticesOnEdge_F[2 * i + 1] - 1); + + // skip the (shared) edge when the associated MPAS vertices are not valid + if((fVertex1>=nVertices_F) || (fVertex2>=nVertices_F)) + continue; + ID triaId_1 = fVertexToTriangleID[fVertex1]; ID triaId_2 = fVertexToTriangleID[fVertex2]; bool isboundary = (triaId_1 == NotAnId) || (triaId_2 == NotAnId); ID iTria1 = fVertexToTriangle[fVertex1]; ID iTria2 = fVertexToTriangle[fVertex2]; - if (iTria1 == NotAnId) + if (trianglesProcIds[fVertex1] != me) { + std::swap(fVertex1, fVertex2); std::swap(iTria1, iTria2); - bool belongsToLocalTriangle = (iTria1 != NotAnId) || (iTria2 != NotAnId); + } + bool belongsToLocalTriangle = (trianglesProcIds[fVertex1] == me); if (belongsToLocalTriangle) { if (isboundary) { @@ -638,36 +521,36 @@ void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cel trianglesOnEdge.push_back(iTria1); trianglesOnEdge.push_back(iTria2); isBoundaryEdge.push_back(true); - } else - interfaceSize += (iTria2 == NotAnId); + } } } numBoundaryEdges = edgeToFEdge.size(); - //procOnInterfaceEdge contains the pairs . - std::vector < std::pair > procOnInterfaceEdge; - procOnInterfaceEdge.reserve(interfaceSize); - //then, we compute the other edges for (int i = 0; i < nEdges_F; i++) { ID fVertex1(verticesOnEdge_F[2 * i] - 1), fVertex2( verticesOnEdge_F[2 * i + 1] - 1); + + // skip the (shared) edge when the associated MPAS vertices are not valid + if((fVertex1>=nVertices_F) || (fVertex2>=nVertices_F)) + continue; + ID iTria1 = fVertexToTriangle[fVertex1]; ID iTria2 = fVertexToTriangle[fVertex2]; ID triaId_1 = fVertexToTriangleID[fVertex1]; //global Triangle ID triaId_2 = fVertexToTriangleID[fVertex2]; //global Triangle - if (iTria1 == NotAnId) { - std::swap(iTria1, iTria2); + if (trianglesProcIds[fVertex1] != me) { std::swap(fVertex1, fVertex2); + std::swap(iTria1, iTria2); } bool belongsToAnyTriangle = (triaId_1 != NotAnId) || (triaId_2 != NotAnId); bool isboundary = (triaId_1 == NotAnId) || (triaId_2 == NotAnId); - bool belongsToLocalTriangle = (iTria1 != NotAnId); + bool belongsToLocalTriangle = (trianglesProcIds[fVertex1] == me); bool isMine = i < nEdgesSolve_F; if (belongsToLocalTriangle && !isboundary) { @@ -676,42 +559,42 @@ void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cel trianglesOnEdge.push_back(iTria1); trianglesOnEdge.push_back(iTria2); isBoundaryEdge.push_back(false); - if (iTria2 == NotAnId) - procOnInterfaceEdge.push_back( - std::make_pair(fEdgeToEdge[i], trianglesProcIds[fVertex2])); - } - - if (belongsToAnyTriangle && isMine) { - edgesToSend.push_back(i); - if (!belongsToLocalTriangle) - edgesToReceive.push_back(i); } - } - //Compute the global number of edges, and the localOffset on the local processor, such that a globalID = localOffset + index - computeLocalOffset(edgesToSend.size(), localOffset, nGlobalEdges); - - //Communicate the globalIDs, computed locally, to the other processors. - for (ID index = 0; index < edgesToSend.size(); index++) - fEdgeToEdgeID[edgesToSend[index]] = index + localOffset; - - allToAll(fEdgeToEdgeID, sendEdgesList_F, recvEdgesList_F); + for (int fEdge = 0; fEdge < nEdges_F; fEdge++) + fEdgeToEdgeID[fEdge] = indexToEdgeID_F[fEdge]; nEdges = edgeToFEdge.size(); indexToEdgeID.resize(nEdges); - floatingEdgesIds.clear(); - floatingEdgesIds.reserve(nEdges); + iceMarginEdgesLIds.clear(); + iceMarginEdgesLIds.reserve(numBoundaryEdges); + int maxEdgeID=std::numeric_limits::min(), minEdgeID=std::numeric_limits::max(), maxGlobalEdgeID, minGlobalEdgeID; for (int index = 0; index < nEdges; index++) { int fEdge = edgeToFEdge[index]; indexToEdgeID[index] = fEdgeToEdgeID[fEdge]; - if((floatingEdgesMask_F[fEdge]!=0)&&(index maxEdgeID) ? indexToEdgeID[index] : maxEdgeID; + minEdgeID = (indexToEdgeID[index] < minEdgeID) ? indexToEdgeID[index] : minEdgeID; + + if(index fCellsToSend; - fCellsToSend.reserve(nCellsSolve_F); + MPI_Allreduce(&maxEdgeID, &maxGlobalEdgeID, 1, MPI_INT, MPI_MAX, comm); + MPI_Allreduce(&minEdgeID, &minGlobalEdgeID, 1, MPI_INT, MPI_MIN, comm); + globalEdgeStride = maxGlobalEdgeID - minGlobalEdgeID + 1; + + // Third, we compute the FE vertices belonging to the FE triangles owned by this processor. + // We need to make sure that an FE vertex is owned by a proc that owns a FE triangle that contain that vertex + // Otherwise we might end up with weird situation where a vertex could belong to a process with no associated triangle. vertexToFCell.clear(); vertexToFCell.reserve(nCells_F); @@ -719,70 +602,97 @@ void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cel fCellToVertex.assign(nCells_F, NotAnId); std::vector fCellToVertexID(nCells_F, NotAnId); - fCellsToReceive.clear(); + vertexProcIDs.clear(); + + std::vector verticesProcIds(nCells_F, NotAnId); - // if(! isDomainEmpty) - // { - fCellsToReceive.reserve(nCells_F - nCellsSolve_F); + vertexProcIDs.reserve(nCells_F); for (int i = 0; i < nCells_F; i++) { bool isMine = i < nCellsSolve_F; bool belongsToLocalTriangle = false; bool belongsToAnyTriangle = false; int nEdg = nEdgesOnCells_F[i]; + bool invalidVertex = false; + int minTriangleProcId = std::numeric_limits::max(); + bool nodeOwnedByATriaProc = false; for (int j = 0; j < nEdg; j++) { ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * i + j] - 1); - ID iTria = fVertexToTriangle[fVertex]; + if(fVertex >= nVertices_F) { + invalidVertex = true; + break; + } + ID triaId = fVertexToTriangleID[fVertex]; - belongsToLocalTriangle = belongsToLocalTriangle || (iTria != NotAnId); + belongsToLocalTriangle = belongsToLocalTriangle || (trianglesProcIds[fVertex] == me); belongsToAnyTriangle = belongsToAnyTriangle || (triaId != NotAnId); - } - if (belongsToAnyTriangle && isMine) { - fCellsToSend.push_back(i); - if (!belongsToLocalTriangle) - fCellsToReceive.push_back(i); + if(triaId != NotAnId) { + nodeOwnedByATriaProc = nodeOwnedByATriaProc || (trianglesProcIds[fVertex] == fCellsProcIds[i]); + minTriangleProcId = (trianglesProcIds[fVertex] < minTriangleProcId) ? trianglesProcIds[fVertex] : minTriangleProcId; + } } + if(invalidVertex) continue; + + if(belongsToAnyTriangle) + verticesProcIds[i] = nodeOwnedByATriaProc ? fCellsProcIds[i] : minTriangleProcId; + if (belongsToLocalTriangle) { fCellToVertex[i] = vertexToFCell.size(); vertexToFCell.push_back(i); + vertexProcIDs.push_back(reduced_ranks[verticesProcIds[i]]); } } - // } - - //Compute the global number of vertices, and the localOffset on the local processor, such that a globalID = localOffset + index - computeLocalOffset(fCellsToSend.size(), localOffset, nGlobalVertices); - - //Communicate the globalIDs, computed locally, to the other processors. - for (int index = 0; index < int(fCellsToSend.size()); index++) - fCellToVertexID[fCellsToSend[index]] = index + localOffset; + nVertices = vertexToFCell.size(); - allToAll(fCellToVertexID, sendCellsList_F, recvCellsList_F); + for (int fcell = 0; fcell < nCells_F; fcell++) + fCellToVertexID[fcell] = indexToCellID_F[fcell]; - nVertices = vertexToFCell.size(); - int vertexColumnShift = (Ordering == 1) ? 1 : nGlobalVertices; - int vertexLayerShift = (Ordering == 0) ? 1 : nLayers + 1; + int maxVertexID=std::numeric_limits::min(), minVertexID=std::numeric_limits::max(), maxGlobalVertexID, minGlobalVertexID; + indexToVertexID.resize(nVertices); + for (int index = 0; index < nVertices; index++) { + int fCell = vertexToFCell[index]; + indexToVertexID[index] = fCellToVertexID[fCell]; + maxVertexID = (indexToVertexID[index] > maxVertexID) ? indexToVertexID[index] : maxVertexID; + minVertexID = (indexToVertexID[index] < minVertexID) ? indexToVertexID[index] : minVertexID; + } + MPI_Allreduce(&maxVertexID, &maxGlobalVertexID, 1, MPI_INT, MPI_MAX, comm); + MPI_Allreduce(&minVertexID, &minGlobalVertexID, 1, MPI_INT, MPI_MIN, comm); + globalVertexStride = maxGlobalVertexID - minGlobalVertexID + 1; - std::cout << "\n nvertices: " << nVertices << " " << nGlobalVertices << "\n" - << std::endl; - indexToVertexID.resize(nVertices); + int vertexColumnShift = (Ordering == 1) ? 1 : globalVertexStride; + int vertexLayerShift = (Ordering == 0) ? 1 : nLayers + 1; dirichletNodesIDs.clear(); dirichletNodesIDs.reserve(nVertices); //need to improve storage efficiency + isVertexBoundary.assign(nVertices, false); for (int index = 0; index < nVertices; index++) { int fCell = vertexToFCell[index]; - indexToVertexID[index] = fCellToVertexID[fCell]; for(int il=0; il< nLayers+1; ++il) { int imask_F = il+(nLayers+1)*fCell; if(dirichletCellsMask_F[imask_F]!=0) dirichletNodesIDs.push_back((nLayers-il)*vertexColumnShift+indexToVertexID[index]*vertexLayerShift); } + + int nEdg = nEdgesOnCells_F[fCell]; + int j = 0; + bool isBoundary; + do { + int fVertex = verticesOnCell_F[maxNEdgesOnCell_F * fCell + j++] - 1; + isBoundary = !(verticesMask_F[fVertex] & dynamic_ice_bit_value); + } while ((j < nEdg) && (!isBoundary)); + isVertexBoundary[index] = isBoundary; } - createReverseCellsExchangeLists(sendCellsListReversed, recvCellsListReversed, - fVertexToTriangleID, fCellToVertexID); + // because we change the ownership of some vertices, we need to first communicate back to the processors that used to own those vertices + // the data of the newly owned vertices. We do this by defining "reversed" send and receive lists, communicate back using that list, and + // then communicate forward with the usual send and receive lists. + // We could join these two step in one communication, but for the moment we do that separately. + // We need to communicate info about the vertices when we get the ice velocity on vertices form the velocity solver/ + + createReverseExchangeLists(sendCellsListReversed, recvCellsListReversed, verticesProcIds, indexToCellID_F, recvCellsList_F); //construct the local vector vertices on triangles making sure the area is positive verticesOnTria.resize(nTriangles * 3); @@ -801,132 +711,24 @@ void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cel std::swap(verticesOnTria[3 * index + 1], verticesOnTria[3 * index + 2]); } - //construct the local vector vertices on edges - trianglesPositionsOnEdge.resize(2 * nEdges); - isVertexBoundary.assign(nVertices, false); - verticesOnEdge.resize(2 * nEdges); - - //contains the local id of a triangle and the global id of the edges of the triangle. - //dataForGhostTria[4*i] contains the triangle id - //dataForGhostTria[4*i+1+k] contains the global id of the edge (at position k = 0,1,2) of the triangle. - //Possible Optimization: for our purposes it would be enough to store two of the three edges of a triangle. - std::vector dataForGhostTria(nVertices_F * 4, NotAnId); - - //* - for (int iV = 0; iV < nVertices; iV++) { - int fCell = vertexToFCell[iV]; - int nEdg = nEdgesOnCells_F[fCell]; - int j = 0; - bool isBoundary; - do { - int fVertex = verticesOnCell_F[maxNEdgesOnCell_F * fCell + j++] - 1; - isBoundary = !(verticesMask_F[fVertex] & dynamic_ice_bit_value); - } while ((j < nEdg) && (!isBoundary)); - isVertexBoundary[iV] = isBoundary; - } - /*/ - for(int index=0; index verticesCoords(3 * nVertices); - - for (int index = 0; index < nVertices; index++) { - int iCell = vertexToFCell[index]; - verticesCoords[index * 3] = xCell_F[iCell] / unit_length; - verticesCoords[index * 3 + 1] = yCell_F[iCell] / unit_length; - verticesCoords[index * 3 + 2] = zCell_F[iCell] / unit_length; - } - - velocity_solver_compute_2d_grid__(nGlobalTriangles, - nGlobalVertices, nGlobalEdges, indexToVertexID, verticesCoords, - isVertexBoundary, verticesOnTria,isBoundaryEdge, trianglesOnEdge, - trianglesPositionsOnEdge, verticesOnEdge, indexToEdgeID, - indexToTriangleID, procOnInterfaceEdge ); -#else - velocity_solver_compute_2d_grid__(reducedComm); -#endif - - /* - - //initialize the mesh - iceProblemPtr->mesh2DPtr.reset (new RegionMesh() ); - - //construct the mesh nodes - constructNodes ( * (iceProblemPtr->mesh2DPtr), indexToVertexID, verticesCoords, isVertexBoundary, nGlobalVertices, 3); - - //construct the mesh elements - constructElements ( * (iceProblemPtr->mesh2DPtr), indexToTriangleID, verticesOnTria, nGlobalTriangles); - - //construct the mesh facets - constructFacets ( * (iceProblemPtr->mesh2DPtr), isBoundaryEdge, trianglesOnEdge, trianglesPositionsOnEdge, verticesOnEdge, indexToEdgeID, procOnInterfaceEdge, nGlobalEdges, 3); - - Switch sw; - std::vector elSign; - checkVolumes ( * (iceProblemPtr->mesh2DPtr), elSign, sw ); - */ + velocity_solver_compute_2d_grid__(reducedComm); } -void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, - double const* lowerSurface_F, double const* thickness_F) { +void velocity_solver_extrude_3d_grid(double const* levelsRatio_F) { if (isDomainEmpty) return; @@ -935,7 +737,6 @@ void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, // !!Indexing of layers is reversed for (int i = 0; i < nLayers; i++) layersRatio[i] = levelsRatio_F[nLayers - 1 - i]; - //std::copy(levelsRatio_F, levelsRatio_F+nLayers, layersRatio.begin()); levelsNormalizedThickness.resize(nLayers + 1); @@ -944,10 +745,6 @@ void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, levelsNormalizedThickness[i + 1] = levelsNormalizedThickness[i] + layersRatio[i]; - std::vector mpasIndexToVertexID(nVertices); - for (int i = 0; i < nVertices; i++) - mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; - //construct the local vector of coordinates std::vector verticesCoords(3 * nVertices); @@ -958,13 +755,22 @@ void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, verticesCoords[index * 3 + 2] = zCell_F[iCell] / unit_length; } - velocity_solver_extrude_3d_grid__(nLayers, nGlobalTriangles, nGlobalVertices, - nGlobalEdges, Ordering, reducedComm, indexToVertexID, mpasIndexToVertexID, - verticesCoords, isVertexBoundary, verticesOnTria, isBoundaryEdge, - trianglesOnEdge, trianglesPositionsOnEdge, verticesOnEdge, indexToEdgeID, - indexToTriangleID, dirichletNodesIDs, floatingEdgesIds); + std::vector> procsSharingVertices(nVertices); + for(int i=0; i< nVertices; i++) + procsSharingVertex(i, procsSharingVertices[i]); + + + std::vector iceMarginEdgesGIds(iceMarginEdgesLIds.size()); + for (int i=0; i< iceMarginEdgesGIds.size(); ++i) { + iceMarginEdgesGIds[i] = indexToEdgeID[iceMarginEdgesLIds[i]]; } + velocity_solver_extrude_3d_grid__(nLayers, globalTriangleStride, globalVertexStride, + globalEdgeStride, Ordering, reducedComm, indexToVertexID, vertexProcIDs, + verticesCoords, verticesOnTria, procsSharingVertices, isBoundaryEdge, + trianglesOnEdge, verticesOnEdge, indexToEdgeID, + indexToTriangleID, dirichletNodesIDs, iceMarginEdgesGIds); + } // Function to set up how the MPAS log file will be used by Albany void interface_init_log(){ @@ -1003,11 +809,7 @@ void interface_init_log(){ } strcat(oformat, ".out"); - if (me == 0) { - sprintf(albany_log_filename, oformat, me); - } else { - strcpy(albany_log_filename, "/dev/null"); - } + sprintf(albany_log_filename, oformat, me); Interface_stdout = open(albany_log_filename, O_CREAT|O_WRONLY|O_TRUNC,0644); if(Interface_stdout >= 0) { @@ -1069,14 +871,9 @@ void get_prism_velocity_on_FEdges(double * uNormal, UInt nPoints3D = nCells_F * (nLayers + 1); - // Loop over all edges of the triangulation - MPAS will decide which edges it should use. - for (int i = 0; i < nEdges; i++) { - - //identifying vertices on the edge - ID lId0 = verticesOnEdge[2 * i]; - ID lId1 = verticesOnEdge[2 * i + 1]; - int iCell0 = vertexToFCell[lId0]; - int iCell1 = vertexToFCell[lId1]; + for (int iEdge = 0; iEdge < nEdgesSolve_F; iEdge++) { + int iCell0 = cellsOnEdge_F[2 * iEdge] - 1; + int iCell1 = cellsOnEdge_F[2 * iEdge + 1] - 1; //computing normal to the cell edge (dual of triangular edge) double nx = xCell_F[iCell1] - xCell_F[iCell0]; @@ -1086,9 +883,14 @@ void get_prism_velocity_on_FEdges(double * uNormal, ny /= n; //identifying triangles that shares the edge - ID iEdge = edgeToFEdge[i]; ID fVertex0 = verticesOnEdge_F[2 * iEdge] - 1; ID fVertex1 = verticesOnEdge_F[2 * iEdge + 1] - 1; + + + int iTria0 = fVertexToTriangleID[fVertex0]; + int iTria1 = fVertexToTriangleID[fVertex1]; + if((iTria0 == NotAnId) && (iTria1 == NotAnId)) continue; + double t0[2*3], t1[2*3]; //t0[0] contains the x-coords of vertices of triangle 0 and t0[1] its y-coords. for (int j = 0; j < 3; j++) { int iCell = cellsOnVertex_F[3 * fVertex0 + j] - 1; @@ -1119,11 +921,11 @@ void get_prism_velocity_on_FEdges(double * uNormal, for (int j = 0; j < 3; j++) iCells[j] = cellsOnVertex_F[3 * fVertex1 + j] - 1; } - else if(i& velocityOnVertices, } } -void createReverseCellsExchangeLists(exchangeList_Type& sendListReverse_F, - exchangeList_Type& receiveListReverse_F, - const std::vector& fVertexToTriangleID, - const std::vector& fCellToVertexID) { - sendListReverse_F.clear(); - receiveListReverse_F.clear(); - //std::map > sendMap; - std::map > sendMap, receiveMap; - std::vector cellsProcId(nCells_F), trianglesProcIds(nVertices_F); - getProcIds(cellsProcId, recvCellsList_F); - getProcIds(trianglesProcIds, recvVerticesList_F); - - //std::cout << "SendList " ; - for (int i = 0; i < nVertices; i++) { - int iCell = vertexToFCell[i]; - if (iCell < nCellsSolve_F) - continue; - bool belongToTriaOnSameProc = false; - int j(0); - int nEdg = nEdgesOnCells_F[iCell]; - do { - ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * iCell + j] - 1); - ID triaId = fVertexToTriangleID[fVertex]; - belongToTriaOnSameProc = (triaId != NotAnId) - && (trianglesProcIds[fVertex] == cellsProcId[iCell]); - } while ((belongToTriaOnSameProc == false) && (++j < nEdg)); - if (!belongToTriaOnSameProc) { - sendMap[cellsProcId[iCell]].insert( - std::make_pair(fCellToVertexID[iCell], iCell)); - // std::cout<< "(" << cellsProcId[iCell] << "," << iCell << ") "; - } - - } - //std::cout < >::const_iterator it = sendMap.begin(); - it != sendMap.end(); it++) { - std::vector sendVec(it->second.size()); - int i = 0; - for (std::map::const_iterator iter = it->second.begin(); - iter != it->second.end(); iter++) - sendVec[i++] = iter->second; - sendListReverse_F.push_back( - exchange(it->first, &sendVec[0], &sendVec[0] + sendVec.size())); - } - - //std::cout << "ReceiveList " ; - for (UInt i = 0; i < fCellsToReceive.size(); i++) { - int iCell = fCellsToReceive[i]; - int nEdg = nEdgesOnCells_F[iCell]; - for (int j = 0; j < nEdg; j++) { - ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * iCell + j] - 1); - ID triaId = fVertexToTriangleID[fVertex]; - if (triaId != NotAnId) { - receiveMap[trianglesProcIds[fVertex]].insert( - std::make_pair(fCellToVertexID[iCell], iCell)); - // std::cout<< "(" << trianglesProcIds[fVertex] << "," << iCell << ") "; - } - } - } - //std::cout < >::const_iterator it = - receiveMap.begin(); it != receiveMap.end(); it++) { - std::vector receiveVec(it->second.size()); - int i = 0; - for (std::map::const_iterator iter = it->second.begin(); - iter != it->second.end(); iter++) - receiveVec[i++] = iter->second; - receiveListReverse_F.push_back( - exchange(it->first, &receiveVec[0], - &receiveVec[0] + receiveVec.size())); - } -} - -void createReverseEdgesExchangeLists(exchangeList_Type& sendListReverse_F, +void createReverseExchangeLists(exchangeList_Type& sendListReverse_F, exchangeList_Type& receiveListReverse_F, - const std::vector& fVertexToTriangleID, - const std::vector& fEdgeToEdgeID) { + const std::vector& newProcIds, const int* indexToID_F, exchangeList_Type const * recvList_F) { sendListReverse_F.clear(); receiveListReverse_F.clear(); - //std::map > sendMap; std::map > sendMap, receiveMap; - std::vector edgesProcId(nEdges_F), trianglesProcIds(nVertices_F); - getProcIds(edgesProcId, recvEdgesList_F); - getProcIds(trianglesProcIds, recvVerticesList_F); - - //std::cout << "EdgesSendList " ; - for (int i = 0; i < nEdges; i++) { - int iEdge = edgeToFEdge[i]; - if (iEdge < nEdgesSolve_F) - continue; - bool belongToTriaOnSameProc = false; - int j(0); - do { - ID fVertex(verticesOnEdge_F[2 * iEdge + j] - 1); - ID triaId = fVertexToTriangleID[fVertex]; - belongToTriaOnSameProc = (triaId != NotAnId) - && (trianglesProcIds[fVertex] == edgesProcId[iEdge]); - } while ((belongToTriaOnSameProc == false) && (++j < 2)); - if (!belongToTriaOnSameProc) { - sendMap[edgesProcId[iEdge]].insert( - std::make_pair(fEdgeToEdgeID[iEdge], iEdge)); - //std::cout<< "(" << edgesProcId[iEdge] << "," << iEdge << ") "; + int nFEntities = newProcIds.size(); + std::vector procIds(nFEntities); + getProcIds(procIds, recvList_F); + int me; + MPI_Comm_rank(comm, &me); + for (int fEntity = 0; fEntity < nFEntities; fEntity++) { + if ((procIds[fEntity] != me) && (newProcIds[fEntity] == me)) { + sendMap[procIds[fEntity]].insert( + std::make_pair(indexToID_F[fEntity], fEntity)); } } - //std::cout < >::const_iterator it = sendMap.begin(); it != sendMap.end(); it++) { @@ -1328,20 +1040,12 @@ void createReverseEdgesExchangeLists(exchangeList_Type& sendListReverse_F, exchange(it->first, &sendVec[0], &sendVec[0] + sendVec.size())); } - //std::cout << "EdgesReceiveList " ; - for (UInt i = 0; i < edgesToReceive.size(); i++) { - int iEdge = edgesToReceive[i]; - for (int j = 0; j < 2; j++) { - ID fVertex(verticesOnEdge_F[2 * iEdge + j] - 1); - ID triaId = fVertexToTriangleID[fVertex]; - if (triaId != NotAnId) { - receiveMap[trianglesProcIds[fVertex]].insert( - std::make_pair(fEdgeToEdgeID[iEdge], iEdge)); - // std::cout<< "(" << trianglesProcIds[fVertex] << "," << iEdge << ") "; - } + for (int fEntity = 0; fEntity < nFEntities; fEntity++) { + if((procIds[fEntity] == me) && (newProcIds[fEntity] != NotAnId) && (newProcIds[fEntity] != me)) { + receiveMap[newProcIds[fEntity]].insert( + std::make_pair(indexToID_F[fEntity], fEntity)); } } - // std::cout < >::const_iterator it = receiveMap.begin(); it != receiveMap.end(); it++) { @@ -1380,19 +1084,6 @@ void mapCellsToVertices(const std::vector& velocityOnCells, } } -bool isGhostTriangle(int i, double relTol) { - double x[3], y[3], area; - - for (int j = 0; j < 3; j++) { - int iCell = cellsOnVertex_F[3 * i + j] - 1; - x[j] = xCell_F[iCell]; - y[j] = yCell_F[iCell]; - } - - area = std::fabs(signedTriangleArea(x, y)); - return false; //(std::fabs(areaTriangle_F[i]-area)/areaTriangle_F[i] > relTol); -} - double signedTriangleArea(const double* x, const double* y) { double u[2] = { x[1] - x[0], y[1] - y[0] }; double v[2] = { x[2] - x[0], y[2] - y[0] }; @@ -1416,36 +1107,25 @@ double signedTriangleAreaOnSphere(const double* x, const double* y, > 0) ? area : -area; } -//TO BE FIXED, Access To verticesOnCell_F is not correct -void extendMaskByOneLayer(int const* verticesMask_F, - std::vector& extendedFVerticesMask) { - extendedFVerticesMask.resize(nVertices_F); - extendedFVerticesMask.assign(&verticesMask_F[0], - &verticesMask_F[0] + nVertices_F); - for (int i = 0; i < nCells_F; i++) { - bool belongsToMarkedTriangle = false; - int nEdg = nEdgesOnCells_F[i]; - for (UInt k = 0; k < nEdg && !belongsToMarkedTriangle; k++) - belongsToMarkedTriangle = belongsToMarkedTriangle - || verticesMask_F[verticesOnCell_F[maxNEdgesOnCell_F * i + k] - 1]; - if (belongsToMarkedTriangle) - for (UInt k = 0; k < nEdg; k++) { - ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * i + k] - 1); - extendedFVerticesMask[fVertex] = !isGhostTriangle(fVertex); - } - } -} -void import2DFields(std::map bdExtensionMap, double const* bedTopography_F, double const * lowerSurface_F, double const * thickness_F, - double const * beta_F, double const * temperature_F, double const * smb_F, double eps) { - +void importFields(std::vector >& marineBdyExtensionMap, double const* bedTopography_F, double const * lowerSurface_F, double const * thickness_F, + double const * beta_F, double const* stiffnessFactor_F, double const* effecPress_F, double const* muFriction_F, + double const * temperature_F, double const * smb_F, double eps) { + + int vertexLayerShift = (Ordering == 0) ? 1 : nLayers + 1; elevationData.assign(nVertices, 1e10); thicknessData.assign(nVertices, 1e10); bedTopographyData.assign(nVertices, 1e10); if (beta_F != 0) betaData.assign(nVertices, 1e10); + if (stiffnessFactor_F != 0) + stiffnessFactorData.assign(nVertices, 1.0); + if (effecPress_F != 0) + effecPressData.assign(nVertices, 1e10); + if (muFriction_F!= 0) + muFrictionData.assign(nVertices, 1e10); if(temperature_F != 0) - temperatureData.assign(nLayers * nTriangles, 1e10); + temperatureDataOnPrisms.assign(nLayers * nTriangles, 1e10); if (smb_F != 0) smbData.assign(nVertices, 1e10); @@ -1459,9 +1139,17 @@ void import2DFields(std::map bdExtensionMap, double const* bedTopograp if (beta_F != 0) betaData[index] = beta_F[iCell] / unit_length; if (smb_F != 0) - smbData[index] = smb_F[iCell] / unit_length * secondsInAYear/rho_ice; + smbData[index] = smb_F[iCell] * secondsInAYear/rho_ice; + if (stiffnessFactor_F != 0) + stiffnessFactorData[index] = stiffnessFactor_F[iCell]; + if (effecPress_F != 0) + effecPressData[index] = effecPress_F[iCell] / unit_length; + if (muFriction_F != 0) + muFrictionData[index] = muFriction_F[iCell]; } + int lElemColumnShift = (Ordering == 1) ? 1 : nTriangles; + int elemLayerShift = (Ordering == 0) ? 1 : nLayers; if(temperature_F != 0) { for (int index = 0; index < nTriangles; index++) { for (int il = 0; il < nLayers; il++) { @@ -1471,112 +1159,115 @@ void import2DFields(std::map bdExtensionMap, double const* bedTopograp for (int iVertex = 0; iVertex < 3; iVertex++) { int v = verticesOnTria[iVertex + 3 * index]; int iCell = vertexToFCell[v]; - //compute temperature by averaging tmeperature values of triangles vertices where ice is present - if (cellsMask_F[iCell] & ice_present_bit_value) { + //compute temperature by averaging temperature values of triangles vertices where ice is present + // Note that thermal_thickness_limit was imported in Albany units (km) but thickness_F is still in + // MPAS units (m), so thermal_thickness_limit is being scaled back to MPAS units for this comparison. + if (thickness_F[iCell] > thermal_thickness_limit * unit_length) { temperature += temperature_F[iCell * nLayers + ilReversed]; nPoints++; } } if (nPoints == 0) //if triangle is in an ice-free area, set the temperature to T0 - temperatureData[index+il*nTriangles] = T0; + temperatureDataOnPrisms[index*elemLayerShift + il*lElemColumnShift] = T0; else - temperatureData[index+il*nTriangles] = temperature / nPoints; + temperatureDataOnPrisms[index*elemLayerShift + il*lElemColumnShift] = temperature / nPoints; } } } - - //extend thickness elevation and basal friction data to the border for floating vertices - std::set::const_iterator iter; + //extend thickness and elevation data to the border for marine vertices + marineBdyExtensionMap.clear(); + marineBdyExtensionMap.reserve(nVertices); for (int iV = 0; iV < nVertices; iV++) { int fCell = vertexToFCell[iV]; - if (isVertexBoundary[iV] && !(cellsMask_F[fCell] & dynamic_ice_bit_value)) { - int c; - int nEdg = nEdgesOnCells_F[fCell]; - bool isFloating = false; - for (int j = 0; (j < nEdg)&&(!isFloating); j++) { - int fEdge = edgesOnCell_F[maxNEdgesOnCell_F * fCell + j] - 1; - isFloating = (floatingEdgesMask_F[fEdge] != 0); - } - if(isFloating) { - - // -- floating margin -- - // Identify the lowest elevation neighboring cell with ice - // Scalar values will be mapped from that location to here. - double elevTemp =1e10; - bool foundNeighbor = false; - for (int j = 0; j < nEdg; j++) { - int fEdge = edgesOnCell_F[maxNEdgesOnCell_F * fCell + j] - 1; - //bool keep = (mask[verticesOnEdge_F[2 * fEdge] - 1] & dynamic_ice_bit_value) - // && (mask[verticesOnEdge_F[2 * fEdge + 1] - 1] & dynamic_ice_bit_value); - //if (!keep) - // continue; - - int c0 = cellsOnEdge_F[2 * fEdge] - 1; - int c1 = cellsOnEdge_F[2 * fEdge + 1] - 1; - c = (fCellToVertex[c0] == iV) ? c1 : c0; - //if(!(cellsMask_F[c] & ice_present_bit_value)) continue; - if((cellsMask_F[c] & dynamic_ice_bit_value)) { - double elev = thickness_F[c] + lowerSurface_F[c]; // - 1e-8*std::sqrt(pow(xCell_F[c0],2)+std::pow(yCell_F[c0],2)); - std::cout << " elev="< v2; - - for (int iTetra = 0; iTetra < 3; iTetra++) - for (int iVertex = 0; iVertex < 4; iVertex++) - { - tetrasIdsOnPrism[iTetra][iVertex] = prismVertexGIds[tetraOfPrism[prismType][iTetra][iVertex]]; - } - - // return; - - int reorderedPrismLIds[6]; - - for (int ii = 0; ii < 6; ii++) - { - reorderedPrismLIds[ii] = prismVertexGIds[PrismVerticesMap[minIndex][ii]]; - } - - for (int iTetra = 0; iTetra < 3; iTetra++) - for (int iVertex = 0; iVertex < 4; iVertex++) - { - tetrasIdsOnPrism[iTetra][iVertex] = reorderedPrismLIds[tetraOfPrism[prismType][iTetra][iVertex]]; - } - } - - - - - void setBdFacesOnPrism (const std::vector > >& prismStruct, const std::vector& prismFaceIds, std::vector& tetraPos, std::vector& facePos) - { - int numTriaFaces = prismFaceIds.size() - 2; - tetraPos.assign(numTriaFaces,-1); - facePos.assign(numTriaFaces,-1); - - - for (int iTetra (0), k (0); (iTetra < 3 && k < numTriaFaces); iTetra++) - { - bool found; - for (int jFaceLocalId = 0; jFaceLocalId < 4; jFaceLocalId++ ) - { - found = true; - for (int ip (0); ip < 3 && found; ip++) - { - int localId = prismStruct[iTetra][jFaceLocalId][ip]; - int j = 0; - found = false; - while ( (j < prismFaceIds.size()) && !found ) - { - found = (localId == prismFaceIds[j]); - j++; - } - } - if (found) - { - tetraPos[k] = iTetra; - facePos[k] = jFaceLocalId; - k += found; - break; - } - } - } - } - void procsSharingVertex(const int vertex, std::vector& procIds) { int fCell = vertexToFCell[vertex]; procIds.clear(); int nEdg = nEdgesOnCells_F[fCell]; int me; MPI_Comm_rank(comm, &me); - procIds.reserve(nEdg); for(int i=0; i regulThk(thicknessData); -// for (int index = 0; index < nVertices; index++) -// regulThk[index] = std::max(1e-4, thicknessData[index]); //TODO Make limit a parameter -// -// importP0Temperature(temperature_F); -// -// std::cout << "\n\nTimeStep: "<< *deltat << "\n\n"<< std::endl; -// -// std::vector mpasIndexToVertexID(nVertices); -// for (int i = 0; i < nVertices; i++) { -// mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; -// } -// -// mapVerticesToCells(velocityOnVertices, &velocityOnCells[0], 2, nLayers, -// Ordering); - - // Write out ASCII format std::cout << "Writing mesh to albany.msh." << std::endl; // msh file std::ofstream outfile; - outfile.open ("albany.msh", std::ios::out | std::ios::trunc); + outfile.precision(15); + std::stringstream name; + int me; + MPI_Comm_rank(comm, &me); + name << "albany.msh." << me; + outfile.open (name.str(), std::ios::out | std::ios::trunc); if (outfile.is_open()) { - int nVerticesBoundaryEdge = 0; - for (int index = 0; index < nVertices; index++) { - if (isBoundaryEdge[index]) nVerticesBoundaryEdge += 1; - } - std::vector verticesOnBoundaryEdge; - verticesOnBoundaryEdge.resize(2 * nVerticesBoundaryEdge); - int iVerticesBoundaryEdge = 0; - for (int index = 0; index < nVertices; index++) { - if (isBoundaryEdge[index]) { - verticesOnBoundaryEdge[0 + 2 * iVerticesBoundaryEdge] = verticesOnEdge[0 + 2 * index]; - verticesOnBoundaryEdge[1 + 2 * iVerticesBoundaryEdge] = verticesOnEdge[1 + 2 * index]; - iVerticesBoundaryEdge += 1; - } + //creating set from vector so that we can find elements in the set in log time + //boundary edges labels: 2 if ice margin, 1 otherwise + std::vector bdEdgesLabels(numBoundaryEdges,1); + for (int i = 0; i < iceMarginEdgesLIds.size(); i++) + bdEdgesLabels[iceMarginEdgesLIds[i]] = 2; + + std::vector boundaryEdges; //list of edge vertices and edge label + boundaryEdges.resize(3 * numBoundaryEdges); + for (int index = 0; index < numBoundaryEdges; index++) { + boundaryEdges[0 + 3 * index] = verticesOnEdge[0 + 2 * index]; + boundaryEdges[1 + 3 * index] = verticesOnEdge[1 + 2 * index]; + boundaryEdges[2 + 3 * index] = bdEdgesLabels[index]; } - //std::cout<<"final count: "< sortingIndex; + computeSortingIndices(sortingIndex, indexToTriangleID, nTriangles); + + for (int iTria = 0; iTria < nTriangles; iTria++) {//triangles lines + int index = sortingIndex[iTria]; + outfile << indexToTriangleID[index] << " " << verticesOnTria[0 + 3 * index] + 1 << " " << verticesOnTria[1 + 3 * index] + 1 << " " << verticesOnTria[2 + 3 * index] + 1 << " " << 1 << "\n"; // last digit can be used to specify a 'material'. Not used by Albany LandIce, so giving dummy value } - for (int index = 0; index < nTriangles; index++) //triangles lines - outfile << verticesOnTria[0 + 3 * index] + 1 << " " << verticesOnTria[1 + 3 * index] + 1 << " " << verticesOnTria[2 + 3 * index] + 1 << " " << 1 << "\n"; // last digit can be used to specify a 'material'. Not used by FELIX, so giving dummy value + // sort edges IDs (needed by Albany) + computeSortingIndices(sortingIndex, indexToEdgeID, numBoundaryEdges); - for (int index = 0; index < nVerticesBoundaryEdge; index++) // boundary edges lines - outfile << verticesOnBoundaryEdge[0 + 2 * index] + 1 << " " << verticesOnBoundaryEdge[1 + 2 * index] + 1 << " " << 1 << "\n"; //last digit can be used to tell whether it's floating or not.. but let's worry about this later. + for (int iEdge = 0; iEdge < numBoundaryEdges; iEdge++) { // boundary edges lines + int index = sortingIndex[iEdge]; + outfile << indexToEdgeID[index] << " " << boundaryEdges[0 + 3 * index] + 1 << " " << boundaryEdges[1 + 3 * index] + 1 << " " << boundaryEdges[2 + 3 * index] << "\n"; //last digit can be used to tell whether it's floating or not.. but let's worry about this later. + } outfile.close(); } @@ -2178,18 +1699,31 @@ int prismType(long long int const* prismVertexMpasIds, int& minIndex) // individual field values // Call needed functions to process MPAS fields to Albany units/format - - std::map bdExtensionMap; // local map to be created by import2DFields - import2DFields(bdExtensionMap, bedTopography_F, lowerSurface_F, thickness_F, beta_F, temperature_F, smb_F, minThickness); - import2DFieldsObservations(bdExtensionMap, + std::vector > marineBdyExtensionMap; // local map to be created by importFields + importFields(marineBdyExtensionMap, bedTopography_F, lowerSurface_F, thickness_F, beta_F, + stiffnessFactor_F, effecPress_F, muFriction_F, temperature_F, smb_F, minThickness); + + import2DFieldsObservations(marineBdyExtensionMap, thicknessUncertainty_F, smbUncertainty_F, bmb_F, bmbUncertainty_F, observedSurfaceVelocityX_F, observedSurfaceVelocityY_F, observedSurfaceVelocityUncertainty_F, observedThicknessTendency_F, observedThicknessTendencyUncertainty_F, + surfaceAirTemperature_F, basalHeatFlux_F, indexToCellID_F); + // apparent mass balance + std::vector appMbData(smbData.size()), + appMbUncertaintyData(smbData.size()); + + for (int i=0; i sortingIndex; + computeSortingIndices(sortingIndex,indexToTriangleID,nTriangles); + + int lElemColumnShift = (Ordering == 1) ? 1 : nTriangles; + int elemLayerShift = (Ordering == 0) ? 1 : nLayers; + for(int il = 0; il& sortingIndices, const std::vector& vectorToSort, int numIndices) { + // sort triangle IDs (needed by Albany) + sortingIndices.resize(numIndices); + + // sortingIndices = [0,1,2,..,nTriangles-1]; + for (int i = 0; i < numIndices; i++) + sortingIndices[i] = i; + + //compute sortingIndices that makes indexToTriangleID sorted + std::sort(sortingIndices.begin(), sortingIndices.end(), [&](int il, int ir) { + return (vectorToSort[il] < vectorToSort[ir]); + }); + } + + void write_ascii_mesh_field(std::vector fieldData, std::string filenamebase) { std::string filename = filenamebase+".ascii"; std::cout << "Writing " << filename << std::endl; std::ofstream outfile; + outfile.precision(15); outfile.open (filename.c_str(), std::ios::out | std::ios::trunc); if (outfile.is_open()) { outfile << nVertices << "\n"; //number of vertices on first line @@ -2287,4 +1878,4 @@ int prismType(long long int const* prismVertexMpasIds, int& minIndex) std::cout << "Error: Failed to open "+filename << std::endl; } } - + diff --git a/src/core_landice/mode_forward/Interface_velocity_solver.hpp b/src/core_landice/mode_forward/Interface_velocity_solver.hpp index 574a87a285..c99cdd3cc8 100644 --- a/src/core_landice/mode_forward/Interface_velocity_solver.hpp +++ b/src/core_landice/mode_forward/Interface_velocity_solver.hpp @@ -10,7 +10,6 @@ distributed with this code, or at http://mpas-dev.github.com/license.html // =================================================== //! Includes // =================================================== -//#include #include #include #include @@ -45,11 +44,6 @@ distributed with this code, or at http://mpas-dev.github.com/license.html #define write_ascii_mesh write_ascii_mesh_ #endif -//#include -//#include - -//#include - struct exchange { const int procID; const std::vector vec; @@ -78,8 +72,9 @@ void velocity_solver_finalize(); void velocity_solver_set_parameters(double const* gravity_F, double const* ice_density_F, double const* ocean_density_F, double const* sea_level_F, double const* flowParamA_F, - double const* enhancementFactor_F, double const* flowLawExponent_F, double const* dynamic_thickness_F, + double const* flowLawExponent_F, double const* dynamic_thickness_F, double const* clausius_clapeyron_coeff, + double const* thermal_thickness_limit_F, int const* li_mask_ValueDynamicIce, int const* li_mask_ValueIce, bool const* use_GLP_F); @@ -94,14 +89,15 @@ void velocity_solver_solve_l1l2(double const* lowerSurface_F, double* xVelocityOnCell = 0, double* yVelocityOnCell = 0); void velocity_solver_solve_fo(double const* bedTopography_F, double const* lowerSurface_F, - double const* thickness_F, double const* beta_F, double const* smb_F, double const* temperature_F, + double const* thickness_F, double * beta_F, double const* smb_F, double const* temperature_F, double const* stiffnessFactor_F, + double const* effecPress_F, double const* muFriction_F, double* const dirichletVelocityXValue = 0, double* const dirichletVelocitYValue = 0, - double* u_normal_F = 0, double* dissipation_heat_F = 0, + double* u_normal_F = 0, double* bodyForce_F = 0, double* dissipation_heat_F = 0, double* xVelocityOnCell = 0, double* yVelocityOnCell = 0, double const * deltat = 0, int *error = 0 ); -void velocity_solver_compute_2d_grid(int const* verticesMask_F, int const* _cellsMask_F, int const* dirichletNodesMask_F, int const* floatingEdgeMask_F); +void velocity_solver_compute_2d_grid(int const* verticesMask_F, int const* _cellsMask_F, int const* dirichletNodesMask_F); void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, int const* _nVertices_F, int const* _nLayers, int const* _nCellsSolve_F, @@ -111,6 +107,8 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, int const* _verticesOnCell_F, int const* _verticesOnEdge_F, int const* _edgesOnCell_F, int const* _nEdgesOnCells_F, int const* _indexToCellID_F, + int const* _indexToEdgeID_F, + int const* _indexToVertexID_F, double const* _xCell_F, double const* _yCell_F, double const* _zCell_F, double const* _xVertex_F, double const* _yVertex_F, double const* _zVertex_F, double const* _areaTriangle_F, @@ -118,15 +116,12 @@ void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, int const* sendEdgesArray_F, int const* recvEdgesArray_F, int const* sendVerticesArray_F, int const* recvVerticesArray_F); -void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, - double const* lowerSurface_F, double const* thickness_F); +void velocity_solver_extrude_3d_grid(double const* levelsRatio_F); void velocity_solver_export_l1l2_velocity(); void velocity_solver_export_fo_velocity(); -//void velocity_solver_estimate_SS_SMB (const double* u_normal_F, double* sfcMassBal); - void interface_init_log(); void interface_redirect_stdout(int const* iTimestep); @@ -136,6 +131,9 @@ void interface_reset_stdout(); void write_ascii_mesh(int const* indexToCellID_F, double const* bedTopography_F, double const* lowerSurface_F, double const* beta_F, double const* temperature_F, + double const* surfaceAirTemperature_F, double const* basalHeatFlux_F, + double const* stiffnessFactor_F, + double const* effecPress_F, double const* muFriction_F, double const* thickness_F, double const* thicknessUncertainty_F, double const* smb_F, double const* smbUncertainty_F, double const* bmb_F, double const* bmbUncertainty_F, @@ -145,25 +143,11 @@ void write_ascii_mesh(int const* indexToCellID_F, } // extern "C" +extern int velocity_solver_init_mpi__(MPI_Comm comm); extern void velocity_solver_finalize__(); -#ifdef LIFEV -extern void velocity_solver_init_l1l2__(const std::vector& layersRatio, const std::vector& velocityOnVertices, bool initialize_velocity); - -extern void velocity_solver_solve_l1l2__(const std::vector& elevationData, - const std::vector& thicknessData, const std::vector& betaData, - const std::vector& temperatureData, const std::vector& indexToVertexID, - std::vector& velocityOnVertices); - -extern void velocity_solver_init_fo__(const std::vector& layersRatio, const std::vector& velocityOnVertices, const std::vector& indexToVertexID, bool initialize_velocity); - -extern void velocity_solver_export_l1l2_velocity__(const std::vector& layersRatio, const std::vector& elevationData, const std::vector& regulThk, - const std::vector& mpasIndexToVertexID, MPI_Comm reducedComm); - -#endif - extern void velocity_solver_set_physical_parameters__(double const& gravity, double const& ice_density, double const& ocean_density, double const& sea_level, double const& flowParamA, - double const& enhancementFactor, double const& flowLawExponent, double const& dynamic_thickness, bool const& useGLP, double const& clausiusClapeyronCoeff); + double const& flowLawExponent, double const& dynamic_thickness, bool const& useGLP, double const& clausiusClapeyronCoeff); extern void velocity_solver_solve_fo__(int nLayers, int nGlobalVertices, int nGlobalTriangles, bool ordering, bool first_time_step, @@ -173,34 +157,20 @@ extern void velocity_solver_solve_fo__(int nLayers, int nGlobalVertices, const std::vector& levelsNormalizedThickness, const std::vector& elevationData, const std::vector& thicknessData, - const std::vector& betaData, + std::vector& betaData, const std::vector& bedTopographyData, const std::vector& smbData, - const std::vector& temperatureOnTetra, - std::vector& dissipationHeatOnTetra, + const std::vector& stiffnessFactorData, + const std::vector& effecPressData, + const std::vector& muFrictionData, + const std::vector& temperatureDataOnPrisms, + std::vector& bodyForceOnBasalCell, + std::vector& dissipationHeatOnPrisms, std::vector& velocityOnVertices, int& error, const double& deltat = 0.0); - -#ifdef LIFEV -extern void velocity_solver_compute_2d_grid__(int nGlobalTriangles, - int nGlobalVertices, int nGlobalEdges, - const std::vector& indexToVertexID, - const std::vector& verticesCoords, - const std::vector& isVertexBoundary, - const std::vector& verticesOnTria, - const std::vector& isBoundaryEdge, - const std::vector& trianglesOnEdge, - const std::vector& trianglesPositionsOnEdge, - const std::vector& verticesOnEdge, - const std::vector& indexToEdgeID, - const std::vector& indexToTriangleID, - const std::vector < std::pair >& procOnInterfaceEdge); - -#else extern void velocity_solver_compute_2d_grid__(MPI_Comm); -#endif extern void velocity_solver_export_2d_data__(MPI_Comm reducedComm, @@ -209,47 +179,37 @@ extern void velocity_solver_export_2d_data__(MPI_Comm reducedComm, const std::vector& betaData, const std::vector& indexToVertexID); -extern void velocity_solver_extrude_3d_grid__(int nLayers, int nGlobalTriangles, - int nGlobalVertices, int nGlobalEdges, int Ordering, MPI_Comm reducedComm, +extern void velocity_solver_extrude_3d_grid__( + int nLayers, int globalTriangleStride, int globalVertexStride, int globalEdgeStride, + int Ordering, MPI_Comm reducedComm, const std::vector& indexToVertexID, - const std::vector& mpasIndexToVertexID, + const std::vector& vertexProcIDs, const std::vector& verticesCoords, - const std::vector& isVertexBoundary, const std::vector& verticesOnTria, + const std::vector> procsSharingVertices, const std::vector& isBoundaryEdge, const std::vector& trianglesOnEdge, - const std::vector& trianglesPositionsOnEdge, const std::vector& verticesOnEdge, const std::vector& indexToEdgeID, const std::vector& indexToTriangleID, const std::vector& dirichletNodes, - const std::vector&floatingEdges); - -//extern void velocity_solver_export_l1l2_velocity__(); + const std::vector& iceMarginEdgesID); extern void velocity_solver_export_fo_velocity__(MPI_Comm reducedComm); - -#ifdef LIFEV -extern int velocity_solver_initialize_iceProblem__(bool keep_proc, MPI_Comm reducedComm); -#endif - -//extern void velocity_solver_estimate_SS_SMB__ (const double* u_normal_F, double* sfcMassBal); - exchangeList_Type unpackMpiArray(int const* array); -bool isGhostTriangle(int i, double relTol = 1e-1); - double signedTriangleArea(const double* x, const double* y); double signedTriangleArea(const double* x, const double* y, const double* z); void createReducedMPI(int nLocalEntities, MPI_Comm& reduced_comm_id); -void import2DFields(std::map bdExtensionMap, double const* bedTopography_F, double const* lowerSurface_F, double const* thickness_F, - double const* beta_F = 0, double const* temperature_F = 0, double const* smb_F = 0, double eps = 0); +void importFields(std::vector >& marineBdyExtensionMap, + double const* bedTopography_F, double const* lowerSurface_F, double const* thickness_F, + double const* beta_F = 0, double const* stiffnessFactor_F = 0, double const* effecPress_F = 0, double const* muFriction_F = 0, double const* temperature_F = 0, double const* smb_F = 0, double eps = 0); -void import2DFieldsObservations(std::map bdExtensionMap, +void import2DFieldsObservations(std::vector >& marineBdyExtensionMap, double const * lowerSurface_F, double const * thickness_F, double const * thicknessUncertainty_F, double const * smbUncertainty_F, @@ -257,20 +217,20 @@ void import2DFieldsObservations(std::map bdExtensionMap, double const * observedSurfaceVelocityX_F, double const * observedSurfaceVelocityY_F, double const * observedSurfaceVelocityUncertainty_F, double const * observedThicknessTendency_F, double const * observedThicknessTendencyUncertainty_F, + double const* surfaceAirTemperature_F, double const* basalHeatFlux_F, int const * indexToCellID_F); - + +void computeSortingIndices(std::vector& sortingIndices, const std::vector& vectorToSort, int numIndices); + void write_ascii_mesh_field(std::vector fieldData, std::string filenamebase); void write_ascii_mesh_field_int(std::vector fieldData, std::string filenamebase); std::vector extendMaskByOneLayer(int const* verticesMask_F); -void extendMaskByOneLayer(int const* verticesMask_F, - std::vector& extendedFVerticesMask); - -void importP0Temperature(); - void exportDissipationHeat(double * dissipationHeat_F); +void exportBodyForce(double * bodyForce_F); +void exportBeta(double * beta_F); void get_prism_velocity_on_FEdges(double* uNormal, const std::vector& velocityOnCells, @@ -278,15 +238,9 @@ void get_prism_velocity_on_FEdges(double* uNormal, int initialize_iceProblem(int nTriangles); -void createReverseCellsExchangeLists(exchangeList_Type& sendListReverse_F, - exchangeList_Type& receiveListReverse_F, - const std::vector& fVertexToTriangleID, - const std::vector& fCellToVertexID); - -void createReverseEdgesExchangeLists(exchangeList_Type& sendListReverse_F, +void createReverseExchangeLists(exchangeList_Type& sendListReverse_F, exchangeList_Type& receiveListReverse_F, - const std::vector& fVertexToTriangleID, - const std::vector& fEdgeToEdgeID); + const std::vector& newProcIds, const int* indexToID_F, exchangeList_Type const * recvList_F); void mapCellsToVertices(const std::vector& velocityOnCells, std::vector& velocityOnVertices, int fieldDim, int numLayers, @@ -295,9 +249,6 @@ void mapCellsToVertices(const std::vector& velocityOnCells, void mapVerticesToCells(const std::vector& velocityOnVertices, double* velocityOnCells, int fieldDim, int numLayers, int ordering); -void computeLocalOffset(int nLocalEntities, int& localOffset, - int& nGlobalEntities); - void getProcIds(std::vector& field, int const* recvArray); void getProcIds(std::vector& field, exchangeList_Type const* recvList); @@ -311,12 +262,6 @@ void allToAll(std::vector& field, exchangeList_Type const* sendList, void allToAll(double* field, exchangeList_Type const* sendList, exchangeList_Type const* recvList, int fieldDim = 1); -int prismType(long long int const* prismVertexMpasIds, int& minIndex); -void tetrasFromPrismStructured (long long int const* prismVertexMpasIds, long long int const* prismVertexGIds, long long int tetrasIdsOnPrism[][4]); -void computeMap(); - -void setBdFacesOnPrism (const std::vector > >& prismStruct, const std::vector& prismFaceIds, std::vector& tetraPos, std::vector& facePos); -void tetrasFromPrismStructured (int const* prismVertexMpasIds, int const* prismVertexGIds, int tetrasIdsOnPrism[][4]); void procsSharingVertex(const int vertex, std::vector& procIds); bool belongToTria(double const* x, double const* t, double bcoords[3], double eps = 1e-3); diff --git a/src/core_landice/mode_forward/Makefile b/src/core_landice/mode_forward/Makefile index 5543e557dd..d635b54016 100644 --- a/src/core_landice/mode_forward/Makefile +++ b/src/core_landice/mode_forward/Makefile @@ -11,10 +11,12 @@ OBJS = mpas_li_core.o \ mpas_li_statistics.o \ mpas_li_velocity.o \ mpas_li_thermal.o \ + mpas_li_iceshelf_melt.o \ mpas_li_sia.o \ mpas_li_velocity_simple.o \ mpas_li_velocity_external.o \ - mpas_li_subglacial_hydro.o + mpas_li_subglacial_hydro.o \ + mpas_li_bedtopo.o ifneq (, $(findstring MPAS_LI_BUILD_INTERFACE,$(CPPFLAGS))) OBJS += Interface_velocity_solver.o @@ -35,9 +37,11 @@ mpas_li_time_integration.o: mpas_li_time_integration_fe.o mpas_li_time_integration_fe.o: mpas_li_advection.o \ mpas_li_calving.o \ mpas_li_thermal.o \ + mpas_li_iceshelf_melt.o \ mpas_li_diagnostic_vars.o \ mpas_li_velocity.o \ - mpas_li_subglacial_hydro.o + mpas_li_subglacial_hydro.o \ + mpas_li_bedtopo.o mpas_li_advection.o: mpas_li_thermal.o \ mpas_li_diagnostic_vars.o @@ -47,12 +51,15 @@ mpas_li_calving.o: mpas_li_thermal.o \ mpas_li_thermal.o: +mpas_li_iceshelf_melt.o: mpas_li_calving.o + mpas_li_diagnostic_vars.o: mpas_li_thermal.o mpas_li_velocity.o: mpas_li_sia.o \ mpas_li_velocity_simple.o \ mpas_li_velocity_external.o \ - mpas_li_advection.o + mpas_li_advection.o \ + mpas_li_thermal.o mpas_li_sia.o: mpas_li_diagnostic_vars.o @@ -64,6 +71,8 @@ mpas_li_statistics.o: mpas_li_diagnostic_vars.o mpas_li_velocity_external.o: Interface_velocity_solver.o +mpas_li_bedtopo.o: mpas_li_advection.o + Interface_velocity_solver.o: clean: diff --git a/src/core_landice/mode_forward/mpas_li_advection.F b/src/core_landice/mode_forward/mpas_li_advection.F index f2d0ed0229..2da5503955 100644 --- a/src/core_landice/mode_forward/mpas_li_advection.F +++ b/src/core_landice/mode_forward/mpas_li_advection.F @@ -138,15 +138,19 @@ subroutine li_advection_thickness_tracers(& real (kind=RKIND), dimension(:), pointer :: & thickness, & ! ice thickness (updated in this subroutine) bedTopography, & ! bed topography - sfcMassBal, & ! surface mass balance + sfcMassBal, & ! surface mass balance (potential forcing) + sfcMassBalApplied, & ! surface mass balance (actually applied) + groundedSfcMassBalApplied, & ! surface mass balance on grounded locations (actually applied) basalMassBal, & ! basal mass balance groundedBasalMassBal, & ! basal mass balance for grounded ice floatingBasalMassBal, & ! basal mass balance for floating ice - dynamicThickening ! dynamic thickening rate + dynamicThickening, & ! dynamic thickening rate + groundedToFloatingThickness, & ! thickness changing from grounded to floating or vice versa + fluxAcrossGroundingLine ! magnitude of flux across GL real (kind=RKIND), dimension(:,:), pointer :: & temperature, & ! interior ice temperature - waterfrac, & ! interior water fraction + waterFrac, & ! interior water fraction enthalpy ! interior ice enthalpy real (kind=RKIND), dimension(:,:), pointer :: & @@ -174,8 +178,16 @@ subroutine li_advection_thickness_tracers(& surfaceTracersField, & ! scratch field containing values of surface tracers basalTracersField ! scratch field containing values of basal tracers + type (field1DInteger), pointer :: & + cellMaskTemporaryField ! scratch field containing old values of cellMask + integer, dimension(:), pointer :: & - cellMask ! integer bitmask for cells + cellMask, & ! integer bitmask for cells + edgeMask ! integer bitmask for edges + + integer, dimension(:,:), pointer :: cellsOnEdge + + real (kind=RKIND), dimension(:), pointer :: dvEdge character (len=StrKIND), pointer :: & config_thickness_advection ! method for advecting thickness and tracers @@ -185,15 +197,20 @@ subroutine li_advection_thickness_tracers(& real (kind=RKIND), pointer :: & config_ice_density, & ! rhoi + config_ocean_density, & ! rhoo config_sea_level ! sea level relative to z = 0 logical :: advectTracers ! if true, then advect tracers as well as thickness + integer :: iCell1 + + real(kind=RKIND) :: GLfluxSign, thicknessFluxEdge + integer :: err_tmp !WHL - debug integer, dimension(:), pointer :: indexToCellID, indexToEdgeID - integer, pointer :: nCells + integer, pointer :: nCells, nEdges integer, pointer :: config_stats_cell_ID integer :: iEdge, iEdgeOnCell integer, dimension(:), pointer :: nEdgesOnCell @@ -233,35 +250,44 @@ subroutine li_advection_thickness_tracers(& call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'sfcMassBalApplied', sfcMassBalApplied) + call mpas_pool_get_array(geometryPool, 'groundedSfcMassBalApplied', groundedSfcMassBalApplied) call mpas_pool_get_array(geometryPool, 'basalMassBal', basalMassBal) call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness) call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) call mpas_pool_get_array(geometryPool, 'dynamicThickening', dynamicThickening) + call mpas_pool_get_array(geometryPool, 'groundedToFloatingThickness', groundedToFloatingThickness) ! get arrays from the velocity pool call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + call mpas_pool_get_array(velocityPool, 'fluxAcrossGroundingLine', fluxAcrossGroundingLine) ! get arrays from the thermal pool call mpas_pool_get_array(thermalPool, 'temperature', temperature) - call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'waterFrac', waterFrac) call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) ! get config variables call mpas_pool_get_config(liConfigs, 'config_thickness_advection', config_thickness_advection) call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) !WHL - debug call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) ! get fields from the scratch pool ! Note: The advectedTracers field is a scratch field containing only the tracers that need to be advected. @@ -287,9 +313,20 @@ subroutine li_advection_thickness_tracers(& call mpas_allocate_scratch_field(basalTracersField, .true.) basalTracers => basalTracersField % array + call mpas_pool_get_field(geometryPool, 'cellMaskTemporary', cellMaskTemporaryField) + call mpas_allocate_scratch_field(cellMaskTemporaryField, .true.) + + ! given the old thickness, compute the thickness in each layer call li_calculate_layerThickness(meshPool, thickness, layerThickness) + ! update masks + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! save old copycellMask for determining cells changing from grounded to floating and vice versa + cellMaskTemporaryField % array(:) = cellMask(:) + !----------------------------------------------------------------- ! Horizontal transport of thickness and tracers !----------------------------------------------------------------- @@ -331,7 +368,7 @@ subroutine li_advection_thickness_tracers(& endif ! Transport thickness and tracers using a first-order upwind scheme - ! Note: For the enthalpy scheme, temperature and waterfrac are the primary prognostic + ! Note: For the enthalpy scheme, temperature and waterFrac are the primary prognostic ! variables to be updated, but enthalpy is the advected tracer (for reasons of ! energy conservation). @@ -339,7 +376,7 @@ subroutine li_advection_thickness_tracers(& do iCell = 1, nCells if (indexToCellID(iCell) == config_stats_cell_ID) then - call mpas_log_write('Before thickness advection, iCell (local=$i, global=$i), thickness=$r, layer 1 tracer:=$r', & + call mpas_log_write('Before thickness advection, iCell (local=$i, global=$i), thickness=$r, layer 1 tracer:=$r',& intArgs=(/iCell, indexToCellID(iCell)/), realArgs=(/thickness(iCell), advectedTracers(1,1,iCell)/)) do iEdgeOnCell = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(iEdgeOnCell,iCell) @@ -362,8 +399,11 @@ subroutine li_advection_thickness_tracers(& layerThicknessEdge, & layerThicknessOld, & advectedTracersOld, & + cellMask, & + edgeMask, & layerThickness, & advectedTracers, & + fluxAcrossGroundingLine, & err) if (config_print_thickness_advection_info) then @@ -379,27 +419,22 @@ subroutine li_advection_thickness_tracers(& enddo endif + ! Calculate dynamicThickening (layerThickness is updated by advection at this point, while thickness is still old) dynamicThickening = (sum(layerThickness, 1) - thickness) / dt * scyr ! units of m/yr + ! Update the thickness and cellMask before applying the mass balance. ! The update is needed because the SMB and BMB depend on whether ice is present. - thickness = sum(layerThickness, 1) call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + + !----------------------------------------------------------------- ! Add the surface and basal mass balance to the layer thickness !----------------------------------------------------------------- - ! Zero out any positive surface mass balance for ice-free ocean cells - - where ( sfcMassBal > 0.0_RKIND .and. bedTopography < config_sea_level .and. .not.(li_mask_is_ice(cellMask) ) ) - - sfcMassBal = 0.0_RKIND - - end where - ! Combine various basal mass balance fields based on mask. ! Grounded and floating basal mass balance should come from the thermal solver. @@ -425,7 +460,11 @@ subroutine li_advection_thickness_tracers(& call apply_mass_balance(& dt, & config_ice_density, & + cellMask, & + bedTopography, & sfcMassBal, & + sfcMassBalApplied, & + groundedSfcMassBalApplied, & basalMassBal, & surfaceTracers, & basalTracers, & @@ -444,17 +483,43 @@ subroutine li_advection_thickness_tracers(& do iCell = 1, nCells if (indexToCellID(iCell) == config_stats_cell_ID) then call mpas_log_write(' ') - call mpas_log_write('After apply_mass_balance, iCell=$i, thickness=$r', intArgs=(/iCell/), realArgs=(/thickness(iCell)/) ) + call mpas_log_write('After apply_mass_balance, iCell=$i, thickness=$r', intArgs=(/iCell/), & + realArgs=(/thickness(iCell)/) ) call mpas_log_write('cellMask=$i, is ice=$l, is grounded=$l, is floating=$l', & - intArgs=(/cellMask(iCell)/), logicArgs=(/li_mask_is_ice(cellMask(iCell)), li_mask_is_grounded_ice(cellMask(iCell)), & - li_mask_is_floating_ice(cellMask(iCell)) /) ) - call mpas_log_write('basalMassBal=$r, grounded=$r, floating=$r', realArgs=(/ basalMassBal(iCell)*31536000./917., & + intArgs=(/cellMask(iCell)/), logicArgs=(/li_mask_is_ice(cellMask(iCell)), & + li_mask_is_grounded_ice(cellMask(iCell)), li_mask_is_floating_ice(cellMask(iCell)) /) ) + call mpas_log_write('basalMassBal=$r, grounded=$r, floating=$r', realArgs=(/ basalMassBal(iCell)*31536000./917.,& groundedBasalMassBal(iCell)*31536000./917., floatingBasalMassBal(iCell)*31536000./917. /) ) endif enddo endif call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! Calculate volume converted from grounded to floating + ! This needs to be determined after SMB/BMB are applied because those can change floating/grounded state + call grounded_to_floating(cellMaskTemporaryField % array, cellMask, thickness, groundedToFloatingThickness, nCells) + + ! Calculate flux across grounding line + ! Do this after new thickness & mask have been calculated, including SMB/BMB + fluxAcrossGroundingLine(:) = 0.0_RKIND + do iEdge = 1, nEdges + if (li_mask_is_grounding_line(edgeMask(iEdge))) then + iCell1 = cellsOnEdge(1,iEdge) + !iCell2 = cellsOnEdge(2,iEdge) + if (li_mask_is_grounded_ice(cellMask(iCell1))) then + GLfluxSign = 1.0_RKIND ! edge sign convention is positive from iCell1 to iCell2 on an edge + else + GLfluxSign = -1.0_RKIND + endif + do k = 1, nVertLevels + thicknessFluxEdge = layerNormalVelocity(k,iEdge) * dvEdge(iEdge) * layerThicknessEdge(k,iEdge) + fluxAcrossGroundingLine(iEdge) = fluxAcrossGroundingLine(iEdge) + GLfluxSign * thicknessFluxEdge / dvEdge(iEdge) + enddo + endif + enddo ! edges + ! Remap tracers to the standard vertical sigma coordinate ! Note: If tracers are not being advected, then this subroutine simply restores the @@ -469,7 +534,8 @@ subroutine li_advection_thickness_tracers(& call mpas_log_write(' ') call mpas_log_write('After vertical remap, iCell, new layer thickness, tracer 1:') do k = 1, nVertLevels - call mpas_log_write("$i $r $r", intArgs=(/iCell/), realArgs=(/layerThickness(k,iCell), advectedTracers(1,k,iCell)/)) + call mpas_log_write("$i $r $r", intArgs=(/iCell/), realArgs=(/layerThickness(k,iCell), & + advectedTracers(1,k,iCell)/)) enddo endif enddo @@ -478,7 +544,7 @@ subroutine li_advection_thickness_tracers(& if (advectTracers) then ! Copy the advectedTracersNew values into the thermal tracer arrays - ! (temperature, waterfrac, enthalpy) + ! (temperature, waterFrac, enthalpy) call tracer_finish(& meshPool, & @@ -494,7 +560,8 @@ subroutine li_advection_thickness_tracers(& case default - call mpas_log_write(trim(config_thickness_advection) // ' is not a valid option for thickness/tracer advection.', MPAS_LOG_ERR) + call mpas_log_write(trim(config_thickness_advection) // ' is not a valid option for thickness/tracer advection.', & + MPAS_LOG_ERR) err_tmp = 1 end select @@ -507,6 +574,7 @@ subroutine li_advection_thickness_tracers(& call mpas_deallocate_scratch_field(layerThicknessOldField, .true.) call mpas_deallocate_scratch_field(basalTracersField, .true.) call mpas_deallocate_scratch_field(surfaceTracersField, .true.) + call mpas_deallocate_scratch_field(cellMaskTemporaryField, .true.) ! === error check if (err > 0) then @@ -516,7 +584,7 @@ subroutine li_advection_thickness_tracers(& !-------------------------------------------------------------------- end subroutine li_advection_thickness_tracers - + !*********************************************************************** ! ! subroutine li_update_geometry @@ -569,7 +637,7 @@ subroutine li_update_geometry(geometryPool) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) - ! Lower surface is based on floatation for floating ice. + ! Lower surface is based on floatation for floating ice. ! For grounded ice (and non-ice areas) it is the bed. where ( li_mask_is_floating_ice(cellMask) ) lowerSurface = config_sea_level - thickness * (config_ice_density / config_ocean_density) @@ -586,7 +654,7 @@ subroutine li_update_geometry(geometryPool) ! Upper surface is the lower surface plus the thickness upperSurface(:) = lowerSurface(:) + thickness(:) - + !-------------------------------------------------------------------- end subroutine li_update_geometry @@ -612,7 +680,11 @@ end subroutine li_update_geometry subroutine apply_mass_balance(& dt, & rhoi, & + cellMask, & + bedTopography, & sfcMassBal, & + sfcMassBalApplied, & + groundedSfcMassBalApplied, & basalMassBal, & surfaceTracers, & basalTracers, & @@ -620,16 +692,17 @@ subroutine apply_mass_balance(& advectedTracers) !----------------------------------------------------------------- - ! ! input variables - ! !----------------------------------------------------------------- real (kind=RKIND), intent(in) :: & dt, & !< Input: time step (s) rhoi !< Input: ice density (kg/m^3) + integer, dimension(:), intent(in) :: cellMask !< Input: mask on cells + real (kind=RKIND), dimension(:), intent(in) :: & + bedTopography, & !< Input: bed elevation (m) sfcMassBal, & !< Input: surface mass balance (kg/m^2/s) basalMassBal !< Input: basal mass balance (kg/m^2/s) @@ -638,16 +711,25 @@ subroutine apply_mass_balance(& basalTracers !< Input: tracer values of new ice at lower surface !----------------------------------------------------------------- - ! ! input/output variables - ! !----------------------------------------------------------------- real(kind=RKIND), dimension(:,:), intent(inout) :: & - layerThickness ! ice thickness in each layer (m) + layerThickness !< Input/Output: ice thickness in each layer (m) real(kind=RKIND), dimension(:,:,:), intent(inout) :: & - advectedTracers ! tracer values in each layer + advectedTracers !< Input/Output: tracer values in each layer + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(out) :: & + sfcMassBalApplied !< Output: surface mass balance actually applied on this time step (kg/m^2/s) + + real(kind=RKIND), dimension(:), intent(out) :: & + groundedSfcMassBalApplied !< Output: surface mass balance actually applied to grounded ice on this time step (kg/m^2/s) + ! local variables @@ -661,6 +743,7 @@ subroutine apply_mass_balance(& integer :: nCells ! number of cells integer :: nLayers ! number of layers integer :: nTracers ! number of tracers + real (kind=RKIND), pointer :: config_sea_level ! sea level relative to z = 0 integer :: iCell, iLayer, iTracer @@ -670,10 +753,15 @@ subroutine apply_mass_balance(& allocate(thckTracerProducts(nTracers)) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + ! apply surface mass balance ! If positive, then add the SMB to the top layer, conserving mass*tracer products ! If negative, then melt from the top down until the melting term is used up or the ice is gone + ! Initialize applied SMB field + sfcMassBalApplied(:) = sfcMassBal(:) + do iCell = 1, nCells ! initialize accumulation/ablation terms @@ -685,12 +773,18 @@ subroutine apply_mass_balance(& ! apply surface mass balance - if (sfcMassBal(iCell) > 0.0_RKIND) then + ! Zero out any positive surface mass balance for ice-free ocean cells + if (sfcMassBalApplied(iCell) > 0.0_RKIND .and. & + bedTopography(iCell) < config_sea_level .and. .not.(li_mask_is_ice(cellMask(iCell)) ) ) then + sfcMassBalApplied(iCell) = 0.0_RKIND + end if + + ! surface accumulation + if (sfcMassBalApplied(iCell) > 0.0_RKIND) then - ! surface accumulation ! modify tracers conservatively in top layer - sfcAccum = sfcMassBal(iCell) * dt / rhoi + sfcAccum = sfcMassBalApplied(iCell) * dt / rhoi ! compute mass-tracer products in top layer thckTracerProducts(:) = layerThickness(1,iCell)*advectedTracers(:,1,iCell) & @@ -702,11 +796,10 @@ subroutine apply_mass_balance(& ! new tracers in top layer advectedTracers(:,1,iCell) = thckTracerProducts(:) / layerThickness(1,iCell) - elseif (sfcMassBal(iCell) < 0.0_RKIND) then - - ! surface ablation from the top down + ! surface ablation + elseif (sfcMassBalApplied(iCell) < 0.0_RKIND) then - sfcAblat = -sfcMassBal(iCell) * dt /rhoi ! positive for melting + sfcAblat = -1.0_RKIND * sfcMassBalApplied(iCell) * dt /rhoi ! positive for melting do iLayer = 1, nLayers if (sfcAblat > layerThickness(iLayer,iCell)) then ! melt the entire layer @@ -720,10 +813,20 @@ subroutine apply_mass_balance(& endif enddo - !TODO - If remaining sfcAblat > 0, then keep track of it to conserve energy + ! Adjust applied SMB to only be the amount actually applied above (this will include ice-free cells) + if (sfcAblat > 0.0_RKIND) then + sfcMassBalApplied(iCell) = sfcMassBalApplied(iCell) + sfcAblat * rhoi / dt + !TODO - If remaining sfcAblat > 0, then keep track of it to conserve energy (?) + endif + endif ! sfcMassBal > 0 + groundedSfcMassBalApplied(:) = 0.0_RKIND + where (li_mask_is_grounded_ice(cellMask) .or. bedTopography > config_sea_level) + groundedSfcMassBalApplied = sfcMassBalApplied + end where + ! apply basal mass balance if (basalMassBal(iCell) > 0.0_RKIND) then @@ -793,7 +896,7 @@ subroutine tracer_setup(& surfaceTracers, & basalTracers) - use li_thermal, only: li_temperature_to_enthalpy + use li_thermal, only: li_temperature_to_enthalpy_kelvin !----------------------------------------------------------------- ! @@ -835,11 +938,11 @@ subroutine tracer_setup(& config_thermal_solver integer, pointer :: & - nCellsSolve ! number of locally owned cells + nCells ! number of cells real (kind=RKIND), dimension(:,:), pointer :: & temperature, & ! interior ice temperature - waterfrac, & ! interior water fraction + waterFrac, & ! interior water fraction enthalpy ! interior ice enthalpy real (kind=RKIND), dimension(:), pointer :: & @@ -858,7 +961,7 @@ subroutine tracer_setup(& integer :: iCell, iTracer ! get dimensions - call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) ! get arrays from mesh pool call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) @@ -868,7 +971,7 @@ subroutine tracer_setup(& ! get arrays from thermal pool call mpas_pool_get_array(thermalPool, 'temperature', temperature) - call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'waterFrac', waterFrac) call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) @@ -882,8 +985,8 @@ subroutine tracer_setup(& ! Notes: ! (1) For the enthalpy solver, it is necessary to transport enthalpy (rather than - ! temperature and waterfrac separately) in order to conserve energy. - ! (2) Temperature and waterfrac must be up to date (including halo cells) + ! temperature and waterFrac separately) in order to conserve energy. + ! (2) Temperature and waterFrac must be up to date (including halo cells) ! before calling this subroutine. ! (3) Surface and basal temperature (or enthalpy) are not transported, but their ! values are applied to new accumulation at either surface. @@ -896,15 +999,15 @@ subroutine tracer_setup(& if (trim(config_thermal_solver) == 'enthalpy') then ! advect enthalpy - ! Rather than assume that enthalpy is up to date, recompute it from temperature and waterfrac + ! Rather than assume that enthalpy is up to date, recompute it from temperature and waterFrac - do iCell = 1, nCellsSolve + do iCell = 1, nCells - call li_temperature_to_enthalpy(& + call li_temperature_to_enthalpy_kelvin(& layerCenterSigma, & thickness(iCell), & temperature(:,iCell), & - waterfrac(:,iCell), & + waterFrac(:,iCell), & enthalpy(:,iCell)) enddo @@ -916,7 +1019,7 @@ subroutine tracer_setup(& advectedTracers(iTracer,:,:) = enthalpy(:,:) ! set enthalpy of new ice at upper and lower surfaces - ! convert temperature to enthalpy assuming waterfrac = 0 for new ice + ! convert temperature to enthalpy assuming waterFrac = 0 for new ice surfaceTracers(iTracer,:) = min(surfaceAirTemperature(:), kelvin_to_celsius) * config_ice_density*cp_ice basalTracers(iTracer,:) = basalTemperature(:) * config_ice_density*cp_ice @@ -958,7 +1061,7 @@ subroutine tracer_finish(& thermalPool, & advectedTracers) - use li_thermal, only: li_enthalpy_to_temperature + use li_thermal, only: li_enthalpy_to_temperature_kelvin !----------------------------------------------------------------- ! @@ -1005,7 +1108,7 @@ subroutine tracer_finish(& real (kind=RKIND), dimension(:,:), pointer :: & temperature, & ! interior ice temperature - waterfrac, & ! water fraction + waterFrac, & ! water fraction enthalpy ! interior ice enthalpy integer :: iCell, iTracer @@ -1021,7 +1124,7 @@ subroutine tracer_finish(& ! get arrays from thermal pool call mpas_pool_get_array(thermalPool, 'temperature', temperature) - call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'waterFrac', waterFrac) call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) ! get config variables @@ -1038,12 +1141,12 @@ subroutine tracer_finish(& do iCell = 1, nCellsSolve - call li_enthalpy_to_temperature(& + call li_enthalpy_to_temperature_kelvin(& layerCenterSigma, & thickness(iCell), & enthalpy(:,iCell), & temperature(:,iCell), & - waterfrac(:,iCell)) + waterFrac(:,iCell)) enddo @@ -1080,8 +1183,11 @@ subroutine advect_thickness_tracers_upwind(& layerThicknessEdge, & layerThicknessOld, & tracersOld, & + cellMask, & + edgeMask, & layerThicknessNew, & tracersNew, & + fluxAcrossGroundingLine, & err, & advectTracersIn) @@ -1111,6 +1217,12 @@ subroutine advect_thickness_tracers_upwind(& real (kind=RKIND), dimension(:,:,:), intent(in) :: & tracersOld !< Input: tracer values + integer, dimension(:), intent(in) :: & + cellMask !< Input: mask on cells + + integer, dimension(:), intent(in) :: & + edgeMask !< Input: mask on edges + !----------------------------------------------------------------- ! ! input/output variables @@ -1129,6 +1241,9 @@ subroutine advect_thickness_tracers_upwind(& real (kind=RKIND), dimension(:,:,:), intent(out) :: & tracersNew !< Output: tracer values + real (kind=RKIND), dimension(:), intent(out) :: & + fluxAcrossGroundingLine !< Output: ice flux at grounding lines + integer, intent(out) :: & err !< Output: error flag @@ -1140,7 +1255,8 @@ subroutine advect_thickness_tracers_upwind(& integer, pointer :: & nCellsSolve, & ! number of locally owned cells - nVertLevels ! number of vertical layers + nVertLevels, & ! number of vertical layers + nEdges ! number of edges integer, dimension(:), pointer :: & nEdgesOnCell ! number of edges on each cell @@ -1186,6 +1302,7 @@ subroutine advect_thickness_tracers_upwind(& ! This is ~300 million years in seconds, but it is small enough not to overflow real(kind=RKIND) :: velSign ! = 1.0_RKIND or -1.0_RKIND depending on sign of velocity + real(kind=RKIND) :: GLfluxSign integer :: err_tmp @@ -1215,6 +1332,7 @@ subroutine advect_thickness_tracers_upwind(& ! get dimensions call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) ! get mesh arrays @@ -1244,6 +1362,7 @@ subroutine advect_thickness_tracers_upwind(& endif + ! Note: This loop structure (nCells loop outside nEdgesOnCell loop) results in double calculation of fluxes ! across each edge. But upwind advection is cheap, so the extra cost is minimal. @@ -1253,7 +1372,8 @@ subroutine advect_thickness_tracers_upwind(& invAreaCell = 1.0_RKIND / areaCell(iCell) if (indexToCellID(iCell) == config_stats_cell_ID .and. config_print_thickness_advection_info) then - call mpas_log_write('In advect_thickness_tracer, iCell (local=$i, global=$i) =', intArgs=(/iCell, indexToCellID(iCell)/)) + call mpas_log_write('In advect_thickness_tracer, iCell (local=$i, global=$i) =', & + intArgs=(/iCell, indexToCellID(iCell)/)) call mpas_log_write('k, iEdgeOnCell, layerNormalVelocity, layerThicknessEdge, ' // & 'thicknessFluxEdge, thicknessTendency, thicknessTracerTendency:') endif @@ -1295,7 +1415,8 @@ subroutine advect_thickness_tracers_upwind(& !WHL - debug if (indexToCellID(iCell) == config_stats_cell_ID .and. k==1 .and. config_print_thickness_advection_info) then - call mpas_log_write("$i $i $r $r $r $r $r", intArgs=(/k, iEdgeOnCell/), realArgs=(/layerNormalVelocity(k,iEdge), & + call mpas_log_write("$i $i $r $r $r $r $r", & + intArgs=(/k, iEdgeOnCell/), realArgs=(/layerNormalVelocity(k,iEdge), & layerThicknessEdge(k,iEdge), thicknessFluxEdge, thicknessTendency, thicknessTracerTendency(1)/) ) endif @@ -1397,6 +1518,7 @@ end subroutine advect_thickness_tracers_upwind subroutine li_layer_normal_velocity(& meshPool, & normalVelocity, & + edgeMask, & layerNormalVelocity, & minOfMaxAllowableDt, & err) @@ -1413,6 +1535,9 @@ subroutine li_layer_normal_velocity(& real (kind=RKIND), dimension(:,:), intent(in) :: & normalVelocity !< Input: normal velocity on cell edges + integer, dimension(:), intent(in) :: & + edgeMask !< Input: normal velocity on cell edges + !----------------------------------------------------------------- ! ! output variables @@ -1474,6 +1599,8 @@ subroutine li_layer_normal_velocity(& ! loop over local edges do iEdge = 1, nEdgesSolve + if (li_mask_is_dynamic_ice(edgeMask(iEdge))) then + ! loop over layers do k = 1, nVertLevels @@ -1491,6 +1618,10 @@ subroutine li_layer_normal_velocity(& enddo ! k + else + layerNormalVelocity(:,iEdge) = 0.0_RKIND + endif + enddo ! iEdge ! === error check @@ -1655,7 +1786,8 @@ subroutine vertical_remap(thickness, cellMask, meshPool, layerThickness, tracers difference = abs(finalEnergySum - initEnergySum) if (initEnergySum > eps11) then if (difference/initEnergySum > eps11) then - call mpas_log_write('vertical_remap, mass*tracer conservation error, iCell = $i', MPAS_LOG_WARN, intArgs=(/iCell/)) + call mpas_log_write('vertical_remap, mass*tracer conservation error, iCell = $i', & + MPAS_LOG_WARN, intArgs=(/iCell/)) call mpas_log_write('init energy, final energy, difference: $r $r $r', & realArgs=(/initEnergySum, finalEnergySum, finalEnergySum - initEnergySum/)) endif @@ -1670,6 +1802,52 @@ subroutine vertical_remap(thickness, cellMask, meshPool, layerThickness, tracers end subroutine vertical_remap + subroutine grounded_to_floating(cellMaskOrig, cellMaskNew, thicknessNew, groundedToFloatingThickness, nCells) + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, dimension(:), intent(in) :: & + cellMaskOrig !< Input: mask for cells before advection + + integer, dimension(:), intent(in) :: & + cellMaskNew !< Input: mask for cells after advection + + real(kind=RKIND), dimension(:), intent(in) :: & + thicknessNew !< Input: ice thickness after advection + + integer, pointer, intent(in) :: & + nCells + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:), intent(out) :: & + groundedToFloatingThickness !< Input: ice thickness + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell + + groundedToFloatingThickness(:) = 0.0_RKIND + + do iCell = 1, nCells + ! find locations that had grounded ice before but now have floating ice + if (li_mask_is_grounded_ice(cellMaskOrig(iCell)) .and. & + li_mask_is_floating_ice(cellMaskNew(iCell)) ) then + groundedToFloatingThickness(iCell) = thicknessNew(iCell) + ! find locations that had floating ice before but now have grounded ice + else if (li_mask_is_floating_ice(cellMaskOrig(iCell)) .and. & + li_mask_is_grounded_ice(cellMaskNew(iCell)) ) then + groundedToFloatingThickness(iCell) = -1.0_RKIND * thicknessNew(iCell) + endif + enddo + + end subroutine grounded_to_floating + !*********************************************************************** end module li_advection diff --git a/src/core_landice/mode_forward/mpas_li_bedtopo.F b/src/core_landice/mode_forward/mpas_li_bedtopo.F new file mode 100644 index 0000000000..851e535b34 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_bedtopo.F @@ -0,0 +1,286 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_bedtopo +! +!> \MPAS land-ice bedtopo driver +!> \author Matt Hoffman +!> \date 20 June 2019 +!> \details +!> This module contains the routines for bed topography for solid earth changes +!> +! +!----------------------------------------------------------------------- + +module li_bedtopo + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_log + use li_mask + use li_setup + + implicit none + private + + !-------------------------------------------------------------------- + ! Public parameters + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! Public member functions + !-------------------------------------------------------------------- + public :: li_bedtopo_init, & + li_bedtopo_finalize, & + li_bedtopo_block_init, & + li_bedtopo_solve + + !-------------------------------------------------------------------- + ! Private module variables + !-------------------------------------------------------------------- + + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_bedtopo_init +! +!> \brief Initializes bedtopo solver +!> \author Matt Hoffman +!> \date 20 June 2019 +!> \details +!> This routine initializes the bedtopo solver. +! +!----------------------------------------------------------------------- + + subroutine li_bedtopo_init(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + + ! No init is needed. + err = 0 + + !-------------------------------------------------------------------- + + end subroutine li_bedtopo_init + + + +!*********************************************************************** +! +! routine li_bedtopo_block_init +! +!> \brief Initializes blocks for bedtopo solver +!> \author Matt Hoffman +!> \date 20 June 2019 +!> \details +!> This routine initializes each block of the bedtopo solver. +! +!----------------------------------------------------------------------- + + subroutine li_bedtopo_block_init(block, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: & + block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! No block init needed. + err = 0 + + !-------------------------------------------------------------------- + end subroutine li_bedtopo_block_init + + + +!*********************************************************************** +! +! subroutine li_bedtopo_solve +! +!> \brief Updates bed topography +!> \author Matt Hoffman +!> \date 20 June 2019 +!> \details +!> This routine updates the bed topography. Currently the only option +!> is a data field passed in as input. +! +!----------------------------------------------------------------------- +subroutine li_bedtopo_solve(domain, err) + + use li_mask + use li_advection + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + character (len=StrKIND), pointer :: config_uplift_method + type (mpas_pool_type), pointer :: meshPool !< mesh information + type (mpas_pool_type), pointer :: geometryPool !< geometry information + type (mpas_pool_type), pointer :: velocityPool !< velocity information + + real (kind=RKIND), dimension(:), pointer :: bedTopography, upliftRate + real (kind=RKIND), pointer :: deltat + integer :: err_tmp + + err = 0 + err_tmp = 0 + + ! Set needed variables and pointers + call mpas_pool_get_config(liConfigs, 'config_uplift_method', config_uplift_method) + if (trim(config_uplift_method)=='none') then + ! do nothing + elseif (trim(config_uplift_method)=='data') then + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'upliftRate', upliftRate) + + bedTopography(:) = bedTopography(:) + upliftRate(:) * deltat + + call li_update_geometry(geometryPool) + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + + block => block % next + end do + + else + call mpas_log_write("Unknown option selected for 'config_uplift_method'", MPAS_LOG_ERR) + endif + + + + + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_bedtopo_solve.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + + end subroutine li_bedtopo_solve + + + + +!*********************************************************************** +! +! routine li_bedtopo_finalize +! +!> \brief finalizes bedtopo solver +!> \author Matt Hoffman +!> \date 20 June 2019 +!> \details +!> This routine finalizes the bedtopo solver. +! +!----------------------------------------------------------------------- + + subroutine li_bedtopo_finalize(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + err = 0 + + !-------------------------------------------------------------------- + end subroutine li_bedtopo_finalize + + + + ! private subroutines + + + + +!*********************************************************************** + +end module li_bedtopo + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/mode_forward/mpas_li_calving.F b/src/core_landice/mode_forward/mpas_li_calving.F index 7970ea1f67..51ade4220d 100644 --- a/src/core_landice/mode_forward/mpas_li_calving.F +++ b/src/core_landice/mode_forward/mpas_li_calving.F @@ -1,4 +1,3 @@ -! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) ! and the University Corporation for Atmospheric Research (UCAR). ! ! Unless noted otherwise source code is licensed under the BSD license. @@ -30,7 +29,6 @@ module li_calving use li_mask use li_constants - implicit none private @@ -46,7 +44,8 @@ module li_calving ! !-------------------------------------------------------------------- - public :: li_calve_ice, li_restore_calving_front + public :: li_calve_ice, li_restore_calving_front, li_apply_front_ablation_velocity, & + li_calculate_damage, li_finalize_damage_after_advection !-------------------------------------------------------------------- ! @@ -193,8 +192,6 @@ subroutine li_calve_ice(domain, err) endif ! compute calvingThickness based on the calving_config option - ! the calvingThickness field gets applied to the thickness state field - ! after this if-construct if (trim(config_calving) == 'thickness_threshold') then call thickness_calving(domain, calvingFraction, err_tmp) @@ -215,6 +212,26 @@ subroutine li_calve_ice(domain, err) call eigencalving(domain, err_tmp) err = ior(err, err_tmp) + elseif (trim(config_calving) == 'damagecalving') then + + call damage_calving(domain, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_calving) == 'mask') then + + call mask_calving(domain, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_calving) == 'von_Mises_stress') then + + call von_Mises_calving(domain, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_calving) == 'specified_calving_velocity') then + + call specified_calving_velocity(domain, err_tmp) + err = ior(err, err_tmp) + else call mpas_log_write("Invalid option for config_calving specified: " // trim(config_calving), MPAS_LOG_ERR) @@ -222,6 +239,8 @@ subroutine li_calve_ice(domain, err) endif + ! now also remove any icebergs + call remove_icebergs(domain) ! Final operations after calving has been applied. block => domain % blocklist @@ -249,7 +268,8 @@ subroutine li_calve_ice(domain, err) call mpas_log_write('Global cell ID, bedTopography, calvingThickness:') do iCell = 1, nCells if (calvingThickness(iCell) > 0.0_RKIND) then - call mpas_log_write("$i $r $r", intArgs=(/indexToCellID(iCell)/), realArgs=(/bedTopography(iCell), calvingThickness(iCell)/)) + call mpas_log_write("$i $r $r", intArgs=(/indexToCellID(iCell)/), & + realArgs=(/bedTopography(iCell), calvingThickness(iCell)/)) endif enddo endif ! config_print_calving_info @@ -353,7 +373,7 @@ subroutine li_restore_calving_front(domain, err) ! These are needed to initialize the temperature profile in restored columns. real (kind=RKIND), dimension(:,:), pointer :: & temperature, & ! interior ice temperature - waterfrac ! interior water fraction + waterFrac ! interior water fraction real (kind=RKIND), dimension(:), pointer :: & surfaceAirTemperature, & ! surface air temperature @@ -369,6 +389,10 @@ subroutine li_restore_calving_front(domain, err) integer :: i, iRow integer :: k + + ! first remove any icebergs - do it first so restore-calving can put back thin ice in those places + call remove_icebergs(domain) + ! block loop block => domain % blocklist do while (associated(block)) @@ -396,7 +420,7 @@ subroutine li_restore_calving_front(domain, err) ! get required fields from the thermal pool call mpas_pool_get_array(thermalPool, 'temperature', temperature) - call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'waterFrac', waterFrac) call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) @@ -475,7 +499,7 @@ subroutine li_restore_calving_front(domain, err) thickness(iCell), & surfaceAirTemperature(iCell), & temperature(:,iCell), & - waterfrac(:,iCell), & + waterFrac(:,iCell), & surfaceTemperature(iCell), & basalTemperature(iCell)) @@ -799,6 +823,8 @@ subroutine thickness_calving(domain, calvingFraction, err) ! === apply calving === thickness(:) = thickness(:) - calvingThickness(:) + call remove_small_islands(meshPool, geometryPool) + block => block % next enddo @@ -817,7 +843,7 @@ end subroutine thickness_calving !> \brief Calve any ice that is floating !> \author William Lipscomb, moved to separate subroutine by Matt Hoffman Feb. 2018 !> \date September 2015 -!> \details +!> \details !----------------------------------------------------------------------- subroutine floating_calving(domain, calvingFraction, err) @@ -842,6 +868,7 @@ subroutine floating_calving(domain, calvingFraction, err) !----------------------------------------------------------------- type (block_type), pointer :: block type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool real (kind=RKIND), dimension(:), pointer :: calvingThickness ! thickness of ice that calves (computed in this subroutine) real (kind=RKIND), dimension(:), pointer :: thickness integer, dimension(:), pointer :: cellMask @@ -854,6 +881,7 @@ subroutine floating_calving(domain, calvingFraction, err) ! get pools call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) @@ -868,11 +896,104 @@ subroutine floating_calving(domain, calvingFraction, err) ! === apply calving === thickness(:) = thickness(:) - calvingThickness(:) + call remove_small_islands(meshPool, geometryPool) + block => block % next enddo end subroutine floating_calving +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine remove_small_islands +! +!> \brief Remove very small islands that lead to velocity solver problems +!> \author Matthew Hoffman +!> \date Summer 2018 +!> \details This routine finds and eliminates very small islands that lead to +!> unrealistic velocities in the Albany velocity solver. Specifically, this +!> finds one- and two-cell masses of ice that are surrounded by open ocean +!> and eliminates them by sending them to the calving flux. +!----------------------------------------------------------------------- + + subroutine remove_small_islands(meshPool, geometryPool) + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh pool + type (mpas_pool_type), pointer, intent(inout) :: geometryPool !< Input: Geometry pool + + logical, pointer :: config_remove_small_islands + real(kind=RKIND), pointer :: config_sea_level + real (kind=RKIND), dimension(:), pointer :: calvingThickness ! thickness of ice that calves (computed in this subroutine) + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + integer, dimension(:), pointer :: cellMask + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, pointer :: nCellsSolve + integer :: iCell, jCell, n, nIceNeighbors, nIceNeighbors2, neighborWithIce + integer :: nOpenOceanNeighbors, nOpenOceanNeighbors2 + + call mpas_pool_get_config(liConfigs, 'config_remove_small_islands', config_remove_small_islands) + if (.not. config_remove_small_islands) then + return ! skip this entire routine if disabled + endif + + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + do iCell = 1, nCellsSolve + if (li_mask_is_ice(cellMask(iCell))) then ! might as well do for both grounded or floating + ! (1 or 2 cell floating masses are icebergs) + nIceNeighbors = 0 + nOpenOceanNeighbors = 0 + do n = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(n, iCell) + if (li_mask_is_ice(cellMask(jCell))) then + nIceNeighbors = nIceNeighbors + 1 + neighborWithIce = jCell + endif + if (.not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level) then + nOpenOceanNeighbors = nOpenOceanNeighbors + 1 + endif + enddo + if ((nIceNeighbors == 0) .and. (nOpenOceanNeighbors == nEdgesOnCell(iCell))) then + ! If this is a single cell of ice surrounded by open ocean, kill this location + calvingThickness(iCell) = calvingThickness(iCell) + thickness(iCell) + thickness(iCell) = 0.0_RKIND + elseif (nIceNeighbors == 1) then + ! check if this neighbor has any additional neighbors with ice + nIceNeighbors2 = 0 + nOpenOceanNeighbors = 0 + do n = 1, nEdgesOnCell(neighborWithIce) + jCell = cellsOnCell(n, neighborWithIce) + if (li_mask_is_ice(cellMask(jCell))) then + nIceNeighbors2 = nIceNeighbors2 + 1 + endif + if (.not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level) then + nOpenOceanNeighbors2 = nOpenOceanNeighbors2 + 1 + endif + enddo + if ((nIceNeighbors2 == 1) .and. (nOpenOceanNeighbors2 == nEdgesOnCell(iCell)-1)) then + ! <- only neighbor with ice must have been iCell + ! kill both cells + calvingThickness(iCell) = calvingThickness(iCell) + thickness(iCell) + thickness(iCell) = 0.0_RKIND + calvingThickness(neighborWithIce) = calvingThickness(neighborWithIce) + thickness(neighborWithIce) + thickness(neighborWithIce) = 0.0_RKIND + endif + + endif ! check on nIceNeighbors + + endif ! check if iCell has ice + end do ! loop over cells + + end subroutine remove_small_islands + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! @@ -905,7 +1026,7 @@ subroutine topographic_calving(domain, calvingFraction, err) ! local variables !----------------------------------------------------------------- type (block_type), pointer :: block - type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: geometryPool, meshPool real (kind=RKIND), dimension(:), pointer :: calvingThickness ! thickness of ice that calves (computed in this subroutine) real(kind=RKIND), pointer :: config_calving_topography real(kind=RKIND), pointer :: config_sea_level @@ -929,6 +1050,7 @@ subroutine topographic_calving(domain, calvingFraction, err) ! get pools call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) @@ -943,6 +1065,8 @@ subroutine topographic_calving(domain, calvingFraction, err) ! === apply calving === thickness(:) = thickness(:) - calvingThickness(:) + call remove_small_islands(meshPool, geometryPool) + block => block % next enddo @@ -991,36 +1115,39 @@ subroutine eigencalving(domain, err) real(kind=RKIND), pointer :: config_calving_eigencalving_parameter_scalar_value character (len=StrKIND), pointer :: config_calving_eigencalving_parameter_source logical, pointer :: config_print_calving_info - real (kind=RKIND), pointer :: config_sea_level real(kind=RKIND), pointer :: config_calving_thickness - real (kind=RKIND), dimension(:), pointer :: thickness - real (kind=RKIND), dimension(:), pointer :: bedTopography real (kind=RKIND), dimension(:), pointer :: eigencalvingParameter - real (kind=RKIND), dimension(:), pointer :: calvingThickness - real (kind=RKIND), dimension(:), pointer :: requiredCalvingVolumeRate real (kind=RKIND), dimension(:), pointer :: calvingVelocity real (kind=RKIND), dimension(:), pointer :: eMax, eMin + real (kind=RKIND), dimension(:), pointer :: angleEdge + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: calvingThickness + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell integer, dimension(:), pointer :: cellMask - type (field1dInteger), pointer :: calvingFrontMaskField integer, dimension(:), pointer :: calvingFrontMask real (kind=RKIND), pointer :: deltat !< time step (s) - integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell - integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell - integer, dimension(:,:), pointer :: edgesOnCell real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: areaCell integer, pointer :: nCells integer :: iCell, jCell, iNeighbor - real(kind=RKIND) :: cellCalvingFrontLength, cellCalvingFrontHeight logical :: dynamicNeighbor + real(kind=RKIND) :: calvingSubtotal integer :: err_tmp + logical :: applyToGrounded, applyToFloating, applyToGroundingLine err = 0 + ! Logical arrays needed for apply_ablation_velocity + applyToGrounded = .true. + applyToFloating = .true. + applyToGroundingLine = .false. + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) call mpas_pool_get_config(liConfigs, 'config_calving_eigencalving_parameter_scalar_value', & config_calving_eigencalving_parameter_scalar_value) - call mpas_pool_get_config(liConfigs, 'config_calving_eigencalving_parameter_source', config_calving_eigencalving_parameter_source) - call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_calving_eigencalving_parameter_source', & + config_calving_eigencalving_parameter_source) call mpas_pool_get_config(liConfigs, 'config_calving_thickness', config_calving_thickness) ! block loop @@ -1033,26 +1160,24 @@ subroutine eigencalving(domain, err) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - ! get fields + ! get dimensions call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + ! get fields call mpas_pool_get_array(meshPool, 'deltat', deltat) - call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) - call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) - call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) - call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) - call mpas_pool_get_array(geometryPool, 'thickness', thickness) - call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'calvingFrontMask', calvingFrontMask) call mpas_pool_get_array(geometryPool, 'eigencalvingParameter', eigencalvingParameter) - call mpas_pool_get_array(geometryPool, 'requiredCalvingVolumeRate', requiredCalvingVolumeRate) call mpas_pool_get_array(geometryPool, 'calvingVelocity', calvingVelocity) call mpas_pool_get_array(velocityPool, 'eMax', eMax) call mpas_pool_get_array(velocityPool, 'eMin', eMin) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) - call mpas_pool_get_field(scratchPool, 'iceCellMask2', calvingFrontMaskField) - call mpas_allocate_scratch_field(calvingFrontMaskField, .true.) - calvingFrontMask => calvingFrontMaskField % array ! get parameter value if (trim(config_calving_eigencalving_parameter_source) == 'scalar') then @@ -1070,38 +1195,28 @@ subroutine eigencalving(domain, err) realArgs=(/minval(eigencalvingParameter), maxval(eigencalvingParameter)/)) endif - ! make mask for effective calving front. - ! This is last dynamic cell, but also make sure it has a neighbor that is open ocean or thin floating ice. - call calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) calvingVelocity(:) = 0.0_RKIND - requiredCalvingVolumeRate(:) = 0.0_RKIND ! First calculate the front retreat rate (Levermann eq. 1) - calvingVelocity(:) = eigencalvingParameter(:) * max(0.0_RKIND, eMax(:)) * max(0.0_RKIND, eMin(:)) & ! m/s - * real(li_mask_is_floating_ice_int(cellMask(:)), kind=RKIND) ! calculate only for floating ice - map of "potential" calving rate - do iCell = 1, nCells - if (calvingFrontMask(iCell) == 1) then - - ! convert to a volume flux per cell masking some assumptions - ! get front length from edge lengths abutting ocean or thin ice - ! get front height from max thickness of neighbors (since thickness near the edge could be screwy) - cellCalvingFrontLength = 0.0_RKIND - cellCalvingFrontHeight = 0.0_RKIND - do iNeighbor = 1, nEdgesOnCell(iCell) - jCell = cellsOnCell(iNeighbor, iCell) - if ( (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) .or. & ! thin ice - (.not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level) ) then ! open ocean - cellCalvingFrontLength = cellCalvingFrontLength + dvEdge(edgesOnCell(iNeighbor, iCell)) - endif - cellCalvingFrontHeight = max(cellCalvingFrontHeight, thickness(jCell)) - enddo - - requiredCalvingVolumeRate(iCell) = calvingVelocity(iCell) * cellCalvingFrontLength * cellCalvingFrontHeight ! m^3/s - endif - enddo - - call distribute_calving_flux(meshPool, geometryPool, scratchPool, calvingFrontMask, err_tmp) - err = ior(err, err_tmp) + calvingVelocity(:) = eigencalvingParameter(:) * max(0.0_RKIND, eMax(:)) * max(0.0_RKIND, eMin(:)) ! m/s + + call mpas_log_write("calling li_apply_front_ablation_velocity from eigencalving") + ! Convert calvingVelocity to calvingThickness + call li_apply_front_ablation_velocity(meshPool, geometryPool, velocityPool, & + calvingThickness, calvingVelocity, applyToGrounded, & + applyToFloating, applyToGroundingLine, domain, err) + ! Update halos on calvingThickness or faceMeltingThickness before + ! applying it. + ! Testing seemed to indicate this is not necessary, but I don't + ! understand + ! why not, so leaving it. + ! NOTE: THIS WILL NOT WORK ON MULTIPLE BLOCKS PER PROCESSOR + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'calvingThickness') + call mpas_timer_stop("halo updates") ! === apply calving === thickness(:) = thickness(:) - calvingThickness(:) @@ -1109,12 +1224,11 @@ subroutine eigencalving(domain, err) ! update mask call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) err = ior(err, err_tmp) - call calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) - ! Now also remove thin floating, dynamic ice (based on chosen thickness threshold) after mask is updated. - !where ((li_mask_is_floating_ice(cellMask) .and. li_mask_is_dynamic_ice(cellMask) .and. thickness block % next enddo @@ -1160,31 +1280,26 @@ end subroutine eigencalving !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! routine distribute_calving_flux +! routine specified_calving_velocity ! -!> \brief Apply a specified calving flux along the calving front +!> \brief use a specified calving velocity given by input data !> \author Matthew Hoffman !> \date Feb. 2018 -!> \details Applies a specified calving flux along the calving front. -!> The calving volume needs to be distributed in three ways: -!> 1. We need to first remove any "thin" ice in front of this cell -!> 2. Then we remove ice from this cell -!> 3. If there is still additional ice to be removed, we have to recursively -!> remove ice "inland" of this cell until the total required mass is removed +!> \details we can specify the calving velocity by i) a constant value +!> given by config_calving_velocity_const in namelist and ii) source data +!> specified by calvingVelocityData !----------------------------------------------------------------------- - subroutine distribute_calving_flux(meshPool, geometryPool, scratchPool, calvingFrontMask, err) + subroutine specified_calving_velocity(domain, err) !----------------------------------------------------------------- ! input variables !----------------------------------------------------------------- - type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh pool - type (mpas_pool_type), pointer, intent(in) :: scratchPool !< Input: scratch pool - integer, dimension(:), intent(in) :: calvingFrontMask !----------------------------------------------------------------- ! input/output variables !----------------------------------------------------------------- - type (mpas_pool_type), pointer, intent(inout) :: geometryPool !< Input: geometry pool + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object !----------------------------------------------------------------- ! output variables @@ -1194,164 +1309,1879 @@ subroutine distribute_calving_flux(meshPool, geometryPool, scratchPool, calvingF !----------------------------------------------------------------- ! local variables !----------------------------------------------------------------- -! logical, pointer :: config_print_calving_info - real (kind=RKIND), dimension(:), pointer :: requiredCalvingVolumeRate !< Input: the specified calving flux at calving front margin cells - real (kind=RKIND), dimension(:), pointer :: calvingThickness !< Output: the applied calving rate as a thickness + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + logical, pointer :: config_print_calving_info + real(kind=RKIND), pointer :: config_calving_thickness + real(kind=RKIND), pointer :: config_calving_velocity_const + character (len=StrKIND), pointer :: config_calving_specified_source + real (kind=RKIND), dimension(:), pointer :: calvingVelocity + real (kind=RKIND), dimension(:), pointer :: calvingVelocityData + real (kind=RKIND), dimension(:), pointer :: angleEdge real (kind=RKIND), dimension(:), pointer :: thickness - real (kind=RKIND), dimension(:), pointer :: areaCell - integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + real (kind=RKIND), dimension(:), pointer :: calvingThickness integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: calvingFrontMask real (kind=RKIND), pointer :: deltat !< time step (s) + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: areaCell integer, pointer :: nCells - integer :: iCell, iNeighbor, jCell - real(kind=RKIND) :: volumeLeft - real(kind=RKIND) :: removeVolumeHere - type (field1dReal), pointer :: cellVolumeField - real(kind=RKIND), dimension(:), pointer :: cellVolume - real(kind=RKIND), dimension(:), pointer :: uncalvedVolume - integer :: inwardNeighbors - integer :: uncalvedCount - real(kind=RKIND) :: uncalvedTotal + integer :: iCell, jCell, iNeighbor + logical :: dynamicNeighbor + real(kind=RKIND) :: calvingSubtotal + integer :: err_tmp err = 0 - ! get fields - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_array(meshPool, 'deltat', deltat) - call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) - call mpas_pool_get_array(geometryPool, 'thickness', thickness) - call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) - call mpas_pool_get_array(geometryPool, 'requiredCalvingVolumeRate', requiredCalvingVolumeRate) - call mpas_pool_get_array(geometryPool, 'uncalvedVolume', uncalvedVolume) - call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) - call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) - call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_calving_thickness', config_calving_thickness) + call mpas_pool_get_config(liConfigs, 'config_calving_velocity_const', config_calving_velocity_const) + call mpas_pool_get_config(liConfigs, 'config_calving_specified_source', config_calving_specified_source) - call mpas_pool_get_field(scratchPool, 'workCell', cellVolumeField) - call mpas_allocate_scratch_field(cellVolumeField, .true.) - cellVolume => cellVolumeField % array + ! block loop + block => domain % blocklist + do while (associated(block)) - calvingThickness(:) = 0.0_RKIND - uncalvedVolume(:) = 0.0_RKIND + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - cellVolume(:) = areaCell(:) * thickness(:) + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - ! The calving volume needs to be distributed in three ways: - ! 1. We need to first remove any "thin" ice in front of this cell - ! 2. Then we remove ice from this cell - ! 3. If there is still additional ice to be removed, we have to recursively - ! remove ice "inland" of this cell until the total required mass is removed - do iCell = 1, nCells - if (calvingFrontMask(iCell) == 1) then - volumeLeft = requiredCalvingVolumeRate(iCell) * deltat ! units m^3 + ! get fields + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'calvingFrontMask', calvingFrontMask) + call mpas_pool_get_array(geometryPool, 'calvingVelocity', calvingVelocity) + call mpas_pool_get_array(geometryPool, 'calvingVelocityData', calvingVelocityData) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) - ! First remove ice from "thin" neighbors - do iNeighbor = 1, nEdgesOnCell(iCell) - jCell = cellsOnCell(iNeighbor, iCell) - if (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) then - ! this is a thin neighbor - remove as much ice from here as we can TODO: could distribute this evenly amongst neighbors - removeVolumeHere = min(volumeLeft, cellVolume(jCell)) ! how much we want to remove here - calvingThickness(jCell) = calvingThickness(jCell) + removeVolumeHere / areaCell(jCell) ! apply to the field that will be used, in thickness units - cellVolume(jCell) = cellVolume(jCell) - removeVolumeHere ! update accounting on cell volume - volumeLeft = volumeLeft - removeVolumeHere ! update accounting on how much left to distribute from current iCell - endif - enddo - if (volumeLeft > 0.0_RKIND) then - ! Now remove ice from iCell - removeVolumeHere = min(volumeLeft, cellVolume(iCell)) - calvingThickness(iCell) = calvingThickness(iCell) + removeVolumeHere / areaCell(iCell) ! apply to the field that will be used in thickness units - cellVolume(iCell) = cellVolume(iCell) - removeVolumeHere ! update accounting on cell volume - volumeLeft = volumeLeft - removeVolumeHere ! update accounting on how much left to distribute from current iCell - endif + ! get parameter value + if (trim(config_calving_specified_source) == 'const') then + calvingVelocity = config_calving_velocity_const + elseif (trim(config_calving_specified_source) == 'data') then + calvingVelocity = calvingVelocityData + else + err = ior(err, 1) + call mpas_log_write("Invalid value specified for option config_calving_specified_source" // & + config_calving_specified_source, MPAS_LOG_ERR) + endif - if (volumeLeft > 0.0_RKIND) then - ! Now remove ice from neighbors inward on shelf - ! first count up how many there are - inwardNeighbors = 0 - do iNeighbor = 1, nEdgesOnCell(iCell) - jCell = cellsOnCell(iNeighbor, iCell) - if (li_mask_is_floating_ice(cellMask(jCell)) .and. li_mask_is_dynamic_ice(cellMask(jCell)) & - .and. (.not. li_mask_is_dynamic_margin(jCell))) then - inwardNeighbors = inwardNeighbors + 1 - endif - enddo - ! Now distribute the flux evenly amongst the neighbors - do iNeighbor = 1, nEdgesOnCell(iCell) - jCell = cellsOnCell(iNeighbor, iCell) - if (li_mask_is_floating_ice(cellMask(jCell)) .and. li_mask_is_dynamic_ice(cellMask(jCell)) & - .and. (.not. li_mask_is_dynamic_margin(jCell))) then - ! this is thick neighbor that is not itself a margin - remove as much ice from here as we can - removeVolumeHere = min(volumeLeft / real(inwardNeighbors, kind=RKIND), cellVolume(jCell)) ! how much we want to remove here - calvingThickness(jCell) = calvingThickness(jCell) + removeVolumeHere / areaCell(jCell) ! apply to the field that will be used in thickness units - cellVolume(jCell) = cellVolume(jCell) - removeVolumeHere ! update accounting on cell volume - volumeLeft = volumeLeft - removeVolumeHere ! update accounting on how much left to distribute from current iCell - endif - enddo - !TODO: need to recursively distribute across neighbors until fully depleted :( - endif + if (config_print_calving_info) then + call mpas_log_write("calvingVelocity(m s) value range on this processor: Min=$r, Max=$r", & + realArgs=(/minval(calvingVelocity), maxval(calvingVelocity)/)) + endif - ! If we didn't calve enough ice, record that to allow assessment of how bad that is. - if (volumeLeft > 0.0_RKIND) then - uncalvedVolume(iCell) = 0.0_RKIND - endif + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) - endif ! if cell is calving margin - enddo ! cell loop + ! Convert calvingVelocity to calvingThickness + call li_apply_front_ablation_velocity(meshPool, geometryPool, velocityPool, calvingThickness, calvingVelocity, & + applyToGrounded=.true., applyToFloating=.true., applyToGroundingLine=.false., & + domain=domain, err=err_tmp) + err = ior(err, err_tmp) - if (maxval(uncalvedVolume) > 0.0_RKIND) then + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) - uncalvedTotal = sum(uncalvedVolume) - uncalvedCount = count(uncalvedVolume > 0.0_RKIND) + ! Now also remove thin floating, dynamic ice (based on chosen thickness threshold) after mask is updated. - call mpas_log_write("distribute_calving_flux failed to distribute all required ice - ice was left after depleting all neighbors." & - // " Search needs to be expanded to neighbors' neighbors.") - call mpas_log_write(" On this processor: $i cells contain uncalved ice, for a total uncalved volume of $r m^3 ($r%).", & - MPAS_LOG_WARN, intArgs=(/uncalvedCount/), & - realArgs=(/uncalvedTotal, 100.0_RKIND * uncalvedTotal/(requiredCalvingVolumeRate(iCell) * deltat)/)) - endif + !where ((li_mask_is_floating_ice(cellMask) .and. li_mask_is_dynamic_ice(cellMask) .and. & + ! thickness block % next + enddo - end subroutine distribute_calving_flux + end subroutine specified_calving_velocity !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! routine calculate_calving_front_mask +! routine von_Mises_calving ! -!> \brief Calculate mask indicating position of effective calving front -!> \author Matthew Hoffman -!> \date Feb. 2018 -!> \details Mmake mask for effective calving front. -!> This is last dynamic floating cell, but also make sure it has a neighbor that is open ocean or thin floating ice. +!> \brief Apply calving law based on von Mises stress to grounded marine margins +!> \author Trevor Hillebrand +!> \date April 2020 +!> \details Uses the calving law described by Morlighem et al. (2016): +!> "Modeling of Store Gletscher’s calving dynamics, West Greenland, in response +!> to ocean thermal forcing" to calculate calvingVelocity at grounded tidewater +!glacier margins !----------------------------------------------------------------------- - subroutine calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + subroutine von_Mises_calving(domain, err) + + use li_diagnostic_vars !----------------------------------------------------------------- ! input variables !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh pool - type (mpas_pool_type), intent(in) :: geometryPool !< Input: geometry pool !----------------------------------------------------------------- ! input/output variables !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object !----------------------------------------------------------------- ! output variables !----------------------------------------------------------------- - integer, dimension(:) :: calvingFrontMask !< Output: calving front mask + integer, intent(out) :: err !< Output: error flag !----------------------------------------------------------------- ! local variables !----------------------------------------------------------------- + integer :: iCell, jCell, iNeighbor, nGroundedNeighbors, err_tmp + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnCell + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool, meshPool, & + velocityPool, scratchPool, thermalPool + real (kind=RKIND), pointer :: config_grounded_von_Mises_threshold_stress, & + config_floating_von_Mises_threshold_stress, & + config_flowLawExponent + logical, pointer :: config_use_Albany_flowA_eqn_for_vM + real (kind=RKIND), dimension(:), pointer :: eMax, eMin, & + calvingVelocity, thickness, & + xvelmean, yvelmean, calvingThickness + real (kind=RKIND), dimension(:,:), pointer :: flowParamA, & + temperature, layerThickness + real (kind=RKIND), pointer :: config_default_flowParamA integer, pointer :: nCells - integer :: iCell, iNeighbor, jCell, jNeighbor, kCell - logical :: oceanNeighbor - integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), dimension(:), pointer :: vonMisesStress + logical :: applyToGrounded, applyToFloating, applyToGroundingLine + + err = 0 + + ! Logical arrays needed for li_apply_front_ablation_velocity + applyToGrounded = .true. + applyToFloating = .true. + applyToGroundingLine = .false. + + call mpas_pool_get_config(liConfigs, 'config_grounded_von_Mises_threshold_stress', & + config_grounded_von_Mises_threshold_stress) + call mpas_pool_get_config(liConfigs, 'config_floating_von_Mises_threshold_stress', & + config_floating_von_Mises_threshold_stress) + + if ( config_grounded_von_Mises_threshold_stress <= 0.0_RKIND ) then + call mpas_log_write("config_grounded_von_Mises_threshold_stress must be >0.0", MPAS_LOG_ERR) + err = 1 + endif + !call mpas_pool_get_config(liConfigs, 'config_default_flowParamA', + !config_default_flowParamA) ! REMOVE THIS ONCE YOU CAN GET A FROM + !ALBANY!!!!! + call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', config_flowLawExponent) + call mpas_pool_get_config(liConfigs, 'config_use_Albany_flowA_eqn_for_vM', config_use_Albany_flowA_eqn_for_vM) + + block => domain % blocklist + + do while (associated(block)) + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + ! get fields + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness) + call mpas_pool_get_array(velocityPool, 'eMax', eMax) + call mpas_pool_get_array(velocityPool, 'eMin', eMin) + call mpas_pool_get_array(velocityPool, 'vonMisesStress', vonMisesStress) + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + call mpas_pool_get_array(velocityPool, 'xvelmean', xvelmean) + call mpas_pool_get_array(velocityPool, 'yvelmean', yvelmean) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'calvingVelocity', calvingVelocity) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + + vonMisesStress(:) = 0.0_RKIND + + ! get flowParamA from MPAS or use Albany-like equation + if ( config_use_Albany_flowA_eqn_for_vM ) then + !calculate Albany-type flowParamA + call mpas_log_write("config_use_Albany_flowA_eqn_for_vM not yet supported", MPAS_LOG_ERR) + err = 1 + else + call li_calculate_flowParamA(meshPool, temperature, thickness,flowParamA,err) ! Get MPAS flowParamA + endif + + !Using a depth-averaged ice viscosity parameter B_depthAvg + !=sum(layerThickness(:,iCell) * + !flowParamA(:,iCell)**(-1.0_RKIND/config_flowLawExponent), dim=1) / + !thickness(iCell) + ! Calculate effective von Mises stress. + calvingVelocity(:) = 0.0_RKIND + + do iCell = 1,nCells + if ( thickness(iCell) > 0.0_RKIND ) then + vonMisesStress(iCell) = sqrt(3.0_RKIND) * sum(layerThickness(:,iCell) * & + flowParamA(:,iCell)**(-1.0_RKIND/config_flowLawExponent),dim=1) / & + thickness(iCell) * ( 0.5_RKIND * ( (max(0.0_RKIND, eMax(iCell)))**2.0_RKIND + & + (max(0.0_RKIND, eMin(iCell)))**2.0_RKIND) )**(1.0_RKIND / (2.0_RKIND * config_flowLawExponent)) + endif + + ! Calculate calving velocity for grounded cells at marine margin + if ( .not. li_mask_is_floating_ice(cellMask(iCell)) ) then + calvingVelocity(iCell) = sqrt(xvelmean(iCell)**2.0_RKIND + yvelmean(iCell)**2.0_RKIND) * & + vonMisesStress(iCell) / config_grounded_von_Mises_threshold_stress + ! If config_floating_von_Mises_threshold_stress is not 0.0, calculate + ! calvingVelocity. If config_floating_von_Mises_threshold_stress is + ! 0.0, remove floating ice in loop below. + elseif (li_mask_is_floating_ice(cellMask(iCell)) .and. config_floating_von_Mises_threshold_stress .ne. 0.0_RKIND) then + calvingVelocity(iCell) = sqrt(xvelmean(iCell)**2 + yvelmean(iCell)**2) * & + vonMisesStress(iCell) / config_floating_von_Mises_threshold_stress + endif + enddo + + call mpas_log_write("calling li_apply_front_ablation_velocity from von Mises stress calving routine") + ! Convert calvingVelocity to calvingThickness + call li_apply_front_ablation_velocity(meshPool, geometryPool,velocityPool, & + calvingThickness, calvingVelocity, applyToGrounded, & + applyToFloating, applyToGroundingLine, domain, err) + ! Update halos on calvingThickness or faceMeltingThickness before + ! applying it. + ! Testing seemed to indicate this is not necessary, but I don't + ! understand + ! why not, so leaving it. + ! NOTE: THIS WILL NOT WORK ON MULTIPLE BLOCKS PER PROCESSOR + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'calvingThickness') + call mpas_timer_stop("halo updates") + + ! If floating VM threshold is zero, remove floating ice. Remove + ! non-dynamic cells adjacent to floating ice, but leave + ! non-dynamic cells adjacent to grounded ice. + ! TODO: Make an exception for the case of floating ice surrounded by grounded ice + ! (i.e., floating on a subglacial lake) using a flood fill routine + if ( config_floating_von_Mises_threshold_stress .eq. 0.0_RKIND ) then + do iCell = 1,nCells + if ( li_mask_is_floating_ice(cellMask(iCell)) .and. & + li_mask_is_dynamic_ice(cellMask(iCell)) ) then + calvingThickness(iCell) = thickness(iCell) + elseif ( li_mask_is_floating_ice(cellMask(icell)) .and. & + (.not. li_mask_is_dynamic_ice(cellMask(iCell))) ) then + nGroundedNeighbors = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if ( li_mask_is_grounded_ice(cellMask(jCell)) ) then + nGroundedNeighbors = nGroundedNeighbors + 1 + endif + enddo + if ( nGroundedNeighbors == 0 ) then + calvingThickness(iCell) = thickness(iCell) + endif + endif + enddo + endif + + + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + call remove_small_islands(meshPool, geometryPool) + + block => block % next + + enddo ! associated(block) + + end subroutine von_Mises_calving + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine li_apply_front_ablation_velocity +! +!> \brief Convert a calving or melting velocity to an ice thickness removal +!> \author Matthew Hoffman and Trevor Hillebrand +!> \date March 2020 +!> \details This routine takes a calving or front melting velocity and converts it to a +!> thickness removal. The basic idea is to multiply the calving velocity +!> by the edge length and height to get a volume flux. However, the details +!> are complicated by the unstructured Voronoi mesh and the use of partially-filled +!> non-dynamic floating grid cells. +!> The algorithm calculates the required calving volume flux based on edges, and then +!> applies the volume removal to grid cells, converting that to a thickness removal using areaCell. +!> This process occurs in phases. +!> +!> The first phase handles non-dynamic floating cells. This is done by calculating the required calving flux +!> at the edges between those non-dynamic floating cells and the open ocean. +!> The calving velocity and ice thickness are copied from the upstream dynamic +!> floating cells, because these quantities do not have valid values on the non-dynamic cells. +!> After calculating the calving flux determined at each of these edges, the non-dyamic floating cells +!> are looped over, and volume is marked for removal based on the calving flux on the edges of each cell. +!> +!> The final phase handles dynamic floating cells that are eligible for calving. +!> There are two ways this can happen - either they are adjacent to the open ocean (no buffer of +!> non-dynamic cells present), or they are adjacent to non-dynamic floating cells that had their +!> entire volume removed by calving, but there still is calving demand remaining. +!> The first case is handled easily by calculating calving flux on edges between dynamic floating cells +!> and open ocean using the calving velocity and ice thickness of the dynamic cell itself. +!> In the second case, "leftover" required calving flux is distributed from a non-dynamic cell that +!> has been completely "drained" to any edges between that non-dynamic cell and dynamic cells upstream. +!> This is done by weighting the flux to be distrbuted by the length of each such edge in the direction +!> perpendicular to the calving flux. +!> +!> The location at which ablation is controlled by applyToGrounded, applyToFloating, and applyToGroundingLine, +!> which are set by the calling routine. +!> If applyToGrounded = .true., ablation is applied to grounded marine margin cells, which do not have dynamic ice shelf neighbors +!> If applyToGroundingLine = .true., ablation is applied to grounding line, regardless of whether there is an ice shelf or not. +!> If there in dynamic floating ice, ablation is applied to the floating cell adjacent to grounding line; +!> otherwise it is applied to the last grounded cell. +!> If applyToFloating = .true., ablation is applied to dynamic floating margin cells +!> The output of this routine is calvingThickness or faceMeltingThickness, which then needs to be applied to thickness +!> by the calling routine. The calling routine should perform a halo update +!> after the call and before applying ablation. +!----------------------------------------------------------------------- + + subroutine li_apply_front_ablation_velocity(meshPool, geometryPool, velocityPool, ablationThickness, ablationVelocity, & + applyToGrounded, applyToFloating, applyToGroundingLine, domain, err) + + use ieee_arithmetic, only : ieee_is_nan + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh pool + type (mpas_pool_type), pointer, intent(in) :: velocityPool !< Input: velocity pool + logical, intent(in) :: applyToFloating, applyToGrounded, & + applyToGroundingLine + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input: domain object + type (mpas_pool_type), pointer, intent(inout) :: geometryPool !< Input: geometry pool + real (kind=RKIND), dimension(:), intent(inout) :: ablationVelocity + real (kind=RKIND), dimension(:), intent(out) :: ablationThickness + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + integer, pointer :: nEdges, nCells, nCellsSolve, maxEdges + integer :: iEdge, iCell, jCell, kCell, iNeighbor, jNeighbor + integer :: nEmptyNeighbors, nGroundedNeighbors, counter, nTwoCellsBack, nOneCellBack + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: lowerSurface + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:,:), pointer :: edgesOnCell + integer, dimension(:), pointer :: cellMask, edgeMask + integer, dimension(:), pointer :: frontAblationMask + real (kind=RKIND), dimension(:), pointer :: calvingThickness, faceMeltingThickness + real (kind=RKIND), pointer :: config_sea_level + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: angleEdge + real (kind=RKIND), dimension(:), pointer :: calvingVelocity, faceMeltSpeed + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:,:), pointer :: uReconstructX + real (kind=RKIND), dimension(:,:), pointer :: uReconstructY + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + real (kind=RKIND), pointer :: deltat !< time step (s) + real (kind=RKIND), dimension(:), allocatable :: thicknessForAblation + real (kind=RKIND), dimension(:), allocatable :: uvelForAblation, vvelForAblation + integer, dimension(:), allocatable :: oneCellBackList, twoCellsBackList + real (kind=RKIND), dimension(:), pointer :: requiredAblationVolumeNonDynEdge, requiredAblationVolumeNonDynCell + real (kind=RKIND), dimension(:), pointer :: requiredAblationVolumeDynEdge, requiredAblationVolumeDynCell + real (kind=RKIND), dimension(:), pointer :: ablatedVolumeNonDynCell, ablatedVolumeDynCell + real (kind=RKIND), dimension(:), pointer :: unablatedVolumeNonDynCell, unablatedVolumeDynCell + real (kind=RKIND), dimension(:), allocatable :: cellVolume + real(kind=RKIND) :: ablationSubtotal1, ablationSubtotal2, ablationSubtotal3 + real(kind=RKIND) :: thkSum, ablationVelSum, uvelSum, vvelSum + integer :: thkCount + real(kind=RKIND) :: removeVolumeHere + real(kind=RKIND) :: volumeAvailableToPass !< a temp. var. for accounting purposes that indicates how much volume + !< to be calved is 'leftover' after trying to calve non-dynamic cells and can be transferred to + !< neighboring dynamic cells + real(kind=RKIND) :: ablateLengthCell, ablateLengthEdge + real(kind=RKIND), dimension(6) :: localInfo, globalInfo + real(kind=RKIND) :: edgeLengthScaling + real(kind=RKIND), parameter :: ablationSmallThk = 1.0e-8_RKIND ! in meters, a small thickness threshold + integer :: err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) + call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(geometryPool, 'calvingVelocity', calvingVelocity) + call mpas_pool_get_array(geometryPool, 'faceMeltSpeed', faceMeltSpeed) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'faceMeltingThickness', faceMeltingThickness) + call mpas_pool_get_array(geometryPool, 'unablatedVolumeNonDynCell', unablatedVolumeNonDynCell) + call mpas_pool_get_array(geometryPool, 'unablatedVolumeDynCell', unablatedVolumeDynCell) + call mpas_pool_get_array(geometryPool, 'ablatedVolumeNonDynCell', ablatedVolumeNonDynCell) + call mpas_pool_get_array(geometryPool, 'ablatedVolumeDynCell', ablatedVolumeDynCell) + call mpas_pool_get_array(geometryPool, 'frontAblationMask', frontAblationMask) + call mpas_pool_get_array(geometryPool, 'requiredAblationVolumeNonDynEdge', requiredAblationVolumeNonDynEdge) + call mpas_pool_get_array(geometryPool, 'requiredAblationVolumeDynEdge', requiredAblationVolumeDynEdge) + call mpas_pool_get_array(geometryPool, 'requiredAblationVolumeNonDynCell', requiredAblationVolumeNonDynCell) + call mpas_pool_get_array(geometryPool, 'requiredAblationVolumeDynCell', requiredAblationVolumeDynCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + + + ! Step 0: Define the frontAblationMask that will be used to determine where ablation should be applied. + ! This mask can be for 1. a floating calving front, 2. a grounded marine margin (no dynamic floating ice shelf attached), + ! 3. along the grounding line (whether or not there is floating ice adjacent), or any combination of the three. + ! The mask includes both the dynamic AND non-dynamic cells along the prescribed interface type(s). + frontAblationMask(:) = 0 + + ! First identify the dynamic cells for each mask type. + + if ( applyToGrounded .or. applyToGroundingLine ) then + do iCell = 1, nCells + ! If applyToGrounded: Define marine marginal cells as those that are (1) at the ice + ! margin, (2) have at least one neighboring cell without ice, (3) contain + ! grounded ice, and (4) have bed topography below sea level. + ! OR is adjacent to an inactive floating margin cell + if (li_mask_is_grounding_line(cellMask(iCell)) & + !< GL here means cell is grounded but has a neighbor that is floating or ocean + .and. bedTopography(iCell) < config_sea_level & + .and. li_mask_is_dynamic_ice(cellMask(iCell)) ) then + + ! Check if neighboring cells contain ice and have bed topo below sea level + nEmptyNeighbors = 0 + do iEdge = 1, nEdgesOnCell(iCell) + iNeighbor = cellsOnCell(iEdge, iCell) + if ( (((thickness(iNeighbor) == 0.0_RKIND) .and. bedTopography(iNeighbor) < config_sea_level)) & + !< these previous conditions are open ocean + .or. & + !> these following conditions are inactive floating margin cells + ((li_mask_is_floating_ice(cellMask(iNeighbor)) & + .and. li_mask_is_margin(cellMask(iNeighbor)) & + .and. (.not. li_mask_is_dynamic_ice(cellMask(iNeighbor)))))) then + nEmptyNeighbors = nEmptyNeighbors + 1 + endif + enddo + if (nEmptyNeighbors > 0) then + frontAblationMask(iCell) = 1 + endif ! nEmptyNeighbors + endif ! cell is GL etc. + enddo ! nCells loop + endif ! applyToGrounded + + if ( applyToGroundingLine ) then + ! If applyToGroundingLine: Define marine marginal cells as those that + ! are at the margin (defined by li_mask_is_grounding_line) if no + ! floating ice or are the first floating cell if there is floating ice. + ! The case of no floating ice was handled above already, so only looking for + ! GL locations with floating ice here. + do iCell = 1,nCells + if ( li_mask_is_grounding_line(cellMask(iCell)) ) then + do iEdge = 1, nEdgesOnCell(iCell) + iNeighbor = cellsOnCell(iEdge, iCell) + if (li_mask_is_floating_ice(cellMask(iNeighbor)) .and. li_mask_is_dynamic_ice(cellMask(iNeighbor))) then + frontAblationMask(iNeighbor) = 1 + endif + enddo + endif + enddo + endif + + if ( applyToFloating ) then + do iCell = 1,nCells + ! If applyToFloating: marine marginal cells are dynamic floating margin cells + if ( li_mask_is_floating_ice(cellMask(iCell)) .and. & + li_mask_is_dynamic_margin(cellMask(iCell)) ) then + frontAblationMask(iCell) = 1 + endif + enddo + endif + + ! Now extend the mask forward to include non-dynamic floating neighbors (appropriate for any of the mask types) + ! Use value 2 to indicate the non-dynamic cells + do iCell = 1,nCells + if (frontAblationMask(iCell) == 1) then + do iEdge = 1, nEdgesOnCell(iCell) + iNeighbor = cellsOnCell(iEdge, iCell) + if ( li_mask_is_floating_ice(cellMask(iNeighbor)) .and. & + .not. li_mask_is_dynamic_ice(cellMask(iNeighbor)) ) then + frontAblationMask(iNeighbor) = 2 ! Use 2 for these non-dynamic floating neighbors + endif + enddo + endif + enddo + ! Make a second pass to include potentially 'stranded' nondynamic floating cells + do iCell = 1, nCells + if (frontAblationMask(iCell) == 2) then + ! Check if this non-dynamic masked cell has any non-dynamic, floating, but unmasked neighbors we need to include + do iEdge = 1, nEdgesOnCell(iCell) + iNeighbor = cellsOnCell(iEdge, iCell) + if ((.not. li_mask_is_dynamic_ice(cellMask(iNeighbor))) .and. li_mask_is_floating_ice(cellMask(iNeighbor)) & + .and. frontAblationMask(iNeighbor) == 0) then + frontAblationMask(iNeighbor) = 3 ! Use 3 for these extended non-dynamic neighbors + endif + enddo + endif + enddo + + ! Init fields for accounting + ablationThickness(:) = 0.0_RKIND + ablatedVolumeNonDynCell(:) = 0.0_RKIND + ablatedVolumeDynCell(:) = 0.0_RKIND + unablatedVolumeNonDynCell(:) = 0.0_RKIND + unablatedVolumeDynCell(:) = 0.0_RKIND + allocate(cellVolume(nCells+1)) + cellVolume(:) = areaCell(:) * thickness(:) + requiredAblationVolumeNonDynEdge(:) = 0.0_RKIND + requiredAblationVolumeNonDynCell(:) = 0.0_RKIND + requiredAblationVolumeDynEdge(:) = 0.0_RKIND + requiredAblationVolumeDynCell(:) = 0.0_RKIND + allocate(thicknessForAblation(nCells+1)) + thicknessForAblation = thickness + allocate(uvelForAblation(nCells+1)) + uvelForAblation(:) = uReconstructX(1,:) + allocate(vvelForAblation(nCells+1)) + vvelForAblation(:) = uReconstructY(1,:) + allocate(oneCellBackList(maxEdges+1)) + oneCellBackList(:) = 0 + allocate(twoCellsBackList((maxEdges+1)**2)) + twoCellsBackList(:) = 0 + + ! 1. Calculate ablation rate for all non-dynamic cells by working with their ocean-going edges + ablationSubtotal1 = 0.0_RKIND + do iCell = 1, nCells + if ((frontAblationMask(iCell)>0) .and. (.not. li_mask_is_dynamic_ice(cellMask(iCell))) ) then + ! a1. Translate the calving front height based on dynamic cells to the non-dynamic locations + ! find mean (or min?) of thickness in dynamic neighbors + ablationVelSum = 0.0_RKIND + thkSum = 0.0_RKIND + uvelSum = 0.0_RKIND + vvelSum = 0.0_RKIND + thkCount = 0 + nGroundedNeighbors = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if ( li_mask_is_dynamic_ice(cellMask(jCell)) .and. li_mask_is_floating_ice(cellMask(jCell)) ) then + thkSum = thkSum + thicknessForAblation(jCell) + uvelSum = uvelSum + uvelForAblation(jCell) + vvelSum = vvelSum + vvelforAblation(jCell) + ablationVelSum = ablationVelSum + ablationVelocity(jCell) + thkCount = thkCount + 1 + else if ( li_mask_is_grounding_line(cellMask(jCell)) .or. li_mask_is_grounded_ice(cellMask(jCell)) ) then + nGroundedNeighbors = nGroundedNeighbors + 1 + endif + enddo + + ! if floating non-dynamic cell has *any* grounded neighbors, use ice thickness and + ! velocity from two cells back instead of the one-cell-back (neighboring) locations above + ! Note the two-cell back averaging is independent of whether those locations + ! are floating are grounded. + ! The method implemented here gives good results for Humboldt Glacier (realistic grounded margin), + ! but as of 3/16/21 it has not been tested at a realistic case that contains both grounded and floating + ! margins (e.g. an ice shelf in an embayment with grounded ice along the lateral margins). If unexpected + ! behavior occurs in such a case in the future, an alternative implementation here might work better: + ! Use 2-back values to populate 1-back locations that are grounded, and then use 1-back values to calculate + ! the value at each primary cell location. This would be a mix of true-1-back values for dynamic floating cells + ! and the corresponding 2-back values at grounded cells. + if ( nGroundedNeighbors > 0 ) then + thkSum = 0.0_RKIND + uvelSum = 0.0_RKIND + vvelSum = 0.0_RKIND + ablationVelSum = 0.0_RKIND + thkCount = 0 + nOneCellBack = 0 + oneCellBackList(:) = 0 + twoCellsBackList(:) = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if ( li_mask_is_ice(cellMask(jCell)) ) then + nOneCellBack = nOneCellBack + 1 + oneCellBackList(nOneCellBack) = jCell + endif + enddo + if ( nOneCellBack > 0 ) then + ! loop over oneCellBackList and add neighbors + counter = 0 + do iNeighbor = 1, nOneCellBack + jCell = oneCellBackList(iNeighbor) + ! create twoCellsBackList so we don't add one cell multiple + ! times + nTwoCellsBack = 0 + do jNeighbor = 1, nEdgesOnCell(jCell) + kCell = cellsOnCell(jNeighbor, jCell) + if ( li_mask_is_dynamic_ice(cellMask(kCell)) .and. & + (.not. any(oneCellBackList==kCell) ) .and. & + (.not. any(twoCellsBackList==kCell) ) ) then + counter = counter+1 + twoCellsBackList(counter) = kCell + endif + enddo + nTwoCellsBack = nTwoCellsBack + counter + enddo + ! loop through twoCellsBackList and average thickness and + ! velocities + do kCell = 1, nTwoCellsBack + thkSum = thkSum + thicknessForAblation(twoCellsBackList(kCell)) + uvelSum = uvelSum + uvelForAblation(twoCellsBackList(kCell)) + vvelSum = vvelSum + vvelforAblation(twoCellsBackList(kCell)) + ablationVelSum = ablationVelSum + ablationVelocity(twoCellsBackList(kCell)) + thkCount = thkCount + 1 + enddo + endif + endif + if (thkCount == 0) then + !call mpas_log_write("Found a stranded non-dynamic floating + !cell: cell $i with thickness=$r m.", MPAS_LOG_WARN, & + ! intArgs=(/iCell/), realArgs=(/thickness(iCell)/)) + else + thicknessForAblation(iCell) = thkSum / real(thkCount, kind=RKIND) + ablationVelocity(iCell) = ablationVelSum / real(thkCount, kind=RKIND) + uvelForAblation(iCell) = uvelSum / real(thkCount, kind=RKIND) + vvelForAblation(iCell) = vvelSum / real(thkCount, kind=RKIND) + endif + endif + enddo + + + ! a2.Set thicknessForAblation and ablationVelocity of stranded non-dynamic cells to + ! average of non-dynamic neighbors. Stranded non-dynamic cells are + ! identified as those with frontAblationMask == 3 + do iCell = 1, nCells + if ( frontAblationMask(iCell) == 3 ) then + ablationVelSum = 0.0_RKIND + thkSum = 0.0_RKIND + uvelSum = 0.0_RKIND + vvelSum = 0.0_RKIND + thkCount = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if ( li_mask_is_floating_ice(cellMask(jCell)) & + .and. (.not. li_mask_is_dynamic_ice(cellMask(jCell))) ) then + thkSum = thkSum + thicknessForAblation(jCell) + uvelSum = uvelSum + uvelForAblation(jCell) + vvelSum = vvelSum + vvelForAblation(jCell) + ablationVelSum = ablationVelSum + ablationVelocity(jCell) + thkCount = thkCount + 1 + endif + enddo + if (thkCount == 0) then + !call mpas_log_write("Found a stranded non-dynamic floating cell: cell $i with thickness=$r m.", MPAS_LOG_WARN, & + ! intArgs=(/iCell/), realArgs=(/thickness(iCell)/)) + else + thicknessForAblation(iCell) = thkSum / real(thkCount, kind=RKIND) + ablationVelocity(iCell) = ablationVelSum / real(thkCount, kind=RKIND) + uvelForAblation(iCell) = uvelSum / real(thkCount, kind=RKIND) + vvelForAblation(iCell) = vvelSum / real(thkCount, kind=RKIND) + endif + endif + enddo + + ! b. Translate the ablationVelocity and thickness from non-dynamic cells to their ocean-going edges + ! to calculate the calving volume on those edges and this cell + do iCell = 1, nCells + if (frontAblationMask(iCell)>0 .and. (.not. li_mask_is_dynamic_ice(cellMask(iCell)))) then + + do iNeighbor = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iNeighbor, iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_margin(edgeMask(iEdge)) .and. & !< edge is a margin + .not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level & ! ensure margin is w/ocn + ) then + edgeLengthScaling = scale_edge_length(angleEdge(iEdge), uvelForAblation(iCell), vvelForAblation(iCell)) + requiredAblationVolumeNonDynEdge(iEdge) = ablationVelocity(iCell) * & + edgeLengthScaling * dvEdge(iEdge) * thicknessForAblation(iCell) * deltat + requiredAblationVolumeNonDynCell(iCell) = requiredAblationVolumeNonDynCell(iCell) + & + requiredAblationVolumeNonDynEdge(iEdge) ! Keep running total + endif + enddo + + ! c. Apply ablationThickness here + removeVolumeHere = min(cellVolume(iCell), requiredAblationVolumeNonDynCell(iCell)) ! Don't use more than available + ablationThickness(iCell) = removeVolumeHere / areaCell(iCell) + ablatedVolumeNonDynCell(iCell) = removeVolumeHere + unablatedVolumeNonDynCell(iCell) = requiredAblationVolumeNonDynCell(iCell) - removeVolumeHere + cellVolume(iCell) = cellVolume(iCell) - removeVolumeHere + if (iCell <= nCellsSolve) ablationSubtotal1 = ablationSubtotal1 + removeVolumeHere + endif + enddo + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'unablatedVolumeNonDynCell') + call mpas_timer_stop("halo updates") + !call mpas_log_write("Done calculating calving for nondynamic floating cells. Removed $r m^3", realArgs=(/calvingSubtotal1/)) + + + + ! 2. Calculate calving for dynamic cells + ablationSubtotal2 = 0.0_RKIND + + ! 2a1. for grounded dynamic margins, don't use the thickness there because it may be unrealistically thin due to ablation + ! from previous time steps. Instead copy forward the thickness from the row cells behind. + do iCell = 1, nCells + if (frontAblationMask(iCell)>0 .and. li_mask_is_margin(cellMask(iCell)) .and. & + li_mask_is_grounded_ice(cellMask(iCell))) then + thkSum = 0.0_RKIND + thkCount = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_dynamic_ice(cellMask(jCell)) .and. li_mask_is_grounded_ice(cellMask(jCell)) .and. & + .not. li_mask_is_margin(cellMask(jCell))) then + thkSum = thkSum + thicknessForAblation(jCell) + thkCount = thkCount + 1 + endif + enddo + if (thkCount == 0) then + !call mpas_log_write("Found a stranded non-dynamic floating cell: cell $i with thickness=$r m.", MPAS_LOG_WARN, & + ! intArgs=(/iCell/), realArgs=(/thickness(iCell)/)) + else + thicknessForAblation(iCell) = thkSum / real(thkCount, kind=RKIND) + endif + endif + enddo + + ! a. Calculate calving on dynamic margin edges + do iCell = 1, nCells + if ( frontAblationMask(iCell)>0 .and. li_mask_is_dynamic_ice(cellMask(iCell)) & ! a dynamic cell in the mask... + .and. li_mask_is_margin(cellMask(iCell)) ) then ! that is at the edge of the ice + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if ((.not. li_mask_is_ice(cellMask(jCell))) .and. (bedTopography(jCell) <= config_sea_level)) then + iEdge = edgesOnCell(iNeighbor, iCell) + edgeLengthScaling = scale_edge_length(angleEdge(iEdge), uvelForAblation(iCell), vvelForAblation(iCell)) + requiredAblationVolumeDynEdge(iEdge) = ablationVelocity(iCell) * & + edgeLengthScaling * dvEdge(iEdge) * thicknessForAblation(iCell) * deltat + endif + enddo + endif + enddo + ! b. copy ablation remaining in non-dynamic cells to dynamic edges + ! Assume height and velocity are uniform, but edge length is not. + do iCell = 1, nCells + if ( (.not. li_mask_is_dynamic_ice(cellMask(iCell))) .and. & + unablatedVolumeNonDynCell(iCell) > 0.0_RKIND) then + ! This is a non-dynamic location that still has calving to offer - a location where calving needs to be propagated + + !call mpas_log_write("Passing calving from non-dynamic cell $i. $r available to pass", & + ! realArgs=(/unablatedVolumeNonDynCell(iCell)/), intArgs=(/iCell/)) + + volumeAvailableToPass = unablatedVolumeNonDynCell(iCell) + + ! Find total length of interface with dynamic cells at this cell + ablateLengthCell = 0.0_RKIND + do iNeighbor = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iNeighbor, iCell) + if (li_mask_is_dynamic_ice(edgeMask(iEdge))) then + edgeLengthScaling = scale_edge_length(angleEdge(iEdge), uvelForAblation(iCell), vvelForAblation(iCell)) + ablateLengthEdge = edgeLengthScaling * dvEdge(iEdge) + ablateLengthCell = ablateLengthCell + ablateLengthEdge + endif + enddo + + ! Now that we know calvLengthCell, pass along the required calving volume relative to the interface length + if ( ablateLengthCell > 0.0_RKIND ) then + do iNeighbor = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iNeighbor, iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_dynamic_ice(edgeMask(iEdge))) then + edgeLengthScaling = scale_edge_length(angleEdge(iEdge), uvelForAblation(iCell), vvelForAblation(iCell)) + ablateLengthEdge = edgeLengthScaling * dvEdge(iEdge) + if (requiredAblationVolumeDynEdge(iEdge) > 0.0_RKIND) then + call mpas_log_write("Unexpectedly found a dynamic edge that already has calving assigned to it." // & + " There is a flaw in li_apply_front_ablation_velocity that needs to be fixed!", MPAS_LOG_ERR) + err_tmp = 1 + err = ior(err, err_tmp) + endif + requiredAblationVolumeDynEdge(iEdge) = ablateLengthEdge / ablateLengthCell * volumeAvailableToPass + unablatedVolumeNonDynCell(iCell) = unablatedVolumeNonDynCell(iCell) - requiredAblationVolumeDynEdge(iEdge) + !call mpas_log_write(" Passed calving $r from non-dynamic cell $i to dynamic cell $i", & + ! realArgs=(/requiredAblationVolumeDynEdge(iEdge)/), intArgs=(/iCell, jCell/)) + endif + enddo + endif + endif + enddo + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'requiredAblationVolumeDynEdge') + call mpas_timer_stop("halo updates") + + ! c. Now apply ablation to each cell + do iCell = 1, nCells + if ((li_mask_is_dynamic_ice(cellMask(iCell))) .and. (bedTopography(iCell) <= config_sea_level)) then + ! Can loop over all dyn cells - only ones with calving on their edges will have calving applied. + ! Note, we ignore any leftover calving/melting if it would be applied to cells where the bed is above sea level. + do iNeighbor = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iNeighbor, iCell) + ! No need to check what type of edge this is - only required edges are nonzero + requiredAblationVolumeDynCell(iCell) = requiredAblationVolumeDynCell(iCell) + & + requiredAblationVolumeDynEdge(iEdge) ! Keep running total + enddo + ! c. Apply calving here + removeVolumeHere = min(cellVolume(iCell), requiredAblationVolumeDynCell(iCell)) ! Don't use more than available + ablationThickness(iCell) = removeVolumeHere / areaCell(iCell) + ablatedVolumeDynCell(iCell) = removeVolumeHere + unablatedVolumeDynCell(iCell) = requiredAblationVolumeDynCell(iCell) - removeVolumeHere + cellVolume(iCell) = cellVolume(iCell) - removeVolumeHere + if (iCell <= nCellsSolve) ablationSubtotal2 = ablationSubtotal2 + removeVolumeHere + endif + enddo + !call mpas_log_write("Done calculating ablation for dynamic floating cells. Removed $r m^3", realArgs=(/calvingSubtotal2/)) + + + ! Clean up to account for roundoff level errors that can occur + do iCell = 1, nCells + if (abs(ablationThickness(iCell) - thickness(iCell)) < ablationSmallThk) then + ablationThickness(iCell) = thickness(iCell) + endif + enddo + + ! Clean up - zap any stranded ice. This only needs to be considered for floating ice. + ablationSubtotal3 = 0.0_RKIND + do iCell = 1, nCells + if (li_mask_is_floating_ice(cellMask(iCell)) .and. (.not. li_mask_is_dynamic_ice(cellMask(iCell)))) then + thkCount = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_dynamic_ice(cellMask(jCell))) then + thkCount = thkCount + 1 + endif + enddo + if (thkCount == 0) then + ablationThickness(iCell) = thickness(iCell) + if (iCell <= nCellsSolve) ablationSubtotal3 = ablationSubtotal3 + ablationThickness(iCell) + endif + endif + if (ieee_is_nan(calvingThickness(iCell))) then + call mpas_log_write("NaN detected in calvingThickness at cell $i", MPAS_LOG_ERR, intArgs=(/iCell/)) + err_tmp = 1 + err = ior(err, err_tmp) + endif + enddo + + ! Clean up to account for roundoff level errors that can occur + do iCell = 1, nCells + if (abs(ablationThickness(iCell) - thickness(iCell)) < ablationSmallThk) then + ablationThickness(iCell) = thickness(iCell) + endif + enddo + + + ! End of routine accounting/reporting + localInfo(1) = ablationSubtotal1 + localInfo(2) = ablationSubtotal2 + localInfo(3) = ablationSubtotal3 + localInfo(4) = sum(ablationThickness(1:nCellsSolve) * areaCell(1:nCellsSolve)) + localInfo(5) = sum(unablatedVolumeNonDynCell(1:nCellsSolve)) + localInfo(6) = sum(unablatedVolumeDynCell(1:nCellsSolve)) + ! NOTE: THIS WILL NOT WORK ON MULTIPLE BLOCKS PER PROCESSOR + call mpas_dmpar_sum_real_array(domain % dminfo, 6, localInfo, globalInfo) + call mpas_log_write("== Ablation complete. Total calved = $r", realArgs = (/globalInfo(4)/)) + call mpas_log_write("== Ablated from non-dynamic cells = $r", realArgs = (/globalInfo(1)/)) + call mpas_log_write("== Ablated from dynamic cells = $r", realArgs = (/globalInfo(2)/)) + call mpas_log_write("== Stranded floating cells deleted = $r", realArgs = (/globalInfo(3)/)) + call mpas_log_write("== Unablated volumes: Non-dynamic cells=$r; Dynamic cells=$r", realArgs=(/globalInfo(5),globalInfo(6)/)) + if (((globalInfo(5) + globalInfo(6)) / (globalInfo(4) + 1.0e-30_RKIND) > 0.001_RKIND) .and. & + (globalInfo(4) > 1000.0_RKIND**2)) then ! Include some small amount of total calving for comparison + call mpas_log_write("Failed to ablate $r m^3. ($r% of total ablated)", MPAS_LOG_WARN, & + realArgs=(/globalInfo(5) + globalInfo(6), & + 100.0_RKIND * (globalInfo(5) + globalInfo(6)) / (globalInfo(4)+1.0e-30_RKIND)/)) + endif + if (((globalInfo(5) + globalInfo(6)) / (globalInfo(4) + 1.0e-30_RKIND) > 0.1_RKIND) .and. & + (globalInfo(4) > 1000.0_RKIND**2)) then ! Include some small amount of total calving for comparison + call mpas_log_write("Failed to ablate an amount greater than 10% of the ice ablated. " // & + "Try reducing time step or li_apply_front_ablation_velocity may need improvements.", & + MPAS_LOG_ERR, realArgs=(/globalInfo(5) + globalInfo(6), & + 100.0_RKIND * (globalInfo(5) + globalInfo(6)) / (globalInfo(4)+1.0e-30_RKIND)/)) + err_tmp = 1 + err = ior(err, err_tmp) + endif + if (globalInfo(3) / (globalInfo(4) + 1.0e-30_RKIND) > 0.01_RKIND) then + ! If stranded ice deletion results in more than a small amount of the total calving flux, this routine will not + ! be accurate. If this error gets triggered, either try using a smaller timstep or this routine needs improving. + call mpas_log_write("Deleting stranded floating cells accounts for more than 1% of ablation loss." // & + " Try using a smaller timestep or li_apply_front_ablation_velocity may need improvements for this simulation.") + err_tmp = 1 + err = ior(err, err_tmp) + endif + + deallocate(cellVolume) + deallocate(thicknessForAblation) + deallocate(uvelForAblation) + deallocate(vvelForAblation) + deallocate(oneCellBackList) + deallocate(twoCellsBackList) + call mpas_log_write("Finished with li_apply_front_ablation_velocity") + + end subroutine li_apply_front_ablation_velocity + + + ! Helper function for subroutine li_apply_front_ablation_velocity + ! Calculates the amount to scale an edge length based on the orientation of the edge with the surface velocity + function scale_edge_length(angleEdgeHere, u, v) + real(kind=RKIND), intent(in) :: angleEdgeHere + real(kind=RKIND), intent(in) :: u + real(kind=RKIND), intent(in) :: v + real(kind=RKIND) :: scale_edge_length + + real(kind=RKIND) :: mag + + mag = sqrt(u**2 + v**2) + if (mag == 0.0_RKIND) mag = 1.0_RKIND + scale_edge_length = abs(u/mag * cos(angleEdgeHere) + v/mag * sin(angleEdgeHere)) ! dot product of unit vectors + !scale_edge_length = abs(cos(angleEdgeHere - atan2(v, u))) + end function scale_edge_length + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine damagecalving +! +!> \brief Calve ice from the calving front based on the Bassis-Ma (2015) damage theory. +!> We can use two options: "calving_rate" or "threshold". +!> If we use "calving_rate", the calving front retreats by removing ice at a speed of k * damage, +!> where k is a calving parameter. If we use "threshold", the ice at the calving front will be completely +!> gone if the damage is above the threshold. +!> \author Tong Zhang +!> \date May. 2019 +!> \details Connect calving with the damage model +!> Bassis, Jeremy N., and Y. Ma. "Evolution of basal crevasses links ice shelf stability to ocean forcing." +!> Earth and Planetary Science Letters 409 (2015): 203-211. + +!----------------------------------------------------------------------- + subroutine damage_calving(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + logical, pointer :: config_print_calving_info + real (kind=RKIND), pointer :: config_sea_level + real(kind=RKIND), pointer :: config_calving_thickness + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: calvingThickness + real (kind=RKIND), dimension(:), pointer :: calvingVelocity + real (kind=RKIND), dimension(:), pointer :: eMax, eMin + real (kind=RKIND), dimension(:), pointer :: damage + integer, dimension(:), pointer :: calvingFrontMask + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: deltat !< time step (s) + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:,:), pointer :: edgesOnCell + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: areaCell + integer, pointer :: nCells + integer :: iCell, jCell, iNeighbor + real(kind=RKIND) :: cellCalvingFrontLength, cellCalvingFrontHeight + integer :: err_tmp + logical :: dynamicNeighbor + real(kind=RKIND), pointer :: config_damagecalvingParameter + real(kind=RKIND) :: calvingSubtotal + character (len=StrKIND), pointer :: config_damage_calving_method + real(kind=RKIND), pointer :: config_damage_calving_threshold + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_calving_thickness', config_calving_thickness) + call mpas_pool_get_config(liConfigs, 'config_damagecalvingParameter', config_damagecalvingParameter) + call mpas_pool_get_config(liConfigs, 'config_damage_calving_method', config_damage_calving_method) + call mpas_pool_get_config(liConfigs, 'config_damage_calving_threshold', config_damage_calving_threshold) + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get fields + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'damage', damage) + call mpas_pool_get_array(geometryPool, 'calvingVelocity', calvingVelocity) + call mpas_pool_get_array(geometryPool, 'calvingFrontMask', calvingFrontMask) + + + call calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + + if (trim(config_damage_calving_method) == 'calving_rate') then + if (config_damage_calving_threshold >= 1.0_RKIND .or. config_damage_calving_threshold < 0.0_RKIND) then + call mpas_log_write("Invalid value of config_damage_calving_threshold specified for 'calving_rate' option. " // & + "Valid values are in the range [0,1).", MPAS_LOG_ERR) + err = ior(err, err_tmp) + endif + ! First calculate the front retreat rate (m/s) + calvingVelocity(:) = config_damagecalvingParameter * & + max(0.0_RKIND, (damage(:) - config_damage_calving_threshold) / (1.0_RKIND - config_damage_calving_threshold)) & + * real(li_mask_is_floating_ice_int(cellMask(:)), kind=RKIND) ! calculate only for floating ice + call li_apply_front_ablation_velocity(meshPool, geometryPool, velocityPool, calvingThickness, calvingVelocity, & + applyToGrounded=.false., applyToFloating=.true., applyToGroundingLine=.false., & + domain=domain, err=err_tmp) + err = ior(err, err_tmp) + elseif (trim(config_damage_calving_method) == 'threshold') then + call apply_calving_damage_threshold(meshPool, geometryPool, scratchPool, err_tmp) + err = ior(err, err_tmp) + else + call mpas_log_write("Unknown value for config_damage_calving_method was specified!", MPAS_LOG_ERR) + endif + + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! Now also remove thin floating, dynamic ice (based on chosen thickness threshold) after mask is updated. + ! This criteria below only remove too-thin ice at the new calving front, + ! meaning just one 'row' of cells per timestep. This could be expanded to continue + ! removing ice backward until all connected too-thin ice has been removed. + ! Tests of the current implementation show reasonable behavior. + do iCell = 1, nCells + if (calvingFrontMask(iCell) == 1 .and. thickness(iCell) < config_calving_thickness) then + calvingThickness(iCell) = calvingThickness(iCell) + thickness(iCell) + thickness(iCell) = 0.0_RKIND + endif + enddo + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! remove abandoned floating ice (i.e. icebergs) and add it to the calving flux + ! Defined as: floating ice (dynamic or non-dynamic) that is not adjacent to dynamic ice (floating or grounded) + ! This won't necessarily find all abandoned ice, but in practice it does a pretty good job at general cleanup + calvingSubtotal = 0.0_RKIND + do iCell = 1, nCells + if (li_mask_is_floating_ice(cellMask(iCell))) then + ! check neighbors for dynamic ice (floating or grounded) + dynamicNeighbor = .false. + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_dynamic_ice(cellMask(jCell))) dynamicNeighbor = .true. + enddo + if (.not. dynamicNeighbor) then ! calve this ice + calvingThickness(iCell) = calvingThickness(iCell) + thickness(iCell) + thickness(iCell) = 0.0_RKIND + calvingSubtotal = calvingSubtotal + calvingThickness(iCell) * areaCell(iCell) + endif + endif + enddo + ! TODO: global reduce & reporting on amount of calving generated in this step + + call remove_small_islands(meshPool, geometryPool) + + block => block % next + + enddo + + end subroutine damage_calving + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine li_calculate_damage +! +!> \author Tong Zhang +!> \date May. 2019 +!> \details calulate the damage tracer for floating ice shelves (damage at grounded ice is constrained as 0) +!> Bassis, Jeremy N., and Y. Ma. "Evolution of basal crevasses links ice shelf stability to ocean forcing." +!> Earth and Planetary Science Letters 409 (2015): 203-211. + +!----------------------------------------------------------------------- + + subroutine li_calculate_damage(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + + real(kind=RKIND), pointer :: config_damage_preserve_threshold + character (len=StrKIND), pointer :: config_damage_gl_setting + real(kind=RKIND), pointer :: config_default_flowParamA + real(kind=RKIND), pointer :: config_flowLawExponent + logical, pointer :: config_print_calving_info + + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: eMax + real (kind=RKIND), dimension(:), pointer :: tauMax, tauMin + real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal + real (kind=RKIND), dimension(:), pointer :: damage + real (kind=RKIND), dimension(:), pointer :: ddamagedt + real (kind=RKIND), dimension(:), pointer :: s0, nstar + real (kind=RKIND), dimension(:), pointer :: damageSource + real (kind=RKIND), dimension(:), pointer :: damageNye + real (kind=RKIND), dimension(:), pointer :: damageMax + real (kind=RKIND), pointer :: & + config_ice_density, & ! ice density + config_ocean_density ! ocean density + + real (kind=RKIND), dimension(:), pointer :: principalStrainRateRatio + + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: deltat !< time step (s) + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, pointer :: nCells + integer :: iCell, jCell, iNeighbor, n_damage_downstream + real(kind=RKIND) :: damage_downstream + real(kind=RKIND), dimension(:,:), pointer :: uReconstructX, uReconstructY + real(kind=RKIND), dimension(6) :: localMinInfo, localMaxInfo, globalMinInfo, globalMaxInfo + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', config_flowLawExponent) + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_damage_preserve_threshold', config_damage_preserve_threshold) + call mpas_pool_get_config(liConfigs, 'config_damage_gl_setting', config_damage_gl_setting) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get fields + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'damage', damage) + call mpas_pool_get_array(geometryPool, 'ddamagedt', ddamagedt) + call mpas_pool_get_array(geometryPool, 's0', s0) + call mpas_pool_get_array(geometryPool, 'nstar', nstar) + call mpas_pool_get_array(geometryPool, 'damageSource', damageSource) + call mpas_pool_get_array(geometryPool, 'damageNye', damageNye) + call mpas_pool_get_array(geometryPool, 'damageMax', damageMax) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + + call mpas_pool_get_array(velocityPool, 'eMax', eMax) + call mpas_pool_get_array(velocityPool, 'tauMax', tauMax) + call mpas_pool_get_array(velocityPool, 'tauMin', tauMin) + call mpas_pool_get_array(velocityPool, 'principalStrainRateRatio', principalStrainRateRatio) + + where (thickness == 0.0_RKIND) + principalStrainRateRatio = 0.0_RKIND + s0 = 0.0_RKIND + nstar = 0.0_RKIND + elsewhere + ! Compute the hydrostatic to tensile stress ratio (equation 24 from Bassis and Ma, 2015, EPSL, 409, C) + ! In Bassis & Ma this is beta, defined as ratio of principal strain rates. Here we calculate as ratio of + ! deviatoric stresses, which is equivalent. + principalStrainRateRatio = tauMin / tauMax + + s0 = & + config_ice_density*(config_ocean_density-config_ice_density)*gravity*thickness/(2.0_RKIND*tauMax*config_ocean_density) + + ! Compute the effective flow law exponent (equation 11 from Bassis and Ma, 2015, EPSL, 409, C) + nstar(:) = 4.0_RKIND * config_flowLawExponent * & + (1.0_RKIND + principalStrainRateRatio(:) + principalStrainRateRatio(:)**2) / & + (4.0_RKIND * (1.0_RKIND + principalStrainRateRatio(:) + principalStrainRateRatio(:)**2) + & + 3.0_RKIND * (config_flowLawExponent - 1.0_RKIND )* principalStrainRateRatio(:)**2) + endwhere + + + do iCell = 1, nCells + if (thickness(iCell) == 0.0_RKIND) then + damageSource(iCell) = 0.0_RKIND + else + damageSource(iCell) = nstar(iCell) * (1.0_RKIND - s0(iCell)) * eMax(iCell) - floatingBasalMassBal(iCell) & + / config_ice_density / thickness(iCell) + ! RHS of equation 26 from Bassis and Ma (2015, EPSL, 409, C). Note that the last set of terms after the minus sign, + ! mdot / rho_i / H, differs from the mdot/H in the paper because our mdot has units of kg/m^2/s and to convert that + ! back to the right units (1/sec) you need to multiply by a factor of m^2/kg (1/rho_i * 1/H gives that factor). + endif + enddo + + !! Commented out lines immediately below here are for use with manufacured experiments + !ddamagedt(:) = damageSource(:)*damage(:) - 0.44/2.0 * (uReconstructX(1,:)/1000.0*(1+epsmax(:)*seconds)* & + ! exp(-uReconstructX(1,:)*seconds/1000.0) + damageSource(:) * (1+exp(-uReconstructX(1,:)*seconds/1000.0))) + !ddamagedt(:) = damageSource(:)*damage(:) - 0.44/2.0 * (uReconstructX(1,:)/1000.0*exp(-uReconstructX(1,:)*seconds/1000.0)+& + ! damageSource(:) * (1+exp(-uReconstructX(1,:)*seconds/1000.0))) + + ddamagedt(:) = damageSource(:) * damage(:) + + damage(:) = damage(:) + ddamagedt(:) * deltat + + do iCell = 1, nCells + if (thickness(iCell) == 0.0_RKIND) then + damageNye(iCell) = 0.0_RKIND + else + damageNye(iCell) = (2.0_RKIND + principalStrainRateRatio(iCell)) * tauMax(iCell) / & + (gravity * (config_ocean_density - config_ice_density) * thickness(iCell)) + endif + enddo + + do iCell = 1, nCells + if (damage(iCell) > config_damage_preserve_threshold) then + damageMax(iCell) = damage(iCell) + end if + end do + ! save the damageMax value for restoring later if that option is chosen (i.e., do NOT allowing for healing to occur) + + do iCell = 1, nCells + if (damage(iCell) < 0.0_RKIND) then + damage(iCell) = 0.0_RKIND + end if + if (damage(iCell) < damageNye(iCell)) then + damage(iCell) = damageNye(iCell) + end if + if (damage(iCell) > 1.0_RKIND) then + damage(iCell) = 1.0_RKIND + end if + end do + ! damage is always larger than the Nye value for initialization of damage evolution. + + do iCell = 1, nCells + if ((li_mask_is_grounded_ice(cellMask(iCell))) .or. (thickness(iCell) .eq. 0.0_RKIND)) then + damage(iCell) = 0.0_RKIND + end if + end do + ! the damage of grounded ice is kept as 0, as the strain rate calculation is only valid for ice shelf + + ! Options for initializing damage where ice goes afloat: + if (trim(config_damage_gl_setting) == 'extrapolate') then + ! set the damage for a cell at the GL to the average damage value for its neighbor cells downstream on the ice shelf + do iCell = 1, nCells + if (li_mask_is_grounding_line(cellMask(iCell))) then + + damage_downstream = 0.0_RKIND + n_damage_downstream = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell))) then + + damage_downstream = damage_downstream + damage(jCell) + n_damage_downstream = n_damage_downstream + 1 + end if + end do + + if (n_damage_downstream == 0) then + damage(iCell) = 0.0_RKIND + else + damage(iCell) = damage_downstream / n_damage_downstream + end if + + end if + end do + elseif (trim(config_damage_gl_setting) == "nye") then + ! set the damage at the first floating cells to the Nye value + do iCell = 1, nCells + if (li_mask_is_grounding_line(cellMask(iCell))) then + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell))) then + damage(jCell) = damageNye(jCell) + end if + end do + end if + end do + else + call mpas_log_write("Unknown value specified for config_damage_gl_setting!", MPAS_LOG_ERR) + err = ior(err, 1) + endif + + if (config_print_calving_info) then + ! End of routine accounting/reporting + localMinInfo(1) = minval(damageSource) + localMaxInfo(1) = maxval(damageSource) + localMinInfo(2) = minval(s0) + localMaxInfo(2) = maxval(s0) + localMinInfo(3) = minval(nstar) + localMaxInfo(3) = maxval(nstar) + localMinInfo(4) = minval(principalStrainRateRatio) + localMaxInfo(4) = maxval(principalStrainRateRatio) + localMinInfo(5) = minval(tauMax) + localMaxInfo(5) = maxval(tauMax) + localMinInfo(6) = minval(damage) + localMaxInfo(6) = maxval(damage) + ! NOTE: THIS WILL NOT WORK ON MULTIPLE BLOCKS PER PROCESSOR + call mpas_dmpar_min_real_array(domain % dminfo, 6, localMinInfo, globalMinInfo) + call mpas_dmpar_max_real_array(domain % dminfo, 6, localMaxInfo, globalMaxInfo) + + call mpas_log_write("damageSource value range: Min=$r, Max=$r", & + realArgs=(/globalMinInfo(1), globalMaxInfo(1)/)) + call mpas_log_write("s0 value range: Min=$r, Max=$r", & + realArgs=(/globalMinInfo(2), globalMaxInfo(2)/)) + call mpas_log_write("nstar value range: Min=$r, Max=$r", & + realArgs=(/globalMinInfo(3), globalMaxInfo(3)/)) + call mpas_log_write("principalStrainRateRatio value range: Min=$r, Max=$r", & + realArgs=(/globalMinInfo(4), globalMaxInfo(4)/)) + call mpas_log_write("tauMax value range: Min=$r, Max=$r", & + realArgs=(/globalMinInfo(5), globalMaxInfo(5)/)) + call mpas_log_write("damage value range: Min=$r, Max=$r", & + realArgs=(/globalMinInfo(6), globalMaxInfo(6)/)) + end if + ! temporary message output in the log file + + + block => block % next + + enddo + + end subroutine li_calculate_damage + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine li_finalize_damage_after_advection +! +!> \author Tong Zhang +!> \date Oct. 2019 +!> \details This routine finalizes the damage calculation after advection by several ways: 1) set the damage at grounding line +!> using an extrapolation method or an Nye method. +!> 2) preserve the damage to the original value before advection if it exceeds some large number +!> (config_damage_preserve_threshold). For example, if config_damage_preserve_threshold = 0.5, +!> then the localtions with damage > 0.5 on the ice shelf will not heal. +!> Thus, if we turn on this function, we do not allow the damage to heal if damage > config_damage_preserve_threshold. +!> 3) coupling rheology to damage if the option config_damage_rheology_coupling is set to True in namelist +!----------------------------------------------------------------------- + + subroutine li_finalize_damage_after_advection(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + real(kind=RKIND), pointer :: config_damage_stiffness_min + logical, pointer :: config_damage_rheology_coupling + logical, pointer :: config_preserve_damage + logical, pointer :: config_print_calving_info + character (len=StrKIND), pointer :: config_damage_gl_setting + real (kind=RKIND), dimension(:), pointer :: damage + real (kind=RKIND), dimension(:), pointer :: damageMax + real (kind=RKIND), dimension(:), pointer :: damageNye + real (kind=RKIND), dimension(:), pointer :: stiffnessFactor + + + integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, pointer :: nCells + integer :: iCell, jCell, iNeighbor, n_damage_downstream + real(kind=RKIND) :: damage_downstream + integer :: err_tmp + + err = 0 + + + call mpas_pool_get_config(liConfigs, 'config_damage_stiffness_min', config_damage_stiffness_min) + call mpas_pool_get_config(liConfigs, 'config_damage_rheology_coupling', config_damage_rheology_coupling) + call mpas_pool_get_config(liConfigs, 'config_damage_gl_setting', config_damage_gl_setting) + call mpas_pool_get_config(liConfigs, 'config_preserve_damage', config_preserve_damage) + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + ! get fields + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'damage', damage) + call mpas_pool_get_array(geometryPool, 'damageMax', damageMax) + call mpas_pool_get_array(geometryPool, 'damageNye', damageNye) + call mpas_pool_get_array(velocityPool, 'stiffnessFactor', stiffnessFactor) + + ! make sure masks are up to date. May not be necessary, but safer to do anyway. + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + if (config_preserve_damage) then + do iCell = 1, nCells + if (damage(iCell) < damageMax(iCell)) then + damage(iCell) = damageMax(iCell) + end if + end do + endif + ! put the damageMax value back to preserve the damage value (no heal) + + where (damage < 0.0_RKIND) + damage = 0.0_RKIND + end where + + where (damage > 1.0_RKIND) + damage = 1.0_RKIND + end where + + + do iCell = 1, nCells + if (li_mask_is_grounded_ice(cellMask(iCell)) .or. .not. li_mask_is_ice(cellMask(iCell))) then + damage(iCell) = 0.0_RKIND + end if + end do + + if (trim(config_damage_gl_setting) == 'extrapolate') then + do iCell = 1, nCells + if (li_mask_is_grounding_line(cellMask(iCell))) then + damage_downstream = 0.0_RKIND + n_damage_downstream = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell))) then + + damage_downstream = damage_downstream + damage(jCell) + n_damage_downstream = n_damage_downstream + 1 + end if + end do + + damage(iCell) = damage_downstream + + if (n_damage_downstream == 0) then + damage(iCell) = 0.0_RKIND + else + damage(iCell) = damage_downstream / n_damage_downstream + end if + + end if + end do + !set the damage at GL to the mean damage value for its neighboring floating cells + + elseif (trim(config_damage_gl_setting) == 'nye') then + do iCell = 1, nCells + if (li_mask_is_grounding_line(cellMask(iCell))) then + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell))) then + damage(jCell) = damageNye(jCell) + end if + end do + end if + end do + ! always set the damage at the first floating cells to the Nye value + endif + + if (config_damage_rheology_coupling) then + do iCell = 1, nCells + if (li_mask_is_floating_ice(cellMask(iCell))) then + stiffnessFactor(iCell) = 1.0_RKIND - damage(iCell) + if (stiffnessFactor(iCell) < config_damage_stiffness_min) then + stiffnessFactor(iCell) = config_damage_stiffness_min + end if + end if + end do + end if + + + block => block % next + + enddo + + end subroutine li_finalize_damage_after_advection + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine apply_calving_damage_threshold +! +!> \brief Calve any ice that is damaged beyond a specified threshold +!> \author Tong Zhang, Matt Hoffman +!> \date Nov. 2020, March 2021 +!> \details This routine specified floating ice to be calved wherever the damage +!> value exceeds a specified threshold, assuming the ice is connected to the calving +!> front. +!----------------------------------------------------------------------- + subroutine apply_calving_damage_threshold(meshPool, geometryPool, scratchPool, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh pool + type (mpas_pool_type), pointer, intent(in) :: scratchPool !< Input: scratch pool + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(inout) :: geometryPool !< Input: geometry pool + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), pointer :: calvingThickness !< Output: the applied calving rate as a thickness + real (kind=RKIND), dimension(:), pointer :: thickness, damage, bedTopography + integer, dimension(:), pointer :: groundedMarineMarginMask + real(kind=RKIND), pointer :: config_damage_calving_threshold + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: cellMask + integer, pointer :: nCells + integer :: iCell, iNeighbor, jCell, iEdge + integer :: nEmptyNeighbors + real (kind=RKIND), pointer :: config_sea_level + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_damage_calving_threshold', config_damage_calving_threshold) + + ! get fields + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'damage', damage) + call mpas_pool_get_array(geometryPool, 'groundedMarineMarginMask', groundedMarineMarginMask) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + + calvingThickness(:) = 0.0_RKIND + + groundedMarineMarginMask(:) = 0 + do iCell = 1, nCells + ! Define marine marginal cells as those that are (1) at the ice + ! margin, (2) have at least one neighboring cell without ice, (3) contain + ! grounded ice, and (4) have bed topography below sea level. + ! OR is adjacent to an inactive floating margin cell + + ! Check if neighboring cells contain ice and have bed topo below sea level + nEmptyNeighbors = 0 + do iEdge = 1, nEdgesOnCell(iCell) + iNeighbor = cellsOnCell(iEdge, iCell) + if ( (((thickness(iNeighbor) == 0.0_RKIND) & + .and. bedTopography(iNeighbor) < config_sea_level)) & + .or. ((li_mask_is_floating_ice(cellMask(iNeighbor)) & + .and. li_mask_is_margin(cellMask(iNeighbor)) & + .and. (.not. li_mask_is_dynamic_ice(cellMask(iNeighbor)))))) then + nEmptyNeighbors = nEmptyNeighbors + 1 + endif + enddo + + if ( nEmptyNeighbors > 0 & + .and. li_mask_is_grounded_ice(cellMask(iCell)) & + .and. bedTopography(iCell) < config_sea_level & + .and. li_mask_is_dynamic_ice(cellMask(iCell)) ) then + + groundedMarineMarginMask(iCell) = 1 + else + groundedMarineMarginMask(iCell) = 0 + endif + enddo + + ! The calving volume needs to be distributed in three ways: + ! 1. We need to first remove any "thin" ice in front of this cell + ! 2. Then we remove ice from this cell if damage > threshold + do iCell = 1, nCells + if ( (li_mask_is_floating_ice(cellMask(iCell)) .and. li_mask_is_dynamic_ice(cellMask(iCell)) ) & ! floating dynamic + .and. li_mask_is_margin(cellMask(iCell)) & + .and. (damage(iCell) .ge. config_damage_calving_threshold)) then + + ! First remove ice from "thin" neighbors + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) then + ! this is a thin neighbor - remove the whole cell volume + calvingThickness(jCell) = thickness(jCell) + endif + enddo + + calvingThickness(iCell) = thickness(iCell) + ! < apply to the field that will be used in thickness units + + ! Now calved away the neighboring cells if the damage is also > the threshold value + ! I choose to disable this section (i.e., no recursive calving) -- TZ + !TODO: a flood fill algorithm might be needed for this threshold method in the future + !do iNeighbor = 1, nEdgesOnCell(iCell) + ! jCell = cellsOnCell(iNeighbor, iCell) + ! if (li_mask_is_floating_ice(cellMask(jCell)) .and. li_mask_is_dynamic_ice(cellMask(jCell)) & + ! .and. (.not. li_mask_is_dynamic_margin(jCell)) .and. (damage(jCell) .gt. config_damage_calving_threshold)) then + ! removeVolumeHere = cellVolume(jCell) + ! < how much we want to remove here + ! calvingThickness(jCell) = removeVolumeHere / areaCell(jCell) + ! < apply to the field that will be used in thickness units + ! cellVolume(jCell) = cellVolume(jCell) - removeVolumeHere + ! update accounting on cell volume + ! endif + !enddo + + endif ! if cell is calving margin + + enddo ! cell loop + + end subroutine apply_calving_damage_threshold + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mask_calving +! +!> \brief Calve ice based on a mask that is input +!> \author Matthew Hoffman +!> \date July 2019 +!> \details This routine uses the field calvingMask to eliminate floating +!> ice. Locations marked with 0 will not be calved. Locations with all +!> all other values will be calved. Note that time-varying values +!> can be input. +!----------------------------------------------------------------------- + subroutine mask_calving(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: calvingThickness + integer, dimension(:), pointer :: calvingMask + integer, dimension(:), pointer :: cellMask + integer :: err_tmp + + err = 0 + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + ! get fields + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'calvingMask', calvingMask) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + calvingThickness = 0.0_RKIND + + ! === apply calving === + where (li_mask_is_floating_ice(cellMask) .and. (calvingMask >= 1)) + calvingThickness = thickness + thickness = 0.0_RKIND + end where + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + call remove_small_islands(meshPool, geometryPool) + + block => block % next + enddo + + end subroutine mask_calving + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine calculate_calving_front_mask +! +!> \brief Calculate mask indicating position of effective calving front +!> \author Matthew Hoffman +!> \date Feb. 2018 +!> \details Mmake mask for effective calving front. +!> This is last dynamic floating cell, but also make sure it has a neighbor that is open ocean or thin floating ice. +!----------------------------------------------------------------------- + subroutine calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh pool + type (mpas_pool_type), intent(in) :: geometryPool !< Input: geometry pool + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, dimension(:), intent(out) :: calvingFrontMask !< Output: calving front mask + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer, pointer :: nCells + integer :: iCell, iNeighbor, jCell, jNeighbor, kCell + logical :: oceanNeighborCell, oceanNeighborEdge + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:,:), pointer :: edgesOnCell ! list of edges that neighbor each cell integer, dimension(:), pointer :: cellMask real (kind=RKIND), dimension(:), pointer :: bedTopography real (kind=RKIND), pointer :: config_sea_level @@ -1362,30 +3192,37 @@ subroutine calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask call mpas_pool_get_dimension(meshPool, 'nCells', nCells) call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + !calvingFrontMaskEdge = 0 !initialize calvingFrontMask = 0 !initialize do iCell = 1, nCells if ( (li_mask_is_floating_ice(cellMask(iCell))) .and. (li_mask_is_dynamic_margin(cellMask(iCell))) ) then - oceanNeighbor = .false. + oceanNeighborCell = .false. do iNeighbor = 1, nEdgesOnCell(iCell) jCell = cellsOnCell(iNeighbor, iCell) + oceanNeighborEdge = .false. if (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) then ! make sure this neighbor is adjacent to open ocean (and not thin floating ice up against the coast) do jNeighbor = 1, nEdgesOnCell(jCell) kCell = cellsOnCell(jNeighbor, jCell) if (.not. li_mask_is_ice(cellMask(kCell)) .and. bedTopography(kCell) < config_sea_level) then - oceanNeighbor = .true. ! iCell neighbors thin ice that in turn neighbors open ocean + oceanNeighborEdge = .true. ! iCell neighbors thin ice that in turn neighbors open ocean endif enddo endif if (.not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level) then - oceanNeighbor = .true. ! this is an open ocean neighbor + oceanNeighborEdge = .true. ! this is an open ocean neighbor endif + !if (oceanNeighborEdge) then + ! calvingFrontMaskEdge(edgesOnCell(iNeighbor, iCell)) = 1 + !endif + oceanNeighborCell = (oceanNeighborCell .or. oceanNeighborEdge) enddo - if (oceanNeighbor) then + if (oceanNeighborCell) then calvingFrontMask(iCell) = 1 endif endif @@ -1394,6 +3231,216 @@ subroutine calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask end subroutine calculate_calving_front_mask + subroutine remove_icebergs(domain) + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + + ! Local variables + logical, pointer :: config_remove_icebergs + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + + type (field1dInteger), pointer :: contiguousDynamicIceField + type (field1dInteger), pointer :: contiguousDynamicIceOldField + + real (kind=RKIND), dimension(:), pointer :: calvingThickness ! thickness of ice that calves (computed in this subroutine) + real (kind=RKIND), dimension(:), pointer :: thickness + integer, dimension(:), pointer :: cellMask + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + + integer, dimension(:), pointer :: contiguousDynamicIce, contiguousDynamicIceOld + integer, pointer :: nCells, nCellsSolve + integer, dimension(:), pointer :: nCellsArray + integer :: iCell, jCell, n + integer :: newMaskCountLocal, newMaskCountLocalAccum, newMaskCountGlobal + integer :: err_tmp, err + integer :: globalLoopCount, localLoopCount + integer :: localIcebergCellCount, globalIcebergCellCount + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_remove_icebergs', config_remove_icebergs) + if (.not. config_remove_icebergs) then + return ! skip this entire routine if disabled + endif + + call mpas_timer_start("iceberg detection") + call mpas_log_write("Iceberg-detection flood-fill begin.") + ! Allocate needed scratch fields + + block => domain % blocklist + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_field(geometryPool, 'contiguousDynamicIce', contiguousDynamicIceField) + call mpas_allocate_scratch_field(contiguousDynamicIceField, single_block_in = .false.) + + call mpas_pool_get_field(geometryPool, 'contiguousDynamicIceOld', contiguousDynamicIceOldField) + call mpas_allocate_scratch_field(contiguousDynamicIceOldField, single_block_in = .false.) + + call mpas_log_write("Iceberg-detection flood-fill: allocated.") + ! First mark grounded ice to initialize flood fill mask + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'contiguousDynamicIce', contiguousDynamicIce) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(geometryPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + + ! make sure masks are up to date. May not be necessary, but safer to do anyway. + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + call mpas_log_write("Iceberg-detection flood-fill: updated masks.") + contiguousDynamicIce(:) = 0 ! initialize + newMaskCountLocal = 0 + do iCell = 1, nCellsSolve + if (li_mask_is_grounded_ice(cellMask(iCell)) .and. li_mask_is_dynamic_ice(cellMask(iCell))) then + contiguousDynamicIce(iCell) = 1 + newMaskCountLocal = newMaskCountLocal + 1 + endif + enddo + call mpas_log_write("Initialized $i cells to local mask", intArgs=(/newMaskCountLocal/)) + + block => block % next + end do + + call mpas_log_write("Iceberg-detection flood-fill initialization complete.") + + ! Outer loop over processors (should also have a loop over blocks) + ! Inner loop over cells on that processor + + ! Initialize global mask count + call mpas_dmpar_sum_int(domain % dminfo, newMaskCountLocal, newMaskCountGlobal) + globalLoopCount = 0 + + call mpas_log_write("Initialized $i cells to global mask", intArgs=(/newMaskCountGlobal/)) + + do while (newMaskCountGlobal > 0) + globalLoopCount = globalLoopCount + 1 + call mpas_log_write(" Starting global processor loop $i", intArgs=(/globalLoopCount/)) + ! First Update halos + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'contiguousDynamicIce') + call mpas_timer_stop("halo updates") + + ! initialize counter of cells locally updated during this outer loop + newMaskCountLocalAccum = 0 + + ! Now update (advance) mask locally + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'contiguousDynamicIce', contiguousDynamicIce) + call mpas_pool_get_array(geometryPool, 'contiguousDynamicIceOld', contiguousDynamicIceOld) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + + + ! initialize local loop + localLoopCount = 0 + newMaskCountLocal = 1 ! need to make sure we enter the loop + do while (newMaskCountLocal > 0) + localLoopCount = localLoopCount + 1 + call mpas_log_write(" Starting local cell loop $i", intArgs=(/localLoopCount/)) + + ! initialize + newMaskCountLocal = 0 + contiguousDynamicIceOld(:) = contiguousDynamicIce(:) + + do iCell = 1, nCellsArray(1) ! this gives owned cells only + if (contiguousDynamicIceOld(iCell) == 0 .and. & ! this cell not yet marked + li_mask_is_dynamic_ice(cellMask(iCell))) then ! and is dynamic + ! If it has a marked neighbor, then add it to the mask + do n = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(n, iCell) + if (contiguousDynamicIceOld(jCell) == 1) then + contiguousDynamicIce(iCell) = 1 + newMaskCountLocal = newMaskCountLocal + 1 + exit ! skip the rest of this do-loop - no need to check additional neighbors + endif + enddo + endif ! if not already marked + enddo ! loop over cells + + ! Accumulate cells added locally until we do the next global reduce + newMaskCountLocalAccum = newMaskCountLocalAccum + newMaskCountLocal + call mpas_log_write(" Added $i new cells to local mask", intArgs=(/newMaskCountLocal/)) + enddo ! local mask loop + + block => block % next + end do + + ! update count of cells added to mask globally + call mpas_dmpar_sum_int(domain % dminfo, newMaskCountLocalAccum, newMaskCountGlobal) + call mpas_log_write(" Added $i new cells to global mask", intArgs=(/newMaskCountGlobal/)) + + if (globalLoopCount>200) then + call mpas_log_write("Too many global loops!", MPAS_LOG_ERR) + endif + end do ! global loop + + + ! Now remove any ice that was not flood-filled - these are icebergs + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'contiguousDynamicIce', contiguousDynamicIce) + call mpas_pool_get_array(geometryPool, 'contiguousDynamicIceOld', contiguousDynamicIceOld) + call mpas_pool_get_dimension(geometryPool, 'nCells', nCells) + call mpas_pool_get_dimension(geometryPool, 'nCellsSolve', nCellsSolve) + + contiguousDynamicIceOld(:) = 0 + do iCell = 1, nCellsSolve + if (contiguousDynamicIce(iCell) == 0 .and. li_mask_is_dynamic_ice(cellMask(iCell))) then + calvingThickness(iCell) = calvingThickness(iCell) + thickness(iCell) ! remove any remaining ice here + thickness(iCell) = 0.0_RKIND + contiguousDynamicIceOld(iCell) = 1 ! debug: make this a mask of where icebergs were removed + endif + enddo + localIcebergCellCount = sum(contiguousDynamicIceOld) + +! where(contiguousDynamicIce == 0 .and. li_mask_is_dynamic_ice(cellMask)) +! calvingThickness = calvingThickness + thickness ! remove any remaining ice here +! thickness = 0.0_RKIND +! end where + + block => block % next + end do + + ! Calculate total iceberg cells removed (not needed but useful for log - note requires extra reduce!) + call mpas_dmpar_sum_int(domain % dminfo, localIcebergCellCount, globalIcebergCellCount) + + ! update halo - probably not needed? + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'thickness') + call mpas_timer_stop("halo updates") + + ! clean up + call mpas_deallocate_scratch_field(contiguousDynamicIceField, single_block_in=.false.) + call mpas_deallocate_scratch_field(contiguousDynamicIceOldField, single_block_in=.false.) + + call mpas_log_write("Iceberg-detection flood-fill complete. Removed $i iceberg cells.", intArgs=(/globalIcebergCellCount/)) + call mpas_timer_stop("iceberg detection") + end subroutine remove_icebergs + + end module li_calving diff --git a/src/core_landice/mode_forward/mpas_li_core.F b/src/core_landice/mode_forward/mpas_li_core.F index f8ebbb81db..2e19d28344 100644 --- a/src/core_landice/mode_forward/mpas_li_core.F +++ b/src/core_landice/mode_forward/mpas_li_core.F @@ -456,7 +456,8 @@ function li_core_run(domain) result(err) ! write the timestamp to file call mpas_new_unit(restartTimestampUnit) - open(restartTimestampUnit, file=config_restart_timestamp_name, form='formatted', status='replace', iostat=err_tmp2) + open(restartTimestampUnit, file=config_restart_timestamp_name, form='formatted', & + status='replace', iostat=err_tmp2) if (err_tmp2 > 0) then call mpas_log_write('Error opening file to write restart timestamp:' // & trim(config_restart_timestamp_name), MPAS_LOG_ERR) @@ -896,7 +897,7 @@ subroutine landice_init_block(block, dminfo, err) ! Init for reconstruction of velocity ! Note: mpas_init_reconstruct fails with the MISMIP3d periodic mesh ! because x_period and y_period are not set properly. - ! That should be fixed for that test, but a workaround is to + ! That should be fixed for that test, but a workaround is to ! disable these two calls for those runs. call mpas_rbf_interp_initialize(meshPool) call mpas_init_reconstruct(meshPool) @@ -1036,7 +1037,8 @@ subroutine li_simulation_clock_init(core_clock, configs, ierr) call mpas_set_time(curr_time=stopTime, dateTimeString=config_stop_time, ierr=err_tmp) ierr = ior(ierr,err_tmp) if(startTime + runduration /= stopTime) then - call mpas_log_write('config_run_duration and config_stop_time are inconsistent: using config_run_duration.', MPAS_LOG_WARN) + call mpas_log_write('config_run_duration and config_stop_time are inconsistent: using config_run_duration.', & + MPAS_LOG_WARN) end if end if else if (trim(config_stop_time) /= "none") then diff --git a/src/core_landice/mode_forward/mpas_li_core_interface.F b/src/core_landice/mode_forward/mpas_li_core_interface.F index db27f38305..df57153dee 100644 --- a/src/core_landice/mode_forward/mpas_li_core_interface.F +++ b/src/core_landice/mode_forward/mpas_li_core_interface.F @@ -100,6 +100,9 @@ function li_setup_packages(configPool, packagePool, iocontext) result(ierr) ! Local variables character (len=StrKIND), pointer :: config_velocity_solver + character (len=StrKIND), pointer :: config_basal_mass_bal_float + character (len=StrKIND), pointer :: config_front_mass_bal_grounded + character (len=StrKIND), pointer :: config_thermal_solver logical, pointer :: config_SGH logical, pointer :: config_adaptive_timestep_include_DCFL logical, pointer :: config_write_albany_ascii_mesh @@ -108,17 +111,26 @@ function li_setup_packages(configPool, packagePool, iocontext) result(ierr) logical, pointer :: SIAvelocityActive logical, pointer :: hydroActive logical, pointer :: observationsActive + logical, pointer :: ismip6ShelfMeltActive + logical, pointer :: ismip6GroundedFaceMeltActive + logical, pointer :: thermalActive ierr = 0 call mpas_pool_get_config(configPool, 'config_velocity_solver', config_velocity_solver) call mpas_pool_get_config(configPool, 'config_SGH', config_SGH) call mpas_pool_get_config(configPool, 'config_write_albany_ascii_mesh', config_write_albany_ascii_mesh) + call mpas_pool_get_config(configPool, 'config_basal_mass_bal_float', config_basal_mass_bal_float) + call mpas_pool_get_config(configPool, 'config_front_mass_bal_grounded', config_front_mass_bal_grounded) + call mpas_pool_get_config(configPool, 'config_thermal_solver', config_thermal_solver) call mpas_pool_get_package(packagePool, 'SIAvelocityActive', SIAvelocityActive) call mpas_pool_get_package(packagePool, 'higherOrderVelocityActive', higherOrderVelocityActive) call mpas_pool_get_package(packagePool, 'hydroActive', hydroActive) call mpas_pool_get_package(packagePool, 'observationsActive', observationsActive) + call mpas_pool_get_package(packagePool, 'ismip6ShelfMeltActive', ismip6ShelfMeltActive) + call mpas_pool_get_package(packagePool, 'ismip6GroundedFaceMeltActive', ismip6GroundedFaceMeltActive) + call mpas_pool_get_package(packagePool, 'thermalActive', thermalActive) if (trim(config_velocity_solver) == 'sia') then SIAvelocityActive = .true. @@ -141,6 +153,26 @@ function li_setup_packages(configPool, packagePool, iocontext) result(ierr) "'config_write_albany_ascii_mesh' is set to .true.") endif + if (trim(config_basal_mass_bal_float) == 'ismip6') then + ismip6ShelfMeltActive = .true. + call mpas_log_write("The 'ismip6Melt' package and assocated variables have been enabled because " // & + "'config_basal_mass_bal_float' is set to 'ismip6'") + endif + + if (trim(config_front_mass_bal_grounded) == 'ismip6') then + ismip6GroundedFaceMeltActive=.true. + call mpas_log_write("The 'ismip6GroundedFaceMelt' package and assocated variables have been enabled because " // & + "'config_front_mass_bal_grounded' is set to 'ismip6'") + endif + + if ((trim(config_thermal_solver) == 'temperature') .or. (trim(config_thermal_solver) == 'enthalpy')) then + thermalActive = .true. + call mpas_log_write("The 'thermal' package and assocated variables have been enabled because " // & + "'config_thermal_solver' is set to either 'temperature' or 'enthalpy'.") + endif + + + ! call setup packages in analysis driver call li_analysis_setup_packages(configPool, packagePool, ierr) diff --git a/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F b/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F index 82c9db2659..ab47f0d226 100644 --- a/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F +++ b/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F @@ -376,7 +376,7 @@ end subroutine li_calculate_apparent_diffusivity !> transition temperature (-10 deg C at 0 pressure). !> Values for $A0, Q, \Phi$ differ from PB1982. !> -!> All options are adjusted by the enhancement factor (which defaults to 1.0). +!> The result is NOT adjusted by an enhancement factor. !----------------------------------------------------------------------- subroutine li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, err) @@ -417,7 +417,6 @@ subroutine li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, integer, pointer :: nCells, nVertLevels character (len=StrKIND), pointer :: config_flowParamA_calculation real (kind=RKIND), pointer :: config_default_flowParamA, & - config_enhancementFactor, & config_dynamic_thickness, & config_ice_density integer :: iCell, iLevel, err_tmp @@ -434,7 +433,6 @@ subroutine li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) call mpas_pool_get_config(liConfigs, 'config_flowParamA_calculation', config_flowParamA_calculation) - call mpas_pool_get_config(liConfigs, 'config_enhancementFactor', config_enhancementFactor) call mpas_pool_get_config(liConfigs, 'config_default_flowParamA', config_default_flowParamA) call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) @@ -499,10 +497,6 @@ subroutine li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, end select !print *,'max flwa', maxval(flowParamA) - !print *,'config_enhancementFactor', config_enhancementFactor - - ! Include enhancement factor - flowParamA = flowParamA * config_enhancementFactor err = ior(err, err_tmp) diff --git a/src/core_landice/mode_forward/mpas_li_iceshelf_melt.F b/src/core_landice/mode_forward/mpas_li_iceshelf_melt.F new file mode 100644 index 0000000000..130a043bf0 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_iceshelf_melt.F @@ -0,0 +1,1396 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_iceshelf_melt +! +!> \brief MPAS land ice vertical temperature/enthalpy solver +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This module contains solvers for the vertical temperature +!> and/or enthalpy profile. +! +!----------------------------------------------------------------------- + +module li_iceshelf_melt + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timer + use mpas_abort + use mpas_log + + use li_setup + use li_mask + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_basal_melt_floating_ice, li_face_melt_grounded_ice + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + contains +!*********************************************************************** + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_face_melt_grounded_ice +! +!> \brief MPAS land ice solver for face melt of grounded ice +!> \author Trevor Hillebrand +!> \date February 2021 +!> \details +!> This routine computes face melting for floating ice. +!> The following options are supported: +!> (1) Do nothing (config_front_mass_bal_grounded = 'none') +!> (2) Prescribed uniform face melt speed (config_front_mass_bal_grounded = 'uniform') +!> (3) Face melt speed as in ISMIP6 (config_front_mass_bal_grounded = 'ismip6') + +!----------------------------------------------------------------------- + + subroutine li_face_melt_grounded_ice(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: geometryPool + character(len=StrKIND), pointer :: & + config_front_mass_bal_grounded ! option for submarine mass balance + ! at grounded glacier front + real (kind=RKIND), dimension(:), pointer :: faceMeltSpeed, faceMeltingThickness + integer :: err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_front_mass_bal_grounded', config_front_mass_bal_grounded) + + if ( trim(config_front_mass_bal_grounded) == 'ismip6' & + .or. trim(config_front_mass_bal_grounded) == 'uniform' ) then + call grounded_face_melt_ismip6(domain, err_tmp) + err = ior(err, err_tmp) + elseif ( trim(config_front_mass_bal_grounded) == 'none' ) then + ! Zero entire field + + ! block loop + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'faceMeltSpeed', faceMeltSpeed) + call mpas_pool_get_array(geometryPool, 'faceMeltingThickness', faceMeltingThickness) + + faceMeltSpeed(:) = 0.0_RKIND + faceMeltingThickness(:) = 0.0_RKIND + + block => block % next + enddo ! associated(block) + else + call mpas_log_write('Unknown option selected for config_front_mass_bal_grounded:' // & + trim(config_front_mass_bal_grounded), MPAS_LOG_ERR) + err = ior(err, 1) + endif + + end subroutine li_face_melt_grounded_ice +!----------------------------------------------------------------------- +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||r +! +! ! routine li_basal_melt_floating_ice +! +!> \brief MPAS land ice solver for basal melt of floating ice +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> This routine computes basal melting for floating ice. +!> The following options are supported: +!> (1) Do nothing (config_basal_mass_bal_float = 'none') +!> (2) Read melt rate from a file (config_basal_mass_bal_float = 'file') +!> (2) Prescribed constant basal melt rate (config_basal_mass_bal_float = 'constant') +!> (3) Basal melt rate as in MISMIP+ (config_basal_mass_bal_float = 'mismip') + +!----------------------------------------------------------------------- + + subroutine li_basal_melt_floating_ice(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool ! needed for mask subroutine + type (mpas_pool_type), pointer :: scratchPool + + integer, pointer :: & + nCellsSolve ! number of locally owned cells + + logical, pointer :: & + config_print_thermal_info ! if true, print debug info + + character(len=StrKIND), pointer :: & + config_basal_mass_bal_float ! option for basal mass balance of floating ice + + character(len=StrKIND), pointer :: & + config_front_mass_bal_grounded ! option for submarine mass balance + ! at grounded glacier front + + real(kind=RKIND), pointer :: & + config_thermal_thickness, & ! minimum thickness (m) for temperature calculations + config_sea_level, & ! sea level (m) relative to z = 0 + config_bmlt_float_flux, & ! constant heat flux (W/m^2) applied to the base of floating ice; positive upward + config_bmlt_float_xlimit ! x value (m) defining region where bmlt_float_flux is applied; + ! melt only where abs(x) > xlimit + real (kind=RKIND), pointer :: config_ice_density !< ice density + + integer, dimension(:), pointer :: & + cellMask ! bit mask describing whether ice is floating, dynamically active, etc. + + type (field1dInteger), pointer :: thermalCellMaskField + + integer, dimension(:), pointer :: & + thermalCellMask ! mask for thermal calculations + ! = 1 where thickness > config_thermal_thickness, elsewhere = 0 + + real (kind=RKIND), dimension(:), pointer :: & + xCell ! x coordinate for each cell (m) + + real (kind=RKIND), dimension(:), pointer :: & + floatingBasalMassBal, & ! basal mass balance for floating ice + thickness, & ! ice thickness (m) + lowerSurface, & ! lower surface elevation (m) + bedTopography ! bed topography (m; negative below sea level) + + real(kind=RKIND), pointer :: daysSinceStart + + integer :: iCell, err_tmp + + ! Local variables for some melt methods + + real(kind=RKIND) :: & + bmlt_float_rate ! constant basal melt rate (m/s) + ! = config_bmlt_float_flux / (config_ice_density*latent_heat_ice) + real(kind=RKIND) :: hCavity ! depth of ice cavity beneath floating ice (m) + real(kind=RKIND) :: zDraft ! draft of floating ice (m below sea level) + + ! basal melting parameters for MISMIP+ experiment + ! Note: These could be made user-configurable, but are hardwired for now because there are no plans + ! to run MISMIP+ with different values + real(kind=RKIND), parameter :: & + bmlt_float_omega = 0.20_RKIND / scyr, & ! time scale for basal melting (s^-1) + ! MISMIP+ default value = 0.2 yr^-1 + bmlt_float_h0 = 75._RKIND, & ! scale for sub-shelf cavity thickness (m) + ! MISMIP+ default value = 75 m + bmlt_float_z0 = -100._RKIND ! scale for ice draft (m) + ! MISMIP+ default value = -100 m + + real(kind=RKIND) :: GLdepth, CFdepth ! ice shelf draft stats needed by the temperature_profile method: + ! characteristic grounding line depth, calving front depth + + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_float', config_basal_mass_bal_float) + call mpas_pool_get_config(liConfigs, 'config_front_mass_bal_grounded', config_front_mass_bal_grounded) + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + + if (config_print_thermal_info) then + call mpas_log_write('Solving for basal melting of floating ice, config_basal_mass_bal_float = ' // & + trim(config_basal_mass_bal_float) ) + endif + + ! Simple options handled here + if (trim(config_basal_mass_bal_float) == 'none') then + + ! Zero entire field + + ! block loop + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + + floatingBasalMassBal = 0.0_RKIND + + block => block % next + enddo ! associated(block) + + return ! skip the rest of this routine + + elseif (trim(config_basal_mass_bal_float) == 'file') then + + return ! already set; nothing to do + + endif + + ! get rest of config variables + call mpas_pool_get_config(liConfigs, 'config_thermal_thickness', config_thermal_thickness) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_bmlt_float_flux', config_bmlt_float_flux) + call mpas_pool_get_config(liConfigs, 'config_bmlt_float_xlimit', config_bmlt_float_xlimit) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + + ! The temp-profile method needs a global reduction before it can operate, so do that here + ! before the main option block + if (trim(config_basal_mass_bal_float) == 'temperature_profile') then + call calc_iceshelf_draft_info(domain, GLdepth, CFdepth, err_tmp) + err = ior(err, err_tmp) + if (config_print_thermal_info) then + call mpas_log_write('GLdepth=$r, CFdepth=$r', realArgs=(/GLdepth, CFdepth/)) + endif + endif + + if (trim(config_basal_mass_bal_float) == 'ismip6') then + call iceshelf_melt_ismip6(domain, err_tmp) + err = ior(err, err_tmp) + + return ! do not enter block loop below - we are done here + endif + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + ! get fields from the mesh pool + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'daysSinceStart',daysSinceStart) + + ! get fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + + ! get fields from the scratch pool + call mpas_pool_get_field(scratchPool, 'iceCellMask', thermalCellMaskField) + call mpas_allocate_scratch_field(thermalCellMaskField, .true.) + thermalCellMask => thermalCellMaskField % array + + ! calculate masks - so we know where the ice is floating + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! calculate a mask to identify ice that is thick enough to be thermally active + do iCell = 1, nCellsSolve + if (thickness(iCell) > config_thermal_thickness) then + thermalCellMask(iCell) = 1 + else + thermalCellMask(iCell) = 0 + endif + enddo + + ! ----------------- + ! Compute basal melting for floating ice. + ! ----------------- + + ! initialize to zero melt + floatingBasalMassBal(:) = 0.0_RKIND + if (trim(config_basal_mass_bal_float) == 'none') then + ! Do nothing, handled above + + elseif (trim(config_basal_mass_bal_float) == 'file') then + ! Do nothing, handled above + + elseif (trim(config_basal_mass_bal_float) == 'constant') then + + ! set melt rate to a constant value for floating ice + ! allow basal melt in ice-free ocean cells, in case ice is advected to those cells by the transport scheme + + bmlt_float_rate = config_bmlt_float_flux / (config_ice_density*latent_heat_ice) ! convert W/m^2 to m/s + + floatingBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + if ( (li_mask_is_floating_ice(cellMask(iCell))) .or. & + (bedTopography(iCell) < config_sea_level .and. thermalCellMask(iCell) == 0) ) then + ! ice is present and floating, or ice-free ocean + + ! Provided xCell > bmlt_float_xlimit, prescribe a uniform basal melt rate. + ! The default is 0.0, but for MISMIP+ the prescribed value of xlimit is 480 km. + if (abs(xCell(iCell)) >= config_bmlt_float_xlimit) then ! basal melting is allowed + floatingBasalMassBal(iCell) = -bmlt_float_rate + endif + + endif ! ice is present and floating + + enddo ! iCell + + ! change units from m/s to kg/m2/s + floatingBasalMassBal(:) = floatingBasalMassBal(:) * config_ice_density + + elseif (trim(config_basal_mass_bal_float) == 'mismip') then + + ! compute melt rate (m/s) based on bed depth and cavity thickness + ! The MISMIP+ formula is as follows: + ! + ! bmlt_float = omega * tanh(H_c/H_0) * max(z_0 - z_d, 0) + ! + ! where H_c = lsrf - topg is the cavity thickness + ! z_d = lsrf - eus is the ice draft + ! omega = a time scale = 0.2 yr^{-1} by default + ! H_0 = 75 m by default + ! z_0 = -100 m by default + + ! allow basal melt in ice-free ocean cells, in case ice is advected to those cells by the transport scheme + + floatingBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + if ( (li_mask_is_floating_ice(cellMask(iCell))) .or. & + (bedTopography(iCell) < config_sea_level .and. thermalCellMask(iCell) == 0) ) then + ! ice is present and floating, or ice-free ocean + + hCavity = lowerSurface(iCell) - bedTopography(iCell) + zDraft = lowerSurface(iCell) - config_sea_level + floatingBasalMassBal(iCell) = -bmlt_float_omega * tanh(hCavity/bmlt_float_h0) * max(bmlt_float_z0 - & + zDraft, 0.0_RKIND) + + endif ! ice is present and floating + enddo ! iCell + + ! change units from m/s to kg/m2/s + floatingBasalMassBal(:) = floatingBasalMassBal(:) * config_ice_density + + elseif (trim(config_basal_mass_bal_float) == 'seroussi') then + + call basal_melt_thwaites_seroussi(floatingBasalMassBal, daysSinceStart, lowerSurface, cellMask, & + config_sea_level, config_ice_density, nCellsSolve, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_basal_mass_bal_float) == 'temperature_profile') then + + call iceshelf_melt_param_from_temp_profile(nCellsSolve, lowerSurface, cellMask, & + config_sea_level, config_ice_density, config_print_thermal_info, & + GLdepth, CFdepth, daysSinceStart, floatingBasalMassBal, err_tmp) + err = ior(err, err_tmp) + + else + + call mpas_log_write('Unknown option selected for config_basal_mass_bal_float:' // & + trim(config_basal_mass_bal_float), MPAS_LOG_ERR) + err = ior(err, 1) + + endif ! config_basal_mass_bal_float + + + ! clean up + call mpas_deallocate_scratch_field(thermalCellMaskField, .true.) + + block => block % next + enddo ! associated(block) + + + end subroutine li_basal_melt_floating_ice +!----------------------------------------------------------------------- + + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** + + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine basal_melt_thwaites_seroussi +! +!> \brief Calculate ice shelf melt rate from depth param. +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> Melt rate parameterization from: +!> Seroussi, H., Y. Nakayama, E. Larour, D. Menemenlis, M. Morlighem, E. Rignot, and A. Khazendar (2017), +!> Continued retreat of Thwaites Glacier, West Antarctica, controlled by bed topography and ocean circulation, +!> Geophys. Res. Lett., 1-9, doi:10.1002/2017GL072910. +!> for Thwaites Glacier. +!> Specifically, this is a linear fit of melt with shelf draft from the Supplemental Information, Figure S1. +!> The linear relation is modified by a: +!> * depth above which there is no melt (Antarctic Surface Water saturation) +!> * a maximum melt rate (Circumpolar Deep Water saturation) +!> * a depth below which melt stops increasing (minimum sill height) + +!----------------------------------------------------------------------- + + subroutine basal_melt_thwaites_seroussi(floatingBasalMassBal, daysSinceStart, lowerSurface, cellMask, & + config_sea_level, config_ice_density, nCellsSolve, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real(kind=RKIND), pointer, intent(in) :: daysSinceStart + real (kind=RKIND), dimension(:), pointer, intent(in) :: lowerSurface !< lower surface elevation (m) + integer, dimension(:), pointer, intent(in) :: & + cellMask !< bit mask describing whether ice is floating, dynamically active, etc. + real(kind=RKIND), pointer, intent(in) :: & + config_sea_level !< sea level (m) relative to z = 0 + real (kind=RKIND), pointer, intent(in) :: config_ice_density !< ice density + integer, pointer :: & + nCellsSolve !< number of locally owned cells + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), pointer, intent(out) :: & + floatingBasalMassBal !< basal mass balance for floating ice + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND) :: slopeSer ! slope of relation between depth and melt rate + real (kind=RKIND) :: interceptSer ! depth at which melting goes to 0 + real (kind=RKIND) :: maxMeltSer ! maximum allowable melt rate + real (kind=RKIND) :: sillDepth ! depth below which melt rate no longer increases + real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_amplitude + real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_period + real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_phase + real(kind=RKIND) :: hCavity ! depth of ice cavity beneath floating ice (m) + real(kind=RKIND) :: zDraft ! draft of floating ice (m below sea level) + integer :: iCell + + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_amplitude', & + config_basal_mass_bal_seroussi_amplitude) ! meters + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_period', & + config_basal_mass_bal_seroussi_period) ! years + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_phase', & + config_basal_mass_bal_seroussi_phase) ! cycles + + slopeSer = 0.088_RKIND ! slope of relation between depth and melt rate (melt (m/yr) per depth (m)) + interceptSer = -100.0_RKIND ! depth (m) at which melting goes to 0 (negative meaning below sea level) + maxMeltSer = 50.0_RKIND ! maximum allowable melt rate (m/yr) (positive meaning melting) + sillDepth = -650.0_RKIND ! depth below which melt stops increasing (m) (negative meaning below sea level) + + if (config_basal_mass_bal_seroussi_period <= 0.0_RKIND) then + call mpas_log_write("Value for config_basal_mass_bal_seroussi_period has to be a positive real value.", MPAS_LOG_ERR) + err = ior(err, 1) + endif + + ! Modify intercept height for variability parameters + interceptSer = interceptSer + config_basal_mass_bal_seroussi_amplitude * & + sin( (2.0_RKIND * pii / config_basal_mass_bal_seroussi_period) * (daysSinceStart/365.0_RKIND) & + + 2.0_RKIND * pii * config_basal_mass_bal_seroussi_phase) + + ! Initialize before computing + floatingBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + ! Shut off melt at an arbitrary shallow depth to discourage ice from disappearing. + if ( (li_mask_is_floating_ice(cellMask(iCell))) .and. (lowerSurface(iCell) < -10.0_RKIND) ) then + ! ice is present and floating + + zDraft = lowerSurface(iCell) - config_sea_level + ! Coefficients for m/yr melt rate (in units of Seroussi figure but without negative meaning melting) + floatingBasalMassBal(iCell) = max(-1.0_RKIND * maxMeltSer, min(0.0_RKIND, slopeSer * & + (max(zDraft, sillDepth) - interceptSer))) + + endif ! ice is present + enddo ! iCell + + ! change units from m/yr to kg/m2/s + floatingBasalMassBal(:) = floatingBasalMassBal(:) * config_ice_density / scyr + + + end subroutine basal_melt_thwaites_seroussi +!----------------------------------------------------------------------- + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine iceshelf_melt_param_from_temp_profile +! +!> \brief Calculate ice shelf melt rate using parameterization based on ocean temperature profile +!> \author Matthew Hoffman, based on parameterization developed by Xylar Asay-Davis +!> \date October 2018 +!> \details Melt rate parameterization by Xylar Asay-Davis used in Thwaites variability paper +!> in prep. by Hoffman et al. +!> Parameterization takes a ocean temperature profile and sill depth as input. +!> The temp. profile is adjusted for cavity temperature behind the sill, +!> assuming constant temp. below the sill depth. +!> Then a local temp profile in the boundary layer is estimated assuming exponential +!> decay from the cavity profile to a plume profile at infinite distance downstream +!> from the grounding line, accounting for the pressure-melt temperature. +!> The melt rate is calculated as proportional to the product of the boundary layer +!> temperature profile and the mean cavity temperature. +!----------------------------------------------------------------------- + + subroutine iceshelf_melt_param_from_temp_profile(nCellsSolve, lowerSurface, cellMask, & + config_sea_level, config_ice_density, print_debug, zGL, zCavityMax, daysSinceStart, floatingBasalMassBal, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, intent(in) :: nCellsSolve !< number of locally owned cells + real (kind=RKIND), dimension(:), intent(in) :: lowerSurface !< ice shelf draft + integer, dimension(:), intent(in) :: cellMask !< bitmask on cells + real (kind=RKIND), intent(in) :: config_sea_level, config_ice_density + logical, intent(in) :: print_debug + real(kind=RKIND), intent(in) :: zGL, & !< mean grounding line elevation (m), + !< it's a scalar now, but eventually may want smoothed spatial field + zCavityMax !< depth below which mean thermal forcing is averaged + real(kind=RKIND), pointer, intent(in) :: daysSinceStart + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(out) :: floatingBasalMassBal !< BMB for ice shelves + integer, intent(out) :: err + + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + ! parameters for param. + real(kind=RKIND), pointer :: slopeDraft !< characteristic slope of ice shelf draft + real(kind=RKIND), pointer :: plumeThickness !< characteristic plume thickness (m) + real(kind=RKIND), parameter :: E0 = 0.036_RKIND !< entrainment parameter (Jenkins 1991) + real(kind=RKIND), parameter :: Stanton = 5.9e-4_RKIND !< Stanton number (Jenkins et al. 2010) + + ! All depths are written as elevation relative to sea level (negative values) + ! variables describing regional profile + real(kind=RKIND), pointer :: zThermoclineTopBase + !< depth at top of thermocline (temp assumed constant above this), before variability adjustment + real(kind=RKIND), pointer :: zThermoclineBotBase + !< depth at bottom of thermocline (temp assumed constant below this), before variability adjustment + real(kind=RKIND) :: zThermoclineTop + !< depth at top of thermocline (temp assumed constant above this), after variability adjustment + real(kind=RKIND) :: zThermoclineBot + !< depth at bottom of thermocline (temp assumed constant below this), after variability adjustment + real(kind=RKIND), pointer :: TThermoclineTop !< temperature at top of thermocline (constant shallow water mass temp.) + real(kind=RKIND), pointer :: TThermoclineBot !< temperature at bottom of thermocline (constant deep water mass temp.) + + real(kind=RKIND), pointer :: variabilityAmplitude !< amplitude in optional sinusoidal variab. applied to thermocline depths + real(kind=RKIND), pointer :: variabilityPeriod !< period in optional sinusoidal variability applied to thermocline depths + real(kind=RKIND), pointer :: variabilityPhase !< phase in optional sinusoidal variability applied to thermocline depths + real(kind=RKIND) :: depthOffset !< depth offset in thermocline depths due to variability + + ! variables that are general to any depth + real(kind=RKIND), pointer :: zSill !< sill depth + real(kind=RKIND) :: TSill !< temperature at the sill depth + real(kind=RKIND) :: TGL !< temperature at grounding line depth + real(kind=RKIND) :: TFmean !< mean thermal forcing within the cavity + real(kind=RKIND), pointer :: kappa !< constant for non-local, quadratic melt param. + ! variables specific to a given depth + real(kind=RKIND) :: draft !< elevation of ice shelf draft (m) + real(kind=RKIND) :: Tregional !< regional temperature at given depth (comes from imposed profile) + real(kind=RKIND) :: Tcavity !< temperature in cavity at given depth + real(kind=RKIND) :: Tfreeze !< freezing temperature at given depth + real(kind=RKIND) :: Tinfinity !< limit of exponential decay of T at given depth + real(kind=RKIND) :: Tplume !< temperature in plume at a given depth + real(kind=RKIND) :: zeta !< length scale of decay of plume temperature + + ! other + integer :: nLevels + integer :: iCell, z + real(kind=RKIND) :: zr + + err = 0 + + ! get namelist options + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_melt_scale_factor', kappa) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_sill_elevation', zSill) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_plume_thickness', plumeThickness) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_draft_slope', slopeDraft) + ! Get temp. profile parameters + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_thermocline_upper_depth', zThermoclineTopBase) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_thermocline_upper_temp', TThermoclineTop) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_thermocline_lower_depth', zThermoclineBotBase) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_thermocline_lower_temp', TThermoclineBot) + ! Get variability parameters + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_variability_amplitude', variabilityAmplitude) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_variability_period', variabilityPeriod) + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_variability_phase', variabilityPhase) + + + ! Account for variability in thermocline depth + if (variabilityPeriod <= 0.0_RKIND) then + call mpas_log_write("Value for config_temperature_profile_variability_period must be a positive real value.", & + MPAS_LOG_ERR) + err = ior(err, 1) + endif + depthOffset = variabilityAmplitude * sin( (2.0_RKIND * pii / variabilityPeriod) * (daysSinceStart/365.0_RKIND) & + + 2.0_RKIND * pii * variabilityPhase) + zThermoclineTop = zThermoclineTopBase + depthOffset + zThermoclineBot = zThermoclineBotBase + depthOffset + + ! Calculate variables independent of depth + zeta = slopeDraft * plumeThickness / (E0 * slopeDraft + Stanton) + + ! calculate temperature at sill + if (zSill >= zThermoclineTop) then + TSill = TThermoclineTop + elseif (zSill <= zThermoclineBot) then + TSill = TThermoclineBot + else + TSill = (TThermoclineTop - TThermoclineBot) * & + (zSill - zThermoclineBot) / (zThermoclineTop - zThermoclineBot) + TThermoclineBot + endif + if (print_debug) then + call mpas_log_write("kappa=$r, plumeThickness=$r, slopeDraft=$r, zeta=$r", realArgs=(/kappa, plumeThickness, & + slopeDraft, zeta/)) + call mpas_log_write("TSill=$r at zSill=$r", realArgs=(/TSill, zSill/)) + endif + + ! calculate temperature at GL (allowing for no sill - sill deeper than GL depth) + if (zGL < zSill) then + TGL = TSill + elseif (zGL >= zThermoclineTop) then + TGL = TThermoclineTop + elseif (zGL <= zThermoclineBot) then + TGL = TThermoclineBot + else + TGL = (TThermoclineTop - TThermoclineBot) * & + (zGL - zThermoclineBot) / (zThermoclineTop - zThermoclineBot) + TThermoclineBot + endif + if (print_debug) then + call mpas_log_write("TGL=$r at zGL=$r", realArgs=(/TGL, zGL/)) + endif + + ! Need to calculate a mean thermal forcing within the plume + ! Ideally this would be a function of the ice shelf hypsometry, + ! but for simplicity calculate assuming a linear (ramp) ice shelf shape. + ! Average between the GL depth and an arbitrary ice shelf thickness at calving front. + ! (We think that is better than averaging all the way to surface, even if that thin + ! shelf thickness does not match that of the modeled ice shelf.) + TFmean = 0.0_RKIND + nLevels = 0 + do z = ceiling(zGL), floor(zCavityMax), 1 ! Discretize in 1 m increments + zr = real(z, kind=RKIND) ! convert once to real data type + nLevels = nLevels + 1 + ! calculate Tcavity for this depth (allowing for no sill - sill deeper than GL depth) + if (zr < zSill) then + Tcavity = TSill + elseif (zr >= zThermoclineTop) then + Tcavity = TThermoclineTop + elseif (zr <= zThermoclineBot) then + Tcavity = TThermoclineBot + else + Tcavity = (TThermoclineTop - TThermoclineBot) * & + (zr - zThermoclineBot) / (zThermoclineTop - zThermoclineBot) + TThermoclineBot + endif + ! calculate Tfreeze for this depth + Tfreeze = ocean_freezing_temperature(zr) + ! calculate Tinfinity for this depth + Tinfinity = (E0 * slopeDraft * Tcavity + Stanton * Tfreeze) / (E0 * slopeDraft + Stanton) + ! calculate Tplume for this depth + Tplume = Tinfinity + (TGL - Tinfinity) * exp(min(-1.0_RKIND * (zr - zGL) / zeta, 0.0_RKIND)) + ! now add to sum of TFmean + TFmean = TFmean + (Tplume - Tfreeze) + enddo + ! now find the average + if (nLevels > 0) then + TFmean = TFmean / real(nLevels, kind=RKIND) + else + call mpas_log_write("iceshelf_melt_param_from_temp_profile found 0 levels over which to average mean thermal forcing!",& + MPAS_LOG_ERR) + err = ior(err, 1) + endif + if (print_debug) then + call mpas_log_write("TFmean=$r", realArgs=(/TFmean/)) + endif + + ! Now calculate melt rate at each grid cell + do iCell = 1, nCellsSolve + if (li_mask_is_floating_ice(cellMask(iCell))) then + if (lowerSurface(iCell) > -20.0_RKIND) then + ! Shut off melt at an arbitrary shallow depth to prevent ice from melting out entirely + floatingBasalMassBal(iCell) = 0.0_RKIND + else + ! Calculate melt rate here + draft = lowerSurface(iCell) - config_sea_level + ! calculate Tcavity for this depth (allowing for no sill - sill deeper than GL depth) + if (draft < zSill) then + Tcavity = TSill + elseif (draft >= zThermoclineTop) then + Tcavity = TThermoclineTop + elseif (draft <= zThermoclineBot) then + Tcavity = TThermoclineBot + else + Tcavity = (TThermoclineTop - TThermoclineBot) * & + (draft - zThermoclineBot) / (zThermoclineTop - zThermoclineBot) + TThermoclineBot + endif + ! calculate Tfreeze for this depth + Tfreeze = ocean_freezing_temperature(draft) + ! calculate Tinfinity for this depth + Tinfinity = (E0 * slopeDraft * Tcavity + Stanton * Tfreeze) / (E0 * slopeDraft + Stanton) + ! calculate Tplume for this depth + Tplume = Tinfinity + (TGL - Tinfinity) * exp(min(-1.0_RKIND * (draft - zGL) / zeta, 0.0_RKIND)) + ! now add to sum of TFmean + floatingBasalMassBal(iCell) = kappa * TFmean * (Tplume - Tfreeze) ! in m/yr, +=melting + !floatingBasalMassBal(iCell) = Tplume + endif + else ! not floating + floatingBasalMassBal(iCell) = 0.0_RKIND + endif + enddo + floatingBasalMassBal(:) = floatingBasalMassBal(:) * (-1.0_RKIND) * config_ice_density / scyr ! convert to BMB + + end subroutine iceshelf_melt_param_from_temp_profile +!----------------------------------------------------------------------- + + + function ocean_freezing_temperature(depth) result(temp) + real(kind=RKIND), intent(in) :: depth + real(kind=RKIND), parameter :: Sref = 34.4_RKIND !< Reference salinity (made up, typical for MISOMIP1) + real(kind=RKIND) :: temp + + ! function to calculate freezing temperature from Jenkins (1991) + temp = 0.0901_RKIND - 0.0575_RKIND * SRef + 7.61e-4_RKIND * depth + + end function ocean_freezing_temperature +!----------------------------------------------------------------------- + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine calc_iceshelf_draft_info +! +!> \brief Calculate characteristic grounding line and calving front depths underneath ice shelves +!> \author Matthew Hoffman +!> \date October 2018 +!> \details Grounding line and calving front depths are used by the +!> temperature_profile iceshelf melt method, but must be calculated before the +!> method is called because they require a global reduce. +!> Different options for how to calculated these depths could be implemented. +!> For the GL depth, an approximation of the mean depth of deepest specified-fraction of the +!> grounding line is used. For the CF depth, the mean of the calving front +!> is used. +!----------------------------------------------------------------------- + + subroutine calc_iceshelf_draft_info(domain, GLdepth, CFdepth, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(out) :: GLdepth !< mean grounding line depth + real (kind=RKIND), intent(out) :: CFdepth !< mean calving front depth + integer, intent(out) :: err + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + + integer, pointer :: & + nCellsSolve ! number of locally owned cells + + logical, pointer :: & + config_print_thermal_info ! if true, print debug info + + real(kind=RKIND), pointer :: & + config_sea_level ! sea level (m) relative to z = 0 + + integer, dimension(:), pointer :: & + cellMask ! bit mask describing whether ice is floating, dynamically active, etc. + + real (kind=RKIND), dimension(:), pointer :: lowerSurface ! lower surface elevation (m) + + real (kind=RKIND), dimension(:), pointer :: areaCell ! cell area (m^2) + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnCell + + real(kind=RKIND) :: sumCFdepth, sumCFarea + integer :: iCell, jCell + real(kind=RKIND), dimension(2) :: localCFSums, globalCFSums ! Vars for CF global reduction + + real(kind=RKIND), parameter :: binWidth = 10.0_RKIND !< width of depth bins in meters + real(kind=RKIND), parameter :: binMinimum = -4000.0_RKIND !< depth of bottom of deepest bin in meters + integer, parameter :: nBinsMax = 500 !< Maximum possible number of bins - needs to be set to cover the range + !< defined by the previous two variables and sea level (which is run-time configurable) + integer :: nBins !< number of bins - calculated below + real(kind=RKIND), dimension(nBinsMax) :: binDepth !< depth in m at bottom of each bin + real(kind=RKIND), dimension(nBinsMax) :: binWeightSum !< sum of cell weights in this bin + real(kind=RKIND), dimension(nBinsMax) :: binWeightedDepthSum !< weighted-depth sum over cells in this bin + real(kind=RKIND), dimension(2*nBinsMax) :: localGLSums, globalGLSums ! Vars for GL global reduction + real(kind=RKIND) :: cumBinWeightedDepthSum, cumBinWeightSum !< cumulative sums over bins for final calculation + real(kind=RKIND) :: totalBinWeight !< sum of weights in all bins + real(kind=RKIND), pointer :: weightThreshold !< fraction of deepest GL depths to average overn + + integer :: indexBin + + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_temperature_profile_GL_depth_fraction', weightThreshold) + + ! need to first determine bin info (could happen on init, but cheap to recompute each timestep) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + nBins = ceiling((config_sea_level - binMinimum) / binWidth) ! round up + do indexBin = 1, nBins + binDepth = binMinimum + real(indexBin-1, kind=RKIND) * binWidth ! calc. lower limit of each bin + enddo + + ! Calculate average GL and CF depths on this processor, looping over blocks. + ! Note that these are somewhat approximate calculations. + ! Note that the GL in cellMask includes grounded cells next to open ocean, + ! which we do NOT want included, so instead we here identify the GL depth at + ! cells that are floating and have a grounded neighbor. + ! (Also, the GL cell mask identifies the last grounded cell, not the first floating cell, + ! which also isn't what we want.) + ! Also, ideally the weighted average for GL and CF depths would be weighted + ! based on GL and CF lengths. That is substantially more work for little + ! expected improvement (and we would have to make a decision about what elevation to + ! assign to edges), so I instead use cell area weighted averages. + ! The GL and CF depths do not need to be highly accurate, so these approximations + ! are assumed to be fine. + binWeightSum(:) = 0.0_RKIND + binWeightedDepthSum(:) = 0.0_RKIND + sumCFdepth = 0.0_RKIND + sumCFarea = 0.0_RKIND + ! init bins for GL calcs + ! block loop + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + do iCell = 1, nCellsSolve + ! First calc. GL stat + if (li_mask_is_floating_ice(cellMask(iCell)) .and. li_mask_is_dynamic_ice(cellMask(iCell))) then + ! See if a neighbor is grounded ice + do jCell = 1, nEdgesOnCell(iCell) + if (li_mask_is_grounded_ice(cellMask(cellsOnCell(jCell, iCell)))) then ! we found the first floating cell + ! check for reasonable values first + if (lowerSurface(iCell) < binMinimum) then + call mpas_log_write("calc_iceshelf_draft_info found a grounding line depth below the minimum search " // & + "depth of $r. Adjust binMinimum and related parameters and recompile.", & + MPAS_LOG_ERR, realArgs = (/binMinimum/)) + err = ior(err, 1) + return + elseif (lowerSurface(iCell) > config_sea_level) then + call mpas_log_write("calc_iceshelf_draft_info found a grounding line depth above sea level."& + // " Adjust hard-coded parameters and recompile.", MPAS_LOG_ERR) + err = ior(err, 1) + return + endif + ! Find which bin this location falls in + indexBin = floor((lowerSurface(iCell) - binMinimum) / binWidth) + 1 + + + ! Use the sqrt of cell area as the weighting function (it should be a linear measure, not an area measure) + binWeightedDepthSum(indexBin) = binWeightedDepthSum(indexBin) + lowerSurface(iCell) * sqrt(areaCell(iCell)) + binWeightSum(indexBin) = binWeightSum(indexBin) + sqrt(areaCell(iCell)) + !call mpas_log_write("GL cell: $i, $r, $r", intArgs=(/iCell/), & + !realArgs=(/lowerSurface(iCell), areaCell(iCell)/)) + exit ! skip the rest of the loop around neighbors + endif + enddo + endif + ! Now calc. CF stat + if (li_mask_is_floating_ice(cellMask(iCell)) .and. li_mask_is_dynamic_margin(cellMask(iCell))) then + ! accumulate area-weighted CF depth and area + sumCFdepth = sumCFdepth + lowerSurface(iCell) * areaCell(iCell) + sumCFarea = sumCFarea + areaCell(iCell) + endif + enddo + + block => block % next + enddo ! associated(block) + + ! --- Global reduction for GL stat --- + ! pack communication array + localGLSums(1:nBins) = binWeightedDepthSum(1:nBins) + localGLSums(nBins+1:2*nBins) = binWeightSum(1:nBins) + ! global reduction + call mpas_dmpar_sum_real_array(domain % dminfo, 2*nBins, localGLSums(:), globalGLSums(:)) + ! unpack communication array + binWeightedDepthSum(1:nBins) = globalGLSums(1:nBins) + binWeightSum(1:nBins) = globalGLSums(nBins+1:2*nBins) + ! sum up through bins until threshold is reached + totalBinWeight = sum(binWeightSum(1:nBins)) + if (totalBinweight > 0.0_RKIND) then + cumBinWeightedDepthSum = 0.0_RKIND + cumBinWeightSum = 0.0_RKIND + do indexBin = 1, nBins + cumBinWeightedDepthSum = cumBinWeightedDepthSum + binWeightedDepthSum(indexBin) + cumBinWeightSum = cumBinWeightSum + binWeightSum(indexBin) + ! check if we've reached threshold + if (cumBinWeightSum / totalBinweight > weightThreshold) then + ! We reached the desired threshold of deepest samples. Make final calculation and quit. + GLdepth = cumBinWeightedDepthSum / cumBinWeightSum + exit ! skip the rest of the loop over bins + endif + enddo + else + ! Most likely situation: no GL! So do nothing. + endif + + ! --- Global reduction for CF stat --- + localCFSums = (/sumCFdepth, sumCFarea/) + ! sum up over all procs + call mpas_dmpar_sum_real_array(domain % dminfo, 2, localCFSums(:), globalCFSums(:)) + if (globalCFSums(2) > 0.0_RKIND) then + CFdepth = globalCFSums(1) / globalCFSums(2) + else + CFdepth = 0.0_RKIND + endif + + end subroutine calc_iceshelf_draft_info +!----------------------------------------------------------------------- + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine iceshelf_melt_ismip6 +! +!> \brief Calculate ice shelf melt rate using parameterization from ISMIP6 +!> \author Matthew Hoffman +!> \date July 2019 +!> \details +!> http://www.climate-cryosphere.org/wiki/index.php?title=ISMIP6-Projections-Antarctica +!> It is a quadratic method of the form: +!> melt = gamma * physicalconstant * (TF(x,z,y_draft) + deltaT_basin) * | + deltatT_basin| +!> where TF is local thermal forcing at each grid cell and is the mean +!> thermal forcing for all of the ice shelf grid cells in an entire basin +!----------------------------------------------------------------------- + + subroutine iceshelf_melt_ismip6(domain, err) + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err + + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell + integer, pointer :: nOceanLayers + integer :: ksup, kk, kinf + integer, pointer :: nCellsSolve + real(kind=RKIND), pointer :: rhoi, rhosw + real(kind=RKIND) :: cste + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool, meshPool + real (kind=RKIND), dimension(:,:), pointer :: TFocean + real (kind=RKIND), dimension(:), pointer :: zOcean + real (kind=RKIND), dimension(:), pointer :: TFdraft + real (kind=RKIND), dimension(:), pointer :: deltaT + real (kind=RKIND), dimension(:), pointer :: ismip6shelfMelt_offset + integer, dimension(:), pointer :: basinNumber + real (kind=RKIND), dimension(:), pointer :: lowerSurface + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), allocatable :: mean_TF, IS_area + integer, parameter :: maxNumBasins = 32 + integer, dimension(:), pointer :: cellMask + real(kind=RKIND), dimension(maxNumBasins*2) :: localSums, globalSums + real (kind=RKIND), pointer :: gamma0 + real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal + real (kind=RKIND) :: coef + integer :: i + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhosw) + cste = (rhosw*cp_seawater/(rhoi*latent_heat_ice))**2 ! in K^(-2) + + allocate(mean_TF(maxNumBasins)) + allocate(IS_area(maxNumBasins)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nISMIP6OceanLayers', nOceanLayers) + + ! Get 3d thermal forcing (had to be read-in) + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_3dThermalForcing', TFocean) + + ! Get z-coords for ocean data + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_zOcean', zOcean) + + ! Get basin numbers + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_basin', basinNumber) + +! NOTE: Would need to do a global max for this check to work +! if (maxval(basinNumber)+1 > maxNumBasins) then +! call mpas_log_write("Field ismip6shelfMelt_basin contains a value greater than the hard-coded value" // & +! "for maxNumBasins in iceshelf_melt_ismip6. Please adjust maxNumBasins and recompile or correct input data.", & +! MPAS_LOG_ERR) +! end if + + ! Get shelf draft and mask + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + + ! Get TFdraft field that we will calculate + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_TFdraft', TFdraft) + + mean_TF(:) = 0.d0 + IS_area(:) = 0.d0 + + do iCell = 1, nCellsSolve + + if ( li_mask_is_floating_ice(cellMask(iCell)) ) then + ! 1 - Linear interpolation of the thermal forcing on the ice draft depth : + ksup=1 + do kk=2,nOceanLayers-1 + if ( zOcean(kk) >= lowerSurface(iCell) ) ksup = kk + enddo + kinf = ksup + 1 + if ((zOcean(ksup)-zOcean(kinf)) == 0) then + call mpas_log_write("iceshelf_melt_ismip6: Invalid value for zOcean. " // & + "ksup=$i kinf=$i zOcean(ksup)=$r zOcean(kinf)=$r iCell=$i lowerSurface=$r", MPAS_LOG_ERR, & + intArgs=(/ksup, kinf, iCell/), & + realArgs=(/zOcean(ksup), zOcean(kinf), lowerSurface(iCell) /) ) + err = ior(err, 1) + endif + !call mpas_log_write("kinf=$i, zOcean(kinf)=$r, TFocean=$r",realArgs=(/zOcean(kinf),TFocean(kinf,iCell)/), & + ! intArgs=(/kinf/)) + !call mpas_log_write("ksup=$i, zOcean(ksup)=$r, TFocean=$r",realArgs=(/zOcean(ksup),TFocean(ksup,iCell)/), & + ! intArgs=(/ksup/)) + TFdraft(iCell) = ( (zOcean(ksup)-lowerSurface(iCell)) * TFocean(kinf, iCell) & + + (lowerSurface(iCell)-zOcean(kinf)) * TFocean(ksup, iCell) ) / (zOcean(ksup)-zOcean(kinf)) + + ! 2 - Mean Thermal forcing in individual basins (NB: fortran norm while basins start at zero): + mean_TF(basinNumber(iCell)+1) = mean_TF(basinNumber(iCell)+1) + areaCell(iCell) * TFdraft(iCell) + IS_area(basinNumber(iCell)+1) = IS_area(basinNumber(iCell)+1) + areaCell(iCell) + + else + TFdraft(iCell) = 0.d0 + endif + enddo + + block => block % next + enddo ! associated(block) + + ! global sum mean_TF and IS_area + localSums(1:maxNumBasins) = mean_TF(:) + localSums(maxNumBasins+1:2*maxNumBasins) = IS_area(:) + call mpas_dmpar_sum_real_array(domain % dminfo, 2*maxNumBasins, localSums(:), globalSums(:)) + mean_TF(:) = globalSums(1:maxNumBasins) + IS_area(:) = globalSums(maxNumBasins+1:2*maxNumBasins) + do i = 1, maxNumBasins + if (IS_area(i) > 0.0_RKIND) then + mean_TF(i) = mean_TF(i) / IS_area(i) ! actual mean TF per basin + call mpas_log_write("basin=$i, mean_TF=$r", intArgs=(/i-1/), realArgs=(/mean_TF(i)/)) + else ! avoid divide by zero for invalid indices + mean_TF(i) = 0.0_RKIND + endif + enddo + + + ! Now calculate melt rate + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_gamma0', gamma0) + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_basin', basinNumber) + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_TFdraft', TFdraft) + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_deltaT', deltaT) + call mpas_pool_get_array(geometryPool, 'ismip6shelfMelt_offset', ismip6shelfMelt_offset) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + call mpas_log_write("iceshelf_melt_ismip6: gamma0=$r", realArgs=(/gamma0/)) + floatingBasalMassBal(:) = 0.0_RKIND + coef = gamma0 * cste / scyr * rhoi + do iCell = 1, nCellsSolve + if ( li_mask_is_floating_ice(cellMask(iCell)) ) then + floatingBasalMassBal(iCell) = -1.0_RKIND * coef * (TFdraft(iCell) + deltaT(iCell)) * & + abs(mean_TF(basinNumber(iCell)+1) + deltaT(iCell)) + ismip6shelfMelt_offset(iCell) + endif + enddo + + block => block % next + enddo ! associated(block) + + deallocate(mean_TF) + deallocate(IS_area) + + end subroutine iceshelf_melt_ismip6 +!----------------------------------------------------------------------- + +!----------------------------------------------------------------------- + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine grounded_face_melt_ismip6 +! +!> \brief Calculate grounded glacier submarine melt rate using parameterization from ISMIP6 +!> \author Trevor Hillebrand +!> \date April 2020 +!> \details +!> http://www.climate-cryosphere.org/wiki/index.php?title=ISMIP6-Projections-Greenland +!> https://www.the-cryosphere.net/14/985/2020/tc-14-985-2020.pdf +!> https://agupubs-onlinelibrary-wiley-com.lanl.idm.oclc.org/doi/full/10.1002/2016GL068784 +!> Melt parameterization of the form: +!> melt_rate = (A * h * q_sg^alpha + B) * TF^beta +!> where TF is local thermal forcing at each grid cell, h is water depth at ice front +!> q_sg is subglacial water glux in m/day. Default values A = 3e-4, alpha = 0.39, B = 0.15 +!> beta=1.18 +!----------------------------------------------------------------------- + + + subroutine grounded_face_melt_ismip6(domain, err) + + use li_calving + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell, iNeighbor, iEdge, nEmptyNeighbors + real (kind=RKIND), pointer :: rhoi + integer, pointer :: nCells, nCellsSolve + integer, dimension(:,:), pointer :: cellsOnCell, edgesOnCell + real (kind=RKIND) :: waterDepth + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool, meshPool, velocityPool + real (kind=RKIND), pointer :: seaLevel + real (kind=RKIND), dimension(:), pointer :: thickness, faceMeltingThickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: lowerSurface + real (kind=RKIND), dimension(:), pointer :: TFocean, ismip6Runoff + real (kind=RKIND), dimension(:), pointer :: areaCell + integer, dimension(:), pointer :: cellMask, edgeMask, nEdgesOnCell + real (kind=RKIND), pointer :: aSubglacial ! param A + real (kind=RKIND), pointer :: alphaSubglacial ! param alpha + real (kind=RKIND), pointer :: B ! param B + real (kind=RKIND), pointer :: betaTF ! param beta + real (kind=RKIND), pointer :: addTFocean ! adjust ocean thermal forcing + real (kind=RKIND), pointer :: config_uniform_face_melt_rate + character(len=StrKIND), pointer :: config_front_mass_bal_grounded + real (kind=RKIND), parameter :: secPerDay = 86400.0_RKIND + real (kind=RKIND), parameter :: rhow = 1000.0_RKIND ! subglacial runoff density + real (kind=RKIND), dimension(:), pointer :: faceMeltSpeed + real (kind=RKIND), dimension(:), allocatable :: faceMeltSpeedVertAvg !vertically averaged faceMeltSpeed + !to pass to li_apply_front_ablation_velocity, + !because faceMeltSpeed is only below water-line + integer :: err_tmp + logical :: applyToFloating, applyToGrounded, applyToGroundingLine + + err = 0 + call mpas_log_write('Starting face melt routine') + + ! Define logicals for call to li_apply_front_ablation_velocity + applyToFloating = .false. ! if true, only apply to floating ice + applyToGrounded = .false. ! if true, only apply to grounded + applyToGroundingLine = .true. ! if true, apply at grounding line + + ! Get sea level, bedTopography, ice density + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_sea_level', seaLevel) + + ! Get melt parameters + call mpas_pool_get_config(liConfigs, 'config_beta_ocean_thermal_forcing', betaTF) + call mpas_pool_get_config(liConfigs, 'config_alpha_subglacial_discharge', alphaSubglacial) + call mpas_pool_get_config(liConfigs, 'config_subglacial_discharge_coefficient', aSubglacial) + call mpas_pool_get_config(liConfigs, 'config_subglacial_discharge_intercept', B) + call mpas_pool_get_config(liConfigs, 'config_add_ocean_thermal_forcing', addTFocean) + call mpas_pool_get_config(liConfigs, 'config_uniform_face_melt_rate', config_uniform_face_melt_rate) + call mpas_pool_get_config(liConfigs, 'config_front_mass_bal_grounded', config_front_mass_bal_grounded) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + ! Get ISMIP6 forcing fields + call mpas_pool_get_array(geometryPool, 'ismip6_2dThermalForcing', TFocean) + call mpas_pool_get_array(geometryPool, 'ismip6Runoff', ismip6Runoff) + + ! Get mesh and geometry arrays + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'faceMeltingThickness', faceMeltingThickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'faceMeltSpeed', faceMeltSpeed) + + faceMeltSpeed(:) = 0.0_RKIND + allocate(faceMeltSpeedVertAvg(nCells+1)) + faceMeltSpeedVertAvg(:) = 0.0_RKIND + + ! Calculate face melting for each cell + do iCell = 1, nCellsSolve + + if ( bedTopography(iCell) < 0.0_RKIND ) then + waterDepth = seaLevel - bedTopography(iCell) + else + waterDepth = 0.0_RKIND + endif + + if (trim(config_front_mass_bal_grounded) == 'ismip6') then + ! Calculate ice front melt rate at each cell + faceMeltSpeed(iCell) = (aSubglacial * waterDepth * & ! m s^-1 + (ismip6Runoff(iCell) / rhow * secPerDay)**alphaSubglacial & + + B) * max(0.0_RKIND, TFocean(iCell) + addTFocean)**betaTF / secPerDay + elseif (trim(config_front_mass_bal_grounded) == 'uniform') then + faceMeltSpeed(iCell) = config_uniform_face_melt_rate + endif + + enddo + + where ( (thickness > 0.0_RKIND) .and. (lowerSurface < 0.0_RKIND) ) + faceMeltSpeedVertAvg(:) = faceMeltSpeed(:) * abs(lowerSurface(:)) / thickness(:) + end where + + call mpas_log_write("calling li_apply_front_ablation_velocity from grounded_face_melt_ismip6") + ! Distribute melt among neighbors + call li_apply_front_ablation_velocity(meshPool, geometryPool, velocityPool, & + faceMeltingThickness, faceMeltSpeedVertAvg, applyToGrounded, & + applyToFloating, applyToGroundingLine, domain, err) + + + ! Update halos on calvingThickness or faceMeltingThickness before applying it. + ! Testing seemed to indicate this is not necessary, but I don't understand + ! why not, so leaving it. + ! NOTE: THIS WILL NOT WORK ON MULTIPLE BLOCKS PER PROCESSOR + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'faceMeltingThickness') + call mpas_timer_stop("halo updates") + + ! Apply facemelt: open the Ark + thickness(:) = thickness(:) - faceMeltingThickness(:) + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + block => block % next + enddo ! associated(block) + + deallocate(faceMeltSpeedVertAvg) + + end subroutine grounded_face_melt_ismip6 +!----------------------------------------------------------------------- + + + !*********************************************************************** + end module li_iceshelf_melt + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + + diff --git a/src/core_landice/mode_forward/mpas_li_sia.F b/src/core_landice/mode_forward/mpas_li_sia.F index dd67a365b0..b22001c4f2 100644 --- a/src/core_landice/mode_forward/mpas_li_sia.F +++ b/src/core_landice/mode_forward/mpas_li_sia.F @@ -297,7 +297,7 @@ subroutine li_sia_solve(meshPool, geometryPool, thermalPool, velocityPool, err) real (kind=RKIND), dimension(:), pointer :: upperSurface integer, dimension(:,:), pointer :: baryCellsOnVertex real (kind=RKIND), dimension(:,:), pointer :: baryWeightsOnVertex - integer :: cell1_is_dynamic, cell2_is_dynamic + real (kind=RKIND) :: cell1_is_dynamic, cell2_is_dynamic integer :: err_tmp err = 0 @@ -398,8 +398,8 @@ subroutine li_sia_solve(meshPool, geometryPool, thermalPool, velocityPool, err) if ( li_mask_is_dynamic_ice(edgeMask(iEdge)) ) then cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - cell1_is_dynamic = li_mask_is_dynamic_ice_int(cellMask(cell1)) - cell2_is_dynamic = li_mask_is_dynamic_ice_int(cellMask(cell2)) + cell1_is_dynamic = real(li_mask_is_dynamic_ice_int(cellMask(cell1)), kind=RKIND) + cell2_is_dynamic = real(li_mask_is_dynamic_ice_int(cellMask(cell2)), kind=RKIND) ! Calculate thickness on edge - 2nd order thicknessEdge = (thickness(cell1) + thickness(cell2) ) * 0.5_RKIND @@ -416,9 +416,7 @@ subroutine li_sia_solve(meshPool, geometryPool, thermalPool, velocityPool, err) do iLevel = nVertInterfaces-1, 1, -1 ! Loop upwards from second lowest level to surface ! Calculate flwa on edge for this level - 2nd order, except can't do centered difference ! into areas where flwa may not be valid, so excluding the downwind flwa value in non-dynamic cells -! flwaLevelEdge = (flowParamA(iLevel, cell1) + flowParamA(iLevel, cell2) ) * 0.5_RKIND - flwaLevelEdge = (flowParamA(iLevel, cell1) * cell1_is_dynamic + & - flowParamA(iLevel, cell2) * cell2_is_dynamic) / & + flwaLevelEdge = (flowParamA(iLevel, cell1) * cell1_is_dynamic + flowParamA(iLevel, cell2) * cell2_is_dynamic) / & (cell1_is_dynamic + cell2_is_dynamic) ! Calculate SIA velocity for this layer interface by adding on incremental velocity for the layer below diff --git a/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F b/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F index 0bb10c0312..4fd30f26c8 100644 --- a/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F +++ b/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F @@ -93,6 +93,7 @@ subroutine li_SGH_init(domain, err) !----------------------------------------------------------------- ! Pools pointers logical, pointer :: config_SGH + logical, pointer :: config_ocean_connection_N type (block_type), pointer :: block type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: hydroPool @@ -117,10 +118,15 @@ subroutine li_SGH_init(domain, err) call mpas_pool_get_config(liConfigs, 'config_SGH', config_SGH) if (.not. config_SGH) then + call mpas_pool_get_config(liConfigs, 'config_ocean_connection_N', config_ocean_connection_N) + if (config_ocean_connection_N) then + call ocean_connection_N(domain) + endif ! If SGH is not active, skip everything return endif + call mpas_timer_start("hydro init") call mpas_log_write('Beginning subglacial hydro init.') @@ -226,6 +232,7 @@ subroutine li_SGH_solve(domain, err) !----------------------------------------------------------------- ! Pools pointers logical, pointer :: config_SGH + logical, pointer :: config_ocean_connection_N logical, pointer :: config_SGH_chnl_active character (len=StrKIND), pointer :: config_SGH_basal_melt type (block_type), pointer :: block @@ -278,6 +285,10 @@ subroutine li_SGH_solve(domain, err) call mpas_pool_get_config(liConfigs, 'config_SGH', config_SGH) if (.not. config_SGH) then + call mpas_pool_get_config(liConfigs, 'config_ocean_connection_N', config_ocean_connection_N) + if (config_ocean_connection_N) then + call ocean_connection_N(domain) + endif ! If SGH is not active, skip everything return endif @@ -335,7 +346,7 @@ subroutine li_SGH_solve(domain, err) call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) call mpas_pool_get_array(geometryPool,'groundedBasalMassBal',groundedBasalMassBal) - basalMeltInput = -1.0 * groundedBasalMassBal ! TODO: Ensure positive flux? + basalMeltInput = -1.0_RKIND * groundedBasalMassBal ! TODO: Ensure positive flux? elseif (trim(config_SGH_basal_melt) == 'basal_heat') then call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) @@ -691,13 +702,16 @@ subroutine calc_edge_quantities(block, err) type (mpas_pool_type), pointer :: geometryPool type (mpas_pool_type), pointer :: hydroPool real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: thickness real (kind=RKIND), dimension(:), pointer :: hydropotentialBase + real (kind=RKIND), dimension(:), pointer :: hydropotential real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseVertex real (kind=RKIND), dimension(:), pointer :: waterPressure real (kind=RKIND), dimension(:), pointer :: waterThicknessEdge real (kind=RKIND), dimension(:), pointer :: waterThicknessEdgeUpwind real (kind=RKIND), dimension(:), pointer :: waterThickness real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseSlopeNormal + real (kind=RKIND), dimension(:), pointer :: hydropotentialSlopeNormal real (kind=RKIND), dimension(:), pointer :: waterPressureSlopeNormal real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseSlopeTangent real (kind=RKIND), dimension(:), pointer :: gradMagPhiEdge @@ -720,6 +734,8 @@ subroutine calc_edge_quantities(block, err) real (kind=RKIND), pointer :: alpha, beta real (kind=RKIND), pointer :: conduc_coeff character (len=StrKIND), pointer :: config_SGH_tangent_slope_calculation + real (kind=RKIND), pointer :: config_sea_level + real (kind=RKIND), pointer :: rhoo integer, pointer :: nEdges integer, pointer :: nCells integer, pointer :: nVertices @@ -745,14 +761,19 @@ subroutine calc_edge_quantities(block, err) call mpas_pool_get_config(liConfigs, 'config_SGH_beta', beta) call mpas_pool_get_config(liConfigs, 'config_SGH_conduc_coeff', conduc_coeff) call mpas_pool_get_config(liConfigs, 'config_SGH_tangent_slope_calculation', config_SGH_tangent_slope_calculation) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhoo) call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) call mpas_pool_get_array(hydroPool, 'waterPressure', waterPressure) call mpas_pool_get_array(hydroPool, 'hydropotentialBase', hydropotentialBase) + call mpas_pool_get_array(hydroPool, 'hydropotential', hydropotential) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_array(hydroPool, 'waterThicknessEdge', waterThicknessEdge) call mpas_pool_get_array(hydroPool, 'waterThicknessEdgeUpwind', waterThicknessEdgeUpwind) call mpas_pool_get_array(hydroPool, 'hydropotentialBaseSlopeNormal', hydropotentialBaseSlopeNormal) + call mpas_pool_get_array(hydroPool, 'hydropotentialSlopeNormal', hydropotentialSlopeNormal) call mpas_pool_get_array(hydroPool, 'waterPressureSlopeNormal', waterPressureSlopeNormal) call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) @@ -767,6 +788,7 @@ subroutine calc_edge_quantities(block, err) call mpas_pool_get_array(hydroPool, 'waterFluxAdvec', waterFluxAdvec) call mpas_pool_get_array(hydroPool, 'waterFluxDiffu', waterFluxDiffu) call mpas_pool_get_array(hydroPool, 'waterFluxMask', waterFluxMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) do iEdge = 1, nEdges @@ -784,14 +806,74 @@ subroutine calc_edge_quantities(block, err) endif hydropotentialBaseSlopeNormal(iEdge) = (hydropotentialBase(cell2) - hydropotentialBase(cell1)) / dcEdge(iEdge) + hydropotentialSlopeNormal(iEdge) = (hydropotential(cell2) - hydropotential(cell1)) / dcEdge(iEdge) waterPressureSlopeNormal(iEdge) = (waterPressure(cell2) - waterPressure(cell1)) / dcEdge(iEdge) end do + ! At terrestrial margin, ignore the downslope bed topography gradient. Including it can lead to unrealistically large + ! hydropotential gradients and unstable channel growth. + ! We also want to do this at marine margins because otherwise the offshore topography can create a barrier to flow, + ! but that is unrealistic. + ! So for all boundaries of the hydro system, the hydropotential at the margin should be determined by the geometry + ! at the edge of the cell in a 1-sided sense + do iEdge = 1, nEdges + if ( (li_mask_is_margin(edgeMask(iEdge)) .and. li_mask_is_grounded_ice(edgeMask(iEdge))) .or. & + (li_mask_is_grounding_line(edgeMask(iEdge)))) 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) = (rho_water * gravity * bedTopography(cell1) + & + max(rhoo * gravity * (config_sea_level - bedTopography(cell1)), 0.0_RKIND) - & + hydropotentialBase(cell1)) / dcEdge(iEdge) + hydropotentialSlopeNormal(iEdge) = (rho_water * gravity * bedTopography(cell1) + & + max(rhoo * gravity * (config_sea_level - bedTopography(cell1)), 0.0_RKIND) - & + hydropotential(cell1)) / dcEdge(iEdge) + else ! cell1 is the icefree cell - replace phi there with cell2 Phig + hydropotentialBaseSlopeNormal(iEdge) = (hydropotentialBase(cell2) - & + ( rho_water * gravity * bedTopography(cell2) + & + max(rhoo * gravity * (config_sea_level - bedTopography(cell2)), 0.0_RKIND) ) ) / dcEdge(iEdge) + hydropotentialSlopeNormal(iEdge) = (hydropotential(cell2) - & + ( rho_water * gravity * bedTopography(cell2) + & + max(rhoo * gravity * (config_sea_level - bedTopography(cell2)), 0.0_RKIND) ) ) / dcEdge(iEdge) + endif ! which cell is icefree + endif ! if edge of grounded ice + end do + + ! Disallow flow from ocean to glacier, or land to glacier, + ! which can occur under some circumstances + ! For ocean this is invalid because ocean water has a different density! + ! For land this would only happen if there is a supply of water, which is not currently handled. + ! Do this by simply zeroing the hydropotential gradient in those cases. + ! (Do this step only after the other hydropotential special cases are treated above.) + do iEdge = 1, nEdges + ! Find edges along GL or margin to check for 'backwards' flow + if ((li_mask_is_grounding_line(edgeMask(iEdge))) .or. & + li_mask_is_margin(edgeMask(iEdge)) ) then + ! Now check if flow is backwards + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + if (hydropotentialBaseSlopeNormal(iEdge) > 0.0_RKIND) then + ! flow is from cell2 to cell1 + if (.not. li_mask_is_grounded_ice(cellMask(cell2))) then + hydropotentialBaseSlopeNormal(iEdge) = 0.0_RKIND + hydropotentialSlopeNormal(iEdge) = 0.0_RKIND + endif + else ! phi<=0 + ! flow is from cell1 to cell2 + if (.not. li_mask_is_grounded_ice(cellMask(cell1))) then + hydropotentialBaseSlopeNormal(iEdge) = 0.0_RKIND + hydropotentialSlopeNormal(iEdge) = 0.0_RKIND + endif + endif + endif ! GL edge or grounded margin + end do + ! Calculate tangent slope of hydropotentialBase - three possible methods to consider ! Calculate hydropotentialBaseVertex if needed - call mpas_pool_get_array(hydroPool, 'hydropotentialBaseVertex', hydropotentialBaseVertex) ! this array could be protected by logic if desired + call mpas_pool_get_array(hydroPool, 'hydropotentialBaseVertex', hydropotentialBaseVertex) + ! < this array could be protected by logic if desired select case (trim(config_SGH_tangent_slope_calculation)) case ('from_vertex_barycentric') call mpas_pool_get_array(meshPool, 'baryCellsOnVertex', baryCellsOnVertex) @@ -830,13 +912,8 @@ subroutine calc_edge_quantities(block, err) gradMagPhiEdge = sqrt(hydropotentialBaseSlopeNormal**2 + hydropotentialBaseSlopeTangent**2) ! calculate effective conductivity on edges - ! OLD: USE REGULARIZATION: effectiveConducEdge(:) = conduc_coeff * waterThicknessEdge(:)**(alpha-1.0_RKIND) * (gradMagPhiEdge(:)+1.0e-10_RKIND)**(beta - 2.0_RKIND) ! 1e-10 used for regularization - ! Do not calculate the conductivity where it is tiny to avoid blowups - where(gradMagPhiEdge < 0.01_RKIND) - effectiveConducEdge(:) = 0.0_RKIND - elsewhere - effectiveConducEdge(:) = conduc_coeff * waterThicknessEdge(:)**(alpha-1.0_RKIND) * gradMagPhiEdge(:)**(beta - 2.0_RKIND) - end where + effectiveConducEdge(:) = conduc_coeff * waterThicknessEdge(:)**(alpha-1.0_RKIND) *& + (gradMagPhiEdge(:)+1.0e-30_RKIND)**(beta - 2.0_RKIND) ! small value used for regularization ! calculate diffusivity on edges diffusivity(:) = rho_water * gravity * effectiveConducEdge(:) * waterThicknessEdge(:) @@ -866,6 +943,12 @@ subroutine calc_edge_quantities(block, err) waterFluxDiffu = 0.0_RKIND waterVelocity = 0.0_RKIND end where + where (waterThicknessEdgeUpwind == 0.0_RKIND) ! if no water to give, should have no flux! + waterFluxAdvec = 0.0_RKIND ! Should already be 0 + waterFluxDiffu = 0.0_RKIND ! Might not be 0 + waterVelocity = 0.0_RKIND + end where + waterFlux(:) = waterFluxAdvec(:) + waterFluxDiffu(:) !-------------------------------------------------------------------- @@ -923,7 +1006,7 @@ subroutine check_timestep(domain, timeLeft, err) type (block_type), pointer :: block real (kind=RKIND), pointer :: deltat integer, pointer :: nEdgesSolve - logical, pointer :: config_SGH_chnl_active + logical, pointer :: config_SGH_chnl_active, config_SGH_chnl_include_DCFL ! in the following variables, "Proc" indicates the value on the current processor, ! and "Block" indicates value on current block real (kind=RKIND) :: dtSGHadvecBlock, dtSGHadvecProc @@ -992,10 +1075,13 @@ subroutine check_timestep(domain, timeLeft, err) if (config_SGH_chnl_active) then ! Calculate channel advection limited time step - dtSGHadvecChanBlock = 0.5_RKIND * minval(dcEdge(1:nEdgesSolve) / (abs(channelVelocity(1:nEdgesSolve)) + 1.0e-12_RKIND)) ! regularize + dtSGHadvecChanBlock = 0.5_RKIND * minval(dcEdge(1:nEdgesSolve) / (abs(channelVelocity(1:nEdgesSolve)) & + + 1.0e-12_RKIND)) + ! regularize dtSGHadvecChanProc = min(dtSGHadvecChanProc, dtSGHadvecChanBlock) ! Calculate channel diffusion limited time step - dtSGHdiffuChanBlock = 0.25_RKIND * minval(dcEdge(1:nEdgesSolve)**2 / (channelDiffusivity(1:nEdgesSolve) + 1.0e-12_RKIND)) + dtSGHdiffuChanBlock = 0.25_RKIND * minval(dcEdge(1:nEdgesSolve)**2 / (channelDiffusivity(1:nEdgesSolve) + & + 1.0e-12_RKIND)) dtSGHdiffuChanProc = min(dtSGHdiffuChanProc, dtSGHdiffuChanBlock) endif @@ -1035,11 +1121,15 @@ subroutine check_timestep(domain, timeLeft, err) ! --- call mpas_pool_get_config(liConfigs, 'config_SGH_adaptive_timestep_fraction', CFLfraction) call mpas_pool_get_config(liConfigs, 'config_SGH_max_adaptive_timestep', maxDt) + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_include_DCFL', config_SGH_chnl_include_DCFL) ! Find smallest of 3 or 5 limiting time steps proposedDt = min(deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure) if (config_SGH_chnl_active) then - proposedDt = min(proposedDt, deltatSGHadvecChannel, deltatSGHdiffuChannel) + proposedDt = min(proposedDt, deltatSGHadvecChannel) + if (config_SGH_chnl_include_DCFL) then + proposedDt = min(proposedDt, deltatSGHdiffuChannel) + endif endif proposedDt = proposedDt * CFLfraction @@ -1048,7 +1138,8 @@ subroutine check_timestep(domain, timeLeft, err) if (proposedDt >= timeLeft) then if (config_SGH_chnl_active) then call mpas_log_write("deltatSGH: used=$r, advec=$r, diffu=$r, pressure=$r, advecChannel=$r, diffuChannel=$r", & - realArgs=(/deltatSGH, deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure, deltatSGHadvecChannel, deltatSGHdiffuChannel/)) + realArgs=(/deltatSGH, deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure, deltatSGHadvecChannel, & + deltatSGHdiffuChannel/)) else call mpas_log_write("deltatSGH: used=$r, advec=$r, diffu=$r, pressure=$r", & realArgs=(/deltatSGH, deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure/)) @@ -1092,10 +1183,12 @@ subroutine check_timestep(domain, timeLeft, err) call mpas_log_write("deltatSGH > deltatSGHpressure $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHpressure/)) endif if (config_SGH_chnl_active .and. (deltatSGH > deltatSGHadvecChannel)) then - call mpas_log_write("deltatSGH > deltatSGHadvecChannel $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHadvecChannel/)) + call mpas_log_write("deltatSGH > deltatSGHadvecChannel $r > $r", MPAS_LOG_WARN, & + realArgs=(/deltatSGH, deltatSGHadvecChannel/)) endif - if (config_SGH_chnl_active .and. (deltatSGH > deltatSGHdiffuChannel)) then - call mpas_log_write("deltatSGH > deltatSGHdiffuChannel $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHdiffuChannel/)) + if (config_SGH_chnl_active .and. config_SGH_chnl_include_DCFL .and. (deltatSGH > deltatSGHdiffuChannel)) then + call mpas_log_write("deltatSGH > deltatSGHdiffuChannel $r > $r", MPAS_LOG_WARN, & + realArgs=(/deltatSGH, deltatSGHdiffuChannel/)) endif !-------------------------------------------------------------------- @@ -1163,10 +1256,12 @@ subroutine calc_pressure(block, err) real (kind=RKIND), pointer :: creepCoeff real (kind=RKIND), pointer :: porosity integer, pointer :: nVertLevels + integer, pointer :: nCells character (len=StrKIND), pointer :: config_SGH_pressure_calc real (kind=RKIND), pointer :: config_sea_level real (kind=RKIND), pointer :: rhoo integer :: err_tmp + integer :: iCell err = 0 err_tmp = 0 @@ -1189,6 +1284,7 @@ subroutine calc_pressure(block, err) call mpas_pool_get_config(liConfigs, 'config_SGH_pressure_calc', config_SGH_pressure_calc) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) call mpas_pool_get_array(hydroPool, 'waterPressure', waterPressure) @@ -1217,7 +1313,8 @@ subroutine calc_pressure(block, err) ! basalMeltInput(:) / rhoi ! Hewitt 2011 opening openingRate = max(0.0_RKIND, openingRate) - closingRate(:) = creepCoeff * flowParamA(nVertLevels, :) * effectivePressure(:)**3 * waterThickness(:) + closingRate(:) = creepCoeff * flowParamA(nVertLevels, :) * & + effectivePressure(:)**3 * waterThickness(:) ! closingRate(:) = waterThickness(:) * effectivePressure(:) / 1.0e13_RKIND ! ! Hewitt 2011 creep closure form. Denominator is ice viscosity @@ -1234,7 +1331,8 @@ subroutine calc_pressure(block, err) elsewhere (.not. li_mask_is_ice(cellMask)) waterPressure = 0.0_RKIND elsewhere - waterPressure = (zeroOrderSum - divergence - divergenceChannel - channelAreaChangeCell) * rho_water * gravity * deltatSGH / porosity + waterPressureOld + waterPressure = (zeroOrderSum - divergence - divergenceChannel - channelAreaChangeCell) * & + rho_water * gravity * deltatSGH / porosity + waterPressureOld end where case ('overburden') @@ -1254,10 +1352,12 @@ subroutine calc_pressure(block, err) waterPressure = max(0.0_RKIND, waterPressure) waterPressure = min(waterPressure, rhoi * gravity * thickness) ! set pressure correctly under floating ice and open ocean - where ( (li_mask_is_floating_ice(cellMask)) .or. & - ((.not. li_mask_is_ice(cellMask)) .and. (bedTopography < config_sea_level) ) ) - waterPressure = rhoo * gravity * (config_sea_level - bedTopography) - end where + do iCell = 1, nCells + if ( li_mask_is_floating_ice(cellMask(iCell)) .or. & + ((.not. li_mask_is_ice(cellMask(iCell))) .and. (bedTopography(iCell) < config_sea_level) ) ) then + waterPressure(iCell) = rhoo * gravity * (config_sea_level - bedTopography(iCell)) + endif + enddo waterPressureTendency = (waterPressure - waterPressureOld) / deltatSGH @@ -1330,8 +1430,9 @@ subroutine calc_pressure_diag_vars(block, err) call mpas_pool_get_array(hydroPool, 'hydropotential', hydropotential) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) - effectivePressure = rhoi * gravity * thickness - waterPressure ! this should evalute to 0 for floating ice if Pw set correctly there. - where (.not. li_mask_is_ice(cellmask)) + effectivePressure = rhoi * gravity * thickness - waterPressure + ! < this should evalute to 0 for floating ice if Pw set correctly there. + where (.not. li_mask_is_grounded_ice(cellmask)) effectivePressure = 0.0_RKIND ! zero effective pressure where no ice to avoid confusion end where @@ -1400,7 +1501,7 @@ subroutine update_channel(block, err) real (kind=RKIND), dimension(:), pointer :: channelVelocity real (kind=RKIND), dimension(:), pointer :: gradMagPhiEdge real (kind=RKIND), dimension(:), pointer :: waterFlux - real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseSlopeNormal + real (kind=RKIND), dimension(:), pointer :: hydropotentialSlopeNormal real (kind=RKIND), dimension(:), pointer :: waterPressureSlopeNormal real (kind=RKIND), dimension(:), pointer :: channelOpeningRate real (kind=RKIND), dimension(:), pointer :: channelClosingRate @@ -1445,7 +1546,7 @@ subroutine update_channel(block, err) call mpas_pool_get_array(hydroPool, 'channelDischarge', channelDischarge) call mpas_pool_get_array(hydroPool, 'channelVelocity', channelVelocity) call mpas_pool_get_array(hydroPool, 'gradMagPhiEdge', gradMagPhiEdge) - call mpas_pool_get_array(hydroPool, 'hydropotentialBaseSlopeNormal', hydropotentialBaseSlopeNormal) + call mpas_pool_get_array(hydroPool, 'hydropotentialSlopeNormal', hydropotentialSlopeNormal) call mpas_pool_get_array(hydroPool, 'waterPressureSlopeNormal', waterPressureSlopeNormal) call mpas_pool_get_array(hydroPool, 'waterFlux', waterFlux) call mpas_pool_get_array(hydroPool, 'channelOpeningRate', channelOpeningRate) @@ -1465,7 +1566,8 @@ subroutine update_channel(block, err) where(gradMagPhiEdge < 0.01_RKIND) channelDischarge(:) = 0.0_RKIND elsewhere - channelDischarge = -1.0_RKIND * Kc * channelArea**alpha_c * gradMagPhiEdge**(beta_c - 2.0_RKIND) * hydropotentialBaseSlopeNormal + channelDischarge = -1.0_RKIND * Kc * channelArea**alpha_c * gradMagPhiEdge**(beta_c - 2.0_RKIND) * & + hydropotentialSlopeNormal end where where (waterFluxMask == 2) @@ -1473,12 +1575,20 @@ subroutine update_channel(block, err) channelArea = 0.0_RKIND end where - ! Note: an edge with only one grounded cell neighbor is called floating, so this logic retains channel vars on those edges to allow channel discharge across GL + ! Note: an edge with only one grounded cell neighbor is called floating, so this logic retains channel vars + ! on those edges to allow channel discharge across GL where (.not. ( (li_mask_is_grounded_ice(edgeMask)) .or. (li_mask_is_grounding_line(edgeMask)) ) ) channelArea = 0.0_RKIND channelDischarge = 0.0_RKIND end where + ! Disable channels from forming if there is no sheet flux + ! TODO: Make a function of sheet dissipation threshold? + where (abs(waterFlux) <= 1e-10_RKIND) + channelArea = 0.0_RKIND + channelDischarge = 0.0_RKIND + end where + channelVelocity = channelDischarge / (channelArea + 1.0e-12_RKIND) ! diffusivity used only to limit channel dt right now @@ -1489,8 +1599,8 @@ subroutine update_channel(block, err) Kc * channelArea**(alpha_c - 1.0_RKIND) * gradMagPhiEdge**(beta_c - 2.0_RKIND)) end where - channelMelt = (abs(channelDischarge * hydropotentialBaseSlopeNormal) & ! channel dissipation - + abs(waterFlux * hydropotentialBaseSlopeNormal * config_SGH_incipient_channel_width) & ! some sheet dissipation + channelMelt = (abs(channelDischarge * hydropotentialSlopeNormal) & ! channel dissipation + + abs(waterFlux * hydropotentialSlopeNormal * config_SGH_incipient_channel_width) & !some sheet dissipation ) / latent_heat_ice channelPressureFreeze = -1.0_RKIND * iceMeltingPointPressureDependence * cp_freshwater * rho_water * & (channelDischarge + waterFlux * config_SGH_incipient_channel_width) & @@ -1509,7 +1619,7 @@ subroutine update_channel(block, err) cell2 = cellsOnEdge(2, iEdge) ! Not sure if these ought to be upwind average, but using centered - flowParamAChannel(iEdge) = 0.5_RKIND * ( flowParamA(nVertLevels, cell1) + flowParamA(nVertLevels, cell2) ) + flowParamAChannel(iEdge) = 0.5_RKIND * (flowParamA(nVertLevels, cell1) + flowParamA(nVertLevels, cell2) ) channelEffectivePressure(iEdge) = 0.5_RKIND * (effectivePressure(cell1) + effectivePressure(cell2)) end do channelClosingRate(:) = creep_coeff * channelArea(:) * flowParamAChannel(:) * channelEffectivePressure(:)**3 @@ -1599,7 +1709,8 @@ subroutine evolve_channel(block, err) iEdge = edgesOnCell(iEdgeOnCell, iCell) ! add on advective & diffusive fluxes divergenceChannel(iCell) = divergenceChannel(iCell) - channelDischarge(iEdge) * edgeSignOnCell(iEdgeOnCell, iCell) - channelAreaChangeCell(iCell) = channelChangeRate(iEdge) * dcEdge(iEdge) * 0.5_RKIND ! only half of channel is in this cell + channelAreaChangeCell(iCell) = channelChangeRate(iEdge) * dcEdge(iEdge) * 0.5_RKIND + ! < only half of channel is in this cell end do ! edges end do ! cells divergenceChannel(1:nCellsSolve) = divergenceChannel(1:nCellsSolve) / areaCell(1:nCellsSolve) @@ -1739,7 +1850,8 @@ subroutine shmip_timevarying_forcing(block, err) call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) temperature0 = -16.0_RKIND * cos(2.0_RKIND * pii / 365.0 * daysSinceStart) - 5.0_RKIND + DT - externalWaterInput(:) = (upperSurface(:) * (-0.0075_RKIND) + temperature0) * (0.01_RKIND / 86400.0_RKIND) * 1000.0_RKIND ! the 1000 factor converts from m/s to kg/m2/s + externalWaterInput(:) = (upperSurface(:) * (-0.0075_RKIND) + temperature0) * (0.01_RKIND / 86400.0_RKIND) * 1000.0_RKIND + ! < the 1000 factor converts from m/s to kg/m2/s where (externalWaterInput < 0.0_RKIND) externalWaterInput = 0.0_RKIND end where @@ -1754,4 +1866,65 @@ subroutine shmip_timevarying_forcing(block, err) end subroutine shmip_timevarying_forcing + +!*********************************************************************** +! +! routine ocean_connection_N +! +!> \brief Calculate effective pressure assuming perfect ocean connection +!> \author Matt Hoffman +!> \date 02 June 2020 +!> \details +!> This routine calculates effective pressure assuming a perfect hydrologic +!> connection with the ocean. It can be used as a simple alternative to the +!> full subglacial hydrology. +!----------------------------------------------------------------------- + subroutine ocean_connection_N(domain) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: geometryPool + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: effectivePressure + real (kind=RKIND), pointer :: rhoi, rhoo + + ! Calculate N assuming perfect ocean connection + call mpas_log_write('Calculating N assuming perfect ocean connection.') + + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhoo) + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) + effectivePressure = rhoi * gravity * thickness - rhoi * gravity * max(0.0_RKIND, -1.0_RKIND * rhoo/rhoi * bedTopography) + effectivePressure = max(effectivePressure, 0.0_RKIND) ! This is just to zero out N in the open ocean to avoid confusion + + block => block % next + end do + + !-------------------------------------------------------------------- + end subroutine ocean_connection_N + + end module li_subglacial_hydro diff --git a/src/core_landice/mode_forward/mpas_li_thermal.F b/src/core_landice/mode_forward/mpas_li_thermal.F index a3774f1224..31da8a4b1a 100644 --- a/src/core_landice/mode_forward/mpas_li_thermal.F +++ b/src/core_landice/mode_forward/mpas_li_thermal.F @@ -51,8 +51,9 @@ module li_thermal public :: li_thermal_init, li_thermal_solver, & li_init_linear_temperature_in_column, & li_temperature_to_enthalpy, li_enthalpy_to_temperature, & + li_temperature_to_enthalpy_kelvin, li_enthalpy_to_temperature_kelvin, & li_compute_pressure_melting_point_fields, & - li_basal_melt_floating_ice + li_basal_friction, li_heat_dissipation_sia !-------------------------------------------------------------------- ! @@ -121,7 +122,8 @@ subroutine li_thermal_init(domain, err) character(len=StrKIND), pointer :: & config_thermal_solver, & ! solver option ('temperature' or 'enthalpy') config_temperature_init, & ! temperature initialization option ('linear' or 'file') - config_surface_air_temperature_source, & ! surface air temperature initialization option ('constant' or 'file' or 'lapse') + config_surface_air_temperature_source, & ! surface air temperature initialization option + ! ('constant' or 'file' or 'lapse') config_basal_heat_flux_source ! basal heat flux initialization option ('constant' or 'file') real (kind=RKIND), pointer :: & @@ -149,11 +151,12 @@ subroutine li_thermal_init(domain, err) real (kind=RKIND), dimension(:), pointer :: & thickness, & ! ice thickness - upperSurface ! ice upper surface + upperSurface, & ! ice upper surface + basalWaterThickness real (kind=RKIND), dimension(:,:), pointer :: & temperature, & ! interior ice temperature (K) - waterfrac, & ! interior water fraction (unitless) + waterFrac, & ! interior water fraction (unitless) enthalpy ! interior ice enthalpy (J m^{-3}) real (kind=RKIND), dimension(:), pointer :: & @@ -236,10 +239,11 @@ subroutine li_thermal_init(domain, err) ! get arrays from the geometry pool call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(geometryPool, 'basalWaterThickness', basalWaterThickness) ! get arrays from the thermal pool call mpas_pool_get_array(thermalPool, 'temperature', temperature) - call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'waterFrac', waterFrac) call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) @@ -257,7 +261,8 @@ subroutine li_thermal_init(domain, err) if (config_print_thermal_info) & call mpas_log_write('Initialize sfc air temp: $r', realArgs=(/config_surface_air_temperature_value/)) elseif (trim(config_surface_air_temperature_source) == 'lapse') then - surfaceAirTemperature(:) = config_surface_air_temperature_value - config_surface_air_temperature_lapse_rate * upperSurface(:) + surfaceAirTemperature(:) = config_surface_air_temperature_value - config_surface_air_temperature_lapse_rate & + * upperSurface(:) if (config_print_thermal_info) & call mpas_log_write('Initialize sfc air temp to $r with lapse rate of $r', & realArgs=(/config_surface_air_temperature_value, config_surface_air_temperature_lapse_rate/)) @@ -367,27 +372,15 @@ subroutine li_thermal_init(domain, err) ! The default is (1). ! If restarting, we always do (3). ! If (2) or (3), then the temperature should already have been read in, and there is - ! nothing to do here (except possibly to set waterfrac). + ! nothing to do here (except possibly to set waterFrac). - if (config_do_restart) then + if (config_do_restart .or. (trim(config_temperature_init) == 'file')) then ! nothing to do; temperature was read from the restart file - !TODO - Make sure waterfrac is also read, if needed if (config_print_thermal_info) then - call mpas_log_write('Initialized ice temperature from the restart file') + call mpas_log_write('Initialized ice temperature (and waterFrac if required) from file.') endif - elseif (trim(config_temperature_init) == 'file') then - - ! Temperature was read from the input file - if (config_print_thermal_info) then - call mpas_log_write('Initialized ice temperature from the input file') - endif - - ! initialize waterfrac, in case we are using the enthalpy solver - !TODO - Allow waterfrac to be read from the input file? - waterfrac(:,:) = 0.0_RKIND - elseif (trim(config_temperature_init) == 'sfc_air_temperature') then allocate(pmpTemperatureCol(nVertLevels)) @@ -407,7 +400,7 @@ subroutine li_thermal_init(domain, err) enddo deallocate(pmpTemperatureCol) - waterfrac(:,:) = 0.0_RKIND + waterFrac(:,:) = 0.0_RKIND if (config_print_thermal_info) then call mpas_log_write('Initialized ice column temperature to the surface air temperature') @@ -430,7 +423,7 @@ subroutine li_thermal_init(domain, err) thickness(iCell), & surfaceAirTemperature(iCell), & temperature(:,iCell), & - waterfrac(:,iCell), & + waterFrac(:,iCell), & surfaceTemperature(iCell), & basalTemperature(iCell)) @@ -504,8 +497,8 @@ subroutine li_thermal_init(domain, err) call mpas_dmpar_field_halo_exch(domain, 'temperature') if (trim(config_thermal_solver) == 'enthalpy') then - ! prognostic variables are temperature and waterfrac, so need to update waterfrac too - call mpas_dmpar_field_halo_exch(domain, 'waterfrac') + ! prognostic variables are temperature and waterFrac, so need to update waterFrac too + call mpas_dmpar_field_halo_exch(domain, 'waterFrac') endif call mpas_timer_stop("halo updates") @@ -540,7 +533,7 @@ subroutine li_init_linear_temperature_in_column(& thickness, & surfaceAirTemperature, & temperature, & - waterfrac, & + waterFrac, & surfaceTemperature, & basalTemperature) @@ -564,7 +557,7 @@ subroutine li_init_linear_temperature_in_column(& real(kind=RKIND), dimension(nVertLevels), intent(out) :: & temperature, & !< Output: interior ice temperature at midpoint of each layer - waterfrac !< Output: interior water fraction at midpoint of each layer + waterFrac !< Output: interior water fraction at midpoint of each layer real(kind=RKIND), intent(out) :: & surfaceTemperature, & !< Output: surface ice temperature @@ -618,8 +611,8 @@ subroutine li_init_linear_temperature_in_column(& temperature(:) = min(temperature(:), pmptemp(:) - pmpt_offset) - ! set waterfrac = 0 - waterfrac(:) = 0.0_RKIND + ! set waterFrac = 0 + waterFrac(:) = 0.0_RKIND end subroutine li_init_linear_temperature_in_column @@ -719,8 +712,8 @@ subroutine li_thermal_solver(domain, err) surfaceAirTemperature, & ! surface air temperature (K) basalHeatFlux, & ! basal heat flux into the ice (W m^{-2}, positive upward) basalFrictionFlux, & ! basal frictional flux into the ice (W m^{-2}) - surfaceConductiveFlux, & ! conductive heat flux at the upper surface (W m^{-2}, positive down) - basalConductiveFlux, & ! conductive heat flux at the lower surface (W m^{-2}, positive down) + surfaceConductiveFlux, & ! conductive heat flux at the upper surface (W m^{-2}, positive down) + basalConductiveFlux, & ! conductive heat flux at the lower surface (W m^{-2}, positive down) groundedBasalMassBal, & ! basal mass balance for grounded ice floatingBasalMassBal, & ! basal mass balance for floating ice basalWaterThickness, & ! basal water thickness (m) @@ -729,7 +722,7 @@ subroutine li_thermal_solver(domain, err) real (kind=RKIND), dimension(:,:), pointer :: & temperature, & ! interior ice temperature (K) - waterfrac, & ! interior water fraction (unitless) + waterFrac, & ! interior water fraction (unitless) enthalpy, & ! interior ice enthalpy (J m^{-3}) heatDissipation ! interior heat dissipation (deg/s) @@ -834,7 +827,7 @@ subroutine li_thermal_solver(domain, err) ! get fields from the thermal pool call mpas_pool_get_array(thermalPool, 'temperature', temperature) - call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'waterFrac', waterFrac) call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) @@ -875,24 +868,15 @@ subroutine li_thermal_solver(domain, err) enddo - ! === Calculate mechanical heating terms === - - if (trim(config_velocity_solver) == 'sia') then - ! Compute interior heat dissipation - call heat_dissipation_sia(domain, err_tmp) - err = ior(err, err_tmp) - endif ! sia - ! (FO dissipation is calculated within dycore) - - ! Compute heat flux due to basal friction - ! appropriate for SIA or FO dycore, assuming Taub=beta*ub - call basal_friction(domain, err_tmp) - err = ior(err, err_tmp) + ! === mechanical heating terms now calculated in li_velocity_solve === + ! (this makes restarts easier because just heatDissipation and basalFrictionFlux + ! need to be restart variables, rather than all of their inputs.) ! === Update surfaceTemperature if using a lapse rate === if (trim(config_surface_air_temperature_source) == 'lapse') then - surfaceAirTemperature(:) = config_surface_air_temperature_value - config_surface_air_temperature_lapse_rate * upperSurface(:) + surfaceAirTemperature(:) = config_surface_air_temperature_value - config_surface_air_temperature_lapse_rate & + * upperSurface(:) endif ! ================================ @@ -966,13 +950,13 @@ subroutine li_thermal_solver(domain, err) if (trim(config_thermal_solver) == 'enthalpy') then - ! Given temperature and waterfrac in ice interior, compute enthalpy + ! Given temperature and waterFrac in ice interior, compute enthalpy call li_temperature_to_enthalpy(& layerCenterSigma, & thickness(iCell), & temperature(:,iCell), & - waterfrac(:,iCell), & + waterFrac(:,iCell), & enthalpy(:,iCell)) surfaceEnthalpy = surfaceTemperature(iCell) * rhoi*cp_ice @@ -982,11 +966,11 @@ subroutine li_thermal_solver(domain, err) call mpas_log_write(' ') call mpas_log_write('Before prognostic enthalpy, iCell = $i', intArgs=(/indexToCellID(iCell)/)) call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) - call mpas_log_write('Temperature (C), waterfrac, enthalpy/(rhoi*cp_ice):') + call mpas_log_write('Temperature (C), waterFrac, enthalpy/(rhoi*cp_ice):') call mpas_log_write('0 $r', realArgs=(/surfaceEnthalpy/(rhoi*cp_ice)/)) do k = 1, nVertLevels call mpas_log_write('$i $r $r $r', intArgs=(/k/), realArgs= & - (/temperature(k,iCell), waterfrac(k,iCell), enthalpy(k,iCell)/(rhoi*cp_ice)/)) + (/temperature(k,iCell), waterFrac(k,iCell), enthalpy(k,iCell)/(rhoi*cp_ice)/)) enddo call mpas_log_write('$i $r', intArgs=(/nVertLevels+1/), realArgs=(/basalEnthalpy/(rhoi*cp_ice)/)) endif @@ -1010,7 +994,7 @@ subroutine li_thermal_solver(domain, err) temperature(:,iCell), & surfaceTemperature(iCell), & basalTemperature(iCell), & - waterfrac(:,iCell), & + waterFrac(:,iCell), & enthalpy(:,iCell), & heatDissipation(:,iCell), & basalHeatFlux(iCell), & @@ -1063,28 +1047,37 @@ subroutine li_thermal_solver(domain, err) basalConductiveFlux(iCell) = -diffusivity(nVertLevels+1)/thickness(iCell) * & denth_bot/(1.0_RKIND - layerCenterSigma(nVertLevels)) - ! convert enthalpy in ice interior back to temperature and waterfrac + ! convert enthalpy in ice interior back to temperature and waterFrac call li_enthalpy_to_temperature(& layerCenterSigma, & thickness(iCell), & enthalpy(:,iCell), & temperature(:,iCell), & - waterfrac(:,iCell), & + waterFrac(:,iCell), & surfaceEnthalpy, & surfaceTemperature(iCell), & basalEnthalpy, & basalTemperature(iCell)) + basalConductiveFlux(iCell) = -iceConductivity/thickness(iCell) * & + (basalTemperature(iCell)-temperature(nVertLevels,iCell))/(1.0_RKIND - layerCenterSigma(nVertLevels)) + ! TZ: The basal conductive flux should be calculated using temperature, not + ! enthalpy. If we use enthalpy, basalConductiveFlux will always equals to basal heat flux, causing problem + ! At ice surface, it is unlikely there will be temperate ice. In addition, we can't know exactly the water + ! fraction if the ice is temperate at surface. So in this case we will just use enthalpy and don't consider + ! the latent heat of water. + + if (verboseColumn) then call mpas_log_write(' ') call mpas_log_write('After prognostic enthalpy, iCell = $i', intArgs=(/indexToCellID(iCell)/)) call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) - call mpas_log_write('Temperature, waterfrac, enthalpy/(rhoi*cp_ice):') + call mpas_log_write('Temperature, waterFrac, enthalpy/(rhoi*cp_ice):') call mpas_log_write('0 $r', realArgs=(/surfaceEnthalpy/(rhoi*cp_ice)/)) do k = 1, nVertLevels call mpas_log_write('$i $r $r $r', intArgs=(/k/), realArgs= & - (/temperature(k,iCell), waterfrac(k,iCell), enthalpy(k,iCell)/(rhoi*cp_ice)/)) + (/temperature(k,iCell), waterFrac(k,iCell), enthalpy(k,iCell)/(rhoi*cp_ice)/)) enddo call mpas_log_write('$i $r', intArgs=(/nVertLevels+1/), realArgs=(/basalEnthalpy/(rhoi*cp_ice)/)) endif @@ -1221,8 +1214,10 @@ subroutine li_thermal_solver(domain, err) call mpas_log_write('config_thermal_thickness: $r', realArgs=(/config_thermal_thickness/)) call mpas_log_write(' ') call mpas_log_write('Interior fluxes:') - call mpas_log_write('sfc conductive flx (W/m^2, positive down)=$r', realArgs=(/surfaceConductiveFlux(iCell)/)) - call mpas_log_write('bed conductive flx (W/m^2, positive down)=$r', realArgs=(/basalConductiveFlux(iCell)/)) + call mpas_log_write('sfc conductive flx (W/m^2, positive down)=$r', & + realArgs=(/surfaceConductiveFlux(iCell)/)) + call mpas_log_write('bed conductive flx (W/m^2, positive down)=$r', & + realArgs=(/basalConductiveFlux(iCell)/)) call mpas_log_write('column heat dissipation (W/m^2) =$r', realArgs=(/columnHeatDissipation/)) call mpas_log_write('Net flux (W/m^2) =$r', realArgs=(/deltaEnergy/deltat/)) call mpas_log_write(' ') @@ -1230,7 +1225,8 @@ subroutine li_thermal_solver(domain, err) call mpas_log_write('initialEnergy (J/m^2) =$r', realArgs=(/initialEnergy/)) call mpas_log_write('finalEnergy (J/m^2) =$r', realArgs=(/finalEnergy/)) call mpas_log_write(' ') - call mpas_log_write('Energy imbalance (J/m^2)=$r', realArgs=(/finalEnergy - initialEnergy - deltaEnergy/)) + call mpas_log_write('Energy imbalance (J/m^2)=$r', & + realArgs=(/finalEnergy - initialEnergy - deltaEnergy/)) call mpas_log_write(' ') call mpas_log_write('Basal fluxes:') call mpas_log_write('frictional =$r', realArgs=(/basalFrictionFlux(iCell)/)) @@ -1240,8 +1236,8 @@ subroutine li_thermal_solver(domain, err) endif ! verboseColumn call mpas_log_write('li_thermal, energy conservation error: iCell=$i, imbalance=$r (W/m2):', MPAS_LOG_WARN, & - intArgs=(/indexToCellID(iCell)/), realArgs=(/(finalEnergy - initialEnergy - deltaEnergy)/deltat/)) - err = 0 + intArgs=(/indexToCellID(iCell)/), realArgs=(/(finalEnergy - initialEnergy - deltaEnergy)/deltat/)) + !err = ior(err, 1) endif ! energy conservation error @@ -1254,7 +1250,7 @@ subroutine li_thermal_solver(domain, err) surfaceTemperature(iCell) = 0.0_RKIND basalTemperature(iCell) = 0.0_RKIND temperature(:,iCell) = 0.0_RKIND - waterfrac(:,iCell) = 0.0_RKIND + waterFrac(:,iCell) = 0.0_RKIND enthalpy(:,iCell) = 0.0_RKIND endif ! thickness > config_thermal_thickness @@ -1283,7 +1279,7 @@ subroutine li_thermal_solver(domain, err) thickness, & temperature, & basalTemperature, & - waterfrac, & + waterFrac, & enthalpy, & basalFrictionFlux, & basalHeatFlux, & @@ -1306,7 +1302,8 @@ subroutine li_thermal_solver(domain, err) mintemp = minval(temperature(:,iCell)) if (maxtemp > maxtempThreshold) then - call mpas_log_write('maxtemp > maxtempThreshold: iCell=$i, maxtemp = $r', intArgs=(/iCell/), realArgs=(/maxtemp/)) + call mpas_log_write('maxtemp > maxtempThreshold: iCell=$i, maxtemp = $r', intArgs=(/iCell/), & + realArgs=(/maxtemp/)) call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) call mpas_log_write('temperature:') do k = 1, nVertLevels @@ -1316,7 +1313,8 @@ subroutine li_thermal_solver(domain, err) endif if (mintemp < mintempThreshold) then - call mpas_log_write('mintemp < mintempThreshold: iCell=$i, mintemp = $r', intArgs=(/iCell/), realArgs=(/mintemp/)) + call mpas_log_write('mintemp < mintempThreshold: iCell=$i, mintemp = $r', intArgs=(/iCell/), & + realArgs=(/mintemp/)) call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) call mpas_log_write('temperature:') do k = 1, nVertLevels @@ -1327,6 +1325,9 @@ subroutine li_thermal_solver(domain, err) enddo ! iCell + case default + call mpas_log_write("Unknown thermal solver specified: " // trim(config_thermal_solver), MPAS_LOG_ERR) + err = ior(err, 1) end select ! config_thermal_solver ! It is possible that internal melting was computed above for floating ice and assigned @@ -1370,8 +1371,8 @@ subroutine li_thermal_solver(domain, err) call mpas_dmpar_field_halo_exch(domain, 'temperature') if (trim(config_thermal_solver) == 'enthalpy') then - ! prognostic variables are temperature and waterfrac, so need to update waterfrac too - call mpas_dmpar_field_halo_exch(domain, 'waterfrac') + ! prognostic variables are temperature and waterFrac, so need to update waterFrac too + call mpas_dmpar_field_halo_exch(domain, 'waterFrac') endif call mpas_timer_stop("halo updates") @@ -1383,209 +1384,10 @@ subroutine li_thermal_solver(domain, err) end subroutine li_thermal_solver -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! ! routine li_basal_melt_floating_ice -! -!> \brief MPAS land ice solver for basal melt of floating ice -!> \author William Lipscomb -!> \date November 2015 -!> \details -!> This routine computes basal melting for floating ice. -!> The following options are supported: -!> (1) Do nothing (config_basal_mass_bal_float = 'none') -!> (2) Read melt rate from a file (config_basal_mass_bal_float = 'file') -!> (2) Prescribed constant basal melt rate (config_basal_mass_bal_float = 'constant') -!> (3) Basal melt rate as in MISMIP+ (config_basal_mass_bal_float = 'mismip') - -!----------------------------------------------------------------------- - - subroutine li_basal_melt_floating_ice(domain, err) - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! input/output variables - !----------------------------------------------------------------- - type (domain_type), intent(inout) :: & - domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - - type (block_type), pointer :: block - - type (mpas_pool_type), pointer :: meshPool - type (mpas_pool_type), pointer :: geometryPool - type (mpas_pool_type), pointer :: velocityPool ! needed for mask subroutine - type (mpas_pool_type), pointer :: scratchPool - - integer, pointer :: & - nCellsSolve ! number of locally owned cells - - logical, pointer :: & - config_print_thermal_info ! if true, print debug info - - character(len=StrKIND), pointer :: & - config_basal_mass_bal_float ! option for basal mass balance of floating ice - - real(kind=RKIND), pointer :: & - config_thermal_thickness, & ! minimum thickness (m) for temperature calculations - config_sea_level, & ! sea level (m) relative to z = 0 - config_bmlt_float_flux, & ! constant heat flux (W/m^2) applied to the base of floating ice; positive upward - config_bmlt_float_xlimit ! x value (m) defining region where bmlt_float_flux is applied; melt only where abs(x) > xlimit - - integer, dimension(:), pointer :: & - cellMask ! bit mask describing whether ice is floating, dynamically active, etc. - - type (field1dInteger), pointer :: thermalCellMaskField - - integer, dimension(:), pointer :: & - thermalCellMask ! mask for thermal calculations - ! = 1 where thickness > config_thermal_thickness, elsewhere = 0 - - real (kind=RKIND), dimension(:), pointer :: & - xCell ! x coordinate for each cell (m) - - real (kind=RKIND), dimension(:), pointer :: & - floatingBasalMassBal, & ! basal mass balance for floating ice - thickness, & ! ice thickness (m) - lowerSurface, & ! lower surface elevation (m) - bedTopography ! bed topography (m; negative below sea level) - - real(kind=RKIND), pointer :: daysSinceStart - - integer :: iCell, err_tmp - - err = 0 - err_tmp = 0 - - call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_float', config_basal_mass_bal_float) - - if (trim(config_basal_mass_bal_float) == 'none') then - - ! Zero entire field - - ! block loop - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) - call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) - - floatingBasalMassBal = 0.0_RKIND - - block => block % next - enddo ! associated(block) - - elseif (trim(config_basal_mass_bal_float) == 'file') then - - return ! already set; nothing to do - - endif - - ! get rest of config variables - call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) - call mpas_pool_get_config(liConfigs, 'config_thermal_thickness', config_thermal_thickness) - call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) - call mpas_pool_get_config(liConfigs, 'config_bmlt_float_flux', config_bmlt_float_flux) - call mpas_pool_get_config(liConfigs, 'config_bmlt_float_xlimit', config_bmlt_float_xlimit) - - if (config_print_thermal_info) then - call mpas_log_write('Solving for basal melting of floating ice, config_basal_mass_bal_float = ' // & - trim(config_basal_mass_bal_float) ) - endif - - ! block loop - block => domain % blocklist - do while (associated(block)) - - ! get pools - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) - call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) - call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - - ! get dimensions - call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) - - ! get fields from the mesh pool - call mpas_pool_get_array(meshPool, 'xCell', xCell) - call mpas_pool_get_array(meshPool, 'daysSinceStart',daysSinceStart) - - ! get fields from the geometry pool - call mpas_pool_get_array(geometryPool, 'thickness', thickness) - call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) - call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) - call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) - call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) - - ! get fields from the scratch pool - call mpas_pool_get_field(scratchPool, 'iceCellMask', thermalCellMaskField) - call mpas_allocate_scratch_field(thermalCellMaskField, .true.) - thermalCellMask => thermalCellMaskField % array - - ! calculate masks - so we know where the ice is floating - call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) - err = ior(err, err_tmp) - - ! calculate a mask to identify ice that is thick enough to be thermally active - do iCell = 1, nCellsSolve - if (thickness(iCell) > config_thermal_thickness) then - thermalCellMask(iCell) = 1 - else - thermalCellMask(iCell) = 0 - endif - enddo - - ! Compute basal melting for floating ice. - - call basal_melt_floating_ice(& - config_basal_mass_bal_float, & - nCellsSolve, & - xCell, & - daysSinceStart, & - thermalCellMask, & - li_mask_is_floating_ice_int(cellMask), & - lowerSurface, & - bedTopography, & - config_sea_level, & - config_bmlt_float_flux, & - config_bmlt_float_xlimit, & - floatingBasalMassBal, & - err_tmp) - err = ior(err, err_tmp) - - !WHL - debug - ! write(stdoutUnit,*) 'Computed basal melt for floating ice' - ! write(stdoutUnit,*) ' ' - ! write(stdoutUnit,*) 'iCell, thickness, basal mbal:' - ! do iCell = 1, nCellsSolve - ! if (li_mask_is_floating_ice(cellMask(iCell))) then - ! write(stdoutUnit,*) iCell, thickness(iCell), floatingBasalMassBal(iCell)*scyr/rhoi - ! endif - ! enddo - ! write(stdoutUnit,*) 'Done with basal melt for floating ice' - - ! clean up - call mpas_deallocate_scratch_field(thermalCellMaskField, .true.) - - block => block % next - enddo ! associated(block) - - - end subroutine li_basal_melt_floating_ice !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! ! routine heat_dissipation_sia +! ! routine li_heat_dissipation_sia ! !> \brief MPAS land ice heat dissipation for SIA velocity solver !> \author William Lipscomb @@ -1595,7 +1397,7 @@ end subroutine li_basal_melt_floating_ice !> in the ice interior, based on the shallow-ice approximation. !----------------------------------------------------------------------- - subroutine heat_dissipation_sia(domain, err) + subroutine li_heat_dissipation_sia(domain, err) !----------------------------------------------------------------- ! input/output variables @@ -1677,7 +1479,7 @@ subroutine heat_dissipation_sia(domain, err) weightEdge ! edge weight for averaging to cell center real (kind=RKIND), dimension(:), allocatable :: & - flowParamAEdge ! flow parameter averaged to edge + flowParamAEdge ! flow parameter averaged to edge. integer :: iCell, iCell1, iCell2, iEdge, iEdgeOnCell, iLayer @@ -1841,12 +1643,12 @@ subroutine heat_dissipation_sia(domain, err) block => block % next enddo ! associated(block) - end subroutine heat_dissipation_sia + end subroutine li_heat_dissipation_sia !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! ! routine basal_friction +! ! routine li_basal_friction ! !> \brief MPAS heat flux due to basal friction for SIA dynamics !> \author William Lipscomb @@ -1856,7 +1658,7 @@ end subroutine heat_dissipation_sia !> base of the ice, based on the shallow-ice approximation. !----------------------------------------------------------------------- - subroutine basal_friction(domain, err) + subroutine li_basal_friction(domain, err) !----------------------------------------------------------------- ! input/output variables @@ -1877,6 +1679,7 @@ subroutine basal_friction(domain, err) type (block_type), pointer :: block type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool type (mpas_pool_type), pointer :: velocityPool type (mpas_pool_type), pointer :: thermalPool @@ -1892,11 +1695,16 @@ subroutine basal_friction(domain, err) integer, pointer :: & nCellsSolve ! number of cells + integer, pointer :: & + nCells ! number of cells + real(kind=RKIND), dimension(:), pointer :: & basalSpeed, & ! basal ice speed, reconstructed at cell centers (m s^{-1}) betaSolve, & ! basal traction parameter (Pa m^{-1} s); use betaSolve to treat floating ice correctly basalFrictionFlux ! heat flux due to basal friction (W^{m-2}), computed in this subroutine + integer, dimension(:), pointer :: cellMask + integer :: iCell !---------------------------------------------------------------- @@ -1924,15 +1732,20 @@ subroutine basal_friction(domain, err) ! get pools call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) ! get dimensions call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) ! get fields from the mesh pool call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) ! diagnostic only + ! get fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + ! get fields from the velocity pool call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) @@ -1948,7 +1761,14 @@ subroutine basal_friction(domain, err) if (config_print_thermal_info) call mpas_log_write('Compute basal friction flux') ! Compute basal frictional heating for each cell - basalFrictionFlux(:) = betaSolve(:) * basalSpeed(:)**2 * scyr ! TODO: If beta units are changed to be SI, remove this factor + do iCell = 1, nCells + if (li_mask_is_grounded_ice(cellMask(iCell))) then + basalFrictionFlux(iCell) = betaSolve(iCell) * basalSpeed(iCell)**2 * scyr + else + basalFrictionFlux(iCell) = 0.0_RKIND ! Ensure no friction flux under floating ice + endif + enddo + ! < TODO: If beta units are changed to be SI, remove scyr factor ! Optional debugging output if (config_print_thermal_info) then @@ -1963,7 +1783,74 @@ subroutine basal_friction(domain, err) block => block % next enddo ! associated(block) - end subroutine basal_friction + end subroutine li_basal_friction + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_temperature_to_enthalpy_kelvin +! +!> \brief MPAS convert temperature to enthalpy +!> \author William Lipscomb, Tong Zhang +!> \date April 2018 +!> \details +!> This routine computes the enthalpy in each layer of an ice column, +!> given the temperature (K) and water fraction. +!----------------------------------------------------------------------- + + subroutine li_temperature_to_enthalpy_kelvin(& + layerCenterSigma, & + thickness, & + temperature, & + waterFrac, & + enthalpy) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: sigma coordinate at midpoint of each layer + + real (kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness + + real (kind=RKIND), dimension(:), intent(in) :: & + temperature, & !< Input: interior ice temperature + waterFrac !< Input: interior water fraction + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + enthalpy !< Output: interior ice enthalpy + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(size(layerCenterSigma)) :: & + pmpTemperature ! pressure melting point temperature + + integer :: k, nVertLevels + + nVertLevels = size(layerCenterSigma) + + ! Find pressure melting point temperature in column + + call pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmpTemperature) + + ! Solve for enthalpy + + do k = 1, nVertLevels + enthalpy(k) = (1.0_RKIND - waterFrac(k)) * rhoi * cp_ice * temperature(k) & + + waterFrac(k) * rhoi * (cp_ice * (pmpTemperature(k)+kelvin_to_celsius) + latent_heat_ice) + end do + + end subroutine li_temperature_to_enthalpy_kelvin !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! @@ -1981,7 +1868,7 @@ subroutine li_temperature_to_enthalpy(& layerCenterSigma, & thickness, & temperature, & - waterfrac, & + waterFrac, & enthalpy) !----------------------------------------------------------------- @@ -1996,7 +1883,7 @@ subroutine li_temperature_to_enthalpy(& real (kind=RKIND), dimension(:), intent(in) :: & temperature, & !< Input: interior ice temperature - waterfrac !< Input: interior water fraction + waterFrac !< Input: interior water fraction !----------------------------------------------------------------- ! output variables @@ -2026,10 +1913,37 @@ subroutine li_temperature_to_enthalpy(& ! Solve for enthalpy do k = 1, nVertLevels - enthalpy(k) = (1.0_RKIND - waterfrac(k)) * rhoi * cp_ice * temperature(k) & - + waterfrac(k) * rho_water * (cp_ice * pmpTemperature(k) + latent_heat_ice) + enthalpy(k) = (1.0_RKIND - waterFrac(k)) * rhoi * cp_ice * temperature(k) & + + waterFrac(k) * rhoi * (cp_ice * pmpTemperature(k) + latent_heat_ice) end do + ! TZ: we should use rhoi instead of rho_water in the second term here. + + + ! TZ: Here is the reason: from Eqn (6) in the Aschwanden et + ! al. (2012) paper "An enthalpy formulation for glaciers and ice sheets", + + ! rho * H = rho_i * H_i + rho_w * H_w, + + ! where rho_i and rho_w are not the ice and water density, but the partial + ! density, i.e., rho_i = mass_ice/volume, rho_w = mass_water/volume. + ! Because water fraction is very small in general, rho_i approximates to + ! rhoi (ice density), rho_w is close to 0. + ! Also note that in MALI the enthalpy is equal to the enthalpy density + ! (rho*H) in Aschwanden's paper (they don't have density in their + ! enthaly definition, but we do). Thus, Let's denote MALI's enthalpy as + ! E, and denote W as the water content, then + + ! E = rho * H = rho_i * H_i + rho_w * H_w = (1-W) * H_i + W * H_w + ! E = (1-W) * rho * cp * T + W * (rho * cp * T_pmp + rho*L) + + ! As rho approximates to rhoi, we have + + ! E = (1-W) * rhoi * cp * T + W * rhoi * (cp*T_pmp + L) + + ! This is what we get in the code. We can easily do the derivation + ! according to Eqns (1-11) in Aschwanden's paper. + end subroutine li_temperature_to_enthalpy !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -2049,7 +1963,7 @@ subroutine li_enthalpy_to_temperature(& thickness, & enthalpy, & temperature, & - waterfrac, & + waterFrac, & surfaceEnthalpy, & surfaceTemperature, & basalEnthalpy, & @@ -2084,7 +1998,7 @@ subroutine li_enthalpy_to_temperature(& temperature !< Output: interior ice temperature real (kind=RKIND), dimension(:), intent(out) :: & - waterfrac !< Output: interior water fraction + waterFrac !< Output: interior water fraction real (kind=RKIND), intent(out), optional :: & surfaceTemperature, & !< Output: surface ice temperature @@ -2119,18 +2033,18 @@ subroutine li_enthalpy_to_temperature(& pmpEnthalpy(:) = pmpTemperature(:) * rhoi*cp_ice pmpEnthalpyBed = pmpTemperatureBed * rhoi*cp_ice - ! Solve for temperature and waterfrac in ice interior + ! Solve for temperature and waterFrac in ice interior ! ice interior do k = 1, nVertLevels if (enthalpy(k) >= pmpEnthalpy(k)) then ! temperate ice temperature(k) = pmpTemperature(k) - waterfrac(k) = (enthalpy(k) - pmpenthalpy(k)) / & - ((rho_water-rhoi)*cp_ice*pmpTemperature(k) + rho_water*latent_heat_ice) + waterFrac(k) = (enthalpy(k) - pmpenthalpy(k)) / & + ((rho_water-rhoi)*cp_ice*pmpTemperature(k) + rhoi *latent_heat_ice) else ! cold ice temperature(k) = enthalpy(k) / (rhoi*cp_ice) - waterfrac(k) = 0.0_RKIND + waterFrac(k) = 0.0_RKIND endif enddo ! k @@ -2169,6 +2083,142 @@ subroutine li_enthalpy_to_temperature(& end subroutine li_enthalpy_to_temperature +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_enthalpy_to_temperature_kelvin +! +!> \brief MPAS convert enthalpy to temperature +!> \author William Lipscomb, Tong Zhang +!> \date April 2018 +!> \details +!> This routine computes the temperature (K) and water fraction in each layer +!> of an ice column, given the enthalpy. +!----------------------------------------------------------------------- + + subroutine li_enthalpy_to_temperature_kelvin(& + layerCenterSigma, & + thickness, & + enthalpy, & + temperature, & + waterFrac, & + surfaceEnthalpy, & + surfaceTemperature, & + basalEnthalpy, & + basalTemperature) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: sigma coordinate at midpoint of each layer + + real (kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(inout) :: & + enthalpy !< Input/output: interior ice enthalpy + + real (kind=RKIND), intent(inout) , optional :: & + surfaceEnthalpy, & !< Input/output: surface ice enthalpy + basalEnthalpy !< Input/output: basal ice enthalpy + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + temperature !< Output: interior ice temperature + + real (kind=RKIND), dimension(:), intent(out) :: & + waterFrac !< Output: interior water fraction + + real (kind=RKIND), intent(out), optional :: & + surfaceTemperature, & !< Output: surface ice temperature + basalTemperature !< Output: basal ice temperature + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(size(layerCenterSigma)) :: pmpTemperature + real (kind=RKIND), dimension(size(layerCenterSigma)) :: pmpEnthalpy + + real (kind=RKIND) :: pmpTemperatureBed + real (kind=RKIND) :: pmpEnthalpyBed + + integer :: k, nVertLevels + + nVertLevels = size(layerCenterSigma) + + ! Find pressure melting point temperature in ice interior + call pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmpTemperature) + + ! find pressure melting point temperature at bed + call pressure_melting_point(& + thickness, & + pmpTemperatureBed) + + ! Find pressure melting point enthalpy + pmpEnthalpy(:) = (pmpTemperature(:)+kelvin_to_celsius) * rhoi*cp_ice + pmpEnthalpyBed = (pmpTemperatureBed+kelvin_to_celsius) * rhoi*cp_ice + + ! Solve for temperature and waterFrac in ice interior + + ! ice interior + + do k = 1, nVertLevels + if (enthalpy(k) >= pmpEnthalpy(k)) then ! temperate ice + temperature(k) = (pmpTemperature(k)+kelvin_to_celsius) + waterFrac(k) = (enthalpy(k) - pmpEnthalpy(k)) / & + ((rho_water-rhoi)*cp_ice*(pmpTemperature(k)+kelvin_to_celsius) + rhoi *latent_heat_ice) + else ! cold ice + temperature(k) = enthalpy(k) / (rhoi*cp_ice) + waterFrac(k) = 0.0_RKIND + endif + enddo ! k + + ! surface temperature (optional) + + if (present(surfaceEnthalpy) .and. present(surfaceTemperature)) then + + if (surfaceEnthalpy >= (kelvin_to_celsius*rhoi*cp_ice)) then ! temperate ice + surfaceTemperature = kelvin_to_celsius + ! Reset surfaceEnthalpy to agree with the surface temperature. + ! This is consistent with energy conservation because the top surface + ! is infinitesimally thin. + surfaceEnthalpy = kelvin_to_celsius*rhoi*cp_ice + else ! cold ice + surfaceTemperature = surfaceEnthalpy / (rhoi*cp_ice) + endif + + endif + + ! basal temperature (optional) + + if (present(basalEnthalpy) .and. present(basalTemperature)) then + + k = nVertLevels + 1 + if (basalEnthalpy >= pmpEnthalpyBed) then ! temperate ice + basalTemperature = (pmpTemperatureBed+kelvin_to_celsius) + ! Reset basalEnthalpy to agree with the surface temperature. + ! This is consistent with energy conservation because the lower surface + ! is infinitesimally thin. + basalEnthalpy = pmpEnthalpyBed + else ! cold ice + basalTemperature = basalEnthalpy / (rhoi*cp_ice) + endif + + endif + + end subroutine li_enthalpy_to_temperature_kelvin !*********************************************************************** !*********************************************************************** ! Private subroutines: @@ -2353,7 +2403,7 @@ subroutine enthalpy_matrix_elements(& temperature, & surfaceTemperature, & basalTemperature, & - waterfrac, & + waterFrac, & enthalpy, & heatDissipation, & basalHeatFlux, & @@ -2393,7 +2443,7 @@ subroutine enthalpy_matrix_elements(& real(kind=RKIND), dimension(nVertLevels), intent(in) :: & temperature, & !< Input: ice temperature (deg C) - waterfrac, & !< Input: water fraction (unitless) + waterFrac, & !< Input: water fraction (unitless) enthalpy !< Input: specific enthalpy (J/m^3) real(kind=RKIND), intent(in) :: & @@ -2442,6 +2492,7 @@ subroutine enthalpy_matrix_elements(& real(kind=RKIND) :: dEnthalpy ! enthalpy difference between adjacent layers real(kind=RKIND) :: dEnthalpyTemp ! difference in temperature component of enthalpy between adjacent layers real(kind=RKIND) :: avgFactor ! factor for averaging diffusivity, 0 <= avgFactor <= 1 + real(kind=RKIND) :: tempDiffusivityFactor ! factor for estimating the diffusivity in temperate ice, normally it should < 1 integer :: k @@ -2449,8 +2500,19 @@ subroutine enthalpy_matrix_elements(& harmonic_avg = .false. ! if true, take the harmonic average of diffusivity in adjacent layers ! if false, take the arithmetic average + real(kind=RKIND), pointer :: & + config_temp_diffusive_factor ! option for basal mass balance of floating ice + + call mpas_pool_get_config(liConfigs, 'config_temp_diffusive_factor', config_temp_diffusive_factor) + + tempDiffusivityFactor = config_temp_diffusive_factor + ! TZ: this value is somehow empirical, and the default value (1.0e-5) is picked according to Kleiner's benchemark B. + ! In Greve and Blatter (2016) 'Comparison of thermodynamics solvers in the polythermal ice sheet model SICOPOLIS', + ! it was 1.0e-3 [] (p14, section 4) + ! i.e., kt = 1.0e-3 * kc + diffusivityCold = iceConductivity / (rhoi*cp_ice) - diffusivityTemperate = diffusivityCold / 100.0_RKIND + diffusivityTemperate = diffusivityCold * tempDiffusivityFactor ! set enthalpy at surface and bed surfaceEnthalpy = surfaceTemperature * rhoi*cp_ice @@ -2503,7 +2565,7 @@ subroutine enthalpy_matrix_elements(& enthalpyTemp(0) = surfaceEnthalpy do k = 1, nVertLevels - enthalpyTemp(k) = (1.0_RKIND - waterfrac(k)) * rhoi*cp_ice*temperature(k) + enthalpyTemp(k) = (1.0_RKIND - waterFrac(k)) * rhoi*cp_ice*temperature(k) enddo enthalpyTemp(nVertLevels+1) = basalEnthalpy @@ -2525,6 +2587,8 @@ subroutine enthalpy_matrix_elements(& if (abs(dEnthalpy) > 1.e-20_RKIND * rho_water * latent_heat_ice) then avgFactor = max(0.0_RKIND, dEnthalpyTemp/dEnthalpy) avgFactor = min(1.0_RKIND, avgFactor) + ! TZ: Here temperature is in Celsius. dEnthalpyTemp <= 0 in temperate ice as waterFrac increases + ! (or temperature decreases) with depth else avgFactor = 0.0_RKIND endif @@ -2540,6 +2604,7 @@ subroutine enthalpy_matrix_elements(& end do + ! Compute subdiagonal, diagonal, and superdiagonal matrix elements ! Assume backward Euler time stepping @@ -2553,10 +2618,12 @@ subroutine enthalpy_matrix_elements(& factor = deltat / thickness**2 - subd(2:nVertLevels+1) = -factor * diffusivity(1:nVertLevels) * dsigmaTerm(1:nVertLevels,1) + subd(2:nVertLevels+1) = -factor * diffusivity(1:nVertLevels) * dsigmaTerm(1:nVertLevels,1) supd(2:nVertLevels+1) = -factor * diffusivity(2:nVertLevels+1) * dsigmaTerm(1:nVertLevels,2) diag(2:nVertLevels+1) = 1.0_RKIND - subd(2:nVertLevels+1) - supd(2:nVertLevels+1) rhs(2:nVertLevels+1) = enthalpy(1:nVertLevels) + heatDissipation(1:nVertLevels) * deltat * rhoi * cp_ice + !TZ: Make sure the heat advection is using the upwind scheme, or the solution will not be stable + ! Note: heatDissipation has units of phi/rhoi/cp_ice, where phi has units of W m^-3.. ! For an enthalpy calculation, we want just phi, hence heatDissipation * rhoi * cp_ice @@ -2577,10 +2644,12 @@ subroutine enthalpy_matrix_elements(& else ! grounded ice + if (abs(temperature(nVertLevels) - pmpTemperature(nVertLevels)) < 0.001_RKIND) then ! Positive-thickness basal temperate boundary Layer !WHL - Not sure whether this condition is ideal. It implies that basalEnthalpy = enthalpy(nVertLevels). + !TZ: This condition means the base is temperate AND there is temperate layer above it. It's physical subd(nVertLevels+2) = -1.0_RKIND diag(nVertLevels+2) = 1.0_RKIND @@ -2641,7 +2710,7 @@ subroutine basal_melt_grounded_ice(& thickness, & temperature, & basalTemperature, & - waterfrac, & + waterFrac, & enthalpy, & basalFrictionFlux, & basalHeatFlux, & @@ -2686,8 +2755,7 @@ subroutine basal_melt_grounded_ice(& thickness, & !< Input: ice thickness (m) basalFrictionFlux, & !< Input: basal frictional heating flux (W m-2), >= 0 basalHeatFlux, & !< Input: geothermal heating flux (W m-2), positive up - basalConductiveFlux, & !< Input: heat conducted from ice interior to bed (W m-2), positive down - basalWaterThickness !< Input: thickness of basal water layer (m) + basalConductiveFlux !< Input: heat conducted from ice interior to bed (W m-2), positive down integer, dimension(:), intent(in) :: & iceMask, & !< Input: = 1 where ice exists (thickness > config_thermal_thickness), else = 0 @@ -2701,10 +2769,11 @@ subroutine basal_melt_grounded_ice(& temperature !< Input/output: temperature (deg C) real(kind=RKIND), dimension(:), intent(inout) :: & - basalTemperature !< Input/output: basal temperature (deg C) + basalTemperature, & !< Input/output: basal temperature (deg C) + basalWaterThickness !< Input: thickness of basal water layer (m) real(kind=RKIND), dimension(:,:), intent(inout) :: & - waterfrac !< Input/output: water fraction + waterFrac !< Input/output: water fraction !----------------------------------------------------------------- ! output variables @@ -2730,12 +2799,15 @@ subroutine basal_melt_grounded_ice(& real(kind=RKIND) :: internalMeltRate ! internal melt rate, transferred to bed (m/s) real(kind=RKIND) :: excessWater ! thickness of excess meltwater (m) - real(kind=RKIND), parameter :: & - maxWaterfrac = 0.01_RKIND ! maximum allowed water fraction; excess drains to bed - + real(kind=RKIND) :: maxwaterFrac ! maximum allowed water fraction; excess drains to bed + real(kind=RKIND), pointer :: & + config_max_water_fraction ! maximum allowable water fraction before additional melt drains real(kind=RKIND), parameter :: & eps11 = 1.0e-11_RKIND ! small number + call mpas_pool_get_config(liConfigs, 'config_max_water_fraction', config_max_water_fraction) + + maxwaterFrac = config_max_water_fraction ! Compute melt rate for grounded ice @@ -2761,6 +2833,8 @@ subroutine basal_melt_grounded_ice(& ! and overlying layers with a different enthalpy also melt. netBasalFlux = basalFrictionFlux(iCell) + basalHeatFlux(iCell) + basalConductiveFlux(iCell) ! W/m^2 + !call mpas_log_write("netBasalFlux G condFlux $r $r $r", realArgs=(/netBasalFlux, basalHeatFlux(iCell), & + !basalConductiveFlux(iCell)/)) if (abs(netBasalFlux) < eps11) then ! netBasalFlux might be slightly different from zero ! because of rounding errors; if so, then zero out @@ -2768,11 +2842,23 @@ subroutine basal_melt_grounded_ice(& endif if (trim(config_thermal_solver) == 'enthalpy') then - groundedBasalMassBal(iCell) = -netBasalFlux / (latent_heat_ice * rhoi - enthalpy(nVertLevels,iCell)) + !groundedBasalMassBal(iCell) = -netBasalFlux / (latent_heat_ice * rhoi - enthalpy(nVertLevels,iCell)) + groundedBasalMassBal(iCell) = -netBasalFlux / (latent_heat_ice * rhoi) / (1-waterFrac(nVertLevels,iCell)) + ! TZ: Don't understand yet why WHL added enthalpy(nVertLevels,iCell) here + ! TZ: After a long discussion with Matt Hoffman, we decide to revise it + ! to the form as Eqn (66) in Aschwanden and others (2012) "An + ! enthalpy formulation for glaciers and ice sheets". An extra + ! term (1-w) would be more accurate. + basalWaterThickness(iCell) = basalWaterThickness(iCell) - deltat*groundedBasalMassBal(iCell) + ! TZ: allow basalWaterThickness freely accumulate here. Change it to something else for difference cases + if (basalWaterThickness(iCell) < 0.0_RKIND) then + basalWaterThickness(iCell) = 0.0_RKIND + endif else ! temperature solver groundedBasalMassBal(iCell) = -netBasalFlux / (latent_heat_ice * rhoi) ! m/s endif + endif ! ice is present and grounded ! Add internal melting @@ -2783,14 +2869,14 @@ subroutine basal_melt_grounded_ice(& if (trim(config_thermal_solver) == 'enthalpy') then - ! Add internal melting associated with waterfrac > maxWaterfrac (1%) + ! Add internal melting associated with waterFrac > maxwaterFrac (1%) !TODO - Add correction for rhoi/rhow here? Or melting ice that is already partly melted? do k = 1, nVertLevels - if (waterfrac(k,iCell) > maxWaterfrac) then + if (waterFrac(k,iCell) > maxwaterFrac) then ! compute melt rate associated with excess water - excessWater = (waterfrac(k,iCell) - maxWaterfrac) * thickness(iCell) * (layerInterfaceSigma(k+1) & + excessWater = (waterFrac(k,iCell) - maxwaterFrac) * thickness(iCell) * (layerInterfaceSigma(k+1) & - layerInterfaceSigma(k)) ! m internalMeltRate = excessWater / deltat @@ -2799,10 +2885,10 @@ subroutine basal_melt_grounded_ice(& ! If so, then this melting will later be switched from groundedBasalMassBall to floatingBasalMassBal. groundedBasalMassBal(iCell) = groundedBasalMassBal(iCell) - internalMeltRate ! m/s - ! reset waterfrac to max value - waterfrac(k,iCell) = maxWaterfrac + ! reset waterFrac to max value + waterFrac(k,iCell) = maxwaterFrac - endif ! waterfrac > maxWaterfrac + endif ! waterFrac > maxwaterFrac enddo ! k elseif (config_thermal_solver == 'temperature') then @@ -2880,238 +2966,6 @@ subroutine basal_melt_grounded_ice(& end subroutine basal_melt_grounded_ice -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! ! routine basal_melt_floating_ice -! -!> \brief MPAS melt rate at base of floating ice -!> \author William Lipscomb -!> \date November 2015 -!> \details -!> This routine computes the melt rate at the base of floating ice. -!----------------------------------------------------------------------- - - subroutine basal_melt_floating_ice(& - config_basal_mass_bal_float, & - nCellsSolve, & - xCell, & - daysSinceStart, & - iceMask, & - floatingMask, & - lowerSurface, & - bedTopography, & - config_sea_level, & - config_bmlt_float_flux, & - config_bmlt_float_xlimit, & - floatingBasalMassBal, & - err) - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - - character(len=strKIND), intent(in) :: & - config_basal_mass_bal_float !< Input: option for computing basal mass balance for floating ice - - integer, intent(in) :: & - nCellsSolve !< Input: number of locally owned cells - - real(kind=RKIND), dimension(:), intent(in) :: & - xCell !< Input: x coordinate for each cell - - real(kind=RKIND), pointer, intent(in) :: daysSinceStart - - real(kind=RKIND), dimension(:), intent(in) :: & - lowerSurface, & !< Input: lower surface elevation (m) - bedTopography !< Input: elevation of bed topography (m) - - real(kind=RKIND), intent(in) :: & - config_sea_level !< Input: sea level relative to z = 0 (m) - - integer, dimension(:), intent(in) :: & - iceMask, & !< Input: = 1 where ice exists (thickness > config_thermal_thickness), else = 0 - floatingMask !< Input: = 1 where ice is floating, else = 0 - - ! inputs for constant melt rate as in MISMIP+ Ice2 experiments - real(kind=RKIND), intent(in) :: & - config_bmlt_float_flux, & !< Input: constant heat flux (W/m^2) applied to the base of floating ice; positive upward - !< MISMIP+ default value = 975.17 W/m^2 (gives melt rate of 100 m/yr) - config_bmlt_float_xlimit !< Input: x value (m) defining region where bmlt_float_flux is applied; - !< melt only where abs(x) > xlimit - !< MISMIP+ default value = 480 km - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - - real(kind=RKIND), dimension(:), intent(out):: & - floatingBasalMassBal !< Output: basal mass balance for floating ice - - integer, intent(out) :: err - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - - real(kind=RKIND) :: & - bmlt_float_rate ! constant basal melt rate (m/s) - ! = config_bmlt_float_flux / (rhoi*latent_heat_ice) - integer :: iCell - - real(kind=RKIND) :: hCavity ! depth of ice cavity beneath floating ice (m) - real(kind=RKIND) :: zDraft ! draft of floating ice (m below sea level) - - ! basal melting parameters for MISMIP+ experiment - ! Note: These could be made user-configurable, but are hardwired for now because there are no plans - ! to run MISMIP+ with different values - real(kind=RKIND), parameter :: & - bmlt_float_omega = 0.20_RKIND / scyr, & ! time scale for basal melting (s^-1) - ! MISMIP+ default value = 0.2 yr^-1 - bmlt_float_h0 = 75._RKIND, & ! scale for sub-shelf cavity thickness (m) - ! MISMIP+ default value = 75 m - bmlt_float_z0 = -100._RKIND ! scale for ice draft (m) - ! MISMIP+ default value = -100 m - - ! basal melt parameters for Seroussi param. - real (kind=RKIND) :: slopeSer ! slope of relation between depth and melt rate - real (kind=RKIND) :: interceptSer ! depth at which melting goes to 0 - real (kind=RKIND) :: maxMeltSer ! maximum allowable melt rate - real (kind=RKIND) :: sillDepth ! depth below which melt rate no longer increases - real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_amplitude - real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_period - real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_phase - - - ! Compute melt rate for floating ice - err = 0 - - ! initialize to zero melt - floatingBasalMassBal(:) = 0.0_RKIND - if (trim(config_basal_mass_bal_float) == 'none') then - ! Do nothing, handled in calling routine - - elseif (trim(config_basal_mass_bal_float) == 'file') then - ! Do nothing, handled in calling routine - - elseif (trim(config_basal_mass_bal_float) == 'constant') then - - ! set melt rate to a constant value for floating ice - ! allow basal melt in ice-free ocean cells, in case ice is advected to those cells by the transport scheme - - bmlt_float_rate = config_bmlt_float_flux / (rhoi*latent_heat_ice) ! convert W/m^2 to m/s - - floatingBasalMassBal(:) = 0.0_RKIND - - do iCell = 1, nCellsSolve - if ( floatingMask(iCell) == 1 .or. & - (bedTopography(iCell) < config_sea_level .and. iceMask(iCell) == 0) ) then - ! ice is present and floating, or ice-free ocean - - ! Provided xCell > bmlt_float_xlimit, prescribe a uniform basal melt rate. - ! The default is 0.0, but for MISMIP+ the prescribed value of xlimit is 480 km. - if (abs(xCell(iCell)) >= config_bmlt_float_xlimit) then ! basal melting is allowed - floatingBasalMassBal(iCell) = -bmlt_float_rate - endif - - endif ! ice is present and floating - - enddo ! iCell - - ! change units from m/s to kg/m2/s - floatingBasalMassBal(:) = floatingBasalMassBal(:) * rhoi - - elseif (trim(config_basal_mass_bal_float) == 'mismip') then - - ! compute melt rate (m/s) based on bed depth and cavity thickness - ! The MISMIP+ formula is as follows: - ! - ! bmlt_float = omega * tanh(H_c/H_0) * max(z_0 - z_d, 0) - ! - ! where H_c = lsrf - topg is the cavity thickness - ! z_d = lsrf - eus is the ice draft - ! omega = a time scale = 0.2 yr^{-1} by default - ! H_0 = 75 m by default - ! z_0 = -100 m by default - - ! allow basal melt in ice-free ocean cells, in case ice is advected to those cells by the transport scheme - - floatingBasalMassBal(:) = 0.0_RKIND - - do iCell = 1, nCellsSolve - - if ( floatingMask(iCell) == 1 .or. & - (bedTopography(iCell) < config_sea_level .and. iceMask(iCell) == 0) ) then - ! ice is present and floating, or ice-free ocean - - hCavity = lowerSurface(iCell) - bedTopography(iCell) - zDraft = lowerSurface(iCell) - config_sea_level - floatingBasalMassBal(iCell) = -bmlt_float_omega * tanh(hCavity/bmlt_float_h0) * max(bmlt_float_z0 - & - zDraft, 0.0_RKIND) - - endif ! ice is present and floating - enddo ! iCell - - ! change units from m/s to kg/m2/s - floatingBasalMassBal(:) = floatingBasalMassBal(:) * rhoi - - elseif (trim(config_basal_mass_bal_float) == 'seroussi') then - - ! Melt rate parameterization from: - ! Seroussi, H., Y. Nakayama, E. Larour, D. Menemenlis, M. Morlighem, E. Rignot, and A. Khazendar (2017), Continued retreat of Thwaites Glacier, West Antarctica, controlled by bed topography and ocean circulation, Geophys. Res. Lett., 1-9, doi:10.1002/2017GL072910. - ! for Thwaites Glacier. - ! Specifically, this is a linear fit of melt with shelf draft from the Supplemental Information, Figure S1. - ! The linear relation is modified by a: - ! * depth above which there is no melt (Antarctic Surface Water saturation) - ! * a maximum melt rate (Circumpolar Deep Water saturation) - ! * a depth below which melt stops increasing (minimum sill height) - - call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_amplitude', config_basal_mass_bal_seroussi_amplitude) ! meters - call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_period', config_basal_mass_bal_seroussi_period) ! years - call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_phase', config_basal_mass_bal_seroussi_phase) ! cycles - - slopeSer = 0.088_RKIND ! slope of relation between depth and melt rate (melt (m/yr) per depth (m)) - interceptSer = -100.0_RKIND ! depth (m) at which melting goes to 0 (negative meaning below sea level) - maxMeltSer = 50.0_RKIND ! maximum allowable melt rate (m/yr) (positive meaning melting) - sillDepth = -650.0_RKIND ! depth below which melt stops increasing (m) (negative meaning below sea level) - - if (config_basal_mass_bal_seroussi_period <= 0.0_RKIND) then - call mpas_log_write("Value for config_basal_mass_bal_seroussi_period has to be a positive real value.", MPAS_LOG_ERR) - err = ior(err, 1) - endif - - ! Modify intercept height for variability parameters - interceptSer = interceptSer + config_basal_mass_bal_seroussi_amplitude * & - sin( (2.0_RKIND * pii / config_basal_mass_bal_seroussi_period) * (daysSinceStart/365.0_RKIND) & - + 2.0_RKIND * pii * config_basal_mass_bal_seroussi_phase) - - ! Initialize before computing - floatingBasalMassBal(:) = 0.0_RKIND - - do iCell = 1, nCellsSolve - - ! Shut off melt at an arbitrary shallow depth to discourage ice from disappearing. - if ( (floatingMask(iCell) == 1) .and. (lowerSurface(iCell) < -10.0_RKIND) ) then - ! ice is present and floating - - zDraft = lowerSurface(iCell) - config_sea_level - ! Coefficients for m/yr melt rate (in units of Seroussi figure but without negative meaning melting) - floatingBasalMassBal(iCell) = max(-1.0_RKIND * maxMeltSer, min(0.0_RKIND, slopeSer * (max(zDraft, sillDepth) - interceptSer))) - - endif ! ice is present - enddo ! iCell - - ! change units from m/yr to kg/m2/s - floatingBasalMassBal(:) = floatingBasalMassBal(:) * rhoi / scyr - - else - - call mpas_log_write('Unknown option selected for config_basal_mass_bal_float:' // trim(config_basal_mass_bal_float), MPAS_LOG_ERR) - err = ior(err, 1) - - endif ! config_basal_mass_bal_float - - - end subroutine basal_melt_floating_ice !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! @@ -3312,5 +3166,3 @@ end module li_thermal !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| - - diff --git a/src/core_landice/mode_forward/mpas_li_time_integration_fe.F b/src/core_landice/mode_forward/mpas_li_time_integration_fe.F index 18178aac71..e36e7ee13e 100644 --- a/src/core_landice/mode_forward/mpas_li_time_integration_fe.F +++ b/src/core_landice/mode_forward/mpas_li_time_integration_fe.F @@ -29,8 +29,9 @@ module li_time_integration_fe use mpas_log use li_advection - use li_calving, only: li_calve_ice, li_restore_calving_front - use li_thermal, only: li_thermal_solver, li_basal_melt_floating_ice + use li_calving, only: li_calve_ice, li_restore_calving_front, li_calculate_damage, li_finalize_damage_after_advection + use li_thermal, only: li_thermal_solver + use li_iceshelf_melt use li_diagnostic_vars use li_setup use li_constants @@ -79,6 +80,7 @@ subroutine li_time_integrator_forwardeuler(domain, err) use li_subglacial_hydro use li_velocity + use li_bedtopo !----------------------------------------------------------------- ! input variables @@ -103,10 +105,14 @@ subroutine li_time_integrator_forwardeuler(domain, err) integer :: err_tmp logical, pointer :: config_restore_calving_front + logical, pointer :: config_calculate_damage + logical, pointer :: config_finalize_damage_after_advection err = 0 call mpas_pool_get_config(liConfigs, 'config_restore_calving_front', config_restore_calving_front) + call mpas_pool_get_config(liConfigs, 'config_calculate_damage',config_calculate_damage) + call mpas_pool_get_config(liConfigs, 'config_finalize_damage_after_advection',config_finalize_damage_after_advection) ! === Prepare for advection (including CFL checks) =========== ! This has to come first currently, because it sets the time step! @@ -115,6 +121,13 @@ subroutine li_time_integrator_forwardeuler(domain, err) err = ior(err, err_tmp) call mpas_timer_stop("advection prep") +!TODO: Determine whether grounded melting should in fact be called first +! === Face melting for grounded ice =========== + call mpas_timer_start("face melting for grounded ice") + call li_face_melt_grounded_ice(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("face melting for grounded ice") + ! === Basal melting for floating ice =========== call mpas_timer_start("basal melting for floating ice") call li_basal_melt_floating_ice(domain, err_tmp) @@ -127,12 +140,28 @@ subroutine li_time_integrator_forwardeuler(domain, err) err = ior(err, err_tmp) call mpas_timer_stop("vertical therm") +! === calculate damage =========== + if (config_calculate_damage) then + call mpas_timer_start("damage") + call li_calculate_damage(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("damage") + endif + ! === Compute new state for prognostic variables ================================== call mpas_timer_start("advect thickness and tracers") call advection_solver(domain, err_tmp) err = ior(err, err_tmp) call mpas_timer_stop("advect thickness and tracers") +! === finalize damage after advection =========== + if (config_finalize_damage_after_advection) then + call mpas_timer_start("finalize damage") + call li_finalize_damage_after_advection(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("finalize damage") + endif + ! === Update subglacial hydrology =========== ! It's not clear where the best place to call this should be. ! Seems sensible to put it after thermal evolution is complete to get updated basal melting source term. @@ -168,6 +197,13 @@ subroutine li_time_integrator_forwardeuler(domain, err) call mpas_dmpar_field_halo_exch(domain, 'vertexMask') call mpas_timer_stop("halo updates") +! === Update bed topo ===================== +! It's not clear when the best time to do this is. +! Seems cleaner to do it either before or after all of the time evolution of the ice +! is complete. Putting it after. + call li_bedtopo_solve(domain, err=err_tmp) + err = ior(err, err_tmp) + ! === Solve Velocity ===================== ! During time-stepping, we always solveVelo call li_velocity_solve(domain, solveVelo=.true., err=err_tmp) @@ -246,11 +282,15 @@ subroutine prepare_advection(domain, err) real (kind=RKIND), dimension(:,:), pointer :: normalVelocity real (kind=RKIND), dimension(:,:), pointer :: layerNormalVelocity + integer, dimension(:), pointer :: edgeMask logical, pointer :: config_print_thickness_advection_info logical, pointer :: config_adaptive_timestep logical, pointer :: config_adaptive_timestep_include_DCFL + character (len=StrKIND), pointer :: & + config_thickness_advection ! method for advecting thickness and tracers + integer :: & allowableAdvecDtProcNumberHere, & allowableAdvecDtProcNumber @@ -300,6 +340,16 @@ subroutine prepare_advection(domain, err) call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep', config_adaptive_timestep) call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) + call mpas_pool_get_config(liConfigs, 'config_thickness_advection', config_thickness_advection) + + if (trim(config_thickness_advection) == 'none') then + if (config_adaptive_timestep) then + call mpas_log_write("Adaptive time stepper cannot be used when config_thickness_advection is set to 'none'", & + MPAS_LOG_ERR) + err = ior(err,1) + endif + return ! skip this routine + endif allowableAdvecDtAllProcs = 0.0_RKIND allowableDiffDtAllProcs = 0.0_RKIND @@ -308,8 +358,6 @@ subroutine prepare_advection(domain, err) ! Initialize - err = 0 - allowableAdvecDtOnProc = 1.0e36_RKIND ! set to large number allowableDiffDtOnProc = 1.0e36_RKIND ! set to large number @@ -322,6 +370,7 @@ subroutine prepare_advection(domain, err) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) ! compute normal velocities and advective CFL limit for this block @@ -329,6 +378,7 @@ subroutine prepare_advection(domain, err) call li_layer_normal_velocity( & meshPool, & normalVelocity, & + edgeMask, & layerNormalVelocity, & allowableAdvecDt, & err_tmp) @@ -475,14 +525,16 @@ subroutine prepare_advection(domain, err) ! Check for CFL error before finishing if (deltat > allowableAdvecDtOnProc) then call mpas_log_write('Advective CFL violation on this processor. ' // & - 'Maximum allowable time step for this processor is (Days_hhh:mmm:sss): ' // trim(allowableAdvecDtOnProcString), MPAS_LOG_ERR) + 'Maximum allowable time step for this processor is (Days_hhh:mmm:sss): ' // trim(allowableAdvecDtOnProcString), & + MPAS_LOG_ERR) err = ior(err,1) endif ! Local diffusive CFL info if ( (config_adaptive_timestep_include_DCFL) .and. (deltat > allowableDiffDtOnProc) ) then call mpas_log_write('Diffusive CFL violation on this processor. ' // & - 'Maximum allowable time step for this processor is (Days_hhh:mmm:sss): ' // trim(allowableDiffDtOnProcString), MPAS_LOG_WARN) + 'Maximum allowable time step for this processor is (Days_hhh:mmm:sss): ' // trim(allowableDiffDtOnProcString), & + MPAS_LOG_WARN) endif if (err > 0) then @@ -568,7 +620,7 @@ subroutine advection_solver(domain, err) real (kind=RKIND), dimension(:), pointer :: thickness real (kind=RKIND), dimension(:,:), pointer :: temperature - real (kind=RKIND), dimension(:,:), pointer :: waterfrac + real (kind=RKIND), dimension(:,:), pointer :: waterFrac real (kind=RKIND), dimension(:,:), pointer :: enthalpy integer, pointer :: nCells @@ -594,7 +646,7 @@ subroutine advection_solver(domain, err) ! Halo updates ! Note: The layer thickness and tracers must be up to date in halos before calling the advection subroutines. - ! The thermal tracers (temperature, waterfrac, enthalpy) are updated at the end of li_thermal_solver. + ! The thermal tracers (temperature, waterFrac, enthalpy) are updated at the end of li_thermal_solver. ! But thickness (which is used by subroutine li_advection_thickness_tracers) needs an update here. TODO: confirm this call mpas_timer_start("halo updates") @@ -730,7 +782,7 @@ subroutine advection_solver(domain, err) call mpas_dmpar_field_halo_exch(domain, 'thickness') call mpas_dmpar_field_halo_exch(domain, 'temperature') - call mpas_dmpar_field_halo_exch(domain, 'waterfrac') + call mpas_dmpar_field_halo_exch(domain, 'waterFrac') call mpas_dmpar_field_halo_exch(domain, 'enthalpy') call mpas_timer_stop("halo updates") @@ -845,7 +897,9 @@ subroutine set_timestep(allowableAdvecDt, allowableDiffDt, clock, dtSeconds, err call mpas_get_timeInterval(intervalToNextForceTime, dt=secondsToNextForceTime, ierr=err_tmp) err = ior(err,err_tmp) if (secondsToNextForceTime - real(floor(secondsToNextForceTime, KIND=8), RKIND) /= 0.0_RKIND) then - call mpas_log_write("set_timestep found secondsToNextForceTime not equal to 0.0: $r, decimal part=$r", MPAS_LOG_ERR, realArgs=(/secondsToNextForceTime, secondsToNextForceTime - real(floor(secondsToNextForceTime, KIND=8), RKIND)/)) + call mpas_log_write("set_timestep found secondsToNextForceTime not equal to 0.0: $r, decimal part=$r", & + MPAS_LOG_ERR, realArgs=(/secondsToNextForceTime, secondsToNextForceTime - & + real(floor(secondsToNextForceTime, KIND=8), RKIND)/)) err = ior(err, 1) endif !print *, proposedDt, secondsToNextForceTime diff --git a/src/core_landice/mode_forward/mpas_li_velocity.F b/src/core_landice/mode_forward/mpas_li_velocity.F index 3cf93c79b5..5bf5e0b03f 100644 --- a/src/core_landice/mode_forward/mpas_li_velocity.F +++ b/src/core_landice/mode_forward/mpas_li_velocity.F @@ -180,6 +180,7 @@ subroutine li_velocity_block_init(block, err) !----------------------------------------------------------------- character (len=StrKIND), pointer :: config_velocity_solver + type (mpas_pool_type), pointer :: velocityPool err = 0 @@ -226,6 +227,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) use mpas_vector_reconstruction use li_mask use li_advection + use li_thermal !----------------------------------------------------------------- ! input variables @@ -256,6 +258,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) type (mpas_pool_type), pointer :: thermalPool type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: hydroPool ! pointers to get from pools character (len=StrKIND), pointer :: config_velocity_solver logical, pointer :: config_do_velocity_reconstruction_for_external_dycore @@ -265,6 +268,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) integer, pointer :: nEdgesSolve integer, pointer :: nEdges integer, pointer :: nVertInterfaces + integer, pointer :: nCells integer, dimension(:), pointer :: edgeMask, cellMask, vertexMask, vertexMaskOld integer, dimension(:,:), pointer :: dirichletVelocityMaskOld, dirichletVelocityMaskNew real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, normalVelocityInitial @@ -273,9 +277,10 @@ subroutine li_velocity_solve(domain, solveVelo, err) real (kind=RKIND), dimension(:,:), pointer :: edgeNormalVectors real (kind=RKIND), dimension(:), pointer :: thickness real (kind=RKIND), dimension(:), pointer :: surfaceSpeed, basalSpeed + real (kind=RKIND), dimension(:), pointer :: xvelmean, yvelmean real (kind=RKIND), dimension(:), pointer :: normalSlopeEdge real (kind=RKIND), dimension(:), pointer :: dcEdge - integer, dimension(:), pointer :: floatingEdges + real (kind=RKIND), dimension(:), pointer :: layerInterfaceFractions integer, dimension(:,:), pointer :: cellsOnEdge integer, dimension(:,:), pointer :: cellsOnVertex integer, dimension(:,:), pointer :: verticesOnEdge @@ -307,7 +312,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) config_do_velocity_reconstruction_for_external_dycore) call mpas_pool_get_config(liConfigs, 'config_print_velocity_cleanup_details', config_print_velocity_cleanup_details) call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) - call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) uphillMarginEdgesFixed = 0 @@ -381,7 +386,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) maxThicknessOnProc = 0.0_RKIND ! initialize to procDynamicVertexMaskChanged = 0 procDirichletMaskChanged = 0 - + block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) @@ -392,11 +397,8 @@ subroutine li_velocity_solve(domain, solveVelo, err) maxThicknessOnProc = max(maxThicknessOnProc, maxval(thickness)) ! The interface expects an array where 1's are floating edges and 0's are non-floating edges. - call mpas_pool_get_array(velocityPool, 'floatingEdges', floatingEdges) call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) - floatingEdges = li_mask_is_floating_ice_int(edgeMask) call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel=1) - call li_calculate_extrapolate_floating_edgemask(meshPool, vertexMask, floatingEdges) ! Determine if the vertex mask changed during this time step for this block (needed for external dycores) ! TODO: There may be some aspects of the mask that are ok change for external dycores, @@ -434,12 +436,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) ! check for thickness on any proc call mpas_dmpar_max_real(domain % dminfo, maxThicknessOnProc, maxThicknessAllProcs) - ! Update halos on mask - the outermost cells/edges/vertices may be wrong for - ! mask components that need neighbor information - call mpas_timer_start("halo updates") - call mpas_dmpar_field_halo_exch(domain, 'floatingEdges') - call mpas_timer_stop("halo updates") - + ! Determine if the vertex mask has changed on any processor and store the value for later use ! (need to exit the block loop to do so) ! TODO Update all blocks with result (if ever support multiple blocks) @@ -464,6 +461,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) @@ -499,7 +497,7 @@ subroutine li_velocity_solve(domain, solveVelo, err) uReconstructZ = 0.0_RKIND call mpas_log_write("Notice: Skipping velocity solve because there is no dynamic ice in domain.", MPAS_LOG_WARN) else - call li_velocity_external_solve(meshPool, geometryPool, thermalPool, scratchPool, velocityPool, err_tmp) + call li_velocity_external_solve(meshPool, geometryPool, thermalPool, hydroPool, scratchPool, velocityPool, err_tmp) endif case('simple') @@ -585,6 +583,8 @@ subroutine li_velocity_solve(domain, solveVelo, err) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) ! --- ! --- Calculate reconstructed velocities @@ -592,6 +592,8 @@ subroutine li_velocity_solve(domain, solveVelo, err) ! do this after velocity halo update in case velocities on the 1-halo edge are wrong (depends on velocity solver) ! Still do this even if we didn't calculate velocity because on a restart these will be defined at the initial time. call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'layerInterfaceFractions', layerInterfaceFractions) call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) @@ -600,6 +602,8 @@ subroutine li_velocity_solve(domain, solveVelo, err) call mpas_pool_get_array(velocityPool, 'uReconstructMeridional', uReconstructMeridional) call mpas_pool_get_array(velocityPool, 'surfaceSpeed', surfaceSpeed) call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) + call mpas_pool_get_array(velocityPool, 'xvelmean', xvelmean) + call mpas_pool_get_array(velocityPool, 'yvelmean', yvelmean) ! Velocities need to be reconstructed at cell centers for the native SIA dycore and for prescribed simple velocities. ! External dycores return their native velocities at cell center locations, @@ -633,28 +637,49 @@ subroutine li_velocity_solve(domain, solveVelo, err) uReconstructMeridional = uReconstructY end if + ! --- + ! --- Remove floating ice where velocity is unrealistically fast + ! --- (Removed ice is added to calving flux) + ! --- (TODO: make this a namelist option?) + ! --- + call remove_fast_ice(meshPool, uReconstructX, uReconstructY, normalVelocity, geometryPool) + ! --- ! --- Calculate diagnostic speed arrays ! --- surfaceSpeed = sqrt(uReconstructX(1,:)**2 + uReconstructY(1,:)**2) basalSpeed = sqrt(uReconstructX(nVertInterfaces,:)**2 + uReconstructY(nVertInterfaces,:)**2) + ! calculate vertical mean velocity + do iCell = 1, nCells + xvelmean(iCell) = sum(uReconstructX(:,iCell) * layerInterfaceFractions(:)) + yvelmean(iCell) = sum(uReconstructY(:,iCell) * layerInterfaceFractions(:)) + enddo ! --- ! --- Calculate strain rates on cell centers ! --- - call calculate_strain_rates(meshPool, velocityPool, err_tmp) + call calculate_strain_rates_and_stresses(meshPool, geometryPool, thermalPool, scratchPool, velocityPool, err) err = ior(err, err_tmp) - ! --- - ! --- Remove floating ice where velocity is unrealistically fast - ! --- (Removed ice is added to calving flux) - ! --- (TODO: make this a namelist option?) - ! --- - call remove_fast_ice(surfaceSpeed, geometryPool) - block => block % next end do + ! calculate velocity-related thermal source term fields + ! (do it here to make restarts easier - this way only the fields need to be restart + ! variables, rather than the fields that are input to the calculations.) + if (solveVelo .and. (trim(config_velocity_solver) /= 'none')) then + + ! (on a restart value, do not try to calculate these - instead use the fields in the restart file) + call li_basal_friction(domain, err_tmp) + err = ior(err, err_tmp) + + if (trim(config_velocity_solver) == 'sia') then + ! For FO solver, heatDissipation is passed back from Albany + call li_heat_dissipation_sia(domain, err_tmp) + err = ior(err, err_tmp) + endif + endif + ! === error check if (err > 0) then call mpas_log_write("An error has occurred in li_velocity_solve.", MPAS_LOG_ERR) @@ -782,12 +807,13 @@ subroutine calculate_beta(block, err) type (mpas_pool_type), pointer :: hydroPool type (mpas_pool_type), pointer :: geometryPool type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: thermalPool type (mpas_pool_type), pointer :: meshPool real (kind=RKIND), dimension(:), pointer :: betaSolve, beta - real (kind=RKIND), dimension(:), pointer :: effectivePressure + real (kind=RKIND), dimension(:), pointer :: basalTemperature, basalPmpTemperature integer, dimension(:), pointer :: cellMask logical, pointer :: config_use_glp - logical, pointer :: config_beta_use_effective_pressure + logical, pointer :: config_beta_thawed_only logical, pointer :: hydroActive real (kind=RKIND) :: betaAccum integer :: nBetaValues @@ -800,10 +826,11 @@ subroutine calculate_beta(block, err) err = 0 call mpas_pool_get_config(liConfigs, 'config_use_glp', config_use_glp) - call mpas_pool_get_config(liConfigs, 'config_beta_use_effective_pressure', config_beta_use_effective_pressure) + call mpas_pool_get_config(liConfigs, 'config_beta_thawed_only', config_beta_thawed_only) call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_array(velocityPool, 'betaSolve', betaSolve) @@ -812,73 +839,35 @@ subroutine calculate_beta(block, err) betaSolve = beta - if (config_beta_use_effective_pressure) then - call mpas_pool_get_package(liPackages, 'hydroActive', hydroActive) - if (.not. hydroActive) then - call mpas_log_write("config_beta_use_effective_pressure can only be used if the subglacial hydrology model is active.", & - MPAS_LOG_ERR) - err = ior(err, 1) - return - endif - call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) - call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) - betaSolve = betaSolve * effectivePressure - - ! if using N-based friction with GLP, copy the value of beta across the grounding line - ! to the floating side for the GLP to work properly - if (config_use_glp) then - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) - call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) - - do iCell = 1, nCells - if (li_mask_is_floating_ice(cellMask(iCell))) then - nBetaValues = 0 - betaAccum = 0.0_RKIND - do iCell2 = 1, nEdgesOnCell(iCell) - neighbor = cellsOnCell(iCell2, iCell) - if (li_mask_is_grounded_ice(cellMask(neighbor))) then - betaAccum = betaAccum + betaSolve(neighbor) - nBetaValues = nBetaValues + 1 - endif - enddo - if (nBetaValues > 0) then - betaSolve(iCell) = betaAccum / real(nBetaValues, kind=RKIND) ! take average of neighboring betas ! TODO: could do this in log space - endif - endif ! if iCell is floating - enddo ! cell loop - endif ! is using GLP with hydro friction law - ! TODO: not sure if halo update needed here. - - end if ! if config_beta_use_effective_pressure - - - if (.not. config_use_glp) then - where (li_mask_is_floating_ice(cellMask)) - betaSolve = 0.0_RKIND + if (config_beta_thawed_only) then + call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) + call mpas_pool_get_array(thermalPool, 'basalPmpTemperature', basalPmpTemperature) + where (basalPmpTemperature - basalTemperature > 0.01_RKIND) + ! Use a small difference to account for roundoff. 0.01 is value used in thermal module. + betaSolve = 1.0e6_RKIND ! no-slip value for frozen bed locations end where endif - !-------------------------------------------------------------------- end subroutine calculate_beta !*********************************************************************** ! -! routine calculate_strain_rates +! routine calculate_strain_rates_and_stresses ! -!> \brief Calulates strain rates on cell centers +!> \brief Calulates strain rates and stresses on cell centers !> \author Matt Hoffman !> \date Feb. 2018 !> \details -!> This routine calculates strain rates on cell centers. +!> This routine calculates strain rates and stresses on cell centers. ! !----------------------------------------------------------------------- - subroutine calculate_strain_rates(meshPool, velocityPool, err) + subroutine calculate_strain_rates_and_stresses(meshPool, geometryPool, thermalPool, scratchPool, velocityPool, err) use li_setup + use li_diagnostic_vars !----------------------------------------------------------------- ! input variables @@ -886,6 +875,13 @@ subroutine calculate_strain_rates(meshPool, velocityPool, err) type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh object + type (mpas_pool_type), intent(in) :: & + geometryPool !< Input: geometry object + type (mpas_pool_type), intent(in) :: & + thermalPool !< Input: thermal object + type (mpas_pool_type), intent(in) :: & + scratchPool !< Input: scratch object + !----------------------------------------------------------------- ! input/output variables !----------------------------------------------------------------- @@ -900,44 +896,118 @@ subroutine calculate_strain_rates(meshPool, velocityPool, err) !----------------------------------------------------------------- ! local variables !----------------------------------------------------------------- - real(kind=RKIND), dimension(:), pointer :: exx, eyy, exy, eyx, eTheta, eMax, eMin - real(kind=RKIND), dimension(:,:), pointer :: uReconstructX, uReconstructY + real(kind=RKIND), dimension(:), pointer :: exx, eyy, exy, eTheta, eMax, eMin, dudy, dvdx + real(kind=RKIND), dimension(:), pointer :: xvelmean, yvelmean + real(kind=RKIND), dimension(:), pointer :: tauxx, tauyy, tauxy, tauMax, tauMin integer :: err_tmp + real(kind=RKIND), pointer :: config_flowLawExponent + + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:,:), pointer :: temperature + real (kind=RKIND), dimension(:,:), pointer :: flowParamA + real (kind=RKIND), dimension(:), pointer :: effectiveViscosity + real (kind=RKIND), dimension(:), pointer :: stiffnessFactor + + type (field1dReal), pointer :: meanFlowParamAVar + real (kind=RKIND), dimension(:), pointer :: meanFlowParamA + + integer, pointer :: nVertLevels + integer, pointer :: nCells + + integer :: iCell + + real(kind=RKIND) :: eEff + err = 0 - call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) - call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(velocityPool, 'xvelmean', xvelmean) + call mpas_pool_get_array(velocityPool, 'yvelmean', yvelmean) + + call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', config_flowLawExponent) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(velocityPool, 'exx', exx) call mpas_pool_get_array(velocityPool, 'eyy', eyy) + call mpas_pool_get_array(velocityPool, 'dudy', dudy) + call mpas_pool_get_array(velocityPool, 'dvdx', dvdx) call mpas_pool_get_array(velocityPool, 'exy', exy) - call mpas_pool_get_array(velocityPool, 'eyx', eyx) call mpas_pool_get_array(velocityPool, 'eTheta', eTheta) call mpas_pool_get_array(velocityPool, 'eMax', eMax) call mpas_pool_get_array(velocityPool, 'eMin', eMin) + call mpas_pool_get_array(velocityPool, 'tauxx', tauxx) + call mpas_pool_get_array(velocityPool, 'tauyy', tauyy) + call mpas_pool_get_array(velocityPool, 'tauxy', tauxy) + call mpas_pool_get_array(velocityPool, 'tauMax', tauMax) + call mpas_pool_get_array(velocityPool, 'tauMin', tauMin) + + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + call mpas_pool_get_array(velocityPool, 'stiffnessFactor', stiffnessFactor) + + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'effectiveViscosity', effectiveViscosity) + + call mpas_pool_get_field(scratchPool, 'meanFlowParamA', meanFlowParamAVar) + call mpas_allocate_scratch_field(meanFlowParamAVar, .true.) + meanFlowParamA => meanFlowParamAVar % array + ! Calculate strain rates - call li_compute_gradient_2d(meshPool, uReconstructX(1,:), exx, exy, err_tmp) + call li_compute_gradient_2d(meshPool, xvelmean, exx, dudy, err_tmp) err = ior(err, err_tmp) - call li_compute_gradient_2d(meshPool, uReconstructY(1,:), eyx, eyy, err_tmp) + call li_compute_gradient_2d(meshPool, yvelmean, dvdx, eyy, err_tmp) err = ior(err, err_tmp) + exy = 0.5_RKIND * (dudy + dvdx) + ! Calculate principal strain rate angle - eTheta = 0.5_RKIND * atan( (exy + eyx) / (exx - eyy + 1.0e-42_RKIND) ) + eTheta = 0.5_RKIND * atan( (2.0_RKIND * exy) / (exx - eyy + 1.0e-42_RKIND) ) ! Calculate principal strain rates - eMax = 0.5_RKIND * (exx + eyy) + sqrt( (0.5_RKIND * (exx - eyy))**2 + (0.25_RKIND*(exy + eyx))**2) - eMin = 0.5_RKIND * (exx + eyy) - sqrt( (0.5_RKIND * (exx - eyy))**2 + (0.25_RKIND*(exy + eyx))**2) + eMax = 0.5_RKIND * (exx + eyy) + sqrt( (0.5_RKIND * (exx - eyy))**2 + exy**2) + eMin = 0.5_RKIND * (exx + eyy) - sqrt( (0.5_RKIND * (exx - eyy))**2 + exy**2) + + call li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, err_tmp) + err = ior(err, err_tmp) + + ! calculate the depth-averaged flow parameter A + meanFlowParamA(:) = sum(flowParamA(:,:), dim=1)/REAL(nVertLevels, kind=RKIND) + + ! calculate effective viscosity from strain rate and flow param. + do iCell = 1, nCells + eEff = sqrt(exx(iCell)**2 + eyy(iCell)**2 + exx(iCell)*eyy(iCell) + exy(iCell)**2) ! effective strain rate + if (eEff == 0.0_RKIND) then + effectiveViscosity(iCell) = 0.0_RKIND + else + effectiveViscosity(iCell) = & + 0.5_RKIND*stiffnessFactor(iCell)*meanFlowParamA(iCell)**(-1.0_RKIND/config_flowLawExponent)* & + eEff**((1.0_RKIND-config_flowLawExponent)/config_flowLawExponent) + endif + enddo + + ! calculate deviatoric stresses + tauxx(:) = 2.0_RKIND*effectiveViscosity(:)*exx(:) + tauyy(:) = 2.0_RKIND*effectiveViscosity(:)*eyy(:) + tauxy(:) = 2.0_RKIND*effectiveViscosity(:)*exy(:) + + ! calculate principal stresses + tauMin(:) = (tauxx(:)+tauyy(:))/2.0_RKIND - sqrt(((tauxx(:)-tauyy(:))/2.0_RKIND)**2 + tauxy(:)**2) + tauMax(:) = (tauxx(:)+tauyy(:))/2.0_RKIND + sqrt(((tauxx(:)-tauyy(:))/2.0_RKIND)**2 + tauxy(:)**2) + + call mpas_deallocate_scratch_field(meanFlowParamAVar, .true.) !-------------------------------------------------------------------- - end subroutine calculate_strain_rates + end subroutine calculate_strain_rates_and_stresses !*********************************************************************** ! ! routine remove_fast_ice ! -!> \brief Removes floating ice with a fast surface speed, presumably an iceberg +!> \brief Removes ice with a fast surface speed, presumably an iceberg !> \author Matt Hoffman !> \date March 2018 !> \details @@ -945,18 +1015,21 @@ end subroutine calculate_strain_rates ! !----------------------------------------------------------------------- - subroutine remove_fast_ice(surfaceSpeed, geometryPool) + subroutine remove_fast_ice(meshPool, uReconstructX, uReconstructY, normalVelocity, geometryPool) use li_mask !----------------------------------------------------------------- ! input variables !----------------------------------------------------------------- - real(kind=RKIND), dimension(:) :: surfaceSpeed + type (mpas_pool_type), intent(in) :: meshPool !----------------------------------------------------------------- ! input/output variables !----------------------------------------------------------------- + real(kind=RKIND), dimension(:,:), intent(inout) :: uReconstructX + real(kind=RKIND), dimension(:,:), intent(inout) :: uReconstructY + real(kind=RKIND), dimension(:,:), intent(inout) :: normalVelocity type (mpas_pool_type), intent(inout) :: geometryPool !----------------------------------------------------------------- @@ -966,19 +1039,51 @@ subroutine remove_fast_ice(surfaceSpeed, geometryPool) !----------------------------------------------------------------- ! local variables !----------------------------------------------------------------- + real(kind=RKIND), pointer :: config_unrealistic_velocity + integer, pointer :: nCells + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell real(kind=RKIND), dimension(:), pointer :: thickness, calvingThickness + real(kind=RKIND), dimension(:), pointer :: upperSurface, lowerSurface integer, dimension(:), pointer :: cellMask - real(kind=RKIND), parameter :: highSpeed = 0.00318471337_RKIND ! 100,000 m/yr in m/s + integer :: iCell, iEdgeOnCell, iEdge + integer :: cellCount + call mpas_pool_get_config(liConfigs, 'config_unrealistic_velocity', config_unrealistic_velocity) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) - where (li_mask_is_floating_ice(cellMask) .and. (surfaceSpeed > highSpeed)) + + cellCount = 0 + do iCell = 1, nCells + if ((li_mask_is_ice(cellMask(iCell))) .and. & + (sqrt(uReconstructX(1,iCell)**2 + uReconstructY(1,iCell)**2) > config_unrealistic_velocity)) then ! "fast" ice that is removed is added to the calving flux - calvingThickness = calvingThickness + thickness - thickness = 0.0_RKIND - end where + calvingThickness(iCell) = calvingThickness(iCell) + thickness(iCell) + thickness(iCell) = 0.0_RKIND + uReconstructX(:,iCell) = 0.0_RKIND + uReconstructY(:,iCell) = 0.0_RKIND + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell,iCell) + normalVelocity(:,iEdge) = 0.0_RKIND + enddo + upperSurface(iCell) = 0.0_RKIND + lowerSurface(iCell) = 0.0_RKIND + ! TODO Should also zero temperature/tracers + cellCount = cellCount + 1 + endif + end do + + if (cellCount > 0) then + call mpas_log_write("Removed $i cells due to speeds greater than $r m/s.", & + intArgs=(/cellCount/), realArgs=(/config_unrealistic_velocity/)) + endif !-------------------------------------------------------------------- end subroutine remove_fast_ice diff --git a/src/core_landice/mode_forward/mpas_li_velocity_external.F b/src/core_landice/mode_forward/mpas_li_velocity_external.F index ecf6e20d07..b769369042 100644 --- a/src/core_landice/mode_forward/mpas_li_velocity_external.F +++ b/src/core_landice/mode_forward/mpas_li_velocity_external.F @@ -62,8 +62,9 @@ module li_velocity_external ! Note: Could add all interface routines to this interface... ! For now, just trying it with this new routine. subroutine velocity_solver_set_parameters(gravity, config_ice_density, config_ocean_density, config_sea_level, & - config_default_flowParamA, config_enhancementFactor, & + config_default_flowParamA, & config_flowLawExponent, config_dynamic_thickness, iceMeltingPointPressureDependence, & + config_thermal_thickness, & li_mask_ValueDynamicIce, li_mask_ValueIce, config_use_glp) & bind(C, name="velocity_solver_set_parameters") @@ -71,8 +72,9 @@ subroutine velocity_solver_set_parameters(gravity, config_ice_density, config_oc INTEGER(C_INT) :: li_mask_ValueDynamicIce, li_mask_ValueIce REAL(C_DOUBLE) :: config_ice_density, config_ocean_density, config_sea_level, config_default_flowParamA, & - config_enhancementFactor, config_flowLawExponent, config_dynamic_thickness, gravity, & - iceMeltingPointPressureDependence + config_flowLawExponent, config_dynamic_thickness, gravity, & + iceMeltingPointPressureDependence, & + config_thermal_thickness LOGICAL(C_BOOL) :: config_use_glp end subroutine velocity_solver_set_parameters @@ -148,8 +150,8 @@ subroutine li_velocity_external_init(domain, err) call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) ! Check for configuration options that are incompatible with external velocity solver conventions - if (config_num_halos < 2) then - call mpas_log_write("External velocity solvers require that config_num_halos >= 2", MPAS_LOG_ERR) + if (config_num_halos < 3) then + call mpas_log_write("External velocity solvers require that config_num_halos >= 3", MPAS_LOG_ERR) err_tmp = 1 endif err = ior(err,err_tmp) @@ -261,7 +263,7 @@ subroutine li_velocity_external_block_init(block, err) real (kind=RKIND), pointer :: radius type (field1DInteger), pointer :: indexToCellIDField, indexToEdgeIDField, indexToVertexIDField real (kind=RKIND), pointer :: config_ice_density, config_ocean_density, config_sea_level, config_default_flowParamA, & - config_enhancementFactor, config_flowLawExponent, config_dynamic_thickness + config_thermal_thickness, config_flowLawExponent, config_dynamic_thickness logical, pointer :: config_use_glp ! halo exchange arrays @@ -327,7 +329,7 @@ subroutine li_velocity_external_block_init(block, err) call velocity_solver_set_grid_data(nCells, nEdges, nVertices, nVertInterfaces, & nCellsSolve, nEdgesSolve, nVerticesSolve, maxNEdgesOnCell, radius, & cellsOnEdge, cellsOnVertex, verticesOnCell, verticesOnEdge, edgesOnCell, & - nEdgesOnCell, indexToCellID, & + nEdgesOnCell, indexToCellID, indexToEdgeID, indexToVertexID, & xCell, yCell, zCell, xVertex, yVertex, zVertex, areaTriangle, & sendCellsArray, recvCellsArray, & sendEdgesArray, recvEdgesArray, & @@ -351,15 +353,16 @@ subroutine li_velocity_external_block_init(block, err) call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) call mpas_pool_get_config(liConfigs, 'config_default_flowParamA', config_default_flowParamA) - call mpas_pool_get_config(liConfigs, 'config_enhancementFactor', config_enhancementFactor) call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', config_flowLawExponent) + call mpas_pool_get_config(liConfigs, 'config_thermal_thickness', config_thermal_thickness) call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) call mpas_pool_get_config(liConfigs, 'config_use_glp', config_use_glp) #if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) call velocity_solver_set_parameters(gravity, config_ice_density, config_ocean_density, config_sea_level, & - config_default_flowParamA, config_enhancementFactor, & + config_default_flowParamA, & config_flowLawExponent, config_dynamic_thickness, & iceMeltingPointPressureDependence, & + config_thermal_thickness, & li_mask_ValueAlbanyActive, li_mask_ValueIce, & logical(config_use_glp, KIND=1) ) @@ -390,7 +393,7 @@ end subroutine li_velocity_external_block_init ! !----------------------------------------------------------------------- - subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scratchPool, velocityPool, err) + subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, hydroPool, scratchPool, velocityPool, err) use li_mask @@ -409,6 +412,9 @@ subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scrat type (mpas_pool_type), intent(in) :: & thermalPool !< Input: thermal information + type (mpas_pool_type), intent(in) :: & + hydroPool !< Input: hydro information + type (mpas_pool_type), intent(in) :: & scratchPool !< Input: scratch information @@ -437,23 +443,34 @@ subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scrat real (kind=RKIND), dimension(:), pointer :: & thickness, bedTopography, lowerSurface, upperSurface, layerThicknessFractions, betaSolve, sfcMassBal + real (kind=RKIND), dimension(:), pointer :: drivingStressVert + real (kind=RKIND), dimension(:), pointer :: drivingStress real (kind=RKIND), dimension(:,:), pointer :: & normalVelocity, uReconstructX, uReconstructY, uReconstructZ real (kind=RKIND), dimension(:,:), pointer :: temperature + real (kind=RKIND), dimension(:), pointer :: stiffnessFactor + real (kind=RKIND), dimension(:), pointer :: effectivePressure + real (kind=RKIND), dimension(:), pointer :: effectivePressureLimited + real (kind=RKIND), dimension(:), pointer :: muFriction real (kind=RKIND), pointer :: deltat - integer, dimension(:), pointer :: vertexMask, cellMask, edgeMask, floatingEdges + integer, dimension(:), pointer :: vertexMask, cellMask, edgeMask integer, dimension(:,:), pointer :: dirichletVelocityMask character (len=StrKIND), pointer :: config_velocity_solver logical, pointer :: config_always_compute_fem_grid logical, pointer :: config_output_external_velocity_solver_data + logical, pointer :: config_nonconvergence_error real (kind=RKIND), pointer :: config_ice_density + real (kind=RKIND), pointer :: config_effective_pressure_max integer, pointer :: anyDynamicVertexMaskChanged integer, pointer :: dirichletMaskChanged integer, pointer :: nEdges + integer, pointer :: nCells + integer, pointer :: nVertLevels integer, pointer :: timestepNumber type (field2dReal), pointer :: dissipationVertexField real (kind=RKIND), dimension(:,:), pointer :: heatDissipation ! on cells integer :: iEdge + integer :: iCell real(kind=RKIND), parameter :: secondsInYear = 365.0_RKIND * 24.0_RKIND * 3600.0_RKIND !< The value of seconds in a year assumed by external dycores integer, target :: err_tmp @@ -467,13 +484,16 @@ subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scrat call mpas_pool_get_config(liConfigs, 'config_always_compute_fem_grid', config_always_compute_fem_grid) call mpas_pool_get_config(liConfigs, 'config_output_external_velocity_solver_data', & config_output_external_velocity_solver_data) + call mpas_pool_get_config(liConfigs, 'config_nonconvergence_error', config_nonconvergence_error) call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_effective_pressure_max', config_effective_pressure_max) ! Mesh variables call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) call mpas_pool_get_array(meshPool, 'deltat', deltat) call mpas_pool_get_array(meshPool, 'timestepNumber', timestepNumber) call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) ! Geometry variables call mpas_pool_get_array(geometryPool, 'thickness', thickness) @@ -495,16 +515,29 @@ subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scrat call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) call mpas_pool_get_array(velocityPool, 'uReconstructZ', uReconstructZ) call mpas_pool_get_array(velocityPool, 'betaSolve', betaSolve) + call mpas_pool_get_array(velocityPool, 'drivingStressVert', drivingStressVert) + call mpas_pool_get_array(velocityPool, 'drivingStress', drivingStress) + call mpas_pool_get_array(velocityPool, 'muFriction', muFriction) call mpas_pool_get_array(velocityPool, 'anyDynamicVertexMaskChanged', anyDynamicVertexMaskChanged) call mpas_pool_get_array(velocityPool, 'dirichletMaskChanged', dirichletMaskChanged) call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMask, timeLevel = 1) - call mpas_pool_get_array(velocityPool, 'floatingEdges', floatingEdges) + call mpas_pool_get_array(velocityPool, 'stiffnessFactor', stiffnessFactor) + + ! Hydro variables + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) + call mpas_pool_get_array(velocityPool, 'effectivePressureLimited', effectivePressureLimited) #if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) ! Capture Albany output call interface_redirect_stdout(timestepNumber) #endif + ! Create version of effectivePressure field with limits applied + do iCell = 1, nCells + effectivePressureLimited(iCell) = max(0.0_RKIND, effectivePressure(iCell)) ! Never pass a negative N + effectivePressureLimited(iCell) = min(config_effective_pressure_max, effectivePressureLimited(iCell)) + enddo + ! ================================================================== ! External dycore calls to be made only when vertex mask changes ! ================================================================== @@ -515,7 +548,7 @@ subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scrat (dirichletMaskChanged == 1) ) then call mpas_log_write("Generating new external velocity solver FEM grid.", flushNow=.true.) call generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, & - floatingEdges, layerThicknessFractions, lowerSurface, thickness, err) + layerThicknessFractions, lowerSurface, thickness, err) endif @@ -564,23 +597,33 @@ subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scrat call mpas_timer_start("velocity_solver_solve_FO") err_albany => err_tmp call velocity_solver_solve_FO(bedTopography, lowerSurface, thickness, & - betaSolve, sfcMassBal, temperature, & + betaSolve, sfcMassBal, temperature, stiffnessFactor, & + effectivePressureLimited, muFriction, & uReconstructX, uReconstructY, & ! Dirichlet boundary values to apply where dirichletVelocityMask=1 - normalVelocity, dissipationVertexField % array, uReconstructX, uReconstructY, & ! return values + normalVelocity, drivingStressVert, dissipationVertexField % array, uReconstructX, uReconstructY, & ! return values deltat, err_albany) ! return values -! call velocity_solver_estimate_SS_SMB(normalVelocity, mesh % sfcMassBal % array) ! this was used only for some ice2sea experiments, and is not a general routine to use call mpas_timer_stop("velocity_solver_solve_FO") - if (err_tmp > 0) then - call mpas_log_write("Albany velocity solve encountered an error! Check log.albany.0000.out for more information.", MPAS_LOG_ERR) + if (err_tmp == 1) then + if (config_nonconvergence_error) then + call mpas_log_write("Albany velocity solve failed to converge! " // & + "Check log.albany.0000.out for more information.", MPAS_LOG_ERR) + else + call mpas_log_write("Albany velocity solve failed to converge! " // & + "Check log.albany.0000.out for more information.", MPAS_LOG_WARN) + err_tmp = 0 + endif endif err = ior(err,err_tmp) + ! Now interpolate from vertices to cell centers call li_interpolate_vertex_to_cell_2d(meshPool, dissipationVertexField % array, heatDissipation) heatDissipation = heatDissipation / (config_ice_density * cp_ice) call mpas_deallocate_scratch_field(dissipationVertexField, .true.) + call li_interpolate_vertex_to_cell_1d(meshPool, drivingStressVert, drivingStress) + if (config_output_external_velocity_solver_data) then call mpas_timer_start("velocity_solver export") call velocity_solver_export_FO_velocity() @@ -683,7 +726,7 @@ subroutine li_velocity_external_finalize(err) #if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) ! This call is needed for using any of the external velocity solvers - ! call velocity_solver_finalize() + call velocity_solver_finalize() #else call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) err = 1 @@ -736,10 +779,16 @@ subroutine li_velocity_external_write_albany_mesh(domain) observedSurfaceVelocityX, observedSurfaceVelocityY, observedSurfaceVelocityUncertainty real (kind=RKIND), dimension(:), pointer :: observedThicknessTendency, observedThicknessTendencyUncertainty real (kind=RKIND), dimension(:,:), pointer :: temperature - integer, dimension(:), pointer :: vertexMask, cellMask, edgeMask, floatingEdges, indexToCellID + real (kind=RKIND), dimension(:), pointer :: surfaceAirTemperature, basalHeatFlux + integer, dimension(:), pointer :: vertexMask, cellMask, edgeMask, indexToCellID + real (kind=RKIND), dimension(:,:), pointer :: layerThickness + real (kind=RKIND), dimension(:), pointer :: stiffnessFactor + real (kind=RKIND), dimension(:), pointer :: effectivePressure + real (kind=RKIND), dimension(:), pointer :: muFriction integer, dimension(:,:), pointer :: dirichletVelocityMask - type (mpas_pool_type), pointer :: meshPool, geometryPool, thermalPool, observationsPool, velocityPool + type (mpas_pool_type), pointer :: meshPool, geometryPool, thermalPool, observationsPool, velocityPool, scratchPool, hydroPool real (kind=RKIND), pointer :: config_sea_level, config_ice_density, config_ocean_density + integer :: iCell integer :: err call mpas_pool_get_config(liConfigs, 'config_write_albany_ascii_mesh', config_write_albany_ascii_mesh) @@ -764,10 +813,6 @@ subroutine li_velocity_external_write_albany_mesh(domain) call mpas_log_write("config_velocity solver needs to be set to 'FO' for config_write_albany_ascii_mesh to work.", & MPAS_LOG_CRIT) endif - ! check nProcs - if (domain % dminfo % nProcs /= 1) then - call mpas_log_write("config_write_albany_ascii_mesh currently only works on 1 processor.", MPAS_LOG_CRIT) - endif ! check nBlocks if (domain % dminfo % total_blocks /= 1) then call mpas_log_write("config_write_albany_ascii_mesh currently only works on 1 block per processor.", MPAS_LOG_CRIT) @@ -778,8 +823,10 @@ subroutine li_velocity_external_write_albany_mesh(domain) call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) call mpas_pool_get_subpool(domain % blocklist % structs, 'geometry', geometryPool) call mpas_pool_get_subpool(domain % blocklist % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'hydro', hydroPool) call mpas_pool_get_subpool(domain % blocklist % structs, 'velocity', velocityPool) call mpas_pool_get_subpool(domain % blocklist % structs, 'observations', observationsPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) ! Mesh variables call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) @@ -794,14 +841,21 @@ subroutine li_velocity_external_write_albany_mesh(domain) call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel = 1) call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness) ! Velocity variables call mpas_pool_get_array(velocityPool, 'beta', beta) - call mpas_pool_get_array(velocityPool, 'floatingEdges', floatingEdges) + call mpas_pool_get_array(velocityPool, 'muFriction', muFriction) call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMask, timeLevel = 1) + call mpas_pool_get_array(velocityPool, 'stiffnessFactor', stiffnessFactor) ! Thermal variables call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) + call mpas_pool_get_array(thermalPool, 'basalHeatFlux', basalHeatFlux) + + ! hydro variables + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) ! Observation variables call mpas_pool_get_array(observationsPool, 'observedSurfaceVelocityX', observedSurfaceVelocityX) @@ -815,7 +869,7 @@ subroutine li_velocity_external_write_albany_mesh(domain) !---- - ! Calculate diagnostic variables to get 1. lowerSurface, 2. mask fields 3. floatingedges and updated edgemask. + ! Calculate diagnostic variables to get 1. lowerSurface, 2. mask fields 3. updated edgemask. ! We could call diagnostic_solve_before_velocity to do all that, but the way ! code is currently organized, that would be a circular dependency.a @@ -828,18 +882,23 @@ subroutine li_velocity_external_write_albany_mesh(domain) lowerSurface = bedTopography end where - floatingEdges = li_mask_is_floating_ice_int(edgeMask) - call li_calculate_extrapolate_floating_edgemask(meshPool, vertexMask, floatingEdges) + ! Ensure BMB uncertainty is 0 for grounded ice (and no ice locations) + where (.not. (li_mask_is_floating_ice(cellMask))) + floatingBasalMassBalUncertainty = 0.0_RKIND + end where ! Create FEM mesh call mpas_log_write("Generating new external velocity solver FEM grid.", flushNow=.true.) call generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, & - floatingEdges, layerThicknessFractions, lowerSurface, thickness, err) + layerThicknessFractions, lowerSurface, thickness, err) ! call the C++ routine to write the mesh call mpas_log_write("Writing Albany ASCII mesh.", flushNow=.true.) call write_ascii_mesh(indexToCellID, bedTopography, lowerSurface, & beta, temperature, & + surfaceAirTemperature, basalHeatFlux, & + stiffnessFactor, & + effectivePressure, muFriction, & thickness, thicknessUncertainty, & sfcMassBal, sfcMassBalUncertainty, & floatingBasalMassBal, floatingBasalMassBalUncertainty, & @@ -850,6 +909,7 @@ subroutine li_velocity_external_write_albany_mesh(domain) call interface_reset_stdout() + ! kill the model so we don't actually run the forward model call mpas_log_write("Write of Albany ASCII mesh complete. Terminating execution normally.", MPAS_LOG_CRIT) @@ -942,14 +1002,14 @@ end subroutine interface_stokes_init ! !----------------------------------------------------------------------- - subroutine generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, floatingEdges, & + subroutine generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, & layerThicknessFractions, lowerSurface, thickness, err) !----------------------------------------------------------------- ! input variables !----------------------------------------------------------------- character (len=StrKIND), pointer :: config_velocity_solver - integer, pointer, dimension(:), intent(in) :: vertexMask, cellMask, floatingEdges + integer, pointer, dimension(:), intent(in) :: vertexMask, cellMask integer, pointer, dimension(:,:), intent(in) :: dirichletVelocityMask real(kind=RKIND), pointer, dimension(:), intent(in) :: layerThicknessFractions, & lowerSurface, thickness @@ -972,7 +1032,7 @@ subroutine generate_fem_grid(config_velocity_solver, vertexMask, cellMask, diric #if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) call mpas_timer_start("velocity_solver_compute_2d_grid") - call velocity_solver_compute_2d_grid(vertexMask, cellMask, dirichletVelocityMask, floatingEdges) + call velocity_solver_compute_2d_grid(vertexMask, cellMask, dirichletVelocityMask) call mpas_timer_stop("velocity_solver_compute_2d_grid") #else call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) @@ -996,7 +1056,7 @@ subroutine generate_fem_grid(config_velocity_solver, vertexMask, cellMask, diric case ('FO') ! =============================================== #ifdef USE_EXTERNAL_FIRSTORDER call mpas_timer_start("velocity_solver_extrude_3d_grid") - call velocity_solver_extrude_3d_grid(layerThicknessFractions, lowerSurface, thickness) + call velocity_solver_extrude_3d_grid(layerThicknessFractions) call mpas_timer_stop("velocity_solver_extrude_3d_grid") call mpas_timer_start("velocity_solver_init_FO") call velocity_solver_init_FO(layerThicknessFractions) diff --git a/src/core_landice/shared/mpas_li_constants.F b/src/core_landice/shared/mpas_li_constants.F index 156642d421..6f3a63181f 100644 --- a/src/core_landice/shared/mpas_li_constants.F +++ b/src/core_landice/shared/mpas_li_constants.F @@ -29,6 +29,7 @@ module li_constants triple_point => SHR_CONST_TKTRIP,& rho_water => SHR_CONST_RHOFW, & cp_freshwater => SHR_CONST_CPFW, & + cp_seawater => SHR_CONST_CPSW, & pii => SHR_CONST_PI, & gravity => SHR_CONST_G implicit none @@ -45,6 +46,7 @@ module li_constants real (kind=RKIND), parameter, public :: triple_point = 273.16_RKIND !< Triple point of water (K) real (kind=RKIND), parameter, public :: rho_water = 1000.0_RKIND !< Density of fresh water (kg m^-3) real (kind=RKIND), parameter, public :: cp_freshwater = 4.188e3_RKIND !< heat capacity of freshwater (J/kg/K) + real (kind=RKIND), parameter, public :: cp_seawater = 3.974e3_RKIND !< heat capacity of seawater (J/kg/K) real (kind=RKIND), parameter, public :: pii = 3.141592653589793_RKIND !< Constant: Pi real (kind=RKIND), parameter, public :: gravity = 9.80616_RKIND !< Constant: Acceleration due to gravity [m s-2] @@ -61,8 +63,8 @@ module li_constants !< These values are from the Ocean Water Freezing Point Calculator, !< http://www.csgnetwork.com/h2ofreezecalc.html (25 Nov. 2014) - real (kind=RKIND), parameter, public :: & - iceMeltingPointPressureDependence = 9.7456e-8_RKIND ! Dependence of ice melting point on pressure (K Pa^-1) + real (kind=RKIND), parameter, public :: iceMeltingPointPressureDependence = 7.9e-8_RKIND + !< Clausius-Clapeyron constant: Dependence of ice melting point on pressure (K Pa^-1) ! conversion factors real (kind=RKIND), parameter, public :: kelvin_to_celsius = 273.15_RKIND !< factor to convert Kelvin to Celsius diff --git a/src/core_landice/shared/mpas_li_mask.F b/src/core_landice/shared/mpas_li_mask.F index 4436e0a511..9ccf0fb4b1 100644 --- a/src/core_landice/shared/mpas_li_mask.F +++ b/src/core_landice/shared/mpas_li_mask.F @@ -200,14 +200,24 @@ subroutine li_calculate_mask_init(geometryPool, err) integer, dimension(:), pointer :: cellMask real(KIND=RKIND), dimension(:), pointer :: thickness logical, pointer :: config_do_restart + integer, pointer :: config_num_halos, config_number_of_blocks + integer :: err_tmp err = 0 + err_tmp = 0 ! Assign pointers and variables - call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask, timeLevel=1) call mpas_pool_get_array(geometryPool, 'thickness', thickness) call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) + call mpas_pool_get_config(liConfigs, 'config_num_halos', config_num_halos) + + if (config_num_halos < 3) then + call mpas_log_write("MALI requires that config_num_halos >= 3. (edgeMask calculations require it.)", MPAS_LOG_ERR) + err_tmp = 1 + endif + err = ior(err,err_tmp) if (config_do_restart .eqv. .false.) then ! We only want to set this bit of the mask when a new simulation starts, ! but not during a restart. @@ -289,6 +299,7 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) logical :: aCellOnEdgeHasIce, aCellOnEdgeHasNoIce, aCellOnEdgeHasDynamicIce, aCellOnEdgeHasNoDynamicIce, & aCellOnEdgeIsFloating logical :: aCellOnEdgeIsGrounded + logical :: aCellOnEdgeIsOpenOcean integer :: numCellsOnVertex integer :: numDiriDynamicCells, numDiriNondynamicCells, numExtendedCells logical :: validVertex @@ -313,7 +324,7 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) - call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask, timeLevel=1) call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel=1) call mpas_pool_get_array(geometryPool, 'thickness', thickness) @@ -357,7 +368,9 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) if (li_mask_is_ice(cellMask(i))) then isMargin = .false. do j=1,nEdgesOnCell(i) ! Check if any neighbors are non-ice - isMargin = ( isMargin .or. (.not. li_mask_is_ice(cellMask(cellsOnCell(j,i)))) ) + if (cellsOnCell(j,i) <= nCells) then + isMargin = ( isMargin .or. (.not. li_mask_is_ice(cellMask(cellsOnCell(j,i)))) ) + endif enddo if (isMargin) then cellMask(i) = ior(cellMask(i), li_mask_ValueMargin) @@ -399,8 +412,8 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) (trim(config_velocity_solver) == 'Stokes') ) then ! HO external FEM dycore ! Identify cells where the ice is above the ice dynamics thickness limit but not with a dirichletVelocity condition set - where ( li_mask_is_dynamic_ice(cellMask) .and. & ! same as for SIA case - (maxval(dirichletVelocityMask(1:nVertInterfaces-1, :), dim=1) == 0) ) ! but exclude dirichletVelocityMask + where ( li_mask_is_dynamic_ice(cellMask) ) ! .and. & ! same as for SIA case + ! (maxval(dirichletVelocityMask(1:nVertInterfaces-1, :), dim=1) == 0) ) ! but exclude dirichletVelocityMask ! locations set as lateral b.c. We don't want to consider dirichlet b.c. on the basal boundary, ! so we ignore the basal level cellMask = ior(cellMask, li_mask_ValueAlbanyActive) @@ -414,7 +427,9 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) if (li_mask_is_dynamic_ice(cellMask(i))) then isMargin = .false. do j=1,nEdgesOnCell(i) ! Check if any neighbors are not dynamic - isMargin = ( isMargin .or. (.not. li_mask_is_dynamic_ice(cellMask(cellsOnCell(j,i)))) ) + if (cellsOnCell(j,i) <= nCells) then + isMargin = ( isMargin .or. (.not. li_mask_is_dynamic_ice(cellMask(cellsOnCell(j,i)))) ) + endif enddo if (isMargin) then cellMask(i) = ior(cellMask(i), li_mask_ValueDynamicMargin) @@ -446,7 +461,7 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) do j=1,nEdgesOnCell(i) ! Check if any neighbors are floating or open ocean iCellNeighbor = cellsOnCell(j,i) if (li_mask_is_floating_ice(cellMask(iCellNeighbor)) .or. & - ((bedTopography(iCellNeighbor) < config_sea_level) .and. (.not. li_mask_is_ice(cellMask(iCellNeighbor)))) ) then + ((bedTopography(iCellNeighbor) < config_sea_level) .and. (.not. li_mask_is_ice(cellMask(iCellNeighbor)))) ) then cellMask(i) = ior(cellMask(i), li_mask_ValueGroundingLine) endif cycle ! no need to look at additional neighbors @@ -580,6 +595,7 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) aCellOnEdgeHasNoDynamicIce = .false. aCellOnEdgeIsFloating = .false. aCellOnEdgeIsGrounded = .false. + aCellOnEdgeIsOpenOcean = .false. do j = 1, 2 iCell = cellsOnEdge(j,i) aCellOnEdgeHasIce = (aCellOnEdgeHasIce .or. li_mask_is_ice(cellMask(iCell))) @@ -588,6 +604,8 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) aCellOnEdgeHasNoDynamicIce = (aCellOnEdgeHasNoDynamicIce .or. (.not. (li_mask_is_dynamic_ice(cellMask(iCell))))) aCellOnEdgeIsFloating = (aCellOnEdgeIsFloating .or. li_mask_is_floating_ice(cellMask(iCell))) aCellOnEdgeIsGrounded = (aCellOnEdgeIsGrounded .or. li_mask_is_grounded_ice(cellMask(iCell))) + aCellOnEdgeIsOpenOcean = aCellOnEdgeIsOpenOcean .or. & + ((bedTopography(iCell) < config_sea_level) .and. (.not. li_mask_is_ice(cellMask(iCell)))) end do if (aCellOnEdgeHasIce) then edgeMask(i) = ior(edgeMask(i), li_mask_ValueIce) @@ -600,7 +618,7 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) if (aCellOnEdgeIsFloating) then edgeMask(i) = ior(edgeMask(i), li_mask_ValueFloating) endif - if (aCellOnEdgeIsFloating .and. aCellOnEdgeIsGrounded) then + if (aCellOnEdgeIsGrounded .and. (aCellOnEdgeIsFloating .or. aCellOnEdgeIsOpenOcean)) then edgeMask(i) = ior(edgeMask(i), li_mask_ValueGroundingLine) endif if (aCellOnEdgeHasIce .and. aCellOnEdgeHasNoIce) then @@ -626,66 +644,6 @@ subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) end subroutine li_calculate_mask -!*********************************************************************** -! -! routine li_calculate_extrapolate_floating_edgemask -! -!> \brief Extrapolates floating edges forward as needed by external FEM dycores -!> \author Matt Hoffman -!> \date 29 January 2015 -!> \details -!> External FEM dycores include the first non-ice cells in their mesh. They -!> also use a mask to apply floating lateral boundary conditions on edges. -!> Because they include extra cell center locations in their meshes, the triangle -!> edges connecting these extra nodes will not be covered by the standard -!> MPAS edge mask. This routine deals with this problem by 'extrapolating' -!> the floating edge mask forward to cover the edges connecting these extra nodes. -!> It does so by looping over edges, and setting as floating any edge that has -!> at least one neighboring vertex that is 'floating'. This makes use of the -!> convention that "Floating vertices have at least one neighboring cell floating". -! -!----------------------------------------------------------------------- - - subroutine li_calculate_extrapolate_floating_edgemask(meshPool, vertexMask, floatingEdges) - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - !----------------------------------------------------------------- - ! input/output variables - !----------------------------------------------------------------- - integer, dimension(:), intent(inout) :: & - vertexMask !< Input/Output: vertexMask - - integer, dimension(:), intent(inout) :: & - floatingEdges !< Input/Output: 0/1 mask of floating edges - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - integer, dimension(:,:), pointer :: verticesOnEdge - integer, pointer :: nEdges - integer :: iEdge - - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) - - ! Build floatingEdges mask that is extended forward one extra edge - do iEdge = 1, nEdges - floatingEdges(iEdge) = maxval(li_mask_is_floating_ice_int(vertexMask(verticesOnEdge(:, iEdge)))) - enddo - - ! Now includes vertices that include 2 cells with ice and one extended floating cell - ! This will be when the two cells with ice were Dirichlet cells - end subroutine li_calculate_extrapolate_floating_edgemask - - ! =================================== ! Functions for decoding bitmasks - will work with cellMask, edgeMask, or vertexMask ! =================================== diff --git a/src/core_landice/shared/mpas_li_setup.F b/src/core_landice/shared/mpas_li_setup.F index 94f38212ef..060f7a67c7 100644 --- a/src/core_landice/shared/mpas_li_setup.F +++ b/src/core_landice/shared/mpas_li_setup.F @@ -49,6 +49,7 @@ module li_setup li_setup_vertical_grid, & li_setup_sign_and_index_fields, & li_setup_wachspress_vertex_to_cell_weights, & + li_interpolate_vertex_to_cell_1d, & li_interpolate_vertex_to_cell_2d, & li_cells_to_vertices_1dfield_using_kiteAreas, & li_calculate_layerThickness, & @@ -186,12 +187,15 @@ subroutine li_setup_vertical_grid(meshPool, geometryPool, err) ! Pool pointers integer, pointer :: nVertLevels ! Dimensions real (kind=RKIND), dimension(:), pointer :: layerThicknessFractions, layerCenterSigma, layerInterfaceSigma + real (kind=RKIND), dimension(:), pointer :: layerInterfaceFractions real (kind=RKIND), dimension(:), pointer :: thickness logical, pointer :: config_do_restart ! Truly locals integer :: k real (kind=RKIND) :: fractionTotal + err = 0 + ! Get pool stuff call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) @@ -199,6 +203,7 @@ subroutine li_setup_vertical_grid(meshPool, geometryPool, err) call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) + call mpas_pool_get_array(meshPool, 'layerInterfaceFractions', layerInterfaceFractions) call mpas_pool_get_array(geometryPool, 'thickness', thickness) ! Check that layerThicknessFractions are valid @@ -231,6 +236,13 @@ subroutine li_setup_vertical_grid(meshPool, geometryPool, err) end do layerInterfaceSigma(nVertLevels+1) = 1.0_RKIND + ! layerInterfaceFractions are the fraction associated with each interface + layerInterfaceFractions(1) = 0.5_RKIND * layerThicknessFractions(1) + do k = 2, nVertLevels + layerInterfaceFractions(k) = 0.5_RKIND * (layerThicknessFractions(k-1) + layerThicknessFractions(k)) + end do + layerInterfaceFractions(nVertLevels+1) = 0.5_RKIND * layerThicknessFractions(nVertLevels) + !-------------------------------------------------------------------- end subroutine li_setup_vertical_grid @@ -371,7 +383,7 @@ subroutine li_setup_wachspress_vertex_to_cell_weights(meshPool) integer :: nVerticesOnThisCell real(kind=RKIND), dimension(:,:), allocatable :: vertexCoordsOnCell type (mpas_pool_type), pointer :: meshPoolPointer - + real(kind=RKIND), dimension(3) :: coords ! Get pool stuff call mpas_pool_get_dimension(meshPool, 'nCells', nCells) call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) @@ -385,7 +397,8 @@ subroutine li_setup_wachspress_vertex_to_cell_weights(meshPool) call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) - meshPoolPointer => meshPool ! mpas_wachspress_coordinates expected a pointer to the mesh pool instead of just the mesh pool itself + meshPoolPointer => meshPool ! mpas_wachspress_coordinates expected a pointer to the mesh pool + ! instead of just the mesh pool itself allocate(vertexCoordsOnCell(3, maxEdges)) @@ -395,9 +408,12 @@ subroutine li_setup_wachspress_vertex_to_cell_weights(meshPool) iVertex = verticesOnCell(v, iCell) vertexCoordsOnCell(:, v) = (/ xVertex(iVertex), yVertex(iVertex), zVertex(iVertex) /) enddo + coords(1) = xCell(iCell) + coords(2) = yCell(iCell) + coords(3) = zCell(iCell) wachspressWeightVertex(1:nVerticesOnThisCell, iCell) = mpas_wachspress_coordinates( & nVerticesOnThisCell, vertexCoordsOnCell(:, 1:nVerticesOnThisCell), & - (/ xCell(iCell), yCell(iCell), zCell(iCell) /), meshPoolPointer) + coords, meshPoolPointer) end do deallocate(vertexCoordsOnCell) @@ -405,6 +421,68 @@ subroutine li_setup_wachspress_vertex_to_cell_weights(meshPool) end subroutine li_setup_wachspress_vertex_to_cell_weights +!*********************************************************************** +! +! routine li_interpolate_vertex_to_cell_1d +! +!> \brief Interpolates from vertices to cell centers using Wachspress functions +!> \author Matt Hoffman +!> \date 03 March 2020 +!> \details +!> This routine interpolated from vertices to cell center values using +!> Wachspress functions in MPAS operators. +! +!----------------------------------------------------------------------- + subroutine li_interpolate_vertex_to_cell_1d(meshPool, vertexValue, cellValue) + use mpas_geometry_utils + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh object + + real(kind=RKIND), dimension(:), pointer, intent(in) :: vertexValue !< value on vertices + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:), intent(out) :: cellValue !< value on cells + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pool pointers + real(kind=RKIND), dimension(:,:), pointer :: wachspressWeightVertex + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: verticesOnCell + integer, pointer :: nCells, maxEdges + integer :: iCell, iVertex, v + integer :: nVerticesOnThisCell + + ! Get pool stuff + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + call mpas_pool_get_array(meshPool, 'wachspressWeightVertex', wachspressWeightVertex) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + + cellValue(:) = 0.0_RKIND + do iCell = 1, nCells + nVerticesOnThisCell = nEdgesOnCell(iCell) + do v = 1, nVerticesOnThisCell + iVertex = verticesOnCell(v, iCell) + cellValue(iCell) = cellValue(iCell) + wachspressWeightVertex(v, iCell) * vertexValue(iVertex) + enddo + end do + + !-------------------------------------------------------------------- + end subroutine li_interpolate_vertex_to_cell_1d + + !*********************************************************************** ! ! routine li_interpolate_vertex_to_cell_2d @@ -434,7 +512,7 @@ subroutine li_interpolate_vertex_to_cell_2d(meshPool, vertexValue, cellValue) !----------------------------------------------------------------- ! output variables !----------------------------------------------------------------- - real(kind=RKIND), dimension(:,:), pointer, intent(in) :: cellValue !< value on cells + real(kind=RKIND), dimension(:,:), intent(out) :: cellValue !< value on cells !----------------------------------------------------------------- ! local variables @@ -525,7 +603,7 @@ subroutine li_cells_to_vertices_1dfield_using_kiteAreas(meshPool, fieldCells, fi cellIndex = cellsOnVertex(iCell, iVertex) baryweight = 0.0_RKIND do iCell2 = 1, vertexDegree - if (iCell2 /= icell) baryweight = baryweight + 0.5 * kiteAreasOnVertex(iCell2, iVertex) + if (iCell2 /= icell) baryweight = baryweight + 0.5_RKIND * kiteAreasOnVertex(iCell2, iVertex) enddo fVertexAccum = fVertexAccum + baryweight * fieldCells(cellIndex) ! add the contribution from this cell's kite weightAccum = weightAccum + kiteAreasOnVertex(iCell, iVertex) ! This doesn't match areaTriangle for obtuse triangles!!! diff --git a/src/core_ocean/build_options.mk b/src/core_ocean/build_options.mk index e979a3b0a3..cea9e4665a 100644 --- a/src/core_ocean/build_options.mk +++ b/src/core_ocean/build_options.mk @@ -8,7 +8,7 @@ FCINCLUDES += -I$(ROOT_DIR)/core_ocean/mode_forward -I$(ROOT_DIR)/core_ocean/mod FCINCLUDES += -I$(ROOT_DIR)/core_ocean/shared -I$(ROOT_DIR)/core_ocean/analysis_members FCINCLUDES += -I$(ROOT_DIR)/core_ocean/cvmix/src/shared FCINCLUDES += -I$(ROOT_DIR)/core_ocean/BGC -FCINCLUDES += -I$(ROOT_DIR)/core_ocean/gotm/include +FCINCLUDES += -I$(ROOT_DIR)/core_ocean/gotm/build/modules override CPPFLAGS += -DCORE_OCEAN report_builds: diff --git a/src/core_seaice/LICENSE b/src/core_seaice/LICENSE new file mode 100644 index 0000000000..0bf5064708 --- /dev/null +++ b/src/core_seaice/LICENSE @@ -0,0 +1,19 @@ +This software is open source software available under the BSD-3 license. + +Copyright (c) 2018 Los Alamos National Security, LLC. + +All rights reserved. + +Copyright 2018. Los Alamos National Security, LLC. This software was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. The U.S. Government has rights to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce derivative works, such modified software should be clearly marked, so as not to confuse it with the version available from LANL. + +Additionally, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + + +THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/core_seaice/Registry.xml b/src/core_seaice/Registry.xml index ec09f05f5e..999756cdab 100644 --- a/src/core_seaice/Registry.xml +++ b/src/core_seaice/Registry.xml @@ -92,6 +92,18 @@ definition="3" description="Number of spectral intervals in the radiation calculation" /> + + + + + + @@ -397,6 +420,14 @@ description="Restart the ice biogeochemistry" possible_values="true or false" /> + + + + + + - - - - + + + + - + + + + @@ -628,6 +691,10 @@ description="Run the ridging column physics calculation." possible_values="true or false" /> + @@ -666,6 +733,20 @@ possible_values="true or false" icepack_name="tr_aero" /> + + + @@ -779,7 +860,7 @@ possible_values="positive real number less than 1" icepack_name="grid_o_t" /> - - - - - - - - - - - - - + - + + + + + + + + + + + + - + @@ -1647,6 +1784,61 @@ /> + + + + + + + + + + + + + + + + + + + + @@ -1872,6 +2068,23 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + @@ -2132,6 +2376,8 @@ + + @@ -2160,12 +2406,24 @@ + + + + + + + + + + + + @@ -2178,6 +2436,10 @@ + + + + @@ -2260,6 +2522,26 @@ + + + + @@ -2390,12 +2672,12 @@ packages="pkgTracerVerticalIron" /> @@ -2431,6 +2713,10 @@ + + + + @@ -2499,6 +2785,10 @@ + + + + @@ -2567,6 +2857,10 @@ + + + + @@ -2634,6 +2928,10 @@ + + + + @@ -2708,6 +3006,14 @@ + + + + + + + + @@ -2847,6 +3153,18 @@ + + + + + + + + + + + + @@ -3030,6 +3348,10 @@ + + + + @@ -3104,6 +3426,14 @@ + + + + + + + + @@ -3354,6 +3684,8 @@ + + @@ -3420,6 +3752,14 @@ + + + + + + + + @@ -3490,6 +3830,17 @@ + + + + + + + + + + + @@ -3570,6 +3921,20 @@ + + + + + + + + + + + + + + @@ -3592,12 +3957,28 @@ + + + + + + + + @@ -3748,6 +4133,23 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + #include "analysis_members/Registry_seaice_analysis_members.xml" diff --git a/src/core_seaice/analysis_members/Registry_seaice_conservation_check.xml b/src/core_seaice/analysis_members/Registry_seaice_conservation_check.xml index 3917b7aa8b..2048529b13 100644 --- a/src/core_seaice/analysis_members/Registry_seaice_conservation_check.xml +++ b/src/core_seaice/analysis_members/Registry_seaice_conservation_check.xml @@ -23,111 +23,182 @@ description="Logical flag determining if the conservation check is written to the log file." possible_values="true or false" /> + + + + + + + + - - - - + - - - - - - - + + + + + - - - - + - - - - - - - - - - - + - - - - + + + + + + + + + + + + + - - - - - + + + + + + + + + + + - - - - - + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + diff --git a/src/core_seaice/analysis_members/Registry_seaice_regional_statistics.xml b/src/core_seaice/analysis_members/Registry_seaice_regional_statistics.xml index c48764ba67..1f58e7e36c 100644 --- a/src/core_seaice/analysis_members/Registry_seaice_regional_statistics.xml +++ b/src/core_seaice/analysis_members/Registry_seaice_regional_statistics.xml @@ -28,12 +28,6 @@ - - diff --git a/src/core_seaice/analysis_members/Registry_seaice_time_series_stats_monthly.xml b/src/core_seaice/analysis_members/Registry_seaice_time_series_stats_monthly.xml index dec40cc486..d817a5661f 100644 --- a/src/core_seaice/analysis_members/Registry_seaice_time_series_stats_monthly.xml +++ b/src/core_seaice/analysis_members/Registry_seaice_time_series_stats_monthly.xml @@ -1,7 +1,7 @@ \brief Setup packages for MPAS-Ocean analysis driver +!> \brief Setup packages for MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details @@ -358,12 +351,12 @@ end subroutine seaice_analysis_bootstrap!}}} ! ! routine seaice_analysis_init ! -!> \brief Initialize MPAS-Ocean analysis driver +!> \brief Initialize MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all initializations required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -500,12 +493,12 @@ end subroutine seaice_analysis_init!}}} ! ! routine seaice_analysis_compute_startup ! -!> \brief Driver for MPAS-Ocean analysis computations +!> \brief Driver for MPAS-Seaice analysis computations !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all computation subroutines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -599,7 +592,7 @@ end subroutine seaice_analysis_compute_startup!}}} ! routine seaice_analysis_precompute ! !> \brief Driver for MPAS-Seaice analysis computations -!> \author MPAS-Ocean/Seaice development team +!> \author MPAS-Seaice/Seaice development team !> \date November 2013 !> \details !> This routine calls all pre timestep computation subroutines @@ -692,12 +685,12 @@ end subroutine seaice_analysis_precompute!}}} ! ! routine seaice_analysis_compute ! -!> \brief Driver for MPAS-Ocean analysis computations +!> \brief Driver for MPAS-Seaice analysis computations !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all computation subroutines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -788,12 +781,12 @@ end subroutine seaice_analysis_compute!}}} ! ! routine seaice_analysis_restart ! -!> \brief Save restart for MPAS-Ocean analysis driver +!> \brief Save restart for MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all subroutines required to prepare to save -!> the restart state for the MPAS-Ocean analysis driver. +!> the restart state for the MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -862,12 +855,12 @@ end subroutine seaice_analysis_restart!}}} ! ! routine seaice_analysis_write ! -!> \brief Driver for MPAS-Ocean analysis output +!> \brief Driver for MPAS-Seaice analysis output !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all output writing subroutines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. !> At this time this is just a stub, and all analysis output is written !> to the output file specified by config_output_name. ! @@ -985,12 +978,12 @@ end subroutine seaice_analysis_write!}}} ! ! routine seaice_analysis_finalize ! -!> \brief Finalize MPAS-Ocean analysis driver +!> \brief Finalize MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all finalize routines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- diff --git a/src/core_seaice/analysis_members/mpas_seaice_area_variables.F b/src/core_seaice/analysis_members/mpas_seaice_area_variables.F index f5e2b9da29..b69518518d 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_area_variables.F +++ b/src/core_seaice/analysis_members/mpas_seaice_area_variables.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_area_variables diff --git a/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F b/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F index a1401357a3..7799655f54 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F +++ b/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_conservation_check @@ -123,6 +116,9 @@ end subroutine seaice_bootstrap_conservation_check!}}} subroutine seaice_init_conservation_check(domain, instance, err)!{{{ + use seaice_constants, only: & + pii + !----------------------------------------------------------------- ! ! input variables @@ -153,8 +149,121 @@ subroutine seaice_init_conservation_check(domain, instance, err)!{{{ ! !----------------------------------------------------------------- + type(block_type), pointer :: & + blockPtr + + type(MPAS_pool_type), pointer :: & + conservationCheckAMPool, & + conservationCheckAreaAMPool, & + meshPool + + real(kind=RKIND), dimension(:), pointer :: & + latCell, & + areaCell + + integer, dimension(:,:), pointer :: & + cellInHemisphere + + real(kind=RKIND), pointer :: & + earthRadius, & + earthArea + + real(kind=RKIND), dimension(:), pointer :: & + domainArea + + integer, pointer :: & + nCellsSolve, & + nHemispheres + + integer :: & + iCell, & + iHemisphere + + real(kind=RKIND), dimension(:), allocatable :: & + sumArray, & + sumArrayOut + + integer, parameter :: & + nVars = 1 + + integer :: & + nSums + err = 0 + call MPAS_pool_get_config(domain % configs, "config_earth_radius", earthRadius) + + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + nSums = nHemispheres * nVars + + allocate(sumArray(nSums)) + allocate(sumArrayOut(nSums)) + + sumArray = 0.0_RKIND + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCellsSolve", nCellsSolve) + + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "earthArea", earthArea) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "domainArea", domainArea) + call MPAS_pool_get_array(meshPool, "latCell", latCell) + call MPAS_pool_get_array(meshPool, "areaCell", areaCell) + + earthArea = 4.0_RKIND * pii * earthRadius**2 + + do iCell = 1, nCellsSolve + + ! global + cellInHemisphere(1,iCell) = 1 + + ! northern hemisphere + if (latCell(iCell) >= 0.0) then + cellInHemisphere(2,iCell) = 1 + else + cellInHemisphere(2,iCell) = 0 + endif + + ! southern hemisphere + if (latCell(iCell) < 0.0) then + cellInHemisphere(3,iCell) = 1 + else + cellInHemisphere(3,iCell) = 0 + endif + + ! domain area + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iHemisphere) = sumArray(iHemisphere) + areaCell(iCell) + endif + enddo ! iHemisphere + + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + ! perform the sums over processors + call MPAS_dmpar_sum_real_array(domain % dminfo, nSums, sumArray, sumArrayOut) + + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + + call MPAS_pool_get_array(conservationCheckAreaAMPool, "domainArea", domainArea) + + do iHemisphere = 1, nHemispheres + domainArea(iHemisphere) = sumArrayOut(iHemisphere) + enddo ! iHemisphere + + ! cleanup + deallocate(sumArray) + deallocate(sumArrayOut) + end subroutine seaice_init_conservation_check!}}} !*********************************************************************** @@ -208,15 +317,20 @@ subroutine seaice_precompute_conservation_check(domain, instance, timeLevel, err conservationCheckAMPool, & conservationCheckEnergyAMPool, & conservationCheckMassAMPool, & - conservationCheckSaltAMPool + conservationCheckSaltAMPool, & + conservationCheckCarbonAMPool integer, pointer :: & performConservationPrecompute - real(kind=RKIND), pointer :: & + logical, pointer :: & + config_use_column_biogeochemistry + + real(kind=RKIND), dimension(:), pointer :: & initialEnergy, & initialMass, & - initialSalt + initialSalt, & + initialCarbon err = 0 @@ -246,6 +360,18 @@ subroutine seaice_precompute_conservation_check(domain, instance, timeLevel, err call compute_total_salt(domain, initialSalt) + ! initial total carbon + call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckCarbonAM", conservationCheckCarbonAMPool) + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "initialCarbon", initialCarbon) + + if (config_use_column_biogeochemistry) then + call compute_total_carbon(domain, initialCarbon) + else + initialCarbon = 0.0_RKIND + endif + performConservationPrecompute = 0 endif @@ -309,8 +435,12 @@ subroutine seaice_compute_conservation_check(domain, instance, timeLevel, err)!{ conservationCheckAMPool integer, pointer :: & + nAccumulate, & performConservationPrecompute + logical, pointer :: & + config_use_column_biogeochemistry + type(MPAS_Time_type) :: & currentTime @@ -319,17 +449,25 @@ subroutine seaice_compute_conservation_check(domain, instance, timeLevel, err)!{ err = 0 - call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & - config_AM_conservationCheck_write_to_logfile) + ! number of accumulations + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_array(conservationCheckAMPool, "nAccumulate", nAccumulate) + nAccumulate = nAccumulate + 1 + + call MPAS_pool_get_config(domain % configs, "config_AM_conservationCheck_write_to_logfile", & + config_AM_conservationCheck_write_to_logfile) if (config_AM_conservationCheck_write_to_logfile .and. & MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then - call mpas_log_write('==========================================================') + call mpas_log_write('===================================================================================') currentTime = MPAS_get_clock_time(domain % clock, MPAS_NOW, ierr=ierr) call MPAS_get_time(currentTime, dateTimeString=timeStr, ierr=ierr) call mpas_log_write(' Conservation checks: '//trim(timeStr)) endif + ! area analysis + call area_analysis(domain, err) + ! energy conservation check call energy_conservation(domain, err) @@ -339,19 +477,182 @@ subroutine seaice_compute_conservation_check(domain, instance, timeLevel, err)!{ ! salt conservation check call salt_conservation(domain, err) + ! initial total carbon + call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + if (config_use_column_biogeochemistry) call carbon_conservation(domain, err) + if (config_AM_conservationCheck_write_to_logfile .and. & MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then - call mpas_log_write('==========================================================') + call mpas_log_write('===================================================================================') + endif - ! set precompute to happen next timestep + ! set precompute to happen next timestep after output + if (MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) call MPAS_pool_get_array(conservationCheckAMPool, "performConservationPrecompute", performConservationPrecompute) performConservationPrecompute = 1 - endif end subroutine seaice_compute_conservation_check!}}} +!*********************************************************************** +! +! routine area_analysis +! +!> \brief Compute MPAS-Seaice analysis member +!> \author Adrian K. Turner +!> \date 1st April 2021 +!> \details +!> Analyses areas used in the model. +! +!----------------------------------------------------------------------- + + subroutine area_analysis(domain, err) + + type(domain_type), intent(inout) :: & + domain + + integer, intent(out) :: & + err !< Output: error flag + + type(block_type), pointer :: & + blockPtr + + type(MPAS_pool_type), pointer :: & + conservationCheckAMPool, & + conservationCheckAreaAMPool, & + meshPool, & + tracersAggregatePool + + real(kind=RKIND), dimension(:), pointer :: & + areaCell, & + latCell, & + iceAreaCell + + real(kind=RKIND), pointer :: & + earthRadius, & + earthArea + + real(kind=RKIND), dimension(:), allocatable :: & + sumArray, & + sumArrayOut + + real(kind=RKIND), dimension(:), pointer :: & + domainArea, & + accumulatedSeaIceArea + + integer, dimension(:,:), pointer :: & + cellInHemisphere + + real(kind=RKIND) :: & + seaiceAreaCell + + logical, pointer :: & + config_AM_conservationCheck_write_to_logfile + + integer, pointer :: & + nCellsSolve, & + nHemispheres + + integer :: & + iCell, & + iHemisphere, & + ierr + + integer, parameter :: & + nVars = 1 + + integer :: & + nSums + + call MPAS_pool_get_config(domain % configs, "config_AM_conservationCheck_write_to_logfile", & + config_AM_conservationCheck_write_to_logfile) + + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + nSums = nHemispheres * nVars + + allocate(sumArray(nSums)) + allocate(sumArrayOut(nSums)) + + sumArray = 0.0_RKIND + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCellsSolve", nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nHemispheres", nHemispheres) + + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) + + call MPAS_pool_get_array(meshPool, "areaCell", areaCell) + call MPAS_pool_get_array(meshPool, "latCell", latCell) + call MPAS_pool_get_array(tracersAggregatePool, "iceAreaCell", iceAreaCell) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) + + do iCell = 1, nCellsSolve + + seaiceAreaCell = areaCell(iCell) * iceAreaCell(iCell) + + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iHemisphere) = sumArray(iHemisphere) + seaiceAreaCell + endif + enddo ! iHemisphere + + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + ! perform the sums over processors + call MPAS_dmpar_sum_real_array(domain % dminfo, nSums, sumArray, sumArrayOut) + + ! accumulate fluxes + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + + call MPAS_pool_get_array(conservationCheckAreaAMPool, "accumulatedSeaIceArea", accumulatedSeaIceArea) + + do iHemisphere = 1, nHemispheres + accumulatedSeaIceArea(iHemisphere) = accumulatedSeaIceArea(iHemisphere) + sumArrayOut(iHemisphere) + enddo ! iHemisphere + + ! cleanup + deallocate(sumArray) + deallocate(sumArrayOut) + + !------------------------------------------------------------- + ! Area analysis + !------------------------------------------------------------- + + if (MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then + + call MPAS_pool_get_config(domain % configs, "config_earth_radius", earthRadius) + + !------------------------------------------------------------- + ! Output to log file + !------------------------------------------------------------- + + call MPAS_pool_get_array(conservationCheckAreaAMPool, "earthArea", earthArea) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "domainArea", domainArea) + + if (config_AM_conservationCheck_write_to_logfile) then + + call mpas_log_write('-----------------------------------------------------------------------------------') + call mpas_log_write(' Area analysis Global NH SH') + call mpas_log_write(' ') + call mpas_log_write(' Earth radius (m) = '//trim(hemisphere_format((/earthRadius/)))) + call mpas_log_write(' Earth area (m2) = '//trim(hemisphere_format((/earthArea/)))) + call mpas_log_write(' Domain area (m2) = '//trim(hemisphere_format(domainArea))) + call mpas_log_write(' Sea-ice area (m2) = '//trim(hemisphere_format(accumulatedSeaIceArea))) + + endif + + endif + + end subroutine area_analysis + !*********************************************************************** ! ! routine energy_conservation @@ -378,25 +679,46 @@ subroutine energy_conservation(domain, err) err !< Output: error flag type(block_type), pointer :: & - block + blockPtr type(MPAS_pool_type), pointer :: & - conservationCheckEnergyAMPool + conservationCheckEnergyAMPool, & + conservationCheckAMPool, & + conservationCheckAreaAMPool - real(kind=RKIND), pointer :: & + real(kind=RKIND), dimension(:), pointer :: & initialEnergy, & finalEnergy, & energyChange, & + energyChangeFlux, & netEnergyFlux, & absoluteEnergyError, & relativeEnergyError - real(kind=RKIND), pointer :: & - accumulatedSurfaceHeatFlux, & - accumulatedOceanHeatFlux, & - accumulatedFreezingPotential, & - accumulatedSnowfallHeat, & - accumulatedLatentHeat + real(kind=RKIND), dimension(:), pointer :: & + energyConsSurfaceHeatFlux, & + energyConsAbsorbedShortwaveFlux, & + energyConsOceanShortwaveFlux, & + energyConsSensibleHeatFlux, & + energyConsLongwaveUp, & + energyConsLongwaveDown, & + energyConsOceanHeatFlux, & + energyConsFreezingPotential, & + energyConsSnowfallHeat, & + energyConsLatentHeat + + real(kind=RKIND) :: & + surfaceHeatCell, & + surfaceHeatFluxCell, & + absorbedShortwaveFluxCell, & + oceanShortwaveFluxCell, & + sensibleHeatFluxCell, & + longwaveUpCell, & + longwaveDownCell, & + oceanHeatCell, & + freezingPotentialCell, & + snowfallHeatCell, & + latentHeatCell real(kind=RKIND), dimension(:), allocatable :: & sumArray, & @@ -428,56 +750,79 @@ subroutine energy_conservation(domain, err) freezingMeltingPotentialInitial, & snowfallRate + integer, dimension(:,:), pointer :: & + cellInHemisphere + real(kind=RKIND), pointer :: & - dt + dt, & + earthArea logical, pointer :: & config_calc_surface_temperature, & config_AM_conservationCheck_write_to_logfile integer, pointer :: & - nCellsSolve + nCellsSolve, & + nHemispheres, & + nAccumulate integer :: & iCell, & + iHemisphere, & + iSumPrev, & ierr - integer, parameter :: & - nSums = 5 + integer :: & + nVars + + integer :: & + nSums - character(len=17) :: & - formatString + real(kind=RKIND) :: & + fluxScale + + logical, parameter :: & + defaultLatentHeatFormulation = .true. err = 0 - call MPAS_pool_get_config(domain % blocklist % configs, "config_dt", dt) - call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & - config_AM_conservationCheck_write_to_logfile) + call MPAS_pool_get_config(domain % blocklist % configs, "config_calc_surface_temperature", config_calc_surface_temperature) + if (config_calc_surface_temperature) then + nVars = 9 + else + nVars = 5 + endif + + call MPAS_pool_get_config(domain % configs, "config_dt", dt) + call MPAS_pool_get_config(domain % configs, "config_AM_conservationCheck_write_to_logfile", & + config_AM_conservationCheck_write_to_logfile) !------------------------------------------------------------- ! Net heat flux to ice !------------------------------------------------------------- + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + nSums = nHemispheres * nVars + allocate(sumArray(nSums)) allocate(sumArrayOut(nSums)) sumArray = 0.0_RKIND - block => domain % blocklist - do while (associated(block)) - - call MPAS_pool_get_config(block % configs, "config_calc_surface_temperature", config_calc_surface_temperature) + blockPtr => domain % blocklist + do while (associated(blockPtr)) - call MPAS_pool_get_dimension(block % dimensions, "nCellsSolve", nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCellsSolve", nCellsSolve) - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracersAggregatePool) - call MPAS_pool_get_subpool(block % structs, "icestate", icestatePool) - call MPAS_pool_get_subpool(block % structs, "shortwave", shortwavePool) - call MPAS_pool_get_subpool(block % structs, "ocean_fluxes", oceanFluxesPool) - call MPAS_pool_get_subpool(block % structs, "atmos_fluxes", atmosFluxesPool) - call MPAS_pool_get_subpool(block % structs, "atmos_coupling", atmosCouplingPool) - call MPAS_pool_get_subpool(block % structs, "diagnostics", diagnosticsPool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "icestate", icestatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "shortwave", shortwavePool) + call MPAS_pool_get_subpool(blockPtr % structs, "ocean_fluxes", oceanFluxesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "atmos_fluxes", atmosFluxesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "atmos_coupling", atmosCouplingPool) + call MPAS_pool_get_subpool(blockPtr % structs, "diagnostics", diagnosticsPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) call MPAS_pool_get_array(meshPool, "areaCell", areaCell) call MPAS_pool_get_array(tracersAggregatePool, "iceAreaCell", iceAreaCell) @@ -493,16 +838,37 @@ subroutine energy_conservation(domain, err) call MPAS_pool_get_array(atmosCouplingPool, "longwaveDown", longwaveDown) call MPAS_pool_get_array(atmosCouplingPool, "snowfallRate", snowfallRate) call MPAS_pool_get_array(diagnosticsPool, "freezingMeltingPotentialInitial", freezingMeltingPotentialInitial) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) - ! surface heat flux - if (config_calc_surface_temperature) then + do iCell = 1, nCellsSolve + + oceanHeatCell = -oceanHeatFluxArea(iCell) * areaCell(iCell) + freezingPotentialCell = -max(0.0_RKIND, freezingMeltingPotentialInitial(iCell)) * areaCell(iCell) + snowfallHeatCell = -snowfallRate(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) * Lfresh + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+1) = sumArray(iSumPrev+1) + oceanHeatCell + sumArray(iSumPrev+2) = sumArray(iSumPrev+2) + freezingPotentialCell + sumArray(iSumPrev+3) = sumArray(iSumPrev+3) + snowfallHeatCell + endif + enddo ! iHemisphere + + enddo ! iCell + + if (defaultLatentHeatFormulation) then do iCell = 1, nCellsSolve - sumArray(1) = sumArray(1) + & - (absorbedShortwaveFlux(iCell) - oceanShortwaveFlux(iCell) + & - sensibleHeatFlux(iCell) + longwaveUp(iCell)) * iceAreaCell(iCell) * areaCell(iCell) + & - longwaveDown(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) + latentHeatCell = latentHeatFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+4) = sumArray(iSumPrev+4) + latentHeatCell + endif + enddo ! iHemisphere enddo ! iCell @@ -510,30 +876,61 @@ subroutine energy_conservation(domain, err) do iCell = 1, nCellsSolve - sumArray(1) = sumArray(1) + & - (surfaceHeatFlux(iCell) - latentHeatFlux(iCell)) * iceAreaCell(iCell) * areaCell(iCell) + latentHeatCell = evaporativeWaterFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) * Lvap + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+4) = sumArray(iSumPrev+4) + latentHeatCell + endif + enddo ! iHemisphere enddo ! iCell endif - do iCell = 1, nCellsSolve + ! surface heat flux + if (config_calc_surface_temperature) then + + do iCell = 1, nCellsSolve + + absorbedShortwaveFluxCell = absorbedShortwaveFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) + oceanShortwaveFluxCell = -oceanShortwaveFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) + sensibleHeatFluxCell = sensibleHeatFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) + longwaveUpCell = longwaveUp(iCell) * iceAreaCell(iCell) * areaCell(iCell) + longwaveDownCell = longwaveDown(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+5) = sumArray(iSumPrev+5) + absorbedShortwaveFluxCell + sumArray(iSumPrev+6) = sumArray(iSumPrev+6) + oceanShortwaveFluxCell + sumArray(iSumPrev+7) = sumArray(iSumPrev+7) + sensibleHeatFluxCell + sumArray(iSumPrev+8) = sumArray(iSumPrev+8) + longwaveUpCell + sumArray(iSumPrev+9) = sumArray(iSumPrev+9) + longwaveDownCell + endif + enddo ! iHemisphere + + enddo ! iCell - ! ocean heat flux - sumArray(2) = sumArray(2) + oceanHeatFluxArea(iCell) * areaCell(iCell) + else - ! freezing potential - sumArray(3) = sumArray(3) + max(0.0_RKIND, freezingMeltingPotentialInitial(iCell)) * areaCell(iCell) + do iCell = 1, nCellsSolve - ! snowfall heat input - sumArray(4) = sumArray(4) - snowfallRate(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) * Lfresh + surfaceHeatFluxCell = (surfaceHeatFlux(iCell) - latentHeatFlux(iCell)) * iceAreaCell(iCell) * areaCell(iCell) - ! latent heat - sumArray(5) = sumArray(5) + evaporativeWaterFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) * Lvap + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+5) = sumArray(iSumPrev+5) + surfaceHeatFluxCell + endif + enddo ! iHemisphere - enddo ! iCell + enddo ! iCell - block => block % next + endif + + blockPtr => blockPtr % next enddo ! perform the sums over processors @@ -542,17 +939,39 @@ subroutine energy_conservation(domain, err) ! accumulate fluxes call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckEnergyAM", conservationCheckEnergyAMPool) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedSurfaceHeatFlux", accumulatedSurfaceHeatFlux) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedOceanHeatFlux", accumulatedOceanHeatFlux) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedFreezingPotential", accumulatedFreezingPotential) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedSnowfallHeat", accumulatedSnowfallHeat) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedLatentHeat", accumulatedLatentHeat) - - accumulatedSurfaceHeatFlux = accumulatedSurfaceHeatFlux + sumArrayOut(1) - accumulatedOceanHeatFlux = accumulatedOceanHeatFlux + sumArrayOut(2) - accumulatedFreezingPotential = accumulatedFreezingPotential + sumArrayOut(3) - accumulatedSnowfallHeat = accumulatedSnowfallHeat + sumArrayOut(4) - accumulatedLatentHeat = accumulatedLatentHeat + sumArrayOut(5) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsOceanHeatFlux", energyConsOceanHeatFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsFreezingPotential", energyConsFreezingPotential) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSnowfallHeat", energyConsSnowfallHeat) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsLatentHeat", energyConsLatentHeat) + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + energyConsOceanHeatFlux (iHemisphere) = energyConsOceanHeatFlux (iHemisphere) + sumArrayOut(iSumPrev+1) + energyConsFreezingPotential(iHemisphere) = energyConsFreezingPotential(iHemisphere) + sumArrayOut(iSumPrev+2) + energyConsSnowfallHeat (iHemisphere) = energyConsSnowfallHeat (iHemisphere) + sumArrayOut(iSumPrev+3) + energyConsLatentHeat (iHemisphere) = energyConsLatentHeat (iHemisphere) + sumArrayOut(iSumPrev+4) + enddo ! iHemisphere + + if (config_calc_surface_temperature) then + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsAbsorbedShortwaveFlux", energyConsAbsorbedShortwaveFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsOceanShortwaveFlux", energyConsOceanShortwaveFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSensibleHeatFlux", energyConsSensibleHeatFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsLongwaveUp", energyConsLongwaveUp) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsLongwaveDown", energyConsLongwaveDown) + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + energyConsAbsorbedShortwaveFlux(iHemisphere) = energyConsAbsorbedShortwaveFlux(iHemisphere) + sumArrayOut(iSumPrev+5) + energyConsOceanShortwaveFlux (iHemisphere) = energyConsOceanShortwaveFlux (iHemisphere) + sumArrayOut(iSumPrev+6) + energyConsSensibleHeatFlux (iHemisphere) = energyConsSensibleHeatFlux (iHemisphere) + sumArrayOut(iSumPrev+7) + energyConsLongwaveUp (iHemisphere) = energyConsLongwaveUp (iHemisphere) + sumArrayOut(iSumPrev+8) + energyConsLongwaveDown (iHemisphere) = energyConsLongwaveDown (iHemisphere) + sumArrayOut(iSumPrev+9) + enddo ! iHemisphere + else + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSurfaceHeatFlux", energyConsSurfaceHeatFlux) + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + energyConsSurfaceHeatFlux(iHemisphere) = energyConsSurfaceHeatFlux(iHemisphere) + sumArrayOut(iSumPrev+5) + enddo ! iHemisphere + endif ! cleanup deallocate(sumArray) @@ -564,6 +983,13 @@ subroutine energy_conservation(domain, err) if (MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then + ! convert fluxes to fluxes per m2 of whole earth and per second + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_array(conservationCheckAMPool, "nAccumulate", nAccumulate) + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "earthArea", earthArea) + fluxScale = 1.0_RKIND / (earthArea * real(nAccumulate)) + ! get initial energy call MPAS_pool_get_array(conservationCheckEnergyAMPool, "initialEnergy", initialEnergy) @@ -573,24 +999,56 @@ subroutine energy_conservation(domain, err) ! compute the energy change call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyChange", energyChange) - energyChange = finalEnergy - initialEnergy + energyChange(:) = finalEnergy(:) - initialEnergy(:) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyChangeFlux", energyChangeFlux) + energyChangeFlux(:) = energyChange(:) * (fluxScale / dt) + + ! surface heat flux + if (config_calc_surface_temperature) then + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSurfaceHeatFlux", energyConsSurfaceHeatFlux) + energyConsSurfaceHeatFlux(:) = & + energyConsAbsorbedShortwaveFlux(:) + & + energyConsOceanShortwaveFlux(:) + & + energyConsSensibleHeatFlux(:) + & + energyConsLongwaveUp(:) + & + energyConsLongwaveDown(:) + endif ! calculate the final net energy flux to the ice call MPAS_pool_get_array(conservationCheckEnergyAMPool, "netEnergyFlux", netEnergyFlux) - - netEnergyFlux = & - accumulatedSurfaceHeatFlux & - + accumulatedSnowfallHeat & - + accumulatedLatentHeat & - - accumulatedOceanHeatFlux & - - accumulatedFreezingPotential - - ! compute the final energy error + netEnergyFlux(:) = & + energyConsSurfaceHeatFlux(:) & + + energyConsSnowfallHeat(:) & + + energyConsLatentHeat(:) & + + energyConsOceanHeatFlux(:) & + + energyConsFreezingPotential(:) + + ! absolute error call MPAS_pool_get_array(conservationCheckEnergyAMPool, "absoluteEnergyError", absoluteEnergyError) + absoluteEnergyError(:) = netEnergyFlux(:) * dt - energyChange(:) + + ! rescale fluxes + energyConsSurfaceHeatFlux(:) = energyConsSurfaceHeatFlux(:) * fluxScale + energyConsAbsorbedShortwaveFlux(:) = energyConsAbsorbedShortwaveFlux(:) * fluxScale + energyConsOceanShortwaveFlux(:) = energyConsOceanShortwaveFlux(:) * fluxScale + energyConsSensibleHeatFlux(:) = energyConsSensibleHeatFlux(:) * fluxScale + energyConsLongwaveUp(:) = energyConsLongwaveUp(:) * fluxScale + energyConsLongwaveDown(:) = energyConsLongwaveDown(:) * fluxScale + energyConsSnowfallHeat(:) = energyConsSnowfallHeat(:) * fluxScale + energyConsLatentHeat(:) = energyConsLatentHeat(:) * fluxScale + energyConsOceanHeatFlux(:) = energyConsOceanHeatFlux(:) * fluxScale + energyConsFreezingPotential(:) = energyConsFreezingPotential(:) * fluxScale + netEnergyFlux(:) = netEnergyFlux(:) * fluxScale + + ! relative flux error call MPAS_pool_get_array(conservationCheckEnergyAMPool, "relativeEnergyError", relativeEnergyError) - - absoluteEnergyError = netEnergyFlux * dt - energyChange - relativeEnergyError = absoluteEnergyError / (finalEnergy - 1.0_RKIND) ! why the minus 1???? + do iHemisphere = 1, nHemispheres + if (abs(finalEnergy(iHemisphere)) > 0.0) then + relativeEnergyError(iHemisphere) = absoluteEnergyError(iHemisphere) / finalEnergy(iHemisphere) + else + relativeEnergyError(iHemisphere) = 0.0_RKIND + endif + enddo ! iHemisphere !------------------------------------------------------------- ! Output to log file @@ -598,26 +1056,31 @@ subroutine energy_conservation(domain, err) if (config_AM_conservationCheck_write_to_logfile) then - formatString = "(a32,2x,1pe24.17)" - - call mpas_log_write('----------------------------------------------------------') + call mpas_log_write('-----------------------------------------------------------------------------------') call mpas_log_write(' Energy conservation check') call mpas_log_write(' ') - call mpas_log_write(' Initial energy (J) = $r', realArgs=(/initialEnergy/)) - call mpas_log_write(' Final energy (J) = $r', realArgs=(/finalEnergy/)) - call mpas_log_write(' Energy change (J) = $r', realArgs=(/energyChange/)) + call mpas_log_write(' Initial energy (J) = '//trim(hemisphere_format(initialEnergy))) + call mpas_log_write(' Final energy (J) = '//trim(hemisphere_format(finalEnergy))) + call mpas_log_write(' Energy change (J) = '//trim(hemisphere_format(energyChange))) + call mpas_log_write(' Energy change flux (W/m2) = '//trim(hemisphere_format(energyChangeFlux))) + call mpas_log_write(' ') + call mpas_log_write(' Surface heat flux (W/m2) = '//trim(hemisphere_format(energyConsSurfaceHeatFlux))) + call mpas_log_write(' Absorbed shortwave flux (W/m2) = '//trim(hemisphere_format(energyConsAbsorbedShortwaveFlux))) + call mpas_log_write(' Ocean Shortwave flux (W/m2) = '//trim(hemisphere_format(energyConsOceanShortwaveFlux))) + call mpas_log_write(' Sensible heat flux (W/m2) = '//trim(hemisphere_format(energyConsSensibleHeatFlux))) + call mpas_log_write(' Longwave up (W/m2) = '//trim(hemisphere_format(energyConsLongwaveUp))) + call mpas_log_write(' Longwave down (W/m2) = '//trim(hemisphere_format(energyConsLongwaveDown))) + call mpas_log_write(' Ocean heat flux (W/m2) = '//trim(hemisphere_format(energyConsOceanHeatFlux))) + call mpas_log_write(' Freezing heat flux (W/m2) = '//trim(hemisphere_format(energyConsFreezingPotential))) + call mpas_log_write(' Snowfall heat flux (W/m2) = '//trim(hemisphere_format(energyConsSnowfallHeat))) + call mpas_log_write(' Latent heat flux (W/m2) = '//trim(hemisphere_format(energyConsLatentHeat))) call mpas_log_write(' ') - call mpas_log_write(' Surface heat flux (W) = $r', realArgs=(/accumulatedSurfaceHeatFlux/)) - call mpas_log_write(' Ocean heat flux (W) = $r', realArgs=(/accumulatedOceanHeatFlux/)) - call mpas_log_write(' Freezing heat flux (W) = $r', realArgs=(/accumulatedFreezingPotential/)) - call mpas_log_write(' Snowfall heat flux (W) = $r', realArgs=(/accumulatedSnowfallHeat/)) - call mpas_log_write(' Latent heat flux (W) = $r', realArgs=(/accumulatedLatentHeat/)) - call mpas_log_write(' Net energy flux (W) = $r', realArgs=(/netEnergyFlux/)) - call mpas_log_write(' Net energy flux (J) = $r', realArgs=(/netEnergyFlux * dt/)) + call mpas_log_write(' Net energy change (J) = '//trim(hemisphere_format((netEnergyFlux * dt) / fluxScale))) + call mpas_log_write(' Net energy flux (W/m2) = '//trim(hemisphere_format(netEnergyFlux))) call mpas_log_write(' ') - call mpas_log_write(' Absolute energy error (J) = $r', realArgs=(/absoluteEnergyError/)) - call mpas_log_write(' Absolute energy error (W) = $r', realArgs=(/absoluteEnergyError / dt/)) - call mpas_log_write(' Relative energy error = $r', realArgs=(/relativeEnergyError/)) + call mpas_log_write(' Absolute energy error (J) = '//trim(hemisphere_format(absoluteEnergyError))) + call mpas_log_write(' Absolute energy error (W/m2) = '//trim(hemisphere_format((absoluteEnergyError * fluxScale) / dt))) + call mpas_log_write(' Relative energy error = '//trim(hemisphere_format(relativeEnergyError))) endif @@ -650,25 +1113,35 @@ subroutine mass_conservation(domain, err) err !< Output: error flag type(block_type), pointer :: & - block + blockPtr type(MPAS_pool_type), pointer :: & - conservationCheckMassAMPool + conservationCheckMassAMPool, & + conservationCheckAMPool, & + conservationCheckAreaAMPool - real(kind=RKIND), pointer :: & + real(kind=RKIND), dimension(:), pointer :: & initialMass, & finalMass, & massChange, & + massChangeFlux, & netMassFlux, & absoluteMassError, & relativeMassError - real(kind=RKIND), pointer :: & - accumulatedRainfallRate, & - accumulatedSnowfallRate, & - accumulatedEvaporation, & - accumulatedFreshWater, & - accumulatedFrazilWater + real(kind=RKIND), dimension(:), pointer :: & + massConsRainfallRate, & + massConsSnowfallRate, & + massConsEvaporation, & + massConsFreshWater, & + massConsFrazilWater + + real(kind=RKIND) :: & + rainfallCell, & + snowfallCell, & + evaporativeWaterCell, & + oceanFreshWaterCell, & + frazilCell real(kind=RKIND), dimension(:), allocatable :: & sumArray, & @@ -693,8 +1166,12 @@ subroutine mass_conservation(domain, err) oceanFreshWaterFluxArea, & frazilFormation + integer, dimension(:,:), pointer :: & + cellInHemisphere + real(kind=RKIND), pointer :: & - dt + dt, & + earthArea logical, pointer :: & config_update_ocean_fluxes, & @@ -704,48 +1181,59 @@ subroutine mass_conservation(domain, err) config_thermodynamics_type integer, pointer :: & - nCellsSolve + nCellsSolve, & + nHemispheres, & + nAccumulate integer :: & iCell, & + iHemisphere, & + iSumPrev, & ierr integer, parameter :: & - nSums = 5 + nVars = 5 + + integer :: & + nSums - character(len=17) :: & - formatString + real(kind=RKIND) :: & + fluxScale err = 0 - call MPAS_pool_get_config(domain % blocklist % configs, "config_dt", dt) - call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & - config_AM_conservationCheck_write_to_logfile) + call MPAS_pool_get_config(domain % configs, "config_dt", dt) + call MPAS_pool_get_config(domain % configs, "config_AM_conservationCheck_write_to_logfile", & + config_AM_conservationCheck_write_to_logfile) !------------------------------------------------------------- ! Net mass flux to ice !------------------------------------------------------------- + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + nSums = nHemispheres * nVars + allocate(sumArray(nSums)) allocate(sumArrayOut(nSums)) sumArray = 0.0_RKIND - block => domain % blocklist - do while (associated(block)) + blockPtr => domain % blocklist + do while (associated(blockPtr)) - call MPAS_pool_get_config(block % configs, "config_update_ocean_fluxes", config_update_ocean_fluxes) - call MPAS_pool_get_config(block % configs, "config_thermodynamics_type", config_thermodynamics_type) + call MPAS_pool_get_config(blockPtr % configs, "config_update_ocean_fluxes", config_update_ocean_fluxes) + call MPAS_pool_get_config(blockPtr % configs, "config_thermodynamics_type", config_thermodynamics_type) - call MPAS_pool_get_dimension(block % dimensions, "nCellsSolve", nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCellsSolve", nCellsSolve) - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracersAggregatePool) - call MPAS_pool_get_subpool(block % structs, "icestate", icestatePool) - call MPAS_pool_get_subpool(block % structs, "atmos_coupling", atmosCouplingPool) - call MPAS_pool_get_subpool(block % structs, "atmos_fluxes", atmosFluxesPool) - call MPAS_pool_get_subpool(block % structs, "ocean_fluxes", oceanFluxesPool) - call MPAS_pool_get_subpool(block % structs, "melt_growth_rates", meltGrowthRatesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "icestate", icestatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "atmos_coupling", atmosCouplingPool) + call MPAS_pool_get_subpool(blockPtr % structs, "atmos_fluxes", atmosFluxesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "ocean_fluxes", oceanFluxesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "melt_growth_rates", meltGrowthRatesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) call MPAS_pool_get_array(meshPool, "areaCell", areaCell) call MPAS_pool_get_array(tracersAggregatePool, "iceAreaCell", iceAreaCell) @@ -755,40 +1243,45 @@ subroutine mass_conservation(domain, err) call MPAS_pool_get_array(atmosFluxesPool, "evaporativeWaterFlux", evaporativeWaterFlux) call MPAS_pool_get_array(oceanFluxesPool, "oceanFreshWaterFluxArea", oceanFreshWaterFluxArea) call MPAS_pool_get_array(meltGrowthRatesPool, "frazilFormation", frazilFormation) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) do iCell = 1, nCellsSolve - ! rainfall - sumArray(1) = sumArray(1) + & - rainfallRate(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) + rainfallCell = rainfallRate(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) + snowfallCell = snowfallRate(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) + evaporativeWaterCell = evaporativeWaterFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) + oceanFreshWaterCell = -oceanFreshWaterFluxArea(iCell) * areaCell(iCell) + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+1) = sumArray(iSumPrev+1) + rainfallCell + sumArray(iSumPrev+2) = sumArray(iSumPrev+2) + snowfallCell + sumArray(iSumPrev+3) = sumArray(iSumPrev+3) + evaporativeWaterCell + sumArray(iSumPrev+4) = sumArray(iSumPrev+4) + oceanFreshWaterCell + endif + enddo ! iHemisphere - ! snowfall - sumArray(2) = sumArray(2) + & - snowfallRate(iCell) * iceAreaCellInitial(iCell) * areaCell(iCell) + enddo ! iCell - ! evaporation - sumArray(3) = sumArray(3) + & - evaporativeWaterFlux(iCell) * iceAreaCell(iCell) * areaCell(iCell) - - ! fresh water flux to ocean - sumArray(4) = sumArray(4) + & - oceanFreshWaterFluxArea(iCell) * areaCell(iCell) - - enddo ! iCell - - if (config_update_ocean_fluxes .and. trim(config_thermodynamics_type) == "mushy") then + ! frazil ice + if (config_update_ocean_fluxes .and. trim(config_thermodynamics_type) == "mushy") then do iCell = 1, nCellsSolve - ! frazil ice - sumArray(5) = sumArray(5) + & - (frazilFormation(iCell) * areaCell(iCell) * rhoi) / dt + frazilCell = (frazilFormation(iCell) * areaCell(iCell) * rhoi) / dt + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+5) = sumArray(iSumPrev+5) + frazilCell + endif + enddo ! iHemisphere enddo ! iCell endif - block => block % next + blockPtr => blockPtr % next enddo ! perform the sums over processors @@ -797,17 +1290,20 @@ subroutine mass_conservation(domain, err) ! accumulate fluxes call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckMassAM", conservationCheckMassAMPool) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedRainfallRate", accumulatedRainfallRate) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedSnowfallRate", accumulatedSnowfallRate) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedEvaporation", accumulatedEvaporation) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedFreshWater", accumulatedFreshWater) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedFrazilWater", accumulatedFrazilWater) - - accumulatedRainfallRate = accumulatedRainfallRate + sumArrayOut(1) - accumulatedSnowfallRate = accumulatedSnowfallRate + sumArrayOut(2) - accumulatedEvaporation = accumulatedEvaporation + sumArrayOut(3) - accumulatedFreshWater = accumulatedFreshWater + sumArrayOut(4) - accumulatedFrazilWater = accumulatedFrazilWater + sumArrayOut(5) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsRainfallRate", massConsRainfallRate) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsSnowfallRate", massConsSnowfallRate) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsEvaporation", massConsEvaporation) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsFreshWater", massConsFreshWater) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsFrazilWater", massConsFrazilWater) + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + massConsRainfallRate(iHemisphere) = massConsRainfallRate(iHemisphere) + sumArrayOut(iSumPrev+1) + massConsSnowfallRate(iHemisphere) = massConsSnowfallRate(iHemisphere) + sumArrayOut(iSumPrev+2) + massConsEvaporation (iHemisphere) = massConsEvaporation (iHemisphere) + sumArrayOut(iSumPrev+3) + massConsFreshWater (iHemisphere) = massConsFreshWater (iHemisphere) + sumArrayOut(iSumPrev+4) + massConsFrazilWater (iHemisphere) = massConsFrazilWater (iHemisphere) + sumArrayOut(iSumPrev+5) + enddo ! iHemisphere ! cleanup deallocate(sumArray) @@ -819,6 +1315,13 @@ subroutine mass_conservation(domain, err) if (MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then + ! convert to fluxes per m2 of whole earth and per second + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_array(conservationCheckAMPool, "nAccumulate", nAccumulate) + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "earthArea", earthArea) + fluxScale = 1.0_RKIND / (earthArea * real(nAccumulate)) + ! get initial mass call MPAS_pool_get_array(conservationCheckMassAMPool, "initialMass", initialMass) @@ -828,24 +1331,40 @@ subroutine mass_conservation(domain, err) ! compute the energy change call MPAS_pool_get_array(conservationCheckMassAMPool, "massChange", massChange) - massChange = finalMass - initialMass + massChange(:) = finalMass(:) - initialMass(:) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massChangeFlux", massChangeFlux) + massChangeFlux(:) = massChange(:) * (fluxScale / dt) ! calculate the final net energy flux to the ice call MPAS_pool_get_array(conservationCheckMassAMPool, "netMassFlux", netMassFlux) + netMassFlux(:) = & + massConsRainfallRate(:) & + + massConsSnowfallRate(:) & + + massConsEvaporation(:) & + + massConsFreshWater(:) & + + massConsFrazilWater(:) + + ! absolute error + call MPAS_pool_get_array(conservationCheckMassAMPool, "absoluteMassError", absoluteMassError) + absoluteMassError(:) = netMassFlux(:) * dt - massChange(:) - netMassFlux = & - accumulatedRainfallRate & - + accumulatedSnowfallRate & - + accumulatedEvaporation & - - accumulatedFreshWater & - + accumulatedFrazilWater + ! rescale fluxes + massConsRainfallRate(:) = massConsRainfallRate(:) * fluxScale + massConsSnowfallRate(:) = massConsSnowfallRate(:) * fluxScale + massConsEvaporation(:) = massConsEvaporation(:) * fluxScale + massConsFreshWater(:) = massConsFreshWater(:) * fluxScale + massConsFrazilWater(:) = massConsFrazilWater(:) * fluxScale + netMassFlux(:) = netMassFlux(:) * fluxScale ! compute the final energy error - call MPAS_pool_get_array(conservationCheckMassAMPool, "absoluteMassError", absoluteMassError) call MPAS_pool_get_array(conservationCheckMassAMPool, "relativeMassError", relativeMassError) - - absoluteMassError = netMassFlux * dt - massChange - relativeMassError = absoluteMassError / (finalmass + 1.0_RKIND) ! why the plus 1???? + do iHemisphere = 1, nHemispheres + if (abs(finalMass(iHemisphere)) > 0.0) then + relativeMassError(iHemisphere) = absoluteMassError(iHemisphere) / finalMass(iHemisphere) + else + relativeMassError(iHemisphere) = 0.0_RKIND + endif + enddo ! iHemisphere !------------------------------------------------------------- ! Output to log file @@ -853,26 +1372,26 @@ subroutine mass_conservation(domain, err) if (config_AM_conservationCheck_write_to_logfile) then - formatString = "(a32,2x,1pe24.17)" - - call mpas_log_write('----------------------------------------------------------') + call mpas_log_write('-----------------------------------------------------------------------------------') call mpas_log_write(' Mass conservation check') call mpas_log_write(' ') - call mpas_log_write(' Initial mass (kg) = $r', realArgs=(/initialMass/)) - call mpas_log_write(' Final mass (kg) = $r', realArgs=(/finalMass/)) - call mpas_log_write(' Mass change (kg) = $r', realArgs=(/massChange/)) + call mpas_log_write(' Initial mass (kg) = '//trim(hemisphere_format(initialMass))) + call mpas_log_write(' Final mass (kg) = '//trim(hemisphere_format(finalMass))) + call mpas_log_write(' Mass change (kg) = '//trim(hemisphere_format(massChange))) + call mpas_log_write(' Mass change flux (kg/m2s) = '//trim(hemisphere_format(massChangeFlux))) call mpas_log_write(' ') - call mpas_log_write(' Rainfall mass flux (kg/s) = $r', realArgs=(/accumulatedRainfallRate/)) - call mpas_log_write(' Snowfall mass flux (kg/s) = $r', realArgs=(/accumulatedSnowfallRate/)) - call mpas_log_write(' Evaporative mass flux (kg/s) = $r', realArgs=(/accumulatedEvaporation/)) - call mpas_log_write(' Fresh water mass flux (kg/s) = $r', realArgs=(/accumulatedFreshWater/)) - call mpas_log_write(' Frazil water flux (kg/s) = $r', realArgs=(/accumulatedFrazilWater/)) - call mpas_log_write(' Net mass flux (kg/s) = $r', realArgs=(/netMassFlux/)) - call mpas_log_write(' Net mass flux (kg) = $r', realArgs=(/netMassFlux * dt/)) + call mpas_log_write(' Rainfall mass flux (kg/m2s) = '//trim(hemisphere_format(massConsRainfallRate))) + call mpas_log_write(' Snowfall mass flux (kg/m2s) = '//trim(hemisphere_format(massConsSnowfallRate))) + call mpas_log_write(' Evaporative mass flux (kg/m2s) = '//trim(hemisphere_format(massConsEvaporation))) + call mpas_log_write(' Fresh water mass flux (kg/m2s) = '//trim(hemisphere_format(massConsFreshWater))) + call mpas_log_write(' Frazil water flux (kg/m2s) = '//trim(hemisphere_format(massConsFrazilWater))) call mpas_log_write(' ') - call mpas_log_write(' Absolute mass error (kg) = $r', realArgs=(/absoluteMassError/)) - call mpas_log_write(' Absolute mass error (kg/s) = $r', realArgs=(/absoluteMassError / dt/)) - call mpas_log_write(' Relative mass error = $r', realArgs=(/relativeMassError/)) + call mpas_log_write(' Net mass change (kg) = '//trim(hemisphere_format((netMassFlux * dt) / fluxScale))) + call mpas_log_write(' Net mass flux (kg/m2s) = '//trim(hemisphere_format(netMassFlux))) + call mpas_log_write(' ') + call mpas_log_write(' Absolute mass error (kg) = '//trim(hemisphere_format(absoluteMassError))) + call mpas_log_write(' Absolute mass error (kg/m2s) = '//trim(hemisphere_format((absoluteMassError * fluxScale) / dt))) + call mpas_log_write(' Relative mass error = '//trim(hemisphere_format(relativeMassError))) endif @@ -906,22 +1425,25 @@ subroutine salt_conservation(domain, err) err !< Output: error flag type(block_type), pointer :: & - block + blockPtr type(MPAS_pool_type), pointer :: & - conservationCheckSaltAMPool + conservationCheckSaltAMPool, & + conservationCheckAMPool, & + conservationCheckAreaAMPool - real(kind=RKIND), pointer :: & + real(kind=RKIND), dimension(:), pointer :: & initialSalt, & finalSalt, & saltChange, & + saltChangeFlux, & netSaltFlux, & absoluteSaltError, & relativeSaltError - real(kind=RKIND), pointer :: & - accumulatedOceanSaltFlux, & - accumulatedFrazilSaltFlux + real(kind=RKIND), dimension(:), pointer :: & + saltConsOceanSaltFlux, & + saltConsFrazilSaltFlux real(kind=RKIND), dimension(:), allocatable :: & sumArray, & @@ -937,8 +1459,16 @@ subroutine salt_conservation(domain, err) oceanSaltFluxArea, & frazilFormation + real(kind=RKIND) :: & + frazilCell, & + oceanSaltCell + + integer, dimension(:,:), pointer :: & + cellInHemisphere + real(kind=RKIND), pointer :: & - dt + dt, & + earthArea logical, pointer :: & config_update_ocean_fluxes, & @@ -948,70 +1478,93 @@ subroutine salt_conservation(domain, err) config_thermodynamics_type integer, pointer :: & - nCellsSolve + nCellsSolve, & + nHemispheres, & + nAccumulate integer :: & iCell, & + iHemisphere, & + iSumPrev, & ierr integer, parameter :: & - nSums = 2 + nVars = 2 - character(len=17) :: & - formatString + integer :: & + nSums + + real(kind=RKIND) :: & + fluxScale err = 0 - call MPAS_pool_get_config(domain % blocklist % configs, "config_dt", dt) - call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & - config_AM_conservationCheck_write_to_logfile) + call MPAS_pool_get_config(domain % configs, "config_dt", dt) + call MPAS_pool_get_config(domain % configs, "config_AM_conservationCheck_write_to_logfile", & + config_AM_conservationCheck_write_to_logfile) !------------------------------------------------------------- ! Net salt flux to ice !------------------------------------------------------------- + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + nSums = nHemispheres * nVars + allocate(sumArray(nSums)) allocate(sumArrayOut(nSums)) sumArray = 0.0_RKIND - block => domain % blocklist - do while (associated(block)) + blockPtr => domain % blocklist + do while (associated(blockPtr)) - call MPAS_pool_get_config(block % configs, "config_update_ocean_fluxes", config_update_ocean_fluxes) - call MPAS_pool_get_config(block % configs, "config_thermodynamics_type", config_thermodynamics_type) + call MPAS_pool_get_config(blockPtr % configs, "config_update_ocean_fluxes", config_update_ocean_fluxes) + call MPAS_pool_get_config(blockPtr % configs, "config_thermodynamics_type", config_thermodynamics_type) - call MPAS_pool_get_dimension(block % dimensions, "nCellsSolve", nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCellsSolve", nCellsSolve) - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "ocean_fluxes", oceanFluxesPool) - call MPAS_pool_get_subpool(block % structs, "melt_growth_rates", meltGrowthRatesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "ocean_fluxes", oceanFluxesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "melt_growth_rates", meltGrowthRatesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) call MPAS_pool_get_array(meshPool, "areaCell", areaCell) call MPAS_pool_get_array(oceanFluxesPool, "oceanSaltFluxArea", oceanSaltFluxArea) call MPAS_pool_get_array(meltGrowthRatesPool, "frazilFormation", frazilFormation) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) do iCell = 1, nCellsSolve - ! salt flux to ocean - sumArray(1) = sumArray(1) + & - oceanSaltFluxArea(iCell) * areaCell(iCell) + oceanSaltCell = -oceanSaltFluxArea(iCell) * areaCell(iCell) + + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+1) = sumArray(iSumPrev+1) + oceanSaltCell + endif + enddo ! iHemisphere enddo ! iCell + ! frazil if (config_update_ocean_fluxes .and. trim(config_thermodynamics_type) == "mushy") then do iCell = 1, nCellsSolve - ! frazil ice - sumArray(2) = sumArray(2) + & - (frazilFormation(iCell) * areaCell(iCell) * rhoi * ice_ref_salinity * 0.001_RKIND) / dt + frazilCell = (frazilFormation(iCell) * areaCell(iCell) * rhoi * ice_ref_salinity * 0.001_RKIND) / dt + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iSumPrev+2) = sumArray(iSumPrev+2) + frazilCell + endif + enddo ! iHemisphere + enddo ! iCell endif - block => block % next + blockPtr => blockPtr % next enddo ! perform the sums over processors @@ -1020,11 +1573,14 @@ subroutine salt_conservation(domain, err) ! accumulate fluxes call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckSaltAM", conservationCheckSaltAMPool) - call MPAS_pool_get_array(conservationCheckSaltAMPool, "accumulatedOceanSaltFlux", accumulatedOceanSaltFlux) - call MPAS_pool_get_array(conservationCheckSaltAMPool, "accumulatedFrazilSaltFlux", accumulatedFrazilSaltFlux) + call MPAS_pool_get_array(conservationCheckSaltAMPool, "saltConsOceanSaltFlux", saltConsOceanSaltFlux) + call MPAS_pool_get_array(conservationCheckSaltAMPool, "saltConsFrazilSaltFlux", saltConsFrazilSaltFlux) - accumulatedOceanSaltFlux = accumulatedOceanSaltFlux + sumArrayOut(1) - accumulatedFrazilSaltFlux = accumulatedFrazilSaltFlux + sumArrayOut(2) + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + saltConsOceanSaltFlux (iHemisphere) = saltConsOceanSaltFlux (iHemisphere) + sumArrayOut(iSumPrev+1) + saltConsFrazilSaltFlux(iHemisphere) = saltConsFrazilSaltFlux(iHemisphere) + sumArrayOut(iSumPrev+2) + enddo ! iHemisphere ! cleanup deallocate(sumArray) @@ -1036,6 +1592,13 @@ subroutine salt_conservation(domain, err) if (MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then + ! convert fluxes to fluxes per m2 of whole earth and per second + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_array(conservationCheckAMPool, "nAccumulate", nAccumulate) + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "earthArea", earthArea) + fluxScale = 1.0_RKIND / (earthArea * real(nAccumulate)) + ! get initial salt content call MPAS_pool_get_array(conservationCheckSaltAMPool, "initialSalt", initialSalt) @@ -1045,21 +1608,34 @@ subroutine salt_conservation(domain, err) ! compute the salt content change call MPAS_pool_get_array(conservationCheckSaltAMPool, "saltChange", saltChange) - saltChange = finalSalt - initialSalt + saltChange(:) = finalSalt(:) - initialSalt(:) + call MPAS_pool_get_array(conservationCheckSaltAMPool, "saltChangeFlux", saltChangeFlux) + saltChangeFlux(:) = saltChange(:) * (fluxScale / dt) ! calculate the final net salt flux to the ice call MPAS_pool_get_array(conservationCheckSaltAMPool, "netSaltFlux", netSaltFlux) + netSaltFlux(:) = & + + saltConsOceanSaltFlux(:) & + + saltConsFrazilSaltFlux(:) + + ! absolute error + call MPAS_pool_get_array(conservationCheckSaltAMPool, "absoluteSaltError", absoluteSaltError) + absoluteSaltError(:) = netSaltFlux(:) * dt - saltChange(:) - netSaltFlux = & - - accumulatedOceanSaltFlux & - + accumulatedFrazilSaltFlux + ! rescale fluxes + saltConsOceanSaltFlux(:) = saltConsOceanSaltFlux(:) * fluxScale + saltConsFrazilSaltFlux(:) = saltConsFrazilSaltFlux(:) * fluxScale + netSaltFlux(:) = netSaltFlux(:) * fluxScale ! compute the final salt error - call MPAS_pool_get_array(conservationCheckSaltAMPool, "absoluteSaltError", absoluteSaltError) call MPAS_pool_get_array(conservationCheckSaltAMPool, "relativeSaltError", relativeSaltError) - - absoluteSaltError = netSaltFlux * dt - saltChange - relativeSaltError = absoluteSaltError / (finalSalt - 1.0_RKIND) ! why the minus 1???? + do iHemisphere = 1, nHemispheres + if (abs(finalSalt(iHemisphere)) > 0.0) then + relativeSaltError(iHemisphere) = absoluteSaltError(iHemisphere) / finalSalt(iHemisphere) + else + relativeSaltError(iHemisphere) = 0.0_RKIND + endif + enddo ! iHemisphere !------------------------------------------------------------- ! Output to log file @@ -1067,23 +1643,23 @@ subroutine salt_conservation(domain, err) if (config_AM_conservationCheck_write_to_logfile) then - formatString = "(a32,2x,1pe24.17)" - - call mpas_log_write('----------------------------------------------------------') + call mpas_log_write('-----------------------------------------------------------------------------------') call mpas_log_write(' Salt conservation check') call mpas_log_write(' ') - call mpas_log_write(' Initial salt (kg) = $r', realArgs=(/initialSalt/)) - call mpas_log_write(' Final salt (kg) = $r', realArgs=(/finalSalt/)) - call mpas_log_write(' Salt change (kg) = $r', realArgs=(/saltChange/)) + call mpas_log_write(' Initial salt (kg) = '//trim(hemisphere_format(initialSalt))) + call mpas_log_write(' Final salt (kg) = '//trim(hemisphere_format(finalSalt))) + call mpas_log_write(' Salt change (kg) = '//trim(hemisphere_format(saltChange))) + call mpas_log_write(' Salt change flux (kg/m2s) = '//trim(hemisphere_format(saltChangeFlux))) call mpas_log_write(' ') - call mpas_log_write(' Ocean salt flux (kg/s) = $r', realArgs=(/accumulatedOceanSaltFlux/)) - call mpas_log_write(' Frazil salt flux (kg/s) = $r', realArgs=(/accumulatedFrazilSaltFlux/)) - call mpas_log_write(' Net salt flux (kg/s) = $r', realArgs=(/netSaltFlux/)) - call mpas_log_write(' Net salt flux (kg) = $r', realArgs=(/netSaltFlux * dt/)) + call mpas_log_write(' Ocean salt flux (kg/m2s) = '//trim(hemisphere_format(saltConsOceanSaltFlux))) + call mpas_log_write(' Frazil salt flux (kg/m2s) = '//trim(hemisphere_format(saltConsFrazilSaltFlux))) call mpas_log_write(' ') - call mpas_log_write(' Absolute mass error (kg) = $r', realArgs=(/absoluteSaltError/)) - call mpas_log_write(' Absolute mass error (kg/s) = $r', realArgs=(/absoluteSaltError / dt/)) - call mpas_log_write(' Relative salt error = $r', realArgs=(/relativeSaltError/)) + call mpas_log_write(' Net salt change (kg) = '//trim(hemisphere_format((netSaltFlux * dt) / fluxScale))) + call mpas_log_write(' Net salt flux (kg/m2s) = '//trim(hemisphere_format(netSaltFlux))) + call mpas_log_write(' ') + call mpas_log_write(' Absolute mass error (kg) = '//trim(hemisphere_format(absoluteSaltError))) + call mpas_log_write(' Absolute mass error (kg/m2s) = '//trim(hemisphere_format((absoluteSaltError * fluxScale) / dt))) + call mpas_log_write(' Relative salt error = '//trim(hemisphere_format(relativeSaltError))) endif @@ -1091,6 +1667,233 @@ subroutine salt_conservation(domain, err) end subroutine salt_conservation +!*********************************************************************** +! +! routine carbon_conservation +! +!> \brief Compute MPAS-Seaice analysis member +!> \author Nicole Jeffery +!> \date 27 May 2020 +!> \details +!> This routine conducts all computations to verify carbon conservation +!> in seaice. +! +!----------------------------------------------------------------------- + + subroutine carbon_conservation(domain, err) + + type (domain_type), intent(inout) :: & + domain + + integer, intent(out) :: & + err !< Output: error flag + + type(block_type), pointer :: & + blockPtr + + type(MPAS_pool_type), pointer :: & + conservationCheckCarbonAMPool, & + conservationCheckAMPool, & + conservationCheckAreaAMPool + + real(kind=RKIND), dimension(:), pointer :: & + initialCarbon, & + finalCarbon, & + carbonChange, & + carbonChangeFlux, & + netCarbonFlux, & + absoluteCarbonError, & + relativeCarbonError + + real(kind=RKIND), dimension(:), pointer :: & + carbonConsOceanCarbonFlux + + real(kind=RKIND) :: & + totalOceanCarbonCell + + real(kind=RKIND), dimension(:), allocatable :: & + sumArray, & + sumArrayOut + + type(MPAS_pool_type), pointer :: & + meshPool, & + biogeochemistryPool + + real(kind=RKIND), dimension(:), pointer :: & + areaCell, & + totalOceanCarbonFlux + + integer, dimension(:,:), pointer :: & + cellInHemisphere + + real(kind=RKIND), pointer :: & + dt, & + earthArea + + logical, pointer :: & + config_AM_conservationCheck_write_to_logfile + + integer, pointer :: & + nCellsSolve, & + nHemispheres, & + nAccumulate + + integer :: & + iCell, & + iHemisphere, & + iSumPrev, & + ierr + + integer, parameter :: & + nVars = 1 + + integer :: & + nSums + + real(kind=RKIND) :: & + fluxScale + + err = 0 + + call MPAS_pool_get_config(domain % configs, "config_dt", dt) + call MPAS_pool_get_config(domain % configs, "config_AM_conservationCheck_write_to_logfile", & + config_AM_conservationCheck_write_to_logfile) + + !------------------------------------------------------------- + ! Net carbon flux to ice + !------------------------------------------------------------- + + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + nSums = nHemispheres * nVars + + allocate(sumArray(nSums)) + allocate(sumArrayOut(nSums)) + + sumArray = 0.0_RKIND + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCellsSolve", nCellsSolve) + + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "biogeochemistry", biogeochemistryPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) + + call MPAS_pool_get_array(meshPool, "areaCell", areaCell) + call MPAS_pool_get_array(biogeochemistryPool, "totalOceanCarbonFlux", totalOceanCarbonFlux) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) + + do iCell = 1, nCellsSolve + + ! carbon flux to ocean + totalOceanCarbonCell = totalOceanCarbonFlux(iCell) * areaCell(iCell) + do iHemisphere = 1, nHemispheres + iSumPrev = (iHemisphere-1) * nVars + if (cellInHemisphere(iHemisphere,iCell) == 1) then + sumArray(iHemisphere) = sumArray(iHemisphere) + totalOceanCarbonCell + endif + enddo ! iHemisphere + + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + ! perform the sums over processors + call MPAS_dmpar_sum_real_array(domain % dminfo, nSums, sumArray, sumArrayOut) + + ! accumulate fluxes + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckCarbonAM", conservationCheckCarbonAMPool) + + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "carbonConsOceanCarbonFlux", carbonConsOceanCarbonFlux) + + do iHemisphere = 1, nHemispheres + carbonConsOceanCarbonFlux(iHemisphere) = carbonConsOceanCarbonFlux(iHemisphere) + sumArrayOut(iHemisphere) + enddo ! iHemisphere + + ! cleanup + deallocate(sumArray) + deallocate(sumArrayOut) + + !------------------------------------------------------------- + ! Carbon conservation error + !------------------------------------------------------------- + + if (MPAS_stream_mgr_ringing_alarms(domain % streamManager, "conservationCheckOutput", ierr=ierr)) then + + ! convert fluxes to fluxes per m2 of whole earth and per second + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_array(conservationCheckAMPool, "nAccumulate", nAccumulate) + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAreaAM", conservationCheckAreaAMPool) + call MPAS_pool_get_array(conservationCheckAreaAMPool, "earthArea", earthArea) + fluxScale = 1.0_RKIND / (earthArea * real(nAccumulate)) + + ! get initial carbon content + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "initialCarbon", initialCarbon) + + ! get final carbon content + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "finalCarbon", finalCarbon) + call compute_total_carbon(domain, finalCarbon) + + ! compute the carbon content change + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "carbonChange", carbonChange) + carbonChange(:) = finalCarbon(:) - initialCarbon(:) + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "carbonChangeFlux", carbonChangeFlux) + carbonChangeFlux(:) = carbonChange(:) * (fluxScale / dt) + + ! calculate the final net carbon flux to the ice + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "netCarbonFlux", netCarbonFlux) + netCarbonFlux(:) = & + - carbonConsOceanCarbonFlux(:) + + ! absolute error + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "absoluteCarbonError", absoluteCarbonError) + absoluteCarbonError(:) = netCarbonFlux(:) * dt - carbonChange(:) + + ! rescale fluxes + carbonConsOceanCarbonFlux(:) = carbonConsOceanCarbonFlux(:) * fluxScale + netCarbonFlux(:) = netCarbonFlux(:) * fluxScale + + ! compute the final carbon error + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "relativeCarbonError", relativeCarbonError) + do iHemisphere = 1, nHemispheres + if (abs(finalCarbon(iHemisphere)) > 0.0) then + relativeCarbonError(iHemisphere) = absoluteCarbonError(iHemisphere) / finalCarbon(iHemisphere) + else + relativeCarbonError(iHemisphere) = 0.0_RKIND + endif + enddo ! iHemisphere + + !------------------------------------------------------------- + ! Output to log file + !------------------------------------------------------------- + + if (config_AM_conservationCheck_write_to_logfile) then + + call mpas_log_write('-----------------------------------------------------------------------------') + call mpas_log_write(' Carbon conservation check') + call mpas_log_write(' ') + call mpas_log_write(' Initial carbon (mmol) = '//trim(hemisphere_format(initialCarbon))) + call mpas_log_write(' Final carbon (mmol) = '//trim(hemisphere_format(finalCarbon))) + call mpas_log_write(' Carbon change (mmol) = '//trim(hemisphere_format(carbonChange))) + call mpas_log_write(' Carbon change flux (mmol/m2s) = '//trim(hemisphere_format(carbonChange))) + call mpas_log_write(' ') + call mpas_log_write(' Ocean carbon flux (mmol/m2s) = '//trim(hemisphere_format(carbonConsOceanCarbonFlux))) + call mpas_log_write(' ') + call mpas_log_write(' Net carbon change (mmol) = '//trim(hemisphere_format((netCarbonFlux * dt) / fluxScale))) + call mpas_log_write(' Net carbon flux (mmol/m2s) = '//trim(hemisphere_format(netCarbonFlux))) + call mpas_log_write(' ') + call mpas_log_write(' Absolute carbon error (mmol) = '//trim(hemisphere_format(absoluteCarbonError))) + call mpas_log_write(' Absolute carbon error/s (mmol/m2s) = '//trim(hemisphere_format((absoluteCarbonError * fluxScale) / dt))) + call mpas_log_write(' Relative carbon error = '//trim(hemisphere_format(relativeCarbonError))) + + endif + + endif + + end subroutine carbon_conservation + !*********************************************************************** ! ! routine compute_total_energy @@ -1108,21 +1911,23 @@ subroutine compute_total_energy(domain, totalEnergy) type (domain_type), intent(inout) :: & domain - real(kind=RKIND), intent(out) :: & + real(kind=RKIND), dimension(:), intent(out) :: & totalEnergy type(block_type), pointer :: & - block + blockPtr type(MPAS_pool_type), pointer :: & meshPool, & - tracersPool + tracersPool, & + conservationCheckAMPool integer, pointer :: & nCellsSolve, & nCategories, & nIceLayers, & - nSnowLayers + nSnowLayers, & + nHemispheres real(kind=RKIND), dimension(:), pointer :: & areaCell @@ -1133,6 +1938,9 @@ subroutine compute_total_energy(domain, totalEnergy) iceVolumeCategory, & snowVolumeCategory + integer, dimension(:,:), pointer :: & + cellInHemisphere + real(kind=RKIND) :: & nIceLayersInverse, & nSnowLayersInverse @@ -1141,39 +1949,51 @@ subroutine compute_total_energy(domain, totalEnergy) iCell, & iCategory, & iIceLayer, & - iSnowLayer + iSnowLayer, & + iHemisphere real(kind=RKIND) :: & + energyCell + + real(kind=RKIND), dimension(:), allocatable :: & energy - energy = 0.0_RKIND + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) - block => domain % blocklist - do while (associated(block)) + allocate(energy(nHemispheres)) + energy(:) = 0.0_RKIND - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "tracers", tracersPool) + blockPtr => domain % blocklist + do while (associated(blockPtr)) - call MPAS_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) - call MPAS_pool_get_dimension(block % dimensions, 'nCategories', nCategories) - call MPAS_pool_get_dimension(block % dimensions, 'nIceLayers', nIceLayers) - call MPAS_pool_get_dimension(block % dimensions, 'nSnowLayers', nSnowLayers) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers", tracersPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) + + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nCellsSolve', nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nCategories', nCategories) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nIceLayers', nIceLayers) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nSnowLayers', nSnowLayers) call MPAS_pool_get_array(meshPool, "areaCell", areaCell) call MPAS_pool_get_array(tracersPool, "iceEnthalpy", iceEnthalpy, 1) call MPAS_pool_get_array(tracersPool, "snowEnthalpy", snowEnthalpy, 1) call MPAS_pool_get_array(tracersPool, "iceVolumeCategory", iceVolumeCategory, 1) call MPAS_pool_get_array(tracersPool, "snowVolumeCategory", snowVolumeCategory, 1) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) nIceLayersInverse = 1.0_RKIND / real(nIceLayers,RKIND) nSnowLayersInverse = 1.0_RKIND / real(nSnowLayers,RKIND) do iCell = 1, nCellsSolve + + energyCell = 0.0; + do iCategory = 1, nCategories do iIceLayer = 1, nIceLayers - energy = energy + & + energyCell = energyCell + & iceEnthalpy(iIceLayer,iCategory,iCell) * & iceVolumeCategory(1,iCategory,iCell) * & nIceLayersInverse * & @@ -1183,7 +2003,7 @@ subroutine compute_total_energy(domain, totalEnergy) do iSnowLayer = 1, nSnowLayers - energy = energy + & + energyCell = energyCell + & snowEnthalpy(iSnowLayer,iCategory,iCell) * & snowVolumeCategory(1,iCategory,iCell) * & nSnowLayersInverse * & @@ -1192,13 +2012,23 @@ subroutine compute_total_energy(domain, totalEnergy) enddo ! iIceLayer enddo ! iCategory + + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + energy(iHemisphere) = energy(iHemisphere) + energyCell + endif + enddo ! iHemisphere + enddo ! iCell - block => block % next + blockPtr => blockPtr % next enddo ! sum across processors - call MPAS_dmpar_sum_real(domain % dminfo, energy, totalEnergy) + call MPAS_dmpar_sum_real_array(domain % dminfo, nHemispheres, energy, totalEnergy) + + ! clean up + deallocate(energy) end subroutine compute_total_energy @@ -1223,20 +2053,22 @@ subroutine compute_total_mass(domain, totalMass) type (domain_type), intent(inout) :: & domain - real(kind=RKIND), intent(out) :: & + real(kind=RKIND), dimension(:), intent(out) :: & totalMass type(block_type), pointer :: & - block + blockPtr type(MPAS_pool_type), pointer :: & meshPool, & tracersPool, & - tracersAggregatePool + tracersAggregatePool, & + conservationCheckAMPool integer, pointer :: & nCellsSolve, & - nCategories + nCategories, & + nHemispheres real(kind=RKIND), dimension(:), pointer :: & areaCell, & @@ -1250,29 +2082,40 @@ subroutine compute_total_mass(domain, totalMass) levelIceArea, & levelIceVolume + integer, dimension(:,:), pointer :: & + cellInHemisphere + logical, pointer :: & config_use_topo_meltponds integer :: & iCell, & - iCategory + iCategory, & + iHemisphere real(kind=RKIND) :: & + massCell + + real(kind=RKIND), dimension(:), allocatable :: & mass - mass = 0.0_RKIND + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + + allocate(mass(nHemispheres)) + mass(:) = 0.0_RKIND - block => domain % blocklist - do while (associated(block)) + blockPtr => domain % blocklist + do while (associated(blockPtr)) - call MPAS_pool_get_config(block % configs, "config_use_topo_meltponds", config_use_topo_meltponds) + call MPAS_pool_get_config(blockPtr % configs, "config_use_topo_meltponds", config_use_topo_meltponds) - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "tracers", tracersPool) - call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers", tracersPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) - call MPAS_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) - call MPAS_pool_get_dimension(block % dimensions, 'nCategories', nCategories) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nCellsSolve', nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nCategories', nCategories) call MPAS_pool_get_array(meshPool, "areaCell", areaCell) call MPAS_pool_get_array(tracersAggregatePool, "iceVolumeCell", iceVolumeCell) @@ -1280,38 +2123,56 @@ subroutine compute_total_mass(domain, totalMass) call MPAS_pool_get_array(tracersPool, "iceAreaCategory", iceAreaCategory, 1) call MPAS_pool_get_array(tracersPool, "levelIceArea", levelIceArea, 1) call MPAS_pool_get_array(tracersPool, "levelIceVolume", levelIceVolume, 1) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) do iCell = 1, nCellsSolve ! ice and snow mass - mass = mass + & + massCell = & (iceVolumeCell(iCell) * rhoi + & snowVolumeCell(iCell) * rhos) * areaCell(iCell) + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + mass(iHemisphere) = mass(iHemisphere) + massCell + endif + enddo ! iHemisphere + enddo ! iCell if (config_use_topo_meltponds) then do iCell = 1, nCellsSolve + massCell = 0.0; + do iCategory = 1, nCategories ! pond mass - mass = mass + & + massCell = massCell + & iceAreaCategory(1,icategory,iCell) * levelIceArea(1,icategory,iCell) * & levelIceVolume(1,icategory,iCell) * areaCell(iCell) enddo ! iCategory + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + mass(iHemisphere) = mass(iHemisphere) + massCell + endif + enddo ! iHemisphere + enddo ! iCell endif - block => block % next + blockPtr => blockPtr % next enddo ! sum across processors - call MPAS_dmpar_sum_real(domain % dminfo, mass, totalMass) + call MPAS_dmpar_sum_real_array(domain % dminfo, nHemispheres, mass, totalMass) + + ! clean up + deallocate(mass) end subroutine compute_total_mass @@ -1337,18 +2198,20 @@ subroutine compute_total_salt(domain, totalSalt) type (domain_type), intent(inout) :: & domain - real(kind=RKIND), intent(out) :: & + real(kind=RKIND), dimension(:), intent(out) :: & totalSalt type(block_type), pointer :: & - block + blockPtr type(MPAS_pool_type), pointer :: & meshPool, & - tracersAggregatePool + tracersAggregatePool, & + conservationCheckAMPool integer, pointer :: & - nCellsSolve + nCellsSolve, & + nHemispheres real(kind=RKIND), dimension(:), pointer :: & areaCell, & @@ -1357,44 +2220,418 @@ subroutine compute_total_salt(domain, totalSalt) real(kind=RKIND), dimension(:,:,:), pointer :: & iceVolumeCategory + integer, dimension(:,:), pointer :: & + cellInHemisphere + integer :: & iCell, & - iCategory + iCategory, & + iHemisphere real(kind=RKIND) :: & + saltCell + + real(kind=RKIND), dimension(:), allocatable :: & salt - salt = 0.0_RKIND + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + + allocate(salt(nHemispheres)) + salt(:) = 0.0_RKIND - block => domain % blocklist - do while (associated(block)) + blockPtr => domain % blocklist + do while (associated(blockPtr)) - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers_aggregate", tracersAggregatePool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) - call MPAS_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nCellsSolve', nCellsSolve) call MPAS_pool_get_array(meshPool, "areaCell", areaCell) call MPAS_pool_get_array(tracersAggregatePool, "iceVolumeCell", iceVolumeCell) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) do iCell = 1, nCellsSolve ! ice and snow mass - salt = salt + & + saltCell = & iceVolumeCell(iCell) * areaCell(iCell) + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + salt(iHemisphere) = salt(iHemisphere) + saltCell + endif + enddo ! iHemisphere + enddo ! iCell - block => block % next + blockPtr => blockPtr % next enddo - salt = salt * rhoi * ice_ref_salinity * 0.001_RKIND + salt(:) = salt(:) * rhoi * ice_ref_salinity * 0.001_RKIND ! sum across processors - call MPAS_dmpar_sum_real(domain % dminfo, salt, totalSalt) + call MPAS_dmpar_sum_real_array(domain % dminfo, nHemispheres, salt, totalSalt) + + ! clean up + deallocate(salt) end subroutine compute_total_salt +!*********************************************************************** +! +! routine compute_total_carbon +! +!> \brief Compute total carbon of sea ice system +!> \author Nicole Jeffery +!> \date 27 May 2020 +!> \details +!> Calculate the total carbon of the sea ice system +! +!----------------------------------------------------------------------- + + subroutine compute_total_carbon(domain, totalCarbon) + + type (domain_type), intent(inout) :: & + domain + + real(kind=RKIND), dimension(:), intent(out) :: & + totalCarbon + + type(block_type), pointer :: & + blockPtr + + type(MPAS_pool_type), pointer :: & + meshPool, & + biogeochemistryPool, & + conservationCheckAMPool + + integer, pointer :: & + nCellsSolve, & + nHemispheres + + real(kind=RKIND), dimension(:), pointer :: & + totalCarbonContentCell, & + areaCell + + integer, dimension(:,:), pointer :: & + cellInHemisphere + + integer :: & + iCell, & + iHemisphere + + real(kind=RKIND) :: & + carbonCell + + real(kind=RKIND), dimension(:), allocatable :: & + carbon + + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nHemispheres", nHemispheres) + + allocate(carbon(nHemispheres)) + carbon(:) = 0.0_RKIND + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "biogeochemistry", biogeochemistryPool) + call MPAS_pool_get_subpool(blockPtr % structs, "conservationCheckAM", conservationCheckAMPool) + + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nCellsSolve', nCellsSolve) + call MPAS_pool_get_dimension(blockPtr % dimensions, 'nHemispheres', nHemispheres) + + call MPAS_pool_get_array(meshPool, "areaCell", areaCell) + call MPAS_pool_get_array(biogeochemistryPool, "totalCarbonContentCell", totalCarbonContentCell) + call MPAS_pool_get_array(conservationCheckAMPool, "cellInHemisphere", cellInHemisphere) + + call compute_carbon_cell(blockPtr,totalCarbonContentCell) + + do iCell = 1, nCellsSolve + + ! ice carbon mass (mmols) + carbonCell = totalCarbonContentCell(iCell) * areaCell(iCell) + + do iHemisphere = 1, nHemispheres + if (cellInHemisphere(iHemisphere,iCell) == 1) then + carbon(iHemisphere) = carbon(iHemisphere) + carbonCell + endif + enddo ! iHemisphere + + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + ! sum across processors + call MPAS_dmpar_sum_real_array(domain % dminfo, nHemispheres, carbon, totalCarbon) + + ! clean up + deallocate(carbon) + + end subroutine compute_total_carbon + +!*********************************************************************** +! +! compute_carbon_cell +! +!> \brief +!> \author Nicole Jeffery, LANL +!> \date 26 May 2020 +!> \details Calculate the total carbon concentration in the sea ice cell +!> by summing the appropriate biogeochemical tracers in units of mmol C +!> +!> Total carbon = algal nitrogen groups * (C to N ratios) + dissolved carbon groups +!> + dissolved inorganic carbon + dissolved organic nitrogen * (C to N ratio) +!> + humic material +! +!----------------------------------------------------------------------- + + subroutine compute_carbon_cell(blockPtr,totalCarbonContentCell) + + use seaice_constants, only: & + skeletalLayerThickness + + real(kind=RKIND), dimension(:), intent(out) :: & + totalCarbonContentCell + + type(block_type), intent(in) :: & + blockPtr + + logical, pointer :: & + config_use_skeletal_biochemistry, & + config_use_vertical_biochemistry, & + config_use_vertical_tracers, & + config_use_carbon, & + config_use_DON, & + config_use_humics + + integer, pointer :: & + nBioLayersP1, & + nBioLayers, & + nAlgae, & + nDOC, & + nDIC, & + nDON + + type(MPAS_pool_type), pointer :: & + mesh, & + biogeochemistry, & + tracers_aggregate + + real(kind=RKIND), dimension(:), pointer :: & + brineFractionCell, & + iceVolumeCell + + real(kind=RKIND), dimension(:,:), pointer :: & + skeletalAlgaeConcCell, & + skeletalDOCConcCell, & + skeletalDICConcCell, & + skeletalDONConcCell, & + skeletalHumicsConcCell, & + verticalAlgaeConcCell, & + verticalDOCConcCell, & + verticalDICConcCell, & + verticalDONConcCell, & + verticalHumicsConcCell + + real(kind=RKIND), pointer :: & + config_ratio_C_to_N_diatoms, & + config_ratio_C_to_N_small_plankton, & + config_ratio_C_to_N_phaeocystis, & + config_ratio_C_to_N_proteins + + integer, pointer :: & + nCellsSolve, & + nCategories + + real(kind=RKIND), dimension(:), allocatable :: & + ratio_C_to_N, & + verticalGridSpace + + real(kind=RKIND) :: & + totalOceanCarbonCell + + integer :: & + iBioTracers, & + iBioCount, & + iLayers, & + iCell + + call MPAS_pool_get_config(blockPtr % configs, "config_use_skeletal_biochemistry", config_use_skeletal_biochemistry) + call MPAS_pool_get_config(blockPtr % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) + call MPAS_pool_get_config(blockPtr % configs, "config_use_vertical_tracers", config_use_vertical_tracers) + call MPAS_pool_get_config(blockPtr % configs, "config_use_carbon", config_use_carbon) + call MPAS_pool_get_config(blockPtr % configs, "config_use_DON", config_use_DON) + call MPAS_pool_get_config(blockPtr % configs, "config_use_humics",config_use_humics) + call MPAS_pool_get_config(blockPtr % configs, "config_ratio_C_to_N_diatoms", config_ratio_C_to_N_diatoms) + call MPAS_pool_get_config(blockPtr % configs, "config_ratio_C_to_N_small_plankton", config_ratio_C_to_N_small_plankton) + call MPAS_pool_get_config(blockPtr % configs, "config_ratio_C_to_N_phaeocystis", config_ratio_C_to_N_phaeocystis) + call MPAS_pool_get_config(blockPtr % configs, "config_ratio_C_to_N_proteins", config_ratio_C_to_N_proteins) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nBioLayers", nBioLayers) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nBioLayersP1", nBioLayersP1) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nAlgae", nAlgae) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nDOC", nDOC) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nDIC", nDIC) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nDON", nDON) + + call MPAS_pool_get_subpool(blockPtr % structs, "tracers_aggregate", tracers_aggregate) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", mesh) + call MPAS_pool_get_subpool(blockPtr % structs, "biogeochemistry", biogeochemistry) + + call MPAS_pool_get_dimension(mesh, "nCellsSolve", nCellsSolve) + + call MPAS_pool_get_array(tracers_aggregate, "skeletalAlgaeConcCell", skeletalAlgaeConcCell) + call MPAS_pool_get_array(tracers_aggregate, "skeletalDOCConcCell", skeletalDOCConcCell) + call MPAS_pool_get_array(tracers_aggregate, "skeletalDICConcCell", skeletalDICConcCell) + call MPAS_pool_get_array(tracers_aggregate, "skeletalDONConcCell", skeletalDONConcCell) + call MPAS_pool_get_array(tracers_aggregate, "skeletalHumicsConcCell", skeletalHumicsConcCell) + call MPAS_pool_get_array(tracers_aggregate, "verticalAlgaeConcCell", verticalAlgaeConcCell) + call MPAS_pool_get_array(tracers_aggregate, "verticalDOCConcCell", verticalDOCConcCell) + call MPAS_pool_get_array(tracers_aggregate, "verticalDICConcCell", verticalDICConcCell) + call MPAS_pool_get_array(tracers_aggregate, "verticalDONConcCell", verticalDONConcCell) + call MPAS_pool_get_array(tracers_aggregate, "verticalHumicsConcCell", verticalHumicsConcCell) + call MPAS_pool_get_array(tracers_aggregate, "brineFractionCell", brineFractionCell) + call MPAS_pool_get_array(tracers_aggregate, "iceVolumeCell", iceVolumeCell) + + allocate(ratio_C_to_N(3)) + allocate(verticalGridSpace(nBioLayersP1)) + + ratio_C_to_N(1) = config_ratio_C_to_N_diatoms + ratio_C_to_N(2) = config_ratio_C_to_N_small_plankton + ratio_C_to_N(3) = config_ratio_C_to_N_phaeocystis + + verticalGridSpace(:) = 1.0_RKIND/real(nBioLayers,kind=RKIND) + verticalGridSpace(1) = verticalGridSpace(1)/2.0_RKIND + verticalGridSpace(nBioLayersP1) = verticalGridSpace(1) + + totalCarbonContentCell(:) = 0.0_RKIND + + if (config_use_skeletal_biochemistry) then + do iCell = 1, nCellsSolve + + ! algal nitrogen + do iBioTracers = 1, nAlgae + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + skeletalAlgaeConcCell(iBioTracers,iCell)* & + skeletalLayerThickness * ratio_C_to_N(iBioTracers) + enddo + + if (config_use_carbon) then + + ! DOC + do iBioTracers = 1, nDOC + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + skeletalDOCConcCell(iBioTracers,iCell)* & + skeletalLayerThickness + enddo + + ! DIC + do iBioTracers = 1, nDIC + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + skeletalDICConcCell(iBioTracers,iCell)* & + skeletalLayerThickness + enddo + endif + + ! DON + if (config_use_DON) then + do iBioTracers = 1, nDON + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + skeletalDONConcCell(iBioTracers,iCell)* & + config_ratio_C_to_N_proteins * skeletalLayerThickness + enddo + endif + + ! humic material + if (config_use_humics) & + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + skeletalHumicsConcCell(1,iCell)* & + skeletalLayerThickness + + enddo ! iCell + + elseif (config_use_vertical_tracers) then + + do iCell = 1, nCellsSolve + + if (config_use_vertical_biochemistry) then + iBioCount = 0 + + ! algal nitrogen + do iBioTracers = 1, nAlgae + + do iLayers = 1,nBioLayersP1 + iBiocount = iBiocount + 1 + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + & + verticalAlgaeConcCell(iBioCount,iCell) * ratio_C_to_N(iBioTracers) * & + verticalGridSpace(iLayers) * iceVolumeCell(iCell) * brineFractionCell(iCell) + enddo + iBiocount = iBioCount + 2 + enddo + endif + + if (config_use_carbon) then + iBioCount = 0 + + ! DOC + do iBioTracers = 1, nDOC + + do iLayers = 1,nBioLayersP1 + iBioCount = iBioCount + 1 + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + & + verticalDOCConcCell(iBioCount,iCell) * verticalGridSpace(iLayers) * & + iceVolumeCell(iCell) * brineFractionCell(iCell) + enddo + iBiocount = iBioCount + 2 + enddo + iBioCount = 0 + + ! DIC + do iBioTracers = 1, nDIC + + do iLayers = 1,nBioLayersP1 + iBioCount = iBioCount + 1 + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + & + verticalDICConcCell(iBioCount,iCell) * verticalGridSpace(iLayers) * & + iceVolumeCell(iCell) * brineFractionCell(iCell) + enddo + iBiocount = iBioCount + 2 + enddo + endif + + if (config_use_DON) then + iBioCount = 0 + + ! dissolve organic nitrogen + do iBioTracers = 1, nDON + + do iLayers = 1,nBioLayersP1 + iBiocount = iBiocount + 1 + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + & + verticalDONConcCell(iBioCount,iCell) * config_ratio_C_to_N_proteins * & + verticalGridSpace(iLayers) * iceVolumeCell(iCell) * brineFractionCell(iCell) + enddo + iBiocount = iBioCount + 2 + enddo + endif + + ! humic material + if (config_use_humics) then + do iLayers = 1, nBioLayersP1 + totalCarbonContentCell(iCell) = totalCarbonContentCell(iCell) + & + verticalHumicsConcCell(iLayers,iCell) * verticalGridSpace(iLayers) * & + iceVolumeCell(iCell) * brineFractionCell(iCell) + enddo + endif + + + enddo ! iCell + endif + + deallocate(ratio_C_to_N) + deallocate(verticalGridSpace) + + end subroutine compute_carbon_cell + !*********************************************************************** ! ! routine reset_accumulated_variables @@ -1413,62 +2650,101 @@ subroutine reset_accumulated_variables(domain) domain type(MPAS_pool_type), pointer :: & + conservationCheckAMPool, & conservationCheckEnergyAMPool, & conservationCheckMassAMPool, & - conservationCheckSaltAMPool + conservationCheckSaltAMPool, & + conservationCheckCarbonAMPool - real(kind=RKIND), pointer :: & - accumulatedSurfaceHeatFlux, & - accumulatedOceanHeatFlux, & - accumulatedFreezingPotential, & - accumulatedSnowfallHeat, & - accumulatedLatentHeat, & - accumulatedRainfallRate, & - accumulatedSnowfallRate, & - accumulatedEvaporation, & - accumulatedFreshWater, & - accumulatedFrazilWater, & - accumulatedOceanSaltFlux, & - accumulatedFrazilSaltFlux + integer, pointer :: & + nAccumulate + + real(kind=RKIND), dimension(:), pointer :: & + energyConsSurfaceHeatFlux, & + energyConsAbsorbedShortwaveFlux, & + energyConsOceanShortwaveFlux, & + energyConsSensibleHeatFlux, & + energyConsLongwaveUp, & + energyConsLongwaveDown, & + energyConsOceanHeatFlux, & + energyConsFreezingPotential, & + energyConsSnowfallHeat, & + energyConsLatentHeat + + real(kind=RKIND), dimension(:), pointer :: & + massConsRainfallRate, & + massConsSnowfallRate, & + massConsEvaporation, & + massConsFreshWater, & + massConsFrazilWater + + real(kind=RKIND), dimension(:), pointer :: & + saltConsOceanSaltFlux, & + saltConsFrazilSaltFlux + + real(kind=RKIND), dimension(:), pointer :: & + carbonConsOceanCarbonFlux + + ! number of accumulations + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckAM", conservationCheckAMPool) + call MPAS_pool_get_array(conservationCheckAMPool, "nAccumulate", nAccumulate) + nAccumulate = 0 ! heat call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckEnergyAM", conservationCheckEnergyAMPool) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedSurfaceHeatFlux", accumulatedSurfaceHeatFlux) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedOceanHeatFlux", accumulatedOceanHeatFlux) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedFreezingPotential", accumulatedFreezingPotential) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedSnowfallHeat", accumulatedSnowfallHeat) - call MPAS_pool_get_array(conservationCheckEnergyAMPool, "accumulatedLatentHeat", accumulatedLatentHeat) - - accumulatedSurfaceHeatFlux = 0.0_RKIND - accumulatedOceanHeatFlux = 0.0_RKIND - accumulatedFreezingPotential = 0.0_RKIND - accumulatedSnowfallHeat = 0.0_RKIND - accumulatedLatentHeat = 0.0_RKIND + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSurfaceHeatFlux", energyConsSurfaceHeatFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsAbsorbedShortwaveFlux", energyConsAbsorbedShortwaveFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsOceanShortwaveFlux", energyConsOceanShortwaveFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSensibleHeatFlux", energyConsSensibleHeatFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsLongwaveUp", energyConsLongwaveUp) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsLongwaveDown", energyConsLongwaveDown) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsOceanHeatFlux", energyConsOceanHeatFlux) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsFreezingPotential", energyConsFreezingPotential) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsSnowfallHeat", energyConsSnowfallHeat) + call MPAS_pool_get_array(conservationCheckEnergyAMPool, "energyConsLatentHeat", energyConsLatentHeat) + + energyConsSurfaceHeatFlux(:) = 0.0_RKIND + energyConsAbsorbedShortwaveFlux(:) = 0.0_RKIND + energyConsOceanShortwaveFlux(:) = 0.0_RKIND + energyConsSensibleHeatFlux(:) = 0.0_RKIND + energyConsLongwaveUp(:) = 0.0_RKIND + energyConsLongwaveDown(:) = 0.0_RKIND + energyConsOceanHeatFlux(:) = 0.0_RKIND + energyConsFreezingPotential(:) = 0.0_RKIND + energyConsSnowfallHeat(:) = 0.0_RKIND + energyConsLatentHeat(:) = 0.0_RKIND ! mass call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckMassAM", conservationCheckMassAMPool) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedRainfallRate", accumulatedRainfallRate) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedSnowfallRate", accumulatedSnowfallRate) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedEvaporation", accumulatedEvaporation) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedFreshWater", accumulatedFreshWater) - call MPAS_pool_get_array(conservationCheckMassAMPool, "accumulatedFrazilWater", accumulatedFrazilWater) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsRainfallRate", massConsRainfallRate) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsSnowfallRate", massConsSnowfallRate) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsEvaporation", massConsEvaporation) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsFreshWater", massConsFreshWater) + call MPAS_pool_get_array(conservationCheckMassAMPool, "massConsFrazilWater", massConsFrazilWater) - accumulatedRainfallRate = 0.0_RKIND - accumulatedSnowfallRate = 0.0_RKIND - accumulatedEvaporation = 0.0_RKIND - accumulatedFreshWater = 0.0_RKIND - accumulatedFrazilWater = 0.0_RKIND + massConsRainfallRate(:) = 0.0_RKIND + massConsSnowfallRate(:) = 0.0_RKIND + massConsEvaporation(:) = 0.0_RKIND + massConsFreshWater(:) = 0.0_RKIND + massConsFrazilWater(:) = 0.0_RKIND ! salt call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckSaltAM", conservationCheckSaltAMPool) - call MPAS_pool_get_array(conservationCheckSaltAMPool, "accumulatedOceanSaltFlux", accumulatedOceanSaltFlux) - call MPAS_pool_get_array(conservationCheckSaltAMPool, "accumulatedFrazilSaltFlux", accumulatedFrazilSaltFlux) + call MPAS_pool_get_array(conservationCheckSaltAMPool, "saltConsOceanSaltFlux", saltConsOceanSaltFlux) + call MPAS_pool_get_array(conservationCheckSaltAMPool, "saltConsFrazilSaltFlux", saltConsFrazilSaltFlux) - accumulatedOceanSaltFlux = 0.0_RKIND - accumulatedFrazilSaltFlux = 0.0_RKIND + saltConsOceanSaltFlux(:) = 0.0_RKIND + saltConsFrazilSaltFlux(:) = 0.0_RKIND + + ! carbon + call MPAS_pool_get_subpool(domain % blocklist % structs, "conservationCheckCarbonAM", conservationCheckCarbonAMPool) + + call MPAS_pool_get_array(conservationCheckCarbonAMPool, "carbonConsOceanCarbonFlux", carbonConsOceanCarbonFlux) + + carbonConsOceanCarbonFlux(:) = 0.0_RKIND end subroutine reset_accumulated_variables @@ -1570,6 +2846,43 @@ subroutine seaice_finalize_conservation_check(domain, instance, err)!{{{ end subroutine seaice_finalize_conservation_check!}}} +!*********************************************************************** +! +! routine hemisphere_format +! +!> \brief Format output string +!> \author Adrian K. Turner +!> \date 13th April 2021 +!> \details +!> Formats output string for hemispheric values +! +!----------------------------------------------------------------------- + + function hemisphere_format(arrayIn) result(outStr) + + real(kind=RKIND), dimension(:), intent(in) :: & + arrayIn + + character(len=strKIND) :: & + outStr + + character(len=16) :: & + valStr + + integer :: & + i + + outStr = "" + + do i = 1, size(arrayIn) + + write(valStr,fmt=' (es15.6)') arrayIn(i) + outStr = trim(outStr)//valStr + + enddo ! i + + end function hemisphere_format + !----------------------------------------------------------------------- end module seaice_conservation_check diff --git a/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F b/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F index 3b202cdecb..63859a1e39 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F +++ b/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F @@ -1,9 +1,3 @@ -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_deactivate_unneeded_fields diff --git a/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F b/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F index e8a7cbed80..b7dc9c79d8 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F +++ b/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_geographical_vectors diff --git a/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F b/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F index bc23a2ce3e..b116060633 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F +++ b/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_high_frequency_output diff --git a/src/core_seaice/analysis_members/mpas_seaice_ice_present.F b/src/core_seaice/analysis_members/mpas_seaice_ice_present.F index 0c1e78399e..6f146235e4 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_ice_present.F +++ b/src/core_seaice/analysis_members/mpas_seaice_ice_present.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_ice_present diff --git a/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F b/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F index 49e667c1b2..2a72d0c19c 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F +++ b/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_ice_shelves diff --git a/src/core_seaice/analysis_members/mpas_seaice_load_balance.F b/src/core_seaice/analysis_members/mpas_seaice_load_balance.F index e3d9138a4b..2ef0138895 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_load_balance.F +++ b/src/core_seaice/analysis_members/mpas_seaice_load_balance.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_load_balance diff --git a/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F b/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F index 119983876d..fdea4d5629 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F +++ b/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_maximum_ice_presence diff --git a/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F b/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F index 6ed36c9932..9575834577 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F +++ b/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_miscellaneous diff --git a/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F b/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F index 7efd727227..17fffe6f10 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F +++ b/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_pointwise_stats diff --git a/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F b/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F index 371b89a2b5..006c48bc8e 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F +++ b/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_pond_diagnostics diff --git a/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F b/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F index 41d1bdd81b..0880357639 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F +++ b/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_regional_statistics @@ -768,6 +761,8 @@ subroutine create_new_output_field_1D(& outputField % isVarArray = .false. outputField % isPersistent = .true. + allocate(outputField % attLists(1)) + end subroutine create_new_output_field_1D !*********************************************************************** @@ -830,6 +825,8 @@ subroutine create_new_output_field_2D(& outputField % isVarArray = .false. outputField % isPersistent = .true. + allocate(outputField % attLists(1)) + end subroutine create_new_output_field_2D !*********************************************************************** @@ -1092,14 +1089,13 @@ subroutine fixed_regional_statistics(domain) call MPAS_pool_get_array(velocitySolverPool, "icePressure", icePressure) call MPAS_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) call MPAS_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) + call MPAS_pool_get_array(velocitySolverPool, "uVelocityCell", uVelocityCell) + call MPAS_pool_get_array(velocitySolverPool, "vVelocityCell", vVelocityCell) call MPAS_pool_get_array(velocitySolverPool, "dynamicallyLockedCellsMask", dynamicallyLockedCellsMask) call MPAS_pool_get_array(regionsPool, 'regionCellMasks', regionCellMasks) call MPAS_pool_get_array(regionsPool, 'regionVertexMasks', regionVertexMasks) - call MPAS_pool_get_array(regionalStatisticsAMPool, 'uVelocityCell', uVelocityCell) - call MPAS_pool_get_array(regionalStatisticsAMPool, 'vVelocityCell', vVelocityCell) - ! cell centre velocity for velocity statistics call seaice_interpolate_vertex_to_cell(meshPool, boundaryPool, uVelocityCell, uVelocity) call seaice_interpolate_vertex_to_cell(meshPool, boundaryPool, vVelocityCell, vVelocity) diff --git a/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F b/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F index fd12e97a98..911bf4582a 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F +++ b/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_ridging_diagnostics diff --git a/src/core_seaice/analysis_members/mpas_seaice_temperatures.F b/src/core_seaice/analysis_members/mpas_seaice_temperatures.F index 412a00962f..587b78bbe6 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_temperatures.F +++ b/src/core_seaice/analysis_members/mpas_seaice_temperatures.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_temperatures diff --git a/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F b/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F index 442494b979..31d108e478 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F +++ b/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F @@ -1,11 +1,3 @@ -! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! seaice_time_series_stats ! @@ -161,7 +153,7 @@ module seaice_time_series_stats 'repeat_interval' character (len=StrKIND), parameter :: RESET_INTERVAL_TOKEN = 'reset_interval' - character (len=StrKIND), parameter :: CURRENT_CORE_NAME = 'MPAS-Ocean' + character (len=StrKIND), parameter :: CURRENT_CORE_NAME = 'MPAS-Seaice' character (len=4), parameter :: NONE_TOKEN = 'none' !*********************************************************************** @@ -214,12 +206,12 @@ end subroutine seaice_bootstrap_time_series_stats!}}} !*********************************************************************** ! routine seaice_init_time_series_stats ! -!> \brief Initialize MPAS-Ocean analysis member +!> \brief Initialize MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts all initializations required for the -!> MPAS-Ocean analysis member. +!> MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_init_time_series_stats(domain, instance, err)!{{{ ! input variables @@ -276,12 +268,12 @@ end subroutine seaice_init_time_series_stats!}}} !*********************************************************************** ! routine seaice_compute_time_series_stats ! -!> \brief Compute MPAS-Ocean analysis member +!> \brief Compute MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts all computation required for this -!> MPAS-Ocean analysis member. +!> MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_compute_time_series_stats(domain, timeLevel, instance, err)!{{{ ! input variables @@ -356,12 +348,12 @@ end subroutine seaice_compute_time_series_stats!}}} !*********************************************************************** ! routine seaice_restart_time_series_stats ! -!> \brief Save restart for MPAS-Ocean analysis member +!> \brief Save restart for MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts computation required to save a restart state -!> for the MPAS-Ocean analysis member. +!> for the MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_restart_time_series_stats(domain, instance, err)!{{{ ! input variables @@ -385,12 +377,12 @@ end subroutine seaice_restart_time_series_stats!}}} !*********************************************************************** ! routine seaice_finalize_time_series_stats ! -!> \brief Finalize MPAS-Ocean analysis member +!> \brief Finalize MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts all finalizations required for this -!> MPAS-Ocean analysis member. +!> MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_finalize_time_series_stats(domain, instance, err)!{{{ ! input variables diff --git a/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F b/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F index 316e542a2e..97e69ca262 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F +++ b/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_unit_conversion diff --git a/src/core_seaice/column/Makefile b/src/core_seaice/column/Makefile index 822ecc1305..c938731957 100644 --- a/src/core_seaice/column/Makefile +++ b/src/core_seaice/column/Makefile @@ -28,7 +28,8 @@ OBJS = ice_colpkg.o \ ice_brine.o \ ice_algae.o \ ice_zbgc.o \ - ice_zbgc_shared.o + ice_zbgc_shared.o \ + ice_snow.o all: $(OBJS) @@ -40,7 +41,7 @@ ice_constants_colpkg.F90: cp constants/cice/ice_constants_colpkg.F90 . endif -ice_colpkg.o: ice_kinds_mod.o ice_constants_colpkg.o ice_warnings.o ice_colpkg_shared.o ice_therm_shared.o ice_orbital.o ice_atmo.o ice_age.o ice_firstyear.o ice_flux_colpkg.o ice_meltpond_cesm.o ice_meltpond_lvl.o ice_meltpond_topo.o ice_therm_vertical.o ice_itd.o ice_therm_itd.o ice_shortwave.o ice_mechred.o ice_colpkg_tracers.o ice_atmo.o ice_mushy_physics.o ice_zbgc.o ice_zbgc_shared.o ice_aerosol.o ice_algae.o ice_brine.o ice_zsalinity.o +ice_colpkg.o: ice_kinds_mod.o ice_constants_colpkg.o ice_warnings.o ice_colpkg_shared.o ice_therm_shared.o ice_orbital.o ice_atmo.o ice_age.o ice_firstyear.o ice_flux_colpkg.o ice_meltpond_cesm.o ice_meltpond_lvl.o ice_meltpond_topo.o ice_therm_vertical.o ice_itd.o ice_therm_itd.o ice_shortwave.o ice_mechred.o ice_colpkg_tracers.o ice_atmo.o ice_mushy_physics.o ice_zbgc.o ice_zbgc_shared.o ice_aerosol.o ice_algae.o ice_brine.o ice_zsalinity.o ice_snow.o ice_kinds_mod.o: @@ -90,7 +91,7 @@ ice_mechred.o: ice_kinds_mod.o ice_constants_colpkg.o ice_itd.o ice_colpkg_trace ice_aerosol.o: ice_kinds_mod.o ice_constants_colpkg.o ice_colpkg_shared.o ice_colpkg_tracers.o ice_zbgc_shared.o -ice_algae.o: ice_kinds_mod.o ice_constants_colpkg.o ice_colpkg_shared.o ice_colpkg_tracers.o ice_zbgc_shared.o ice_aerosol.o +ice_algae.o: ice_kinds_mod.o ice_constants_colpkg.o ice_colpkg_shared.o ice_colpkg_tracers.o ice_zbgc_shared.o ice_aerosol.o ice_warnings.o ice_brine.o: ice_kinds_mod.o ice_constants_colpkg.o ice_colpkg_shared.o ice_colpkg_tracers.o ice_zbgc_shared.o ice_therm_mushy.o ice_mushy_physics.o ice_therm_shared.o @@ -98,6 +99,8 @@ ice_zbgc.o: ice_kinds_mod.o ice_constants_colpkg.o ice_colpkg_shared.o ice_colpk ice_zbgc_shared.o: ice_kinds_mod.o ice_constants_colpkg.o ice_colpkg_shared.o +ice_snow.o: ice_kinds_mod.o ice_constants_colpkg.o ice_warnings.o ice_therm_vertical.o ice_colpkg_shared.o + .F90.o: $(RM) $@ $*.mod diff --git a/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 b/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 index c8adacab52..b1a73fd337 100644 --- a/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 +++ b/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 @@ -25,8 +25,13 @@ module ice_constants_colpkg rhoi = SHR_CONST_RHOICE ,&! density of ice (kg/m^3) rhow = SHR_CONST_RHOSW ,&! density of seawater (kg/m^3) cp_air = SHR_CONST_CPDAIR ,&! specific heat of air (J/kg/K) + ! (Briegleb JGR 97 11475-11485 July 1992) - emissivity= 0.95_dbl_kind ,&! emissivity of snow and ice + !emissivity= 0.95_dbl_kind ,&! emissivity of snow and ice + ! Emissivity has been changed to unity here so that coupling is + ! physically correct - instantaneous radiative coupling in CIME + emissivity= 1.0_dbl_kind ,&! emissivity of snow and ice + cp_ice = SHR_CONST_CPICE ,&! specific heat of fresh ice (J/kg/K) cp_ocn = SHR_CONST_CPSW ,&! specific heat of ocn (J/kg/K) ! freshwater value needed for enthalpy @@ -38,11 +43,12 @@ module ice_constants_colpkg dragio = 0.00536_dbl_kind ,&! ice-ocn drag coefficient #endif - albocn = 0.06_dbl_kind ,&! ocean albedo + albocn = 0.06_dbl_kind ,&! ocean albedo gravit = SHR_CONST_G ,&! gravitational acceleration (m/s^2) viscosity_dyn = 1.79e-3_dbl_kind, & ! dynamic viscosity of brine (kg/m/s) - Tocnfrz= -34.0_dbl_kind*depressT,&! freezing temp of seawater (C), - ! used as Tsfcn for open water + Tocnfrz = -1.8_dbl_kind ,&! freezing temp of seawater (C), used + ! as Tsfcn for open water only when + ! tfrz_option is 'minus1p8' or null rhofresh = SHR_CONST_RHOFW ,&! density of fresh water (kg/m^3) zvir = SHR_CONST_ZVIR ,&! rh2o/rair - 1.0 vonkar = SHR_CONST_KARMAN,&! von Karman constant @@ -80,12 +86,16 @@ module ice_constants_colpkg ksno = 0.30_dbl_kind ,&! thermal conductivity of snow (W/m/deg) zref = 10._dbl_kind ,&! reference height for stability (m) hs_min = 1.e-4_dbl_kind ,&! min snow thickness for computing zTsn (m) - snowpatch = 0.005_dbl_kind ! parameter for fractional snow area (m) + snowpatch = 0.005_dbl_kind , & ! parameter for fractional snow area (m) !tcx note cice snowpatch = 0.02 - integer (kind=int_kind), parameter :: & - nspint = 3 ! number of solar spectral intervals - + ! biogeochemistry + sk_l = 0.03_dbl_kind ! (m) skeletal layer thickness + + integer (kind=int_kind), parameter, public :: & + nspint = 3 ,& ! number of solar spectral intervals + nspint_5bd = 5 ! number of solar spectral intervals with config_use_snicar_ad + ! weights for albedos ! 4 Jan 2007 BPB Following are appropriate for complete cloud ! in a summer polar atmosphere with 1.5m bare sea ice surface: @@ -116,6 +126,12 @@ module ice_constants_colpkg real(kind=dbl_kind),public :: eccf ! earth orbit eccentricity factor logical(kind=log_kind),public :: log_print ! Flags print of status/error + ! snow parameters + real (kind=dbl_kind), parameter, public :: & + snwlvlfac = 0.3_dbl_kind, & ! 30% rule: fractional increase in snow depth + ! over ridged ice, compared with level ice + rhosmin = 100.0_dbl_kind ! minimum snow density (kg/m^3) + !----------------------------------------------------------------- ! numbers used in column package !----------------------------------------------------------------- diff --git a/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 b/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 index 0a6c705164..a42413cacc 100644 --- a/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 +++ b/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 @@ -26,6 +26,7 @@ module ice_constants_colpkg cp_air = 1005.0_dbl_kind ,&! specific heat of air (J/kg/K) ! (Briegleb JGR 97 11475-11485 July 1992) emissivity= 0.95_dbl_kind ,&! emissivity of snow and ice +!echmod emissivity= 0.985_dbl_kind ,&! emissivity of snow and ice cp_ice = 2106._dbl_kind ,&! specific heat of fresh ice (J/kg/K) cp_ocn = 4218._dbl_kind ,&! specific heat of ocn (J/kg/K) ! freshwater value needed for enthalpy @@ -34,8 +35,9 @@ module ice_constants_colpkg albocn = 0.06_dbl_kind ,&! ocean albedo gravit = 9.80616_dbl_kind ,&! gravitational acceleration (m/s^2) viscosity_dyn = 1.79e-3_dbl_kind, & ! dynamic viscosity of brine (kg/m/s) - Tocnfrz = -1.8_dbl_kind ,&! freezing temp of seawater (C), - ! used as Tsfcn for open water + Tocnfrz = -1.8_dbl_kind ,&! freezing temp of seawater (C), used + ! as Tsfcn for open water only when + ! tfrz_option is 'minus1p8' or null rhofresh = 1000.0_dbl_kind ,&! density of fresh water (kg/m^3) zvir = 0.606_dbl_kind ,&! rh2o/rair - 1.0 vonkar = 0.4_dbl_kind ,&! von Karman constant @@ -72,11 +74,15 @@ module ice_constants_colpkg ksno = 0.30_dbl_kind ,&! thermal conductivity of snow (W/m/deg) zref = 10._dbl_kind ,&! reference height for stability (m) hs_min = 1.e-4_dbl_kind ,&! min snow thickness for computing zTsn (m) - snowpatch = 0.02_dbl_kind ! parameter for fractional snow area (m) + snowpatch = 0.02_dbl_kind, & ! parameter for fractional snow area (m) + + ! biogeochemistry + sk_l = 0.03_dbl_kind ! (m) skeletal layer thickness integer (kind=int_kind), parameter, public :: & - nspint = 3 ! number of solar spectral intervals - + nspint = 3 ,& ! number of solar spectral intervals + nspint_5bd = 5 ! number of solar spectral intervals used in SNICAR + ! weights for albedos ! 4 Jan 2007 BPB Following are appropriate for complete cloud ! in a summer polar atmosphere with 1.5m bare sea ice surface: @@ -107,6 +113,12 @@ module ice_constants_colpkg real(kind=dbl_kind),public :: eccf ! earth orbit eccentricity factor logical(kind=log_kind),public :: log_print ! Flags print of status/error + ! snow parameters + real (kind=dbl_kind), parameter, public :: & + snwlvlfac = 0.3_dbl_kind, & ! 30% rule: fractional increase in snow depth + ! over ridged ice, compared with level ice + rhosmin = 100.0_dbl_kind ! minimum snow density (kg/m^3) + !----------------------------------------------------------------- ! numbers used in column package !----------------------------------------------------------------- diff --git a/src/core_seaice/column/ice_aerosol.F90 b/src/core_seaice/column/ice_aerosol.F90 index b5f5e8007d..c3e6a9d0bf 100644 --- a/src/core_seaice/column/ice_aerosol.F90 +++ b/src/core_seaice/column/ice_aerosol.F90 @@ -455,12 +455,13 @@ subroutine update_snow_bgc (dt, nblyr, & vice_old, vsno_old, & vicen, vsnon, & aicen, flux_bio_atm,& - zbgc_atm, flux_bio) + zbgc_atm, flux_bio, & + bio_index_o) - use ice_colpkg_shared, only: hi_ssl, hs_ssl + use ice_colpkg_shared, only: hi_ssl, hs_ssl, hs_ssl_min use ice_constants_colpkg, only: c0, rhos, rhoi, hs_min, puny, & - c2, c1 - use ice_zbgc_shared, only: kscavz + c2, c1, p5 + use ice_zbgc_shared, only: kscavz integer (kind=int_kind), intent(in) :: & nbtrcr, & ! number of distinct snow tracers @@ -469,7 +470,8 @@ subroutine update_snow_bgc (dt, nblyr, & ntrcr ! number of tracers integer (kind=int_kind), dimension (nbtrcr), intent(in) :: & - bio_index + bio_index, & + bio_index_o ! provides index of scavenging (kscavz) data array real (kind=dbl_kind), intent(in) :: & dt ! time step @@ -488,9 +490,11 @@ subroutine update_snow_bgc (dt, nblyr, & vice_old, & vsno_old - real (kind=dbl_kind),dimension(nbtrcr), intent(inout) :: & + real (kind=dbl_kind),dimension(nbtrcr), intent(out) :: & zbgc_snow, & ! aerosol contribution from snow to ice - zbgc_atm, & ! and atm to ice concentration * volume (kg or mmol/m^3*m) + zbgc_atm ! and atm to ice concentration * volume (kg or mmol/m^3*m) + + real (kind=dbl_kind),dimension(nbtrcr), intent(inout) :: & flux_bio ! total ocean tracer flux (mmol/m^2/s) real (kind=dbl_kind), dimension(nbtrcr), & @@ -508,6 +512,9 @@ subroutine update_snow_bgc (dt, nblyr, & real (kind=dbl_kind) :: & dzssl, dzssl_new, & ! snow ssl thickness dzint, dzint_new, & ! snow interior thickness + dz, & ! + hi, & ! ice thickness (m) + hilyr, & ! ice layer thickness (m) hs, & ! snow thickness (m) dhs_evap, & ! snow thickness change due to evap dhs_melts, & ! ... due to surface melt @@ -541,6 +548,11 @@ subroutine update_snow_bgc (dt, nblyr, & zbgc_atm(:) = c0 hs_old = vsno_old/aice_old + if (aice_old .gt. puny) then + hs_old = vsno_old/aice_old + else + hs_old = c0 + end if hslyr_old = hs_old/real(nslyr,kind=dbl_kind) dzssl = min(hslyr_old/c2, hs_ssl) @@ -549,40 +561,46 @@ subroutine update_snow_bgc (dt, nblyr, & if (aicen > c0) then ar = c1/aicen hs = vsnon*ar - dhs_melts = -melts - dhs_snoice = snoice*rhoi/rhos + hi = vicen*ar else ! ice disappeared during time step - ar = c1 - hs = vsnon/aice_old - dhs_melts = -melts - dhs_snoice = snoice*rhoi/rhos + ar = c1 + hs = c0 + hi = c0 + if (aice_old > c0) hs = vsnon/aice_old endif - + hilyr = hi/real(nblyr,kind=dbl_kind) + hslyr = hs/real(nslyr,kind=dbl_kind) + dzssl_new = min(hslyr/c2, hs_ssl) + dhs_melts = -melts + dhs_snoice = snoice*rhoi/rhos dhs_evap = hs - (hs_old + dhs_melts - dhs_snoice & + fsnow/rhos*dt) ! trcrn() has units kg/m^3 - if ((vsno_old .le. puny) .or. (vsnon .le. puny)) then - + if (dzssl_new .lt. hs_ssl_min) then ! Put atm BC/dust flux directly into the sea ice do k=1,nbtrcr flux_bio(k) = flux_bio(k) + & (trcrn(bio_index(k)+ nblyr+1)*dzssl+ & - trcrn(bio_index(k)+ nblyr+2)*dzint)/dt + trcrn(bio_index(k)+ nblyr+2)*dzint)/dt trcrn(bio_index(k) + nblyr+1) = c0 trcrn(bio_index(k) + nblyr+2) = c0 - zbgc_atm(k) = zbgc_atm(k) & - + flux_bio_atm(k)*dt + if (hilyr .lt. hs_ssl_min) then + flux_bio(k) = flux_bio(k) + flux_bio_atm(k) + else + zbgc_atm(k) = zbgc_atm(k) & + + flux_bio_atm(k)*dt + end if enddo - else - + else + do k=1,nbtrcr flux_bio_o(k) = flux_bio(k) aerosno (k,1) = trcrn(bio_index(k)+ nblyr+1) * dzssl aerosno (k,2) = trcrn(bio_index(k)+ nblyr+2) * dzint aerosno0(k,:) = aerosno(k,:) - aerotot0(k) = aerosno(k,2) + aerosno(k,1) + aerotot0(k) = aerosno(k,2) + aerosno(k,1) enddo !------------------------------------------------------------------- @@ -590,7 +608,47 @@ subroutine update_snow_bgc (dt, nblyr, & !------------------------------------------------------------------- dzint = dzint + min(dzssl + dhs_evap, c0) dzssl = max(dzssl + dhs_evap, c0) - + if (dzssl <= puny) then + do k = 1,nbtrcr + aerosno(k,2) = aerosno(k,2) + aerosno(k,1) + aerosno(k,1) = c0 + end do + end if + if (dzint <= puny) then + do k = 1,nbtrcr + flux_bio(k) = flux_bio(k) + (aerosno(k,2) + aerosno(k,1))/dt + aerosno(k,2) = c0 + aerosno(k,1) = c0 + end do + end if + !------------------------------------------------------------------ + ! snowfall + !------------------------------------------------------------------- + if (fsnow > c0) then + sloss1 = c0 + dz = min(fsnow/rhos*dt,dzssl) + do k = 1, nbtrcr + if (dzssl > puny) & + sloss1 = aerosno(k,1)*dz/dzssl + aerosno(k,1) = max(c0,aerosno(k,1) - sloss1) + aerosno(k,2) = aerosno(k,2) + sloss1 + end do + dzssl = dzssl - dz + fsnow/rhos*dt + dzint = dzint + dz + end if + if (dzssl <= puny) then + do k = 1,nbtrcr + aerosno(k,2) = aerosno(k,2) + aerosno(k,1) + aerosno(k,1) = c0 + end do + end if + if (dzint <= puny) then + do k = 1,nbtrcr + flux_bio(k) = flux_bio(k) + (aerosno(k,2) + aerosno(k,1))/dt + aerosno(k,2) = c0 + aerosno(k,1) = c0 + end do + end if !------------------------------------------------------------------- ! surface snow melt !------------------------------------------------------------------- @@ -598,39 +656,37 @@ subroutine update_snow_bgc (dt, nblyr, & do k = 1, nbtrcr sloss1 = c0 sloss2 = c0 - if (dzssl > puny) & - sloss1 = kscavz(k)*aerosno(k,1) & - *min(-dhs_melts,dzssl)/dzssl - aerosno(k,1) = aerosno(k,1) - sloss1 - if (dzint > puny) & - sloss2 = kscavz(k)*aerosno(k,2) & - *max(-dhs_melts-dzssl,c0)/dzint - aerosno(k,2) = aerosno(k,2) - sloss2 - zbgc_snow(k) = zbgc_snow(k) + (sloss1+sloss2) - enddo ! + if (dzssl > puny) & + sloss1 = kscavz(bio_index_o(k))*aerosno(k,1) & + *min(-dhs_melts,dzssl)/dzssl + aerosno(k,1) = max(c0,aerosno(k,1) - sloss1) + if (dzint > puny) & + sloss2 = kscavz(bio_index_o(k))*aerosno(k,2) & + *max(-dhs_melts-dzssl,c0)/dzint + aerosno(k,2) = max(c0,aerosno(k,2) - sloss2) + flux_bio(k) = flux_bio(k) + (sloss1+sloss2)/dt ! all not scavenged ends in ocean + enddo ! update snow thickness dzint=dzint+min(dzssl+dhs_melts, c0) dzssl=max(dzssl+dhs_melts, c0) - if ( dzssl <= puny ) then ! ssl melts away - aerosno(:,2) = aerosno(:,1) + aerosno(:,2) - aerosno(:,1) = c0 + if ( dzssl .le. puny ) then ! ssl melts away + do k = 1,nbtrcr + aerosno(k,2) = aerosno(k,1) + aerosno(k,2) + aerosno(k,1) = c0 + end do dzssl = max(dzssl, c0) endif - if (dzint <= puny ) then ! all snow melts away - zbgc_snow(:) = zbgc_snow(:) & - + max(c0,aerosno(:,1) + aerosno(:,2)) - aerosno(:,:) = c0 + if (dzint .le. puny ) then ! all snow melts away + do k = 1,nbtrcr + zbgc_snow(k) = zbgc_snow(k) & + + aerosno(k,1) + aerosno(k,2) + aerosno(k,:) = c0 + enddo dzint = max(dzint, c0) endif - endif - - !------------------------------------------------------------------- - ! snowfall - !------------------------------------------------------------------- - if (fsnow > c0) dzssl = dzssl + fsnow/rhos*dt - + endif ! -dhs_melts > puny !------------------------------------------------------------------- ! snow-ice formation !------------------------------------------------------------------- @@ -638,39 +694,46 @@ subroutine update_snow_bgc (dt, nblyr, & do k = 1, nbtrcr sloss1 = c0 sloss2 = c0 - if (dzint > puny) & - sloss2 = min(dhs_snoice, dzint) & - *aerosno(k,2)/dzint - aerosno(k,2) = aerosno(k,2) - sloss2 - if (dzssl > puny) & + if (dzint > puny .and. aerosno(k,2) > c0) & + sloss2 = min(dhs_snoice, dzint) & + *aerosno(k,2)/dzint + aerosno(k,2) = max(c0,aerosno(k,2) - sloss2) + if (dzssl > puny .and. aerosno(k,1) > c0) & sloss1 = max(dhs_snoice-dzint, c0) & *aerosno(k,1)/dzssl - aerosno(k,1) = aerosno(k,1) - sloss1 + + aerosno(k,1) = max(c0,aerosno(k,1) - sloss1) + flux_bio(k) = flux_bio(k) & + + kscavz(bio_index_o(k)) * (sloss2+sloss1)/dt zbgc_snow(k) = zbgc_snow(k) & - + (sloss2+sloss1) + + (c1-kscavz(bio_index_o(k)))*(sloss2+sloss1) enddo - dzssl = dzssl - max(dhs_snoice-dzint, c0) + dzssl = max(c0,dzssl - max(dhs_snoice-dzint, c0)) dzint = max(dzint-dhs_snoice, c0) - endif + endif ! dhs_snowice > puny !------------------------------------------------------------------- ! aerosol deposition !------------------------------------------------------------------- - if (aicen > c0) then - hs = vsnon * ar - else - hs = c0 - endif - if (hs >= hs_min) then !should this really be hs_min or 0? - ! should use same hs_min value as in radiation + ! if (aicen > c0) then + ! hs = vsnon * ar + ! else + ! hs = c0 + ! endif + ! Spread out the atm dust flux in the snow interior for small snow surface layers + if (dzssl .ge. hs_ssl*p5) then + do k=1,nbtrcr aerosno(k,1) = aerosno(k,1) & + flux_bio_atm(k)*dt enddo - else + else + dz = (hs_ssl*p5 - dzssl)/(hs_ssl*p5) do k=1,nbtrcr - zbgc_atm(k) = zbgc_atm(k) & - + flux_bio_atm(k)*dt + aerosno(k,1) = aerosno(k,1) & + + flux_bio_atm(k)*dt*(c1-dz) + aerosno(k,2) = aerosno(k,2) & + + flux_bio_atm(k)*dt*dz enddo endif @@ -690,30 +753,31 @@ subroutine update_snow_bgc (dt, nblyr, & endif if (dzint <= puny) then ! nothing in Snow Int do k = 1, nbtrcr - zbgc_snow(k) = zbgc_snow(k) + max(c0,aerosno(k,2)) + zbgc_snow(k) = zbgc_snow(k) + max(c0,aerosno(k,2)+aerosno(k,1)) + aerosno(k,1) = c0 aerosno(k,2) = c0 enddo endif hslyr = hs/real(nslyr,kind=dbl_kind) dzssl_new = min(hslyr/c2, hs_ssl) - dzint_new = hs - dzssl_new + dzint_new = max(c0,hs - dzssl_new) - if (hs > hs_min) then !should this really be hs_min or 0? + if (hs > hs_min) then do k = 1, nbtrcr dznew = min(dzssl_new-dzssl, c0) sloss1 = c0 - if (dzssl > puny) & + if (dzssl > puny .and. aerosno(k,1) > c0) & sloss1 = dznew*aerosno(k,1)/dzssl ! not neccesarily a loss - dznew = max(dzssl_new-dzssl, c0) - if (dzint > puny) & - sloss1 = sloss1 + aerosno(k,2)*dznew/dzint - aerosno(k,1) = aerosno(k,1) + sloss1 - aerosno(k,2) = aerosno(k,2) - sloss1 + dznew = max(dzssl_new-dzssl, c0) + if (dzint > puny .and. aerosno(k,2) > c0) & + sloss1 = aerosno(k,2)*dznew/dzint + aerosno(k,1) = max(c0,aerosno(k,1) + sloss1) + aerosno(k,2) = max(c0,aerosno(k,2) - sloss1) enddo else zbgc_snow(:) = zbgc_snow(:) & - + max(c0,aerosno(:,1) + aerosno(:,2)) + + aerosno(:,1) + aerosno(:,2) aerosno(:,:) = c0 endif @@ -722,11 +786,16 @@ subroutine update_snow_bgc (dt, nblyr, & !------------------------------------------------------------------- do k = 1, nbtrcr aerotot(k) = aerosno(k,2) + aerosno(k,1) & - + zbgc_snow(k) + zbgc_atm(k) + + zbgc_snow(k) + zbgc_atm(k) aero_cons(k) = aerotot(k)-aerotot0(k) & - - ( flux_bio_atm(k) & + - ( flux_bio_atm(k) & - (flux_bio(k)-flux_bio_o(k))) * dt - if (aero_cons(k) > puny .or. zbgc_snow(k) + zbgc_atm(k) < c0) then + if (aerotot0(k) > aerotot(k) .and. aerotot0(k) > c0) then + aero_cons(k) = aero_cons(k)/aerotot0(k) + else if (aerotot(k) > c0) then + aero_cons(k) = aero_cons(k)/aerotot(k) + end if + if (aero_cons(k) > puny .or. zbgc_snow(k) + zbgc_atm(k) < c0) then write(warning,*) 'Conservation failure: aerosols in snow' call add_warning(warning) write(warning,*) 'test aerosol 1' @@ -754,14 +823,13 @@ subroutine update_snow_bgc (dt, nblyr, & !------------------------------------------------------------------- ! reload tracers !------------------------------------------------------------------- - if (vsnon > puny) then + if (dzssl_new > puny .and. dzint_new > puny .and. vsnon > puny) then do k = 1,nbtrcr - trcrn(bio_index(k)+nblyr+1)=aerosno(k,1)/dzssl_new + trcrn(bio_index(k)+nblyr+1)=aerosno(k,1)/dzssl_new trcrn(bio_index(k)+nblyr+2)=aerosno(k,2)/dzint_new enddo else do k = 1,nbtrcr - zbgc_snow(k) = (zbgc_snow(k) + aerosno(k,1) + aerosno(k,2)) trcrn(bio_index(k)+nblyr+1)= c0 trcrn(bio_index(k)+nblyr+2)= c0 enddo diff --git a/src/core_seaice/column/ice_algae.F90 b/src/core_seaice/column/ice_algae.F90 index cb63dddf7a..1468fc9b0a 100644 --- a/src/core_seaice/column/ice_algae.F90 +++ b/src/core_seaice/column/ice_algae.F90 @@ -33,11 +33,11 @@ subroutine zbio (dt, nblyr, & snoice, nbtrcr, & fsnow, ntrcr, & trcrn, bio_index, & - aice_old, & + bio_index_o, aice_old, & vice_old, vsno_old, & vicen, vsnon, & - aicen, flux_bio_atm,& - n_cat, n_algae, & + aicen, flux_bio_atm,& + n_cat, n_algae, & n_doc, n_dic, & n_don, & n_fed, n_fep, & @@ -62,12 +62,13 @@ subroutine zbio (dt, nblyr, & PP_net, ice_bio_net, & snow_bio_net, grow_net, & totalChla, & + flux_bion, & l_stop, stop_label) use ice_aerosol, only: update_snow_bgc - use ice_constants_colpkg, only: c0, c1, puny + use ice_constants_colpkg, only: c0, c1, puny, p5 use ice_zbgc, only: merge_bgc_fluxes - + integer (kind=int_kind), intent(in) :: & nblyr, & ! number of bio layers nslyr, & ! number of snow layers @@ -80,7 +81,8 @@ subroutine zbio (dt, nblyr, & ntrcr ! number of tracers integer (kind=int_kind), dimension (nbtrcr), intent(in) :: & - bio_index + bio_index, & ! references index of bio tracer (nbtrcr) to tracer array (ntrcr) + bio_index_o ! references index of data arrays (eg. kscavz) real (kind=dbl_kind), intent(in) :: & dt, & ! time step @@ -109,14 +111,15 @@ subroutine zbio (dt, nblyr, & dh_top_chl, & ! change in brine top (m) felt by algae dh_direct ! surface flooding or surface runoff (m) - real (kind=dbl_kind), dimension (nbtrcr), intent(inout) :: & + real (kind=dbl_kind), dimension (:), intent(inout) :: & snow_bio_net,& ! net bio tracer in snow (mmol/m^2) ice_bio_net, & ! net bio tracer in ice (mmol/m^2) fbio_atmice, & ! bio flux from atm to ice (mmol/m^2/s) fbio_snoice, & ! bio flux from snow to ice (mmol/m^2/s) - flux_bio ! total ocean tracer flux (mmol/m^2/s) + flux_bio, & ! total ocean tracer flux (mmol/m^2/s) + flux_bion ! category ocean tracer flux (mmol/m^2/s) - real (kind=dbl_kind), intent(inout) :: & + real (kind=dbl_kind), intent(in) :: & hbri_old ! brine height (m) real (kind=dbl_kind), dimension (nblyr+2), intent(inout) :: & @@ -125,40 +128,40 @@ subroutine zbio (dt, nblyr, & real (kind=dbl_kind), dimension (nblyr+1), intent(in) :: & igrid , & ! biology vertical interface points iTin , & ! salinity vertical interface points - iphin , & ! Porosity on the igrid + iphin , & ! Porosity on the igrid iDin ! Diffusivity/h on the igrid (1/s) - + real (kind=dbl_kind), dimension (nilyr+1), intent(in) :: & - cgrid , & ! CICE vertical coordinate - icgrid , & ! CICE interface coordinate - fswthrul ! visible short wave radiation on icgrid (W/m^2) + cgrid , & ! CICE vertical coordinate + icgrid , & ! CICE interface coordinate + fswthrul ! visible short wave radiation on icgrid (W/m^2) - real (kind=dbl_kind), dimension(nbtrcr), & + real (kind=dbl_kind), dimension(:), & intent(in) :: & flux_bio_atm ! aerosol/bgc deposition rate (mmol/m^2 s) real (kind=dbl_kind), dimension(ntrcr), & intent(inout) :: & - trcrn + trcrn - real (kind=dbl_kind), dimension (nblyr+1), intent(inout) :: & - zfswin ! visible Short wave flux on igrid (W/m^2) - - real (kind=dbl_kind), dimension (nblyr+1), intent(inout) :: & + real (kind=dbl_kind), dimension (nblyr+1), intent(inout) :: & + zfswin ! visible Short wave flux on igrid (W/m^2) + + real (kind=dbl_kind), dimension (nblyr+1), intent(inout) :: & Zoo ! N losses to the system from reaction terms - ! (ie. zooplankton/bacteria) (mmol/m^3) + ! (ie. zooplankton/bacteria) (mmol/m^3) - real (kind=dbl_kind), dimension (nbtrcr), intent(in) :: & + real (kind=dbl_kind), dimension (nbtrcr), intent(in) :: & !change to inout when updating ocean fields - ocean_bio ! ocean concentrations (mmol/m^3) + ocean_bio ! ocean concentrations (mmol/m^3) real (kind=dbl_kind), dimension (nblyr+2), intent(in) :: & bphin ! Porosity on the bgrid - real (kind=dbl_kind), intent(inout):: & + real (kind=dbl_kind), intent(inout):: & PP_net , & ! net PP (mg C/m^2/d) times aice grow_net , & ! net specific growth (m/d) times vice - upNO , & ! tot nitrate uptake rate (mmol/m^2/d) times aice + upNO , & ! tot nitrate uptake rate (mmol/m^2/d) times aice upNH , & ! tot ammonium uptake rate (mmol/m^2/d) times aice totalChla ! total chla (mg chla/m^2) @@ -181,9 +184,6 @@ subroutine zbio (dt, nblyr, & upNHn , & ! algal ammonium uptake rate (mmol/m^3/s) grow_alg ! algal growth rate (mmol/m^3/s) - real (kind=dbl_kind), dimension (nbtrcr) :: & - flux_bion !tracer flux to ocean - real (kind=dbl_kind),dimension(nbtrcr) :: & zbgc_snown, & ! aerosol contribution from snow to ice zbgc_atmn ! and atm to ice concentration * volume (mmol/m^3*m) @@ -194,9 +194,15 @@ subroutine zbio (dt, nblyr, & flux_bio_sno ! real (kind=dbl_kind) :: & - Tot_Nit, & ! - hsnow_i, & ! initial snow thickness (m) - hsnow_f ! final snow thickness (m) + Tot_Nit, & ! + hsnow_i, & ! initial snow thickness (m) + hsnow_f, & ! final snow thickness (m) + carbonError ! carbon conservation error (mmol/m2) + + real (kind=dbl_kind) :: & + carbonInitial, & ! initial carbon content (mmol/m2) + carbonFinal, & ! final carbon content (mmol/m2) + carbonFlux ! carbon flux (mmol/m2/s) logical (kind=log_kind) :: & write_flux_diag @@ -204,8 +210,18 @@ subroutine zbio (dt, nblyr, & real (kind=dbl_kind) :: & a_ice + real (kind=dbl_kind), parameter :: & + accuracy = 1.0e-13_dbl_kind + character(len=char_len_long) :: & - warning + warning + + real (kind=dbl_kind), dimension (nblyr+1) :: & + zspace ! vertical grid spacing + + zspace(:) = c1/real(nblyr,kind=dbl_kind) + zspace(1) = p5*zspace(1) + zspace(nblyr+1) = p5*zspace(nblyr+1) zbgc_snown(:) = c0 zbgc_atmn (:) = c0 @@ -213,21 +229,22 @@ subroutine zbio (dt, nblyr, & flux_bio_sno(:) = c0 Tot_BGC_i (:) = c0 Tot_BGC_f (:) = c0 + Zoo (:) = c0 hsnow_i = c0 hsnow_f = c0 write_flux_diag = .false. - - if (write_flux_diag) then - if (aice_old > c0) then - hsnow_i = vsno_old/aice_old - do mm = 1,nbtrcr - call bgc_column_sum (nblyr, nslyr, hsnow_i, hbri_old, & + + call bgc_carbon_sum(nblyr, hbri_old, trcrn(:), carbonInitial,n_doc,n_dic,n_algae,n_don) + + if (aice_old > puny) then + hsnow_i = vsno_old/aice_old + do mm = 1,nbtrcr + call bgc_column_sum (nblyr, nslyr, hsnow_i, hbri_old, & trcrn(bio_index(mm):bio_index(mm)+nblyr+2), & Tot_BGC_i(mm)) - enddo - endif + enddo endif - + call update_snow_bgc (dt, nblyr, & nslyr, & meltt, melts, & @@ -239,25 +256,26 @@ subroutine zbio (dt, nblyr, & vice_old, vsno_old, & vicen, vsnon, & aicen, flux_bio_atm, & - zbgc_atmn, flux_bio_sno) + zbgc_atmn, flux_bio_sno, & + bio_index_o) call z_biogeochemistry (n_cat, dt, & nilyr, nslyr, & nblyr, nbtrcr, & - n_algae, n_doc, & + n_algae, n_doc, & n_dic, n_don, & n_fed, n_fep, & n_zaero, first_ice, & - aicen, vicen, & - hice_old, ocean_bio, & + aicen, vicen, & + hice_old, ocean_bio, & flux_bion, bphin, & - iphin, trcrn, & + iphin, trcrn, & iDin, sss, & fswthrul, grow_alg, & upNOn, upNHn, & dh_top, dh_bot, & dh_top_chl, dh_bot_chl,& - zfswin, hbri, & + zfswin, hbri, & hbri_old, darcy_V, & darcy_V_chl, bgrid, & igrid, icgrid, & @@ -267,14 +285,39 @@ subroutine zbio (dt, nblyr, & Zoo, meltb, & congel, l_stop, & stop_label) - + do mm = 1,nbtrcr flux_bion(mm) = flux_bion(mm) + flux_bio_sno(mm) enddo - if (write_flux_diag) then - if (aicen > c0) then + call bgc_carbon_sum(nblyr, hbri, trcrn(:), carbonFinal,n_doc,n_dic,n_algae,n_don) + call bgc_carbon_flux(flux_bio_atm,flux_bion,n_doc,n_dic,n_algae,n_don,carbonFlux) + + carbonError = carbonInitial-carbonFlux*dt-carbonFinal + + if (abs(carbonError) > accuracy * maxval ((/carbonInitial, carbonFinal/))) then + write(warning,*) 'carbonError:', carbonError + call add_warning(warning) + write(warning,*) 'carbonInitial:', carbonInitial + call add_warning(warning) + write(warning,*) 'carbonFinal:', carbonFinal + call add_warning(warning) + write(warning,*) 'carbonFlux (positive into ocean):', carbonFlux + call add_warning(warning) + write(warning,*) 'accuracy * maxval ((/carbonInitial, carbonFinal/:)', accuracy * maxval ((/carbonInitial, carbonFinal/)) + call add_warning(warning) + if (aicen > c0) then hsnow_f = vsnon/aicen + write(warning,*) 'after z_biogeochemistry' + call add_warning(warning) + write(warning,*) 'Remaining carbon after algal_dyn: Zoo' + call add_warning(warning) + do mm = 1,nblyr+1 + write(warning,*) 'layer mm, Zoo(mm)' + call add_warning(warning) + write(warning,*) mm,Zoo(mm) + call add_warning(warning) + end do do mm = 1,nbtrcr call bgc_column_sum (nblyr, nslyr, hsnow_f, hbri, & trcrn(bio_index(mm):bio_index(mm)+nblyr+2), & @@ -295,6 +338,8 @@ subroutine zbio (dt, nblyr, & call add_warning(warning) write(warning,*) Tot_BGC_i(mm) + flux_bio_atm(mm)*dt - flux_bion(mm)*dt call add_warning(warning) + !l_stop = .true. + !stop_label = "carbon conservation in ice_algae.F90" enddo endif endif @@ -303,7 +348,7 @@ subroutine zbio (dt, nblyr, & call merge_bgc_fluxes (dt, nblyr, & bio_index, n_algae, & - nbtrcr, aicen, & + nbtrcr, aicen, & vicen, vsnon, & ntrcr, iphin, & trcrn, & @@ -314,8 +359,9 @@ subroutine zbio (dt, nblyr, & fbio_snoice, fbio_atmice,& PP_net, ice_bio_net,& snow_bio_net, grow_alg, & - grow_net, totalChla) - + grow_net, totalChla, & + nslyr) + if (write_flux_diag) then if (aicen > c0) then if (n_cat .eq. 1) a_ice = c0 @@ -337,12 +383,12 @@ subroutine zbio (dt, nblyr, & endif endif - end subroutine zbio + end subroutine zbio !======================================================================= - subroutine sklbio (dt, ntrcr, & - nilyr, & + subroutine sklbio (dt, Tf, & + ntrcr, nilyr, & nbtrcr, n_algae, & n_zaero, n_doc, & n_dic, n_don, & @@ -375,6 +421,7 @@ subroutine sklbio (dt, ntrcr, & real (kind=dbl_kind), intent(in) :: & dt, & ! time step + Tf, & ! basal freezing temperature (C) hmix, & ! mixed layer depth (m) aicen, & ! ice area fraction meltb, & ! bottom melt (m) @@ -428,8 +475,8 @@ subroutine sklbio (dt, ntrcr, & fswthru, first_ice, & trcrn, upNOn, & upNHn, grow_alg, & - hin, l_stop, & - stop_label) + hin, Tf, & + l_stop, stop_label) if (l_stop) return @@ -459,12 +506,12 @@ subroutine skl_biogeochemistry (dt, nilyr, & fswthru, first_ice, & trcrn, upNOn, & upNHn, grow_alg_skl, & - hin, l_stop, & - stop_label) + hin, Tf, & + l_stop, stop_label) - use ice_constants_colpkg, only: p5, p05, p1, c1, c0, puny, c10 + use ice_constants_colpkg, only: p5, p05, p1, c1, c0, puny, c10, sk_l use ice_colpkg_tracers, only: nt_bgc_N, ntrcr, bio_index - use ice_colpkg_shared, only: dEdd_algae, bgc_flux_type, sk_l, R_chl2N + use ice_colpkg_shared, only: dEdd_algae, bgc_flux_type, R_chl2N integer (kind=int_kind), intent(in) :: & nilyr , & ! number of ice layers @@ -478,6 +525,7 @@ subroutine skl_biogeochemistry (dt, nilyr, & aicen , & ! ice area meltb , & ! bottom ice melt congel , & ! bottom ice growth + Tf , & ! bottom freezing temperature fswthru ! shortwave passing through ice to ocean logical (kind=log_kind), intent(in) :: & @@ -527,20 +575,20 @@ subroutine skl_biogeochemistry (dt, nilyr, & grow_val , & ! (m/x) rphi_sk , & ! 1 / skeletal layer porosity cinit_tmp , & ! temporary variable for concentration (mmol/m^2) - Nerror ! change in total nitrogen from reactions + Cerror , & ! change in total carbon from reactions (mmol/m^3) + nitrification ! nitrate from nitrification (mmol/m^3) real (kind=dbl_kind), parameter :: & PVc = 1.e-6_dbl_kind , & ! type 'constant' piston velocity for interface (m/s) PV_scale_growth = p5 , & ! scale factor in Jin code PV during ice growth PV_scale_melt = p05 , & ! scale factor in Jin code PV during ice melt growth_max = 1.85e-10_dbl_kind , & ! PVt function reaches maximum here. (m/s) - Tin_bot = -1.8_dbl_kind , & ! temperature of the ice bottom (oC) MJ1 = 9.667e-9_dbl_kind , & ! (m/s) coefficients in Jin2008 MJ2 = 38.8_dbl_kind , & ! (1) from:4.49e-4_dbl_kind*secday MJ3 = 1.04e7_dbl_kind , & ! 1/(m/s) from: 1.39e-3_dbl_kind*secday^2 PV_frac_max = 0.9_dbl_kind ! Maximum Piston velocity is 90% of skeletal layer/dt - logical (kind=log_kind) :: conserve_N + logical (kind=log_kind) :: conserve_C character(len=char_len_long) :: & warning ! warning message @@ -550,11 +598,11 @@ subroutine skl_biogeochemistry (dt, nilyr, & !----------------------------------------------------------------- l_stop = .false. - conserve_N = .true. + conserve_C = .true. Zoo_skl = c0 rphi_sk = c1/phi_sk PVt = c0 - iTin = Tin_bot + iTin = Tf do nn = 1, nbtrcr cinit (nn) = c0 @@ -564,6 +612,7 @@ subroutine skl_biogeochemistry (dt, nilyr, & react (nn) = c0 PVflag (nn) = c1 cling (nn) = c0 + nitrification = c0 !----------------------------------------------------------------- ! only the dominant tracer_type affects behavior @@ -577,9 +626,6 @@ subroutine skl_biogeochemistry (dt, nilyr, & endif ice_growth = (congel-meltb)/dt - if (first_ice) then - trcrn(bio_index(nn)) = ocean_bio(nn) ! * sk_l*rphi_sk - endif ! first_ice cinit (nn) = trcrn(bio_index(nn)) * sk_l * rphi_sk cinit_v(nn) = cinit(nn)/sk_l if (cinit(nn) < c0) then @@ -661,7 +707,8 @@ subroutine skl_biogeochemistry (dt, nilyr, & iTin, & upNOn, upNHn, & Zoo_skl, & - Nerror, conserve_N) + Cerror, conserve_C,& + nitrification) !----------------------------------------------------------------- ! compute new tracer concencentrations @@ -691,8 +738,8 @@ subroutine skl_biogeochemistry (dt, nilyr, & ! Currently not coupled with ocean biogeochemistry ! ocean_bio(nn) = ocean_bio(nn) + flux_bio(nn)/hmix*aicen - if (.not. conserve_N) then - write(warning,*) 'N not conserved in skl_bgc, Nerror:',Nerror + if (.not. conserve_C) then + write(warning,*) 'C not conserved in skl_bgc, Cerror:',Cerror call add_warning(warning) write(warning,*) 'sk_bgc < 0 after algal fluxes, nn,cinit,flux_bio',& nn,cinit(nn),flux_bio(nn) @@ -735,14 +782,14 @@ end subroutine skl_biogeochemistry !======================================================================= ! -! Solve the scalar vertical diffusion equation implicitly using +! Solve the scalar vertical diffusion equation implicitly using ! tridiag_solver. Calculate the diffusivity from temperature and salinity. -! -! NOTE: In this subroutine, trcrn(nt_fbri) is the volume fraction of ice with -! dynamic salinity or the height ratio == hinS/vicen*aicen, where hinS is the +! +! NOTE: In this subroutine, trcrn(nt_fbri) is the volume fraction of ice with +! dynamic salinity or the height ratio == hinS/vicen*aicen, where hinS is the ! height of the brine surface relative to the bottom of the ice. This volume fraction -! may be > 1 in which case there is brine above the ice surface (meltponds). -! +! may be > 1 in which case there is brine above the ice surface (meltponds). +! subroutine z_biogeochemistry (n_cat, dt, & nilyr, nslyr, & @@ -751,16 +798,16 @@ subroutine z_biogeochemistry (n_cat, dt, & n_dic, n_don, & n_fed, n_fep, & n_zaero, first_ice, & - aicen, vicen, & - hice_old, ocean_bio, & + aicen, vicen, & + hice_old, ocean_bio, & flux_bio, bphin, & - iphin, trcrn, & + iphin, trcrn, & iDin, sss, & fswthrul, grow_alg, & upNOn, upNHn, & dh_top, dh_bot, & dh_top_chl, dh_bot_chl,& - zfswin, hbri, & + zfswin, hbri, & hbri_old, darcy_V, & darcy_V_chl, bgrid, & i_grid, ic_grid, & @@ -768,14 +815,14 @@ subroutine z_biogeochemistry (n_cat, dt, & dhice, zbgc_atm, & iTin, dh_direct, & Zoo, meltb, & - congel, l_stop, & + congel, l_stop, & stop_label) use ice_colpkg_tracers, only: nt_fbri, nt_zbgc_frac, & ntrcr, nlt_bgc_Nit, tr_bgc_Fe, tr_zaero, & nlt_bgc_Fed, nlt_zaero, bio_index, tr_bgc_N, & - nlt_bgc_N - use ice_constants_colpkg, only: c0, c1, c2, p5, puny, pi + nlt_bgc_N, tr_bgc_C, nlt_bgc_DIC + use ice_constants_colpkg, only: c0, c1, c2, p5, puny, pi, p1 use ice_colpkg_shared, only: hi_ssl, dEdd_algae, solve_zbgc, & R_dFe2dust, dustFe_sol, algal_vel @@ -787,12 +834,12 @@ subroutine z_biogeochemistry (n_cat, dt, & nbtrcr, n_algae, & ! number of bgc tracers, number of autotrophs n_zaero, & ! number of aerosols n_doc, n_dic, n_don, n_fed, n_fep - + logical (kind=log_kind), intent(in) :: & first_ice ! initialized values should be used real (kind=dbl_kind), intent(in) :: & - dt , & ! time step + dt , & ! time step hbri , & ! brine height (m) dhice , & ! change due to sublimation/condensation (m) bphi_min , & ! surface porosity @@ -813,24 +860,24 @@ subroutine z_biogeochemistry (n_cat, dt, & real (kind=dbl_kind), dimension (:), intent(inout) :: & bgrid , & ! biology nondimensional vertical grid points flux_bio , & ! total ocean tracer flux (mmol/m^2/s) - zfswin , & ! visible Short wave flux on igrid (W/m^2) + zfswin , & ! visible Short wave flux on igrid (W/m^2) Zoo , & ! N losses to the system from reaction terms - ! (ie. zooplankton/bacteria) (mmol/m^3) + ! (ie. zooplankton/bacteria) (mmol/m^3) trcrn ! bulk tracer concentration (mmol/m^3) real (kind=dbl_kind), dimension (:), intent(in) :: & i_grid , & ! biology vertical interface points iTin , & ! salinity vertical interface points - iphin , & ! Porosity on the igrid + iphin , & ! Porosity on the igrid iDin , & ! Diffusivity/h on the igrid (1/s) - ic_grid , & ! CICE interface coordinate - fswthrul , & ! visible short wave radiation on icgrid (W/m^2) + ic_grid , & ! CICE interface coordinate + fswthrul , & ! visible short wave radiation on icgrid (W/m^2) zbgc_snow , & ! tracer input from snow (mmol/m^3*m) zbgc_atm , & ! tracer input from atm (mmol/m^3 *m) - ocean_bio , & ! ocean concentrations (mmol/m^3) + ocean_bio , & ! ocean concentrations (mmol/m^3) bphin ! Porosity on the bgrid - real (kind=dbl_kind), intent(inout) :: & + real (kind=dbl_kind), intent(in) :: & hbri_old ! brine height (m) real (kind=dbl_kind), dimension (:,:), intent(out) :: & @@ -853,15 +900,16 @@ subroutine z_biogeochemistry (n_cat, dt, & ! local variables integer (kind=int_kind) :: & - k, m, mm, nn ! vertical biology layer index + k, m, mm, nn ! vertical biology layer index real (kind=dbl_kind) :: & - hin , & ! ice thickness (m) + hin , & ! ice thickness (m) hin_old , & ! ice thickness before current melt/growth (m) ice_conc , & ! algal concentration in ice above hin > hinS sum_old , & ! sum_new , & ! sum_tot , & ! + sum_initial , & ! zspace , & ! 1/nblyr darcyV , & ! dhtop , & ! @@ -871,7 +919,7 @@ subroutine z_biogeochemistry (n_cat, dt, & dhflood ! >=0 (m) surface flooding from the ocean real (kind=dbl_kind), dimension (nblyr+2) :: & - bphin_N ! porosity for tracer model has minimum + bphin_N ! porosity for tracer model has minimum ! bphin_N >= bphimin real (kind=dbl_kind), dimension (nblyr+1) :: & @@ -884,7 +932,7 @@ subroutine z_biogeochemistry (n_cat, dt, & D_spdiag , & ! artificial diffusion matrix D_sbdiag , & ! artificial diffusion matrix biomat_low , & ! Low order solution - Nerror ! Change in N after reactions + Cerror ! Change in N after reactions real (kind=dbl_kind), dimension(nblyr+1,nbtrcr):: & react ! biological sources and sinks for equation matrix @@ -915,21 +963,23 @@ subroutine z_biogeochemistry (n_cat, dt, & trtmp ! temporary, remapped tracers logical (kind=log_kind), dimension(nblyr+1) :: & - conserve_N + conserve_C real (kind=dbl_kind), dimension(nblyr+1):: & ! temporary variables for - Diff , & ! diffusivity + Diff , & ! diffusivity initcons , & ! initial concentration biocons , & ! new concentration dmobile , & ! initcons_mobile,&! - initcons_stationary - + initcons_stationary, & + dz , & ! normalized vertical grid spacing + nitrification ! nitrate produced from nitrification (mmol/m3) + real (kind=dbl_kind), dimension (nilyr+1):: & icegrid ! correct for large ice surface layers real (kind=dbl_kind):: & - top_conc ! 1% (min_bgc) of surface concentration + top_conc ! 1% (min_bgc) of surface concentration ! when hin > hbri: just used in sw calculation real (kind=dbl_kind):: & @@ -943,34 +993,39 @@ subroutine z_biogeochemistry (n_cat, dt, & V_c , & ! volume of collector (um^3) V_alg ! volume of algae (um^3) - real (kind=dbl_kind), dimension(nbtrcr) :: & - mobile ! c1 if mobile, c0 otherwise + real (kind=dbl_kind), dimension(nbtrcr) :: & + mobile ! c0 if mobile, c1 otherwise ! local parameters - + real (kind=dbl_kind), parameter :: & accuracy = 1.0e-14_dbl_kind, & r_c = 3.0e3_dbl_kind , & ! ice crystal radius (um) - r_bac= 15.0_dbl_kind , & ! diatom large radius (um) + r_bac= 4.7_dbl_kind , & ! diatom large radius (um) r_alg= 10.0_dbl_kind , & ! diatom small radius (um) - N_vol = 0.04e-12_dbl_kind , & ! (g) Nitrogen per um^3 - Ng_to_mmol =0.0140067_dbl_kind , & ! (g/mmol) Nitrogen - f_s = c1 , & ! fracton of sites available for saturation - f_a = c1 , & ! fraction of collector available for attachment - f_v = 0.7854 ! fraction of algal coverage on area availabel for attachment 4(pi r^2)/(4r)^2 [Johnson et al, 1995, water res. research] - + Nquota_A = 0.88_dbl_kind, & ! slope in Nitrogen quota to cell volume fit + ! (Lomas et al. 2019, Edwards et al. 2012) + Nquota_I = 0.0408_dbl_kind, & ! Intercept in N quota to cell volume fit + f_s = p1, & ! fracton of sites available for saturation + f_a = 0.3_dbl_kind, & !c1 , & ! fraction of collector available for attachment + f_v = 0.7854 ! fraction of algal coverage on area availabel for attachment + ! 4(pi r^2)/(4r)^2 [Johnson et al, 1995, water res. research] + integer, parameter :: & nt_zfswin = 1 ! for interpolation of short wave to bgrid character(len=char_len_long) :: & - warning ! warning message + warning ! warning message !------------------------------------- - ! Initialize - !----------------------------------- + ! Initialize + !----------------------------------- l_stop = .false. zspace = c1/real(nblyr,kind=dbl_kind) + dz(:) = zspace + dz(1) = zspace/c2 + dz(nblyr+1) = zspace/c2 in_init_cons(:,:) = c0 atm_add_cons(:) = c0 sum_react(:) = c0 @@ -979,7 +1034,8 @@ subroutine z_biogeochemistry (n_cat, dt, & darcyV = c0 C_top(:) = c0 mobile(:) = c0 - conserve_N(:) = .true. + conserve_C(:) = .true. + nitrification(:) = c0 do m = 1, nbtrcr do k = 1, nblyr+1 @@ -989,22 +1045,20 @@ subroutine z_biogeochemistry (n_cat, dt, & iphin_N(k) = iphin(k) bphin_N(1) = bphi_min - if (first_ice) then - trcrn(bio_index(m) + k-1) = ocean_bio(m)*zbgc_init_frac(m) - in_init_cons(k,m) = trcrn(bio_index(m) + k-1)*hbri_old - elseif (abs(trcrn(bio_index(m) + k-1)) < puny) then + if (abs(trcrn(bio_index(m) + k-1)) < puny) then + flux_bio(m) = flux_bio(m) + trcrn(bio_index(m) + k-1)* hbri_old * dz(k)/dt trcrn(bio_index(m) + k-1) = c0 in_init_cons(k,m) = c0 else in_init_cons(k,m) = trcrn(bio_index(m) + k-1)* hbri_old - endif ! first_ice + endif if (trcrn(bio_index(m) + k-1) < c0 ) then write(warning,*)'zbgc initialization error, first ice = ', first_ice call add_warning(warning) write(warning,*)'Category,m:',n_cat,m call add_warning(warning) - write(warning,*)'hbri,hbri_old' + write(warning,*)'hbri,hbri_old' call add_warning(warning) write(warning,*) hbri,hbri_old call add_warning(warning) @@ -1014,7 +1068,7 @@ subroutine z_biogeochemistry (n_cat, dt, & call add_warning(warning) l_stop = .true. stop_label = 'zbgc initialization error' - endif + endif if (l_stop) return enddo !k enddo !m @@ -1025,7 +1079,7 @@ subroutine z_biogeochemistry (n_cat, dt, & ice_conc = c0 hin = vicen/aicen - hin_old = hice_old + hin_old = hice_old !----------------------------------------------------------------- ! calculate the saturation concentration for attachment: Sat_conc @@ -1034,13 +1088,13 @@ subroutine z_biogeochemistry (n_cat, dt, & phi_max = maxval(bphin_N(2:nblyr+1)) S_col = 4.0_dbl_kind*pi*r_c**2 P_b = pi*r_bac**2 !*10-6 for colloids - V_c = 4.0_dbl_kind*pi*r_c**3/3.0_dbl_kind*(1.0e-6_dbl_kind)**3 ! (m^3) sphere + V_c = 4.0_dbl_kind*pi*r_c**3/3.0_dbl_kind !*(1.0e-6_dbl_kind)**3 (m^3) sphere V_alg = pi/6.0_dbl_kind*r_bac*r_alg**2 ! prolate spheroid (*10-9 for colloids) - Sat_conc= f_s*f_a*f_v*(c1-phi_max)/V_c*S_col/P_b*N_vol*V_alg/Ng_to_mmol - !mmol/m^3 (algae, don, hum...) and umols/m^3 for colloids + Sat_conc= f_s*f_a*f_v*(c1-phi_max)/V_c*S_col/P_b*(V_alg)**Nquota_A*Nquota_I * 1.0e9_dbl_kind + !mmol/m^3 (algae, don, hum...) and umols/m^3 for colloids !----------------------------------------------------------------- - ! convert surface dust flux (n_zaero > 2) to dFe(1) flux + ! convert surface dust flux (n_zaero > 2) to dFe(1) flux !----------------------------------------------------------------- dust_Fe(:) = c0 @@ -1052,22 +1106,22 @@ subroutine z_biogeochemistry (n_cat, dt, & R_dFe2dust * dustFe_sol ! dust_Fe(nlt_zaero(m)) = -(zbgc_snow(nlt_zaero(m)) + zbgc_atm(nlt_zaero(m))) * & ! dustFe_sol - enddo + enddo endif - do m = 1,nbtrcr + do m = 1,nbtrcr !----------------------------------------------------------------- ! time constants for mobile/stationary phase changes !----------------------------------------------------------------- - - if (m .ne. nlt_bgc_N(1)) then + + if (m .ne. nlt_bgc_N(1)) then if (hin_old > hin) then !melting rtau_rel(m) = c1/tau_rel(m) rtau_ret(m) = c0 else !not melting rtau_ret(m) = c1/tau_ret(m) rtau_rel(m) = c0 - endif + endif elseif (tr_bgc_N .and. hin_old > hin + algal_vel*dt) then rtau_rel(m) = c1/tau_rel(m) rtau_ret(m) = c0 @@ -1082,38 +1136,38 @@ subroutine z_biogeochemistry (n_cat, dt, & darcyV = darcy_V C_top(m) = in_init_cons(1,m)*trcrn(nt_zbgc_frac+m-1)!mobile fraction source(m) = abs(zbgc_snow(m) + zbgc_atm(m) + dust_Fe(m)) - dhflood = max(c0,-dh_direct) ! ocean water flooding surface - dhrunoff = max(c0,dh_direct) - - if (dhtop+darcyV/bphin_N(1)*dt < -puny) then !snow/top ice melt - C_top(m) = (zbgc_snow(m)+zbgc_atm(m) + dust_Fe(m))/abs(dhtop & - + darcyV/bphin_N(1)*dt + puny)*hbri_old - elseif (dhtop+darcyV/bphin_N(1)*dt >= -puny .and. & - abs((zbgc_snow(m)+zbgc_atm(m) + dust_Fe(m)) + & - ocean_bio(m)*bphin_N(1)*dhflood) > puny) then - atm_add_cons(m) = abs(zbgc_snow(m) + zbgc_atm(m)+ dust_Fe(m)) + & - ocean_bio(m)*bphin_N(1)*dhflood - else ! only positive fluxes + dhflood = max(c0,-dh_direct) ! ocean water flooding surface + dhrunoff = max(c0,dh_direct) + + if (dhtop+darcyV/bphin_N(1)*dt < -puny) then !snow/top ice melt + C_top(m) = (zbgc_snow(m)+zbgc_atm(m) + dust_Fe(m))/abs(dhtop & + + darcyV/bphin_N(1)*dt + puny)*hbri_old + elseif (dhtop+darcyV/bphin_N(1)*dt >= -puny .and. & + abs((zbgc_snow(m)+zbgc_atm(m) + dust_Fe(m)) + & + ocean_bio(m)*bphin_N(1)*dhflood) > puny) then + atm_add_cons(m) = abs(zbgc_snow(m) + zbgc_atm(m)+ dust_Fe(m)) + & + ocean_bio(m)*bphin_N(1)*dhflood + else ! only positive fluxes atm_add_cons(m) = abs(zbgc_snow(m) + zbgc_atm(m)+ dust_Fe(m)) - endif + endif - C_bot(m) = ocean_bio(m)*hbri_old*iphin_N(nblyr+1) + C_bot(m) = ocean_bio(m)*hbri_old*iphin_N(nblyr+1) enddo ! m !----------------------------------------------------------------- - ! Interpolate shortwave flux, fswthrul (defined at top to bottom with nilyr+1 + ! Interpolate shortwave flux, fswthrul (defined at top to bottom with nilyr+1 ! evenly spaced with spacing = (1/nilyr) to grid variable zfswin: !----------------------------------------------------------------- - trtmp(:) = c0 + trtmp(:) = c0 trtmp0(:)= c0 zfswin(:) = c0 do k = 1, nilyr+1 ! contains cice values (fswthrul(1) is surface value) ! and fwsthrul(nilyr+1) is output - trtmp0(nt_zfswin+k-1) = fswthrul(k) + trtmp0(nt_zfswin+k-1) = fswthrul(k) enddo !k call remap_zbgc(ntrcr, nilyr+1, & @@ -1123,7 +1177,7 @@ subroutine z_biogeochemistry (n_cat, dt, & hin, hbri, & ic_grid(1:nilyr+1), & i_grid(1:nblyr+1),ice_conc, & - l_stop, stop_label) + l_stop, stop_label) if (l_stop) return @@ -1131,8 +1185,8 @@ subroutine z_biogeochemistry (n_cat, dt, & zfswin(k) = trtmp(nt_zfswin+k-1) enddo !----------------------------------------------------------------- - ! Initialze Biology - !----------------------------------------------------------------- + ! Initialze Biology + !----------------------------------------------------------------- do mm = 1, nbtrcr mobile(mm) = c0 @@ -1145,31 +1199,34 @@ subroutine z_biogeochemistry (n_cat, dt, & !----------------------------------------------------------------- ! Compute FCT - !----------------------------------------------------------------- + !----------------------------------------------------------------- - do mm = 1, nbtrcr + do mm = 1, nbtrcr - if (hbri_old > thinS .and. hbri > thinS) then + if (hbri_old > thinS .and. hbri > thinS) then do k = 1,nblyr+1 initcons_mobile(k) = in_init_cons(k,mm)*trcrn(nt_zbgc_frac+mm-1) - initcons_stationary(k) = mobile(mm)*(in_init_cons(k,mm)-initcons_mobile(k)) + initcons_stationary(k) = max(c0,in_init_cons(k,mm)-initcons_mobile(k)) + +! allow release of Nitrate/silicate, but not adsorption * dmobile(k) = mobile(mm)*(initcons_mobile(k)*(exp(-dt*rtau_ret( mm))-c1) + & - initcons_stationary(k)*(c1-exp(-dt*rtau_rel(mm)))) + initcons_stationary(k)*(c1-exp(-dt*rtau_rel(mm)))) + & + (1-mobile(mm))*initcons_stationary(k)*(c1-exp(-dt*rtau_rel(mm))) initcons_mobile(k) = max(c0,initcons_mobile(k) + dmobile(k)) initcons_stationary(k) = max(c0,initcons_stationary(k) - dmobile(k)) if (initcons_stationary(k)/hbri_old > Sat_conc) then initcons_mobile(k) = initcons_mobile(k) + initcons_stationary(k) - Sat_conc*hbri_old - initcons_stationary(k) = Sat_conc*hbri_old + initcons_stationary(k) = Sat_conc*hbri_old endif - Diff(k) = iDin(k) - initcons(k) = initcons_mobile(k) + Diff(k) = iDin(k) + initcons(k) = initcons_mobile(k) biocons(k) = initcons_mobile(k) enddo call compute_FCT_matrix & (initcons,sbdiagz, dt, nblyr, & - diagz, spdiagz, rhsz, bgrid, & + diagz, spdiagz, rhsz, bgrid, & i_grid, darcyV, dhtop, & dhbot, iphin_N, & Diff, hbri_old, & @@ -1197,19 +1254,19 @@ subroutine z_biogeochemistry (n_cat, dt, & source(mm)) if (l_stop) return - - call compute_FCT_corr & + + call compute_FCT_corr & (initcons, & biocons, dt, nblyr, & - D_sbdiag, D_spdiag, ML_diag) + D_sbdiag, D_spdiag, ML_diag) top_conc = c0 ! or frazil ice concentration - + ! assume diatoms actively maintain there relative position in the ice - if (mm .ne. nlt_bgc_N(1)) then - - call regrid_stationary & + if (mm .ne. nlt_bgc_N(1)) then + + call regrid_stationary & (initcons_stationary, hbri_old, & hbri, dt, & ntrcr, & @@ -1218,8 +1275,8 @@ subroutine z_biogeochemistry (n_cat, dt, & l_stop, stop_label, & meltb, congel) - elseif (tr_bgc_N .and. mm .eq. nlt_bgc_N(1)) then - if (meltb > algal_vel*dt .or. aicen < 0.001_dbl_kind) then + elseif (tr_bgc_N .and. mm .eq. nlt_bgc_N(1)) then + if (meltb > algal_vel*dt .or. aicen < 0.001_dbl_kind) then call regrid_stationary & (initcons_stationary, hbri_old, & @@ -1228,7 +1285,7 @@ subroutine z_biogeochemistry (n_cat, dt, & nblyr, top_conc, & i_grid, flux_bio(mm),& l_stop, stop_label, & - meltb, congel) + meltb, congel) endif endif @@ -1236,21 +1293,32 @@ subroutine z_biogeochemistry (n_cat, dt, & biomat_cons(:,mm) = biocons(:) + initcons_stationary(:) + sum_initial = (in_init_cons(1,mm) + in_init_cons(nblyr+1,mm))*zspace/c2 sum_old = (biomat_low(1) + biomat_low(nblyr+1))*zspace/c2 sum_new = (biocons(1)+ biocons(nblyr+1))*zspace/c2 sum_tot = (biomat_cons(1,mm) + biomat_cons(nblyr+1,mm))*zspace/c2 do k = 2,nblyr + sum_initial = sum_initial + in_init_cons(k,mm)*zspace sum_old = sum_old + biomat_low(k)*zspace sum_new = sum_new + biocons(k)*zspace sum_tot = sum_tot + biomat_cons(k,mm)*zspace enddo trcrn(nt_zbgc_frac+mm-1) = zbgc_frac_init(mm) - if (sum_tot > c0 .and. mobile(mm) > c0) trcrn(nt_zbgc_frac+mm-1) = sum_new/sum_tot - - if (abs(sum_new-sum_old) > accuracy*sum_old .or. & + if (sum_tot > c0) trcrn(nt_zbgc_frac+mm-1) = sum_new/sum_tot + + if (abs(sum_initial-sum_tot-flux_bio(mm)*dt + source(mm)) > accuracy*max(sum_initial,sum_tot) .or. & +! if (abs(sum_new-sum_old) > accuracy*sum_old .or. & minval(biocons(:)) < c0 .or. minval(initcons_stationary(:)) < c0 & .or. l_stop) then - write(warning,*)'zbgc FCT tracer solution failed,nn', nn + write(warning,*)'zbgc FCT tracer solution failed,mm', mm + call add_warning(warning) + write(warning,*)'sum_new,sum_tot,sum_initial,flux_bio(mm),source(mm):' + call add_warning(warning) + write(warning,*)sum_new,sum_tot,sum_initial,flux_bio(mm),source(mm) + call add_warning(warning) + write(warning,*)'error = sum_initial-sum_tot-flux_bio(mm)*dt+source(mm)' + call add_warning(warning) + write(warning,*)sum_initial-sum_tot-flux_bio(mm)*dt+source(mm) call add_warning(warning) write(warning,*)'sum_new,sum_old:',sum_new,sum_old call add_warning(warning) @@ -1279,54 +1347,76 @@ subroutine z_biogeochemistry (n_cat, dt, & write(warning,*)'Category,mm:',n_cat,mm call add_warning(warning) ! l_stop = .true. - stop_label = 'zbgc FCT tracer solution failed' + stop_label = 'zbgc FCT tracer solution warning' endif if (l_stop) return - else - + else + Call thin_ice_flux(hbri,hbri_old,iphin_N, biomat_cons(:,mm), & flux_bio(mm),source(mm), & i_grid, dt, nblyr,ocean_bio(mm)) endif ! thin or not - do k = 1,nblyr+1 - biomat_brine(k,mm) = biomat_cons(k,mm)/hbri/iphin_N(k) + do k = 1,nblyr+1 + biomat_brine(k,mm) = biomat_cons(k,mm)/hbri/iphin_N(k) enddo ! k - enddo ! mm + enddo ! mm - react(:,:) = c0 + react(:,:) = c0 grow_alg(:,:) = c0 if (solve_zbgc) then - do k = 1, nblyr+1 + do k = 1, nblyr+1 call algal_dyn (dt, & n_zaero, n_doc, n_dic, n_don, n_fed, n_fep, & dEdd_algae, & - zfswin(k), react(k,:), & + zfswin(k), react(k,:), & biomat_brine(k,:), nbtrcr, & grow_alg(k,:), n_algae, & iTin(k), & upNOn(k,:), upNHn(k,:), & Zoo(k), & - Nerror(k), conserve_N(k)) - + Cerror(k), conserve_C(k), & + nitrification(k)) enddo ! k endif ! solve_zbgc !----------------------------------------------------------------- ! Update the tracer variable !----------------------------------------------------------------- - + + sum_new = c0 + sum_tot = c0 + do m = 1,nbtrcr do k = 1,nblyr+1 ! back to bulk quantity - bio_tmp = (biomat_brine(k,m) + react(k,m))*iphin_N(k) - - if (.not. conserve_N(k)) then - write(warning, *) 'N in algal_dyn not conserved' + bio_tmp = (biomat_brine(k,m) + react(k,m))*iphin_N(k) + if (tr_bgc_C .and. m .eq. nlt_bgc_DIC(1) .and. bio_tmp < -puny) then ! satisfy DIC demands from ocean + write(warning, *) 'DIC demand from ocean' + call add_warning(warning) + write(warning, *) 'm, nlt_bgc_DIC(1), bio_tmp, react(k,m):' + call add_warning(warning) + write(warning, *) m, nlt_bgc_DIC(1), bio_tmp, react(k,m) call add_warning(warning) - write(warning, *) 'Nerror(k):', Nerror(k) + flux_bio(m) = flux_bio(m) + bio_tmp*dz(k)*hbri_old/dt + bio_tmp = c0 + end if + if (m .eq. nlt_bgc_Nit) then + initcons_mobile(k) = max(c0,(biomat_brine(k,m)-nitrification(k) + & + react(k,m))*iphin_N(k)*trcrn(nt_zbgc_frac+m-1)) + initcons_stationary(k) = max(c0,((c1-trcrn(nt_zbgc_frac+m-1))*(biomat_brine(k,m)- & + nitrification(k) + react(k,m)) + nitrification(k))*iphin_N(k)) + + sum_new = sum_new + initcons_mobile(k)*dz(k) + sum_tot = sum_tot + (initcons_mobile(k) + initcons_stationary(k))*dz(k) + + end if ! m .eq. nlt_bgc_Nit + if (.not. conserve_C(k)) then + write(warning, *) 'C in algal_dyn not conserved' + call add_warning(warning) + write(warning, *) 'Cerror(k):', Cerror(k) call add_warning(warning) write(warning, *) 'k,m,hbri,hbri_old,bio_tmp,biomat_cons(k,m),ocean_bio(m)' call add_warning(warning) @@ -1337,10 +1427,11 @@ subroutine z_biogeochemistry (n_cat, dt, & write(warning, *) react(k,m),iphin_N(k),biomat_brine(k,m) call add_warning(warning) l_stop = .true. - stop_label = 'N in algal_dyn not conserved' - elseif (abs(bio_tmp) < puny) then + stop_label = 'C in algal_dyn not conserved' + elseif (abs(bio_tmp) < puny) then + flux_bio(m) = flux_bio(m) + bio_tmp*dz(k)*hbri_old/dt bio_tmp = c0 - elseif (bio_tmp > 1.0e6_dbl_kind) then + elseif (bio_tmp > 1.0e8_dbl_kind) then write(warning, *) 'very large bgc value' call add_warning(warning) write(warning, *) 'k,m,hbri,hbri_old,bio_tmp,biomat_cons(k,m),ocean_bio(m)' @@ -1373,6 +1464,7 @@ subroutine z_biogeochemistry (n_cat, dt, & l_stop = .true. stop_label = 'negative bgc' endif + trcrn(bio_index(m)+k-1) = max(c0, bio_tmp) if (l_stop) then write(warning, *) 'trcrn(nt_zbgc_frac+m-1):',trcrn(nt_zbgc_frac+m-1) call add_warning(warning) @@ -1386,26 +1478,14 @@ subroutine z_biogeochemistry (n_cat, dt, & call add_warning(warning) return endif - trcrn(bio_index(m)+k-1) = max(c0, bio_tmp) - if (ocean_bio(m) .le. c0 .and. flux_bio(m) < c0) then - ! if (flux_bio(m) < -1.0e-12_dbl_kind) then - ! write(warning, *) 'no ocean_bio but flux_bio < c0' - ! call add_warning(warning) - ! write(warning, *) 'm,ocean_bio(m),flux_bio(m)' - ! call add_warning(warning) - ! write(warning, *) m,ocean_bio(m),flux_bio(m) - ! call add_warning(warning) - ! write(warning, *) 'setting flux_bio(m) = c0' - ! call add_warning(warning) - ! l_stop = .true. - ! stop_label = 'flux_bio < 0 when ocean_bio = 0' - ! endif - flux_bio(m) = max(c0,flux_bio(m)) - endif enddo ! k - enddo ! m - -770 format (I6,D16.6) + if (m .eq. nlt_bgc_Nit .and. MAXVAL(nitrification) > c0) then + trcrn(nt_zbgc_frac+m-1) = zbgc_frac_init(m) + if (sum_tot > c0) trcrn(nt_zbgc_frac+m-1) = sum_new/sum_tot + end if + enddo ! m + +770 format (I6,D16.6) 781 format (I6,I6,I6) 790 format (I6,I6) 791 format (f24.17) @@ -1425,29 +1505,30 @@ end subroutine z_biogeochemistry subroutine algal_dyn (dt, & n_zaero, n_doc, n_dic, n_don, n_fed, n_fep, & dEdd_algae, & - fswthru, reactb, & + fswthru, reactb, & ltrcrn, nbtrcr, & grow_alg, n_algae, & T_bot, & upNOn, upNHn, & Zoo, & - Nerror, conserve_N) + Cerror, conserve_C, & + nitrification) use ice_constants_colpkg, only: p1, p5, c0, c1, secday, puny use ice_colpkg_shared, only: max_algae, max_DON, max_DOC, R_C2N, R_chl2N, & - T_max, fsal , fr_resp , & - op_dep_min , fr_graze_s , & - fr_graze_e , fr_mort2min , & - fr_dFe , k_nitrif , & - t_iron_conv , max_loss , & - max_dfe_doc1 , fr_resp_s , & - y_sk_DMS , t_sk_conv , & - t_sk_ox + T_max, fsal , fr_resp , & + op_dep_min , fr_graze_s , & + fr_graze_e , fr_mort2min , & + fr_dFe , k_nitrif , & + t_iron_conv , max_loss , & + max_dfe_doc1 , fr_resp_s , & + y_sk_DMS , t_sk_conv , & + t_sk_ox , R_C2N_DON use ice_zbgc_shared, only: chlabs, alpha2max_low, beta2max, mu_max, & grow_Tdep, fr_graze, mort_pre, mort_Tdep, & - k_exude, K_Nit, K_Am, K_Sil, K_Fe, & - f_don, kn_bac, f_don_Am, & + k_exude, K_Nit, K_Am, K_Sil, K_Fe, & + f_don, kn_bac, f_don_Am, & f_doc, f_exude, k_bac use ice_colpkg_tracers, only: tr_brine, nt_fbri, & @@ -1468,13 +1549,14 @@ subroutine algal_dyn (dt, & n_algae ! number of autotrophic types real (kind=dbl_kind), intent(in) :: & - dt , & ! time step + dt , & ! time step T_bot , & ! ice temperature (oC) fswthru ! average shortwave passing through current ice layer (W/m^2) real (kind=dbl_kind), intent(inout) :: & Zoo, & ! N losses from zooplankton/bacteria... (mmol/m^3) - Nerror ! Change in N after reactions (mmol/m^3) + Cerror, & ! Change in C after reactions (mmol/m^3) + nitrification ! nitrate produced through nitrification (mmol/m3) real (kind=dbl_kind), dimension (:), intent(out) :: & grow_alg,& ! algal growth rate (mmol/m^3/s) @@ -1485,55 +1567,62 @@ subroutine algal_dyn (dt, & reactb ! biological reaction terms (mmol/m3) real (kind=dbl_kind), dimension(:), intent(in) :: & - ltrcrn ! brine concentrations in layer (mmol/m^3) + ltrcrn ! brine concentrations in layer (mmol/m^3) - logical (kind=log_kind), intent(inout) :: & - conserve_N + logical (kind=log_kind), intent(inout) :: & + conserve_C - logical (kind=log_kind), intent(in) :: & + logical (kind=log_kind), intent(in) :: & dEdd_algae ! .true. chla impact on shortwave computed in dEdd ! local variables !------------------------------------------------------------------------------------ ! 3 possible autotrophs nt_bgc_N(1:3): diatoms, flagellates, phaeocystis - ! 2 types of dissolved organic carbon nt_bgc_DOC(1:2): + ! 2 types of dissolved organic carbon nt_bgc_DOC(1:2): ! polysaccharids, lipids ! 1 DON (proteins) ! 1 particulate iron (nt_bgc_Fe) n_fep - ! 1 dossp;ved orpm m+fed - ! Limiting macro/micro nutrients: nt_bgc_Nit -> nitrate, nt_bgc_NH -> ammonium, - ! nt_bgc_Sil -> silicate, nt_bgc_Fe -> dissolved iron + ! 1 dossp;ved orpm m+fed + ! Limiting macro/micro nutrients: nt_bgc_Nit -> nitrate, nt_bgc_NH -> ammonium, + ! nt_bgc_Sil -> silicate, nt_bgc_Fe -> dissolved iron ! -------------------------------------------------------------------------------------- real (kind=dbl_kind), parameter, dimension(max_algae) :: & alpha2max_high = (/ 0.25_dbl_kind, 0.25_dbl_kind, 0.25_dbl_kind/) ! light limitation (1/(W/m^2)) + real (kind=dbl_kind), parameter, dimension(max_algae) :: & + graze_exponent = (/ 0.333_dbl_kind, c1, c1/) ! Implicit grazing exponent (Dunneet al. 2005) + + real (kind=dbl_kind), parameter :: & + graze_conc = 1.36_dbl_kind ! (mmol N/m^3) converted from Dunne et al 2005 + ! data fit for phytoplankton (1.9 mmol C/m^3) to + ! ice algal N with 20% porosity and C/N = 7 integer (kind=int_kind) :: k, n real (kind=dbl_kind), dimension(n_algae) :: & - Nin , & ! algal nitrogen concentration on volume (mmol/m^3) + Nin , & ! algal nitrogen concentration on volume (mmol/m^3) Cin , & ! algal carbon concentration on volume (mmol/m^3) chlin ! algal chlorophyll concentration on volume (mg/m^3) real (kind=dbl_kind), dimension(n_doc) :: & - Docin ! dissolved organic carbon concentration on volume (mmolC/m^3) + Docin ! dissolved organic carbon concentration on volume (mmolC/m^3) real (kind=dbl_kind), dimension(n_dic) :: & - Dicin ! dissolved inorganic carbon concentration on volume (mmolC/m^3) + Dicin ! dissolved inorganic carbon concentration on volume (mmolC/m^3) real (kind=dbl_kind), dimension(n_don) :: & !proteins - Donin ! dissolved organic nitrogen concentration on volume (mmolN/m^3) + Donin ! dissolved organic nitrogen concentration on volume (mmolN/m^3) real (kind=dbl_kind), dimension(n_fed) :: & !iron - Fedin ! dissolved iron concentration on volume (umol/m^3) + Fedin ! dissolved iron concentration on volume (umol/m^3) real (kind=dbl_kind), dimension(n_fep) :: & !iron - Fepin ! algal nitrogen concentration on volume (umol/m^3) + Fepin ! algal nitrogen concentration on volume (umol/m^3) real (kind=dbl_kind) :: & - Nitin , & ! nitrate concentration on volume (mmol/m^3) - Amin , & ! ammonia/um concentration on volume (mmol/m^3) - Silin , & ! silicon concentration on volume (mmol/m^3) + Nitin , & ! nitrate concentration on volume (mmol/m^3) + Amin , & ! ammonia/um concentration on volume (mmol/m^3) + Silin , & ! silicon concentration on volume (mmol/m^3) DMSPpin , & ! DMSPp concentration on volume (mmol/m^3) DMSPdin , & ! DMSPd concentration on volume (mmol/m^3) DMSin , & ! DMS concentration on volume (mmol/m^3) @@ -1542,7 +1631,7 @@ subroutine algal_dyn (dt, & Iavg_loc ! bottom layer attenuated Fswthru (W/m^2) real (kind=dbl_kind), dimension(n_algae) :: & - L_lim , & ! overall light limitation + L_lim , & ! overall light limitation Nit_lim , & ! overall nitrate limitation Am_lim , & ! overall ammonium limitation N_lim , & ! overall nitrogen species limitation @@ -1580,7 +1669,7 @@ subroutine algal_dyn (dt, & fr_graze_p , & ! fraction of N grazed that becomes protein ! (rest is assimilated) < (1-fr_graze_a) ! and fr_graze_a*fr_graze_e becomes ammonia - fr_mort_p ! fraction of N mortality that becomes protein + fr_mort_p ! fraction of N mortality that becomes protein ! < (1-fr_mort2min) real (kind=dbl_kind), dimension(n_algae) :: & @@ -1598,6 +1687,10 @@ subroutine algal_dyn (dt, & DOC_r , & ! net DOC removal (mmol/m^3) DOC_s ! net DOC sources (mmol/m^3) + real (kind=dbl_kind), dimension(n_dic) :: & + DIC_r , & ! net DIC removal (mmol/m^3) + DIC_s ! net DIC sources (mmol/m^3) + real (kind=dbl_kind), dimension(n_don) :: & DON_r , & ! net DON removal (mmol/m^3) DON_s ! net DON sources (mmol/m^3) @@ -1614,7 +1707,8 @@ subroutine algal_dyn (dt, & rFep ! ratio of particulate Fe to tot Fep real (kind=dbl_kind) :: & - dN , & ! change in N (mmol/m^3) + dN , & ! change in Nitrogen (mmol N/m^3) + dC , & ! change in Carbon (mmol C/m^3) N_s_p , & ! algal nitrogen photosynthesis (mmol/m^3) N_r_g , & ! algal nitrogen losses to grazing (mmol/m^3) N_r_r , & ! algal nitrogen losses to respiration (mmol/m^3) @@ -1626,7 +1720,7 @@ subroutine algal_dyn (dt, & Nit_r , & ! net nitrate removal (mmol/m^3) Am_s_e , & ! ammonium source from excretion (mmol/m^3) Am_s_r , & ! ammonium source from respiration (mmol/m^3) - Am_s_mo , & ! ammonium source from mort/remin (mmol/m^3) + Am_s_mo , & ! ammonium source from mort/remin (mmol/m^3) Am_r_p , & ! ammonium uptake by algae (mmol/m^3) Am_r_n , & ! ammonium removal to nitrification (mmol/m^3) Am_s , & ! net ammonium sources (mmol/m^3) @@ -1636,7 +1730,7 @@ subroutine algal_dyn (dt, & Fe_r_p , & ! iron uptake by algae (nM) DOC_r_c , & ! net doc removal from bacterial consumption (mmol/m^3) doc_s_m , & ! protein source due to algal mortality (mmol/m^3) - doc_s_g ! protein source due to grazing (mmol/m^3) + doc_s_g ! protein source due to grazing (mmol/m^3) real (kind=dbl_kind) :: & DMSPd_s_r , & ! skl dissolved DMSP from respiration (mmol/m^3) @@ -1660,12 +1754,12 @@ subroutine algal_dyn (dt, & character(len=char_len_long) :: & warning ! warning message - + !----------------------------------------------------------------------- ! Initialize !----------------------------------------------------------------------- - conserve_N = .true. + conserve_C = .true. Nin(:) = c0 Cin(:) = c0 chlin(:) = c0 @@ -1680,7 +1774,7 @@ subroutine algal_dyn (dt, & DMSPpin = c0 DMSPdin = c0 DMSin = c0 - PONin = c0 + PONin = c0 U_Am_tot = c0 U_Nit_tot = c0 U_Sil_tot = c0 @@ -1691,8 +1785,10 @@ subroutine algal_dyn (dt, & U_Fe_f(:) = c0 DOC_s(:) = c0 DOC_r(:) = c0 + DIC_s(:) = c0 + DIC_r(:) = c0 DOC_r_c = c0 - nitrif = c0 + nitrif = c0 mort_N = c0 mort_C = c0 graze_N = c0 @@ -1700,15 +1796,15 @@ subroutine algal_dyn (dt, & exude_C = c0 resp_N = c0 growth_N = c0 - Nit_r = c0 + Nit_r = c0 Am_s = c0 - Am_r = c0 + Am_r = c0 Sil_r = c0 Fed_r(:) = c0 Fed_s(:) = c0 Fep_r(:) = c0 Fep_s(:) = c0 - DMSPd_s = c0 + DMSPd_s = c0 dTemp = min(T_bot-T_max,c0) Fed_tot = c0 Fed_tot_r = c0 @@ -1718,12 +1814,12 @@ subroutine algal_dyn (dt, & Fep_tot_r = c0 Fep_tot_s = c0 rFep(:) = c0 - + Nitin = ltrcrn(nlt_bgc_Nit) op_dep = c0 do k = 1, n_algae Nin(k) = ltrcrn(nlt_bgc_N(k)) - chlin(k) = R_chl2N(k)* Nin(k) + chlin(k) = R_chl2N(k)* Nin(k) op_dep = op_dep + chlabs(k)*chlin(k) enddo if (tr_bgc_C) then @@ -1742,19 +1838,19 @@ subroutine algal_dyn (dt, & if (tr_bgc_DMS) then ! DMSPpin = ltrcrn(nlt_bgc_DMSPp) DMSPdin = ltrcrn(nlt_bgc_DMSPd) - DMSin = ltrcrn(nlt_bgc_DMS) + DMSin = ltrcrn(nlt_bgc_DMS) endif - if (tr_bgc_PON) PONin = ltrcrn(nlt_bgc_PON) + if (tr_bgc_PON) PONin = ltrcrn(nlt_bgc_PON) if (tr_bgc_DON) then do k = 1, n_don DONin(k) = ltrcrn(nlt_bgc_DON(k)) enddo endif if (tr_bgc_Fe ) then - do k = 1, n_fed + do k = 1, n_fed Fedin(k) = ltrcrn(nlt_bgc_Fed(k)) enddo - do k = 1, n_fep + do k = 1, n_fep Fepin(k) = ltrcrn(nlt_bgc_Fep(k)) enddo endif @@ -1793,10 +1889,10 @@ subroutine algal_dyn (dt, & do k = 1, n_algae ! With light inhibition ! Maybe include light inhibition for diatoms but phaeocystis - L_lim = (c1 - exp(-alpha2max_low(k)*Iavg_loc)) * exp(-beta2max(k)*Iavg_loc) + L_lim = (c1 - exp(-alpha2max_low(k)*Iavg_loc)) * exp(-beta2max(k)*Iavg_loc) ! Without light inhibition - !L_lim(k) = (c1 - exp(-alpha2max_low(k)*Iavg_loc)) + !L_lim(k) = (c1 - exp(-alpha2max_low(k)*Iavg_loc)) !----------------------------------------------------------------------- ! Nutrient limitation @@ -1807,7 +1903,7 @@ subroutine algal_dyn (dt, & N_lim(k) = Nit_lim(k) if (tr_bgc_Am) then Am_lim(k) = Amin/(Amin + K_Am(k)) - N_lim(k) = min(c1, Nit_lim(k) + Am_lim(k)) + N_lim(k) = min(c1, Nit_lim(k) + Am_lim(k)) endif Sil_lim(k) = c1 if (tr_bgc_Sil .and. K_Sil(k) > c0) Sil_lim(k) = Silin/(Silin + K_Sil(k)) @@ -1816,13 +1912,13 @@ subroutine algal_dyn (dt, & ! Iron limitation !----------------------------------------------------------------------- - Fe_lim(k) = c1 + Fe_lim(k) = c1 if (tr_bgc_Fe .and. K_Fe (k) > c0) Fe_lim (k) = Fed_tot/(Fed_tot + K_Fe(k)) - + !---------------------------------------------------------------------------- - ! Growth and uptake computed within the bottom layer - ! Note here per A93 discussions and MBJ model, salinity is a universal - ! restriction. Comparison with available column nutrients inserted + ! Growth and uptake computed within the bottom layer + ! Note here per A93 discussions and MBJ model, salinity is a universal + ! restriction. Comparison with available column nutrients inserted ! but in tests had no effect. ! Primary production reverts to SE form, see MBJ below and be careful !---------------------------------------------------------------------------- @@ -1830,7 +1926,7 @@ subroutine algal_dyn (dt, & growmax_N(k) = mu_max(k) / secday * exp(grow_Tdep(k) * dTemp)* Nin(k) *fsal grow_N(k) = min(L_lim(k), N_lim(k), Sil_lim(k), Fe_lim(k)) * growmax_N(k) potU_Nit(k) = Nit_lim(k)* growmax_N(k) - potU_Am(k) = Am_lim(k)* growmax_N(k) + potU_Am(k) = Am_lim(k)* growmax_N(k) U_Am(k) = min(grow_N(k), potU_Am(k)) U_Nit(k) = grow_N(k) - U_Am(k) U_Sil(k) = R_Si2N(k) * grow_N(k) @@ -1850,8 +1946,8 @@ subroutine algal_dyn (dt, & if (tr_bgc_Sil) U_Sil_tot = min(U_Sil_tot, max_loss * Silin/dt) if (tr_bgc_Fe) U_Fe_tot = min(U_Fe_tot, max_loss * Fed_tot/dt) - U_Nit_tot = min(U_Nit_tot, max_loss * Nitin/dt) - U_Am_tot = min(U_Am_tot, max_loss * Amin/dt) + U_Nit_tot = min(U_Nit_tot, max_loss * Nitin/dt) + U_Am_tot = min(U_Am_tot, max_loss * Amin/dt) do k = 1, n_algae U_Am(k) = U_Am_f(k)*U_Am_tot @@ -1875,13 +1971,13 @@ subroutine algal_dyn (dt, & U_Am(k) = fr_Am(k) * grow_N(k) U_Sil(k) = R_Si2N(k) * grow_N(k) U_Fe (k) = R_Fe2N(k) * grow_N(k) - + !----------------------------------------------------------------------- ! Define reaction terms !----------------------------------------------------------------------- ! Since the framework remains incomplete at this point, - ! it is assumed as a starting expedient that + ! it is assumed as a starting expedient that ! DMSP loss to melting results in 10% conversion to DMS ! which is then given a ten day removal constant. ! Grazing losses are channeled into rough spillage and assimilation @@ -1889,32 +1985,32 @@ subroutine algal_dyn (dt, & !-------------------------------------------------------------------- ! Algal reaction term - ! N_react = (grow_N*(c1 - fr_graze-fr_resp) - mort)*dt + ! v1: N_react = (grow_N*(c1 - fr_graze-fr_resp) - mort)*dt + ! v2: N_react = (grow_N*(c1 - fr_graze * (N/graze_conc)**graze_exp-fr_resp) - mort)*dt + ! with maximum grazing less than max_loss * Nin(k)/dt !-------------------------------------------------------------------- - resp(k) = fr_resp * grow_N(k) - graze(k) = fr_graze(k) * grow_N(k) + resp(k) = fr_resp * grow_N(k) + graze(k) = min(max_loss * Nin(k)/dt, grow_N(k) * fr_graze(k) * (Nin(k)/graze_conc)**graze_exponent(k)) mort(k) = min(max_loss * Nin(k)/dt, mort_pre(k)* exp(mort_Tdep(k)*dTemp) * Nin(k) / secday) - + ! history variables grow_alg(k) = grow_N(k) upNOn(k) = U_Nit(k) upNHn(k) = U_Am(k) - N_s_p = grow_N(k) * dt - N_r_g = graze(k) * dt + N_s_p = grow_N(k) * dt + N_r_g = graze(k) * dt N_r_r = resp(k) * dt N_r_mo = mort(k) * dt - N_s(k) = (c1- fr_resp - fr_graze(k)) * grow_N(k) *dt !N_s_p - N_r(k) = mort(k) * dt !N_r_g + N_r_mo + N_r_r - + N_s(k) = N_s_p !(c1- fr_resp - fr_graze(k)) * grow_N(k) *dt + N_r(k) = N_r_g + N_r_mo + N_r_r !mort(k) * dt graze_N = graze_N + graze(k) graze_C = graze_C + R_C2N(k)*graze(k) - mort_N = mort_N + mort(k) + mort_N = mort_N + mort(k) mort_C = mort_C + R_C2N(k)*mort(k) resp_N = resp_N + resp(k) growth_N = growth_N + grow_N(k) - enddo ! n_algae !-------------------------------------------------------------------- ! Ammonium source: algal grazing, respiration, and mortality @@ -1928,35 +2024,36 @@ subroutine algal_dyn (dt, & !-------------------------------------------------------------------- ! Nutrient net loss terms: algal uptake !-------------------------------------------------------------------- - + do k = 1, n_algae Am_r_p = U_Am(k) * dt - Am_r = Am_r + Am_r_p - Nit_r_p = U_Nit(k) * dt - Nit_r = Nit_r + Nit_r_p + Am_r = Am_r + Am_r_p + Nit_r_p = U_Nit(k) * dt + Nit_r = Nit_r + Nit_r_p Sil_r_p = U_Sil(k) * dt - Sil_r = Sil_r + Sil_r_p + Sil_r = Sil_r + Sil_r_p Fe_r_p = U_Fe (k) * dt - Fed_tot_r = Fed_tot_r + Fe_r_p - exude_C = exude_C + k_exude(k)* R_C2N(k)*Nin(k) / secday + Fed_tot_r = Fed_tot_r + Fe_r_p + exude_C = exude_C + k_exude(k)* R_C2N(k)*Nin(k) / secday + DIC_r(1) = DIC_r(1) + (c1-fr_resp)*grow_N(k) * R_C2N(k) * dt enddo !-------------------------------------------------------------------- ! nitrification !-------------------------------------------------------------------- - - nitrif = k_nitrif /secday * Amin + nitrification = c0 + nitrif = k_nitrif /secday * Amin Am_r = Am_r + nitrif*dt Nit_s_n = nitrif * dt !source from NH4 - Nit_s = Nit_s_n + Nit_s = Nit_s_n !-------------------------------------------------------------------- ! PON: currently using PON to shadow nitrate ! - ! N Losses are counted in Zoo. These arise from mortality not - ! remineralized (Zoo_s_m), assimilated grazing not excreted (Zoo_s_a), - !spilled N not going to DON (Zoo_s_s) and bacterial recycling - ! of DON (Zoo_s_b). + ! N Losses are counted in Zoo. These arise from mortality not + ! remineralized (Zoo_s_m), assimilated grazing not excreted (Zoo_s_a), + !spilled N not going to DON (Zoo_s_s) and bacterial recycling + ! of DON (Zoo_s_b). !-------------------------------------------------------------------- if (tr_bgc_Am) then @@ -1966,29 +2063,31 @@ subroutine algal_dyn (dt, & else Zoo_s_a = graze_N*dt*(c1-fr_graze_s) Zoo_s_s = graze_N*fr_graze_s*dt - Zoo_s_m = mort_N*dt + Zoo_s_m = mort_N*dt endif Zoo_s_b = c0 !-------------------------------------------------------------------- ! DON (n_don = 1) - ! Proteins + ! Proteins !-------------------------------------------------------------------- DON_r(:) = c0 DON_s(:) = c0 if (tr_bgc_DON) then - do n = 1, n_don + do n = 1, n_don DON_r(n) = kn_bac(n)/secday * DONin(n) * dt - DON_s(n) = graze_N*f_don(n)*fr_graze_s * dt + !DON_s(n) = (c1 - fr_graze_s + fr_graze_e*fr_graze_s)* graze_N * dt !fr_graze_N*f_don(n)*fr_graze_s * dt + DON_s(n) = graze_N*dt - Am_s_e + mort_N*dt - Am_s_mo Zoo_s_s = Zoo_s_s - DON_s(n) Zoo_s_b = Zoo_s_b + DON_r(n)*(c1-f_don_Am(n)) - !Am_s = Am_s + DON_r(n)*f_don_Am(n) + Am_s = Am_s + DON_r(n)*f_don_Am(n) + DIC_s(1) = DIC_s(1) + DON_r(n) * R_C2N_DON(n) enddo endif - + Zoo = Zoo_s_a + Zoo_s_s + Zoo_s_m + Zoo_s_b !-------------------------------------------------------------------- @@ -1996,18 +2095,19 @@ subroutine algal_dyn (dt, & ! polysaccharids, lipids !-------------------------------------------------------------------- - do n = 1, n_doc - + do n = 1, n_doc DOC_r(n) = k_bac(n)/secday * DOCin(n) * dt - DOC_s(n) = f_doc(n)*(fr_graze_s *graze_C + mort_C)*dt & - + f_exude(n)*exude_C +! DOC_s(n) = f_doc(n)*(fr_graze_s *graze_C + mort_C)*dt & +! + f_exude(n)*exude_C + DOC_s(n) = f_doc(n) * (graze_C*dt + mort_C*dt - DON_s(1) * R_C2N_DON(1)) + DIC_s(1) = DIC_s(1) + DOC_r(n) enddo !-------------------------------------------------------------------- ! Iron sources from remineralization (follows ammonium but reduced) ! only Fed_s(1) has remineralized sources !-------------------------------------------------------------------- - + Fed_s(1) = Fed_s(1) + Am_s * R_Fe2N(1) * fr_dFe ! remineralization source !-------------------------------------------------------------------- @@ -2016,38 +2116,39 @@ subroutine algal_dyn (dt, & !-------------------------------------------------------------------- if (tr_bgc_C .and. tr_bgc_Fe) then - if (DOCin(1) > c0) then - if (Fed_tot/DOCin(1) > max_dfe_doc1) then - do n = 1,n_fed ! low saccharid:dFe ratio leads to - Fed_r_l(n) = Fedin(n)/t_iron_conv*dt/secday ! loss of bioavailable Fe to particulate fraction - Fep_tot_s = Fep_tot_s + Fed_r_l(n) - Fed_r(n) = Fed_r_l(n) ! removal due to particulate scavenging - enddo - do n = 1,n_fep - Fep_s(n) = rFep(n)* Fep_tot_s ! source from dissolved Fe - enddo - elseif (Fed_tot/DOCin(1) < max_dfe_doc1) then - do n = 1,n_fep ! high saccharid:dFe ratio leads to + if (DOCin(1) > c0) then + !if (Fed_tot/DOCin(1) > max_dfe_doc1) then + ! do n = 1,n_fed ! low saccharid:dFe ratio leads to + ! Fed_r_l(n) = Fedin(n)/t_iron_conv*dt/secday ! loss of bioavailable Fe to particulate fraction + ! Fep_tot_s = Fep_tot_s + Fed_r_l(n) + ! Fed_r(n) = Fed_r_l(n) ! removal due to particulate scavenging + ! enddo + ! do n = 1,n_fep + ! Fep_s(n) = rFep(n)* Fep_tot_s ! source from dissolved Fe + ! enddo + !elseif (Fed_tot/DOCin(1) < max_dfe_doc1) then + if (Fed_tot/DOCin(1) < max_dfe_doc1) then + do n = 1,n_fep ! high saccharid:dFe ratio leads to Fep_r(n) = Fepin(n)/t_iron_conv*dt/secday ! gain of bioavailable Fe from particulate fraction Fed_tot_s = Fed_tot_s + Fep_r(n) - enddo + enddo do n = 1,n_fed Fed_s(n) = Fed_s(n) + rFed(n)* Fed_tot_s ! source from particulate Fe - enddo + enddo endif endif !Docin(1) > c0 endif if (tr_bgc_Fe) then do n = 1,n_fed Fed_r(n) = Fed_r(n) + rFed(n)*Fed_tot_r ! scavenging + uptake - enddo + enddo - ! source from algal mortality/grazing and fraction of remineralized nitrogen that does + ! source from algal mortality/grazing and fraction of remineralized nitrogen that does ! not become immediately bioavailable do n = 1,n_fep - Fep_s(n) = Fep_s(n) + rFep(n)* (Am_s * R_Fe2N(1) * (c1-fr_dFe)) - enddo ! losses not direct to Fed + Fep_s(n) = Fep_s(n) + rFep(n)* (Am_s * R_Fe2N(1) * (c1-fr_dFe)) + enddo ! losses not direct to Fed endif !-------------------------------------------------------------------- @@ -2055,7 +2156,7 @@ subroutine algal_dyn (dt, & !-------------------------------------------------------------------- ! Grazing losses are channeled into rough spillage and assimilation ! then onward and the MBJ mortality channel is included - ! It is assumed as a starting expedient that + ! It is assumed as a starting expedient that ! DMSP loss to melting gives partial conversion to DMS in product layer ! which then undergoes Stefels removal. @@ -2069,16 +2170,16 @@ subroutine algal_dyn (dt, & DMSPd_s_r = fr_resp_s * R_S2N(k) * resp(k) * dt !respiration fraction to DMSPd DMSPd_s_mo= fr_mort2min * R_S2N(k)* mort(k) * dt !mortality and extracellular excretion - DMSPd_s = DMSPd_s + DMSPd_s_r + DMSPd_s_mo + DMSPd_s = DMSPd_s + DMSPd_s_r + DMSPd_s_mo enddo DMSPd_r = (c1/t_sk_conv) * (c1/secday) * (DMSPdin) * dt !-------------------------------------------------------------------- - ! DMS reaction term + DMSPd loss term + ! DMS reaction term + DMSPd loss term ! DMS_react = ([\DMSPd]*y_sk_DMS/t_sk_conv - c1/t_sk_ox *[\DMS])*dt !-------------------------------------------------------------------- - DMS_s_c = y_sk_DMS * DMSPd_r + DMS_s_c = y_sk_DMS * DMSPd_r DMS_r_o = DMSin * dt / (t_sk_ox * secday) DMS_s = DMS_s_c DMS_r = DMS_r_o @@ -2088,19 +2189,27 @@ subroutine algal_dyn (dt, & !----------------------------------------------------------------------- dN = c0 + dC = c0 do k = 1,n_algae reactb(nlt_bgc_N(k)) = N_s(k) - N_r(k) dN = dN + reactb(nlt_bgc_N(k)) + dC = dC + reactb(nlt_bgc_N(k)) * R_C2N(k) enddo if (tr_bgc_C) then ! do k = 1,n_algae ! reactb(nlt_bgc_C(k)) = R_C2N(k)*reactb(nlt_bgc_N(k)) ! enddo do k = 1,n_doc - reactb(nlt_bgc_DOC(k))= DOC_s(k) - DOC_r(k) + reactb(nlt_bgc_DOC(k))= DOC_s(k) - DOC_r(k) + dC = dC + reactb(nlt_bgc_DOC(k)) + enddo + do k = 1,n_dic + reactb(nlt_bgc_DIC(k))= DIC_s(k) - DIC_r(k) + dC = dC + reactb(nlt_bgc_DIC(k)) enddo endif reactb(nlt_bgc_Nit) = Nit_s - Nit_r + nitrification = Nit_s_n dN = dN + reactb(nlt_bgc_Nit) if (tr_bgc_Am) then reactb(nlt_bgc_Am) = Am_s - Am_r @@ -2111,42 +2220,92 @@ subroutine algal_dyn (dt, & endif if (tr_bgc_DON) then do k = 1,n_don - reactb(nlt_bgc_DON(k))= DON_s(k) - DON_r(k) + reactb(nlt_bgc_DON(k))= DON_s(k) - DON_r(k) dN = dN + reactb(nlt_bgc_DON(k)) + dC = dC + reactb(nlt_bgc_DON(k)) * R_C2N_DON(k) enddo - endif + endif + Cerror = dC if (tr_bgc_Fe ) then do k = 1,n_fed - reactb(nlt_bgc_Fed(k))= Fed_s (k) - Fed_r (k) + reactb(nlt_bgc_Fed(k))= Fed_s (k) - Fed_r (k) enddo do k = 1,n_fep - reactb(nlt_bgc_Fep(k))= Fep_s (k) - Fep_r (k) + reactb(nlt_bgc_Fep(k))= Fep_s (k) - Fep_r (k) enddo - endif + endif if (tr_bgc_DMS) then reactb(nlt_bgc_DMSPd) = DMSPd_s - DMSPd_r reactb(nlt_bgc_DMS) = DMS_s - DMS_r endif - Nerror = dN + Zoo - ! if (abs(Nerror) > max(reactb(:))*1.0e-5) then - ! conserve_N = .false. - ! write(warning, *) 'Conservation error!' - ! call add_warning(warning) - ! write(warning, *) 'Nerror,dN, DONin(1),kn_bac(1),secday,dt,n_doc' - ! call add_warning(warning) - ! write(warning, *) Nerror,dN, DONin(1),kn_bac(1),secday,dt,n_doc - ! call add_warning(warning) - ! write(warning, *) 'reactb(nlt_bgc_Nit),reactb(nlt_bgc_N(1)),reactb(nlt_bgc_N(2)' - ! call add_warning(warning) - ! write(warning, *) reactb(nlt_bgc_Nit),reactb(nlt_bgc_N(1)),reactb(nlt_bgc_N(2)) - ! call add_warning(warning) - ! write(warning, *) 'reactb(nlt_bgc_Am),reactb(nlt_bgc_DON(1)), DON_r(1),DON_s(1)' - ! call add_warning(warning) - ! write(warning, *) reactb(nlt_bgc_Am),reactb(nlt_bgc_DON(1)),DON_r(1),DON_s(1) - ! call add_warning(warning) - ! write(warning, *) 'Zoo:',Zoo - ! endif - + if (tr_bgc_C) then + if (abs(dC) > maxval(abs(reactb(:)))*1.0e-13_dbl_kind .or. & + abs(dN) > maxval(abs(reactb(:)))*1.0e-13_dbl_kind) then + conserve_C = .false. + write(warning, *) 'Conservation error!' + call add_warning(warning) + if (tr_bgc_DON) then + write(warning, *) 'dN,DONin(1), kn_bac(1),secday,dt,n_doc' + call add_warning(warning) + write(warning, *) dN, DONin(1),kn_bac(1),secday,dt,n_doc + call add_warning(warning) + write(warning, *) 'reactb(nlt_bgc_DON(1)), DON_r(1),DON_s(1)' + call add_warning(warning) + write(warning, *) reactb(nlt_bgc_DON(1)),DON_r(1),DON_s(1) + call add_warning(warning) + end if + write(warning, *) 'dN,secday,dt,n_doc' + call add_warning(warning) + write(warning, *) dN,secday,dt,n_doc + call add_warning(warning) + write(warning, *) 'reactb(nlt_bgc_Nit),reactb(nlt_bgc_N(1)),reactb(nlt_bgc_N(2)' + call add_warning(warning) + write(warning, *) reactb(nlt_bgc_Nit),reactb(nlt_bgc_N(1)),reactb(nlt_bgc_N(2)) + call add_warning(warning) + if (tr_bgc_Am) then + write(warning, *) 'reactb(nlt_bgc_Am),Am_r, Am_s' + call add_warning(warning) + write(warning, *) reactb(nlt_bgc_Am),Am_r, Am_s + call add_warning(warning) + end if + write(warning, *) 'dC' + call add_warning(warning) + write(warning, *) dC + call add_warning(warning) + do k = 1,n_doc + write(warning, *) 'DOCin' + call add_warning(warning) + write(warning, *) DOCin(k) + call add_warning(warning) + write(warning, *) 'reactb(nlt_bgc_DOC)' + call add_warning(warning) + write(warning, *) reactb(nlt_bgc_DOC(k)) + call add_warning(warning) + write(warning, *) 'DOC_r,DOC_s' + call add_warning(warning) + write(warning, *) DOC_r(k),DOC_s(k) + end do + do k = 1,n_dic + write(warning, *) 'DICin' + call add_warning(warning) + write(warning, *) DICin(k) + call add_warning(warning) + write(warning, *) 'reactb(nlt_bgc_DIC)' + call add_warning(warning) + write(warning, *) reactb(nlt_bgc_DIC(k)) + call add_warning(warning) + write(warning, *) 'DIC_r,DIC_s' + call add_warning(warning) + write(warning, *) DIC_r(k),DIC_s(k) + end do + call add_warning(warning) + write(warning, *) 'Zoo' + call add_warning(warning) + write(warning, *) Zoo + call add_warning(warning) + endif + endif + end subroutine algal_dyn !======================================================================= @@ -2618,7 +2777,7 @@ subroutine check_conservation_FCT & (C_init, C_new, C_low, S_top, & S_bot, L_bot, L_top, dt, & fluxbio, l_stop, nblyr, & - source) + source) use ice_constants_colpkg, only: p5, c1, c4, c0 @@ -2637,13 +2796,13 @@ subroutine check_conservation_FCT & S_bot , & ! bottom flux into ice (mmol/m^2/s) L_bot , & ! remaining bottom flux into ice (mmol/m^2/s) L_top , & ! remaining top flux into ice (mmol/m^2/s) - dt , & + dt , & source ! nutrient source from snow and atmosphere (mmol/m^2) real (kind=dbl_kind), intent(inout) :: & fluxbio ! (mmol/m^2/s) positive down (into the ocean) - logical (kind=log_kind), intent(inout) :: & + logical (kind=log_kind), intent(inout) :: & l_stop ! false if conservation satisfied within error ! local variables @@ -2660,13 +2819,13 @@ subroutine check_conservation_FCT & character(len=char_len_long) :: & warning ! warning message - + zspace = c1/real(nblyr,kind=dbl_kind) l_stop = .false. !------------------------------------- ! Ocean flux: positive into the ocean - !------------------------------------- + !------------------------------------- C_init_tot = (C_init(1) + C_init(nblyr+1))*zspace*p5 C_new_tot = (C_new(1) + C_new(nblyr+1))*zspace*p5 C_low(1) = C_new(1) @@ -2678,35 +2837,51 @@ subroutine check_conservation_FCT & C_low(k) = C_new(k) enddo - accuracy = 1.0e-14_dbl_kind*max(c1, C_init_tot, C_new_tot) + accuracy = 1.0e-14_dbl_kind*max(c1, C_init_tot, C_new_tot) fluxbio = (C_init_tot - C_new_tot + source)/dt diff_dt =C_new_tot - C_init_tot - (S_top+S_bot+L_bot*C_new(nblyr+1)+L_top*C_new(1))*dt - if (minval(C_low) < c0) then + if (minval(C_low) < c0) then write(warning,*) 'Positivity of zbgc low order solution failed: C_low:',C_low + call add_warning(warning) l_stop = .true. endif - + if (abs(diff_dt) > accuracy ) then !l_stop = .true. write(warning,*) 'Conservation of zbgc low order solution failed: diff_dt:',& diff_dt + call add_warning(warning) write(warning,*) 'Total initial tracer', C_init_tot + call add_warning(warning) write(warning,*) 'Total final1 tracer', C_new_tot + call add_warning(warning) write(warning,*) 'bottom final tracer', C_new(nblyr+1) + call add_warning(warning) write(warning,*) 'top final tracer', C_new(1) + call add_warning(warning) write(warning,*) 'Near bottom final tracer', C_new(nblyr) + call add_warning(warning) write(warning,*) 'Near top final tracer', C_new(2) + call add_warning(warning) write(warning,*) 'Top flux*dt into ice:', S_top*dt + call add_warning(warning) write(warning,*) 'Bottom flux*dt into ice:', S_bot*dt + call add_warning(warning) write(warning,*) 'Remaining bot flux*dt into ice:', L_bot*C_new(nblyr+1)*dt + call add_warning(warning) write(warning,*) 'S_bot*dt + L_bot*C_new(nblyr+1)*dt' + call add_warning(warning) write(warning,*) S_bot*dt + L_bot*C_new(nblyr+1)*dt + call add_warning(warning) write(warning,*) 'fluxbio*dt:', fluxbio*dt + call add_warning(warning) write(warning,*) 'fluxbio:', fluxbio + call add_warning(warning) write(warning,*) 'Remaining top flux*dt into ice:', L_top*C_new(1)*dt + call add_warning(warning) endif - + end subroutine check_conservation_FCT !======================================================================= @@ -2717,7 +2892,7 @@ end subroutine check_conservation_FCT subroutine bgc_column_sum (nblyr, nslyr, hsnow, hbrine, xin, xout) - use ice_colpkg_shared, only: hs_ssl + use ice_colpkg_shared, only: hs_ssl use ice_constants_colpkg, only: p5, c1, c0 integer (kind=int_kind), intent(in) :: & @@ -2729,7 +2904,7 @@ subroutine bgc_column_sum (nblyr, nslyr, hsnow, hbrine, xin, xout) real (kind=dbl_kind), intent(in) :: & hsnow, & ! snow thickness - hbrine ! brine height + hbrine ! brine height real (kind=dbl_kind), intent(out) :: & xout ! output field @@ -2748,7 +2923,7 @@ subroutine bgc_column_sum (nblyr, nslyr, hsnow, hbrine, xin, xout) hslyr = hsnow/real(nslyr,kind=dbl_kind) dzssl = min(hslyr*p5, hs_ssl) dzint = max(c0,hsnow - dzssl) - zspace = c1/real(nblyr,kind=dbl_kind) + zspace = c1/real(nblyr,kind=dbl_kind) xout = c0 xout = (xin(1) + xin(nblyr+1))*hbrine*p5*zspace @@ -2759,6 +2934,153 @@ subroutine bgc_column_sum (nblyr, nslyr, hsnow, hbrine, xin, xout) end subroutine bgc_column_sum +!======================================================================= + +! Find the total carbon concentration by summing the appropriate +! biogeochemical tracers in units of mmol C/m2 +! +! author: Nicole Jeffery, LANL + + subroutine bgc_carbon_sum (nblyr, hbrine, xin, xout, n_doc, n_dic, n_algae, n_don) + + use ice_colpkg_shared, only: hs_ssl, R_C2N, R_C2N_DON + use ice_constants_colpkg, only: p5, c1, c0 + use ice_colpkg_tracers, only: tr_bgc_N, tr_bgc_C, tr_bgc_hum, & + tr_bgc_DON, nt_bgc_hum, nt_bgc_N, nt_bgc_DOC, nt_bgc_DIC, nt_bgc_DON + + integer (kind=int_kind), intent(in) :: & + nblyr, & ! number of ice layers + n_doc, n_dic, n_algae, n_don + + real (kind=dbl_kind), dimension(:), intent(in) :: & + xin ! input field, all tracers and column + + real (kind=dbl_kind), intent(in) :: & + hbrine ! brine height + + real (kind=dbl_kind), intent(out) :: & + xout ! output field mmol/m2 carbon + + ! local variables + + real (kind=dbl_kind), dimension(nblyr+1) :: & + zspace ! brine layer thickness/hbrine + + integer (kind=int_kind) :: & + n, m, iBioCount, iLayer, nBGC ! category/layer index + + zspace(:) = c1/real(nblyr,kind=dbl_kind) + zspace(1) = p5*zspace(1) + zspace(nblyr+1) = zspace(1) + + xout = c0 + + if (tr_bgc_N) then + iBioCount = c0 + do m = 1, n_algae + nBGC = nt_bgc_N(1) + do n = 1, nblyr+1 + iLayer = iBioCount + n-1 + xout = xout + xin(nBGC+iLayer)*zspace(n)*hbrine*R_C2N(m) + enddo + iBioCount = iBioCount + nblyr+3 + enddo + endif + if (tr_bgc_C) then + iBioCount = c0 + nBGC = nt_bgc_DOC(1) + do m = 1, n_doc + do n = 1, nblyr+1 + iLayer = iBioCount + n-1 + xout = xout + xin(nBGC+iLayer)*zspace(n)*hbrine + enddo + iBioCount = iBioCount + nblyr+3 + enddo + do m = 1, n_dic + do n = 1, nblyr+1 + iLayer = iBioCount + n-1 + xout = xout + xin(nBGC+iLayer)*zspace(n)*hbrine + enddo + iBioCount = iBioCount + nblyr+3 + enddo + endif + + if (tr_bgc_DON) then + iBioCount = c0 + do m = 1, n_don + nBGC = nt_bgc_DON(1) + do n = 1, nblyr+1 + iLayer = iBioCount + n-1 + xout = xout + xin(nBGC+iLayer)*zspace(n)*hbrine*R_C2N_DON(m) + enddo + iBioCount = iBioCount + nblyr+3 + enddo + endif + if (tr_bgc_hum) then + nBGC = nt_bgc_hum + do n = 1, nblyr+1 + iLayer = n-1 + xout = xout + xin(nBGC+iLayer)*zspace(n)*hbrine + enddo + endif + + end subroutine bgc_carbon_sum + +!======================================================================= + +! Find the total carbon flux by summing the fluxes for the appropriate +! biogeochemical each grid cell, sum field over all ice and snow layers +! +! author: Nicole Jeffery, LANL + + subroutine bgc_carbon_flux (flux_bio_atm, flux_bion, n_doc, & + n_dic, n_algae, n_don, Tot_Carbon_flux) + + use ice_colpkg_shared, only: R_C2N, R_C2N_DON + use ice_constants_colpkg, only: c0 + use ice_colpkg_tracers, only: tr_bgc_N, tr_bgc_C, tr_bgc_hum, & + tr_bgc_DON, nlt_bgc_hum, nlt_bgc_N, nlt_bgc_C, nlt_bgc_DOC, & + nlt_bgc_DIC, nlt_bgc_DON + + integer (kind=int_kind), intent(in) :: & + n_doc, n_dic, n_algae, n_don + + real (kind=dbl_kind), dimension(:), intent(in) :: & + flux_bio_atm, & ! input field, all tracers and column + flux_bion + + real (kind=dbl_kind), intent(out) :: & + Tot_Carbon_flux ! output field mmol/m2/s carbon + + ! local variables + integer (kind=int_kind) :: & + m ! biology index + + Tot_Carbon_flux = c0 + + if (tr_bgc_N) then + do m = 1, n_algae + Tot_Carbon_flux = Tot_Carbon_flux - (flux_bio_atm(nlt_bgc_N(m)) - flux_bion(nlt_bgc_N(m)))*R_C2N(m) + enddo + endif + if (tr_bgc_C) then + do m = 1, n_doc + Tot_Carbon_flux = Tot_Carbon_flux - flux_bio_atm(nlt_bgc_DOC(m)) + flux_bion(nlt_bgc_DOC(m)) + enddo + do m = 1, n_dic + Tot_Carbon_flux = Tot_Carbon_flux - flux_bio_atm(nlt_bgc_DIC(m)) + flux_bion(nlt_bgc_DIC(m)) + enddo + endif + if (tr_bgc_DON) then + do m = 1, n_don + Tot_Carbon_flux = Tot_Carbon_flux - (flux_bio_atm(nlt_bgc_DON(m)) - flux_bion(nlt_bgc_DON(m)))*R_C2N_DON(m) + enddo + endif + if (tr_bgc_hum) & + Tot_Carbon_flux = Tot_Carbon_flux - flux_bio_atm(nlt_bgc_hum) + flux_bion(nlt_bgc_hum) + + end subroutine bgc_carbon_flux + !======================================================================= end module ice_algae diff --git a/src/core_seaice/column/ice_atmo.F90 b/src/core_seaice/column/ice_atmo.F90 index c5cd8c95a0..e1a1d4bd86 100644 --- a/src/core_seaice/column/ice_atmo.F90 +++ b/src/core_seaice/column/ice_atmo.F90 @@ -129,6 +129,7 @@ subroutine atmo_boundary_layer (sfctype, & real (kind=dbl_kind) :: & ustar , & ! ustar (m/s) + ustar_prev , & ! ustar_prev (m/s) tstar , & ! tstar qstar , & ! qstar rdn , & ! sqrt of neutral exchange coefficient (momentum) @@ -256,7 +257,13 @@ subroutine atmo_boundary_layer (sfctype, & ! iterate to converge on Z/L, ustar, tstar and qstar !------------------------------------------------------------ - do k = 1, natmiter + ustar_prev = c2 * ustar + + k = 0 + do while (abs(ustar - ustar_prev)/ustar > 0 .and. k <= natmiter) + + ustar_prev = ustar + k = k + 1 ! compute stability & evaluate all stability functions hol = vonkar * gravit * zlvl & diff --git a/src/core_seaice/column/ice_brine.F90 b/src/core_seaice/column/ice_brine.F90 index 25d0b9a4ac..442aa45ca7 100644 --- a/src/core_seaice/column/ice_brine.F90 +++ b/src/core_seaice/column/ice_brine.F90 @@ -50,10 +50,10 @@ subroutine preflushing_changes (n_cat, & fbri, dhbr_top, dhbr_bot, & hbr_old, hin,hsn, firstice, & l_stop, stop_label) - + integer (kind=int_kind), intent(in) :: & n_cat ! category - + real (kind=dbl_kind), intent(in) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) @@ -61,10 +61,11 @@ subroutine preflushing_changes (n_cat, & meltb , & ! bottom ice melt (m) meltt , & ! top ice melt (m) congel , & ! bottom ice growth (m) - snoice ! top ice growth from flooding (m) - + snoice , & ! top ice growth from flooding (m) + hice_old ! old ice thickness (m) + real (kind=dbl_kind), intent(out) :: & - hbr_old ! old brine height (m) + hbr_old ! old brine height (m) real (kind=dbl_kind), intent(inout) :: & hin , & ! ice thickness (m) @@ -74,8 +75,7 @@ subroutine preflushing_changes (n_cat, & real (kind=dbl_kind), intent(inout) :: & fbri , & ! trcrn(nt_fbri) dhbr_top , & ! brine change in top for diagnostics (m) - dhbr_bot , & ! brine change in bottom for diagnostics (m) - hice_old ! old ice thickness (m) + dhbr_bot logical (kind=log_kind), intent(in) :: & firstice ! if true, initialized values should be used @@ -98,29 +98,32 @@ subroutine preflushing_changes (n_cat, & !----------------------------------------------------------------- l_stop = .false. - if (fbri <= c0) then + if (fbri < c0) then write(warning, *) 'fbri, hice_old', fbri, hice_old call add_warning(warning) write(warning, *) 'vicen, aicen', vicen, aicen - call add_warning(warning) + call add_warning(warning) l_stop = .true. stop_label = 'ice_brine preflushing: fbri <= c0' endif - - hin = vicen / aicen - hsn = vsnon / aicen + hin = c0 + hsn = c0 + if (aicen > puny) then + hin = vicen / aicen + hsn = vsnon / aicen + endif hin_old = max(c0, hin + meltb + meltt - congel - snoice) dhice = hin_old - hice_old ! change due to subl/cond - dhbr_top = meltt - snoice - dhice + dhbr_top = meltt - snoice - dhice dhbr_bot = congel - meltb - if ((hice_old < puny) .OR. (hin_old < puny) .OR. firstice) then - hice_old = hin - dhbr_top = c0 - dhbr_bot = c0 - dhice = c0 - fbri = c1 - endif + !if ((hice_old < puny) .OR. (hin_old < puny) ) then !.OR. firstice) then + ! hice_old = hin + ! dhbr_top = c0 + ! dhbr_bot = c0 + ! dhice = c0 + ! fbri = c1 + !endif hbr_old = fbri * hice_old @@ -177,7 +180,7 @@ subroutine compute_microS_mushy (n_cat, nilyr, nblyr, & kperm , & ! average ice permeability (m^2) bphi_min ! surface porosity - real (kind=dbl_kind), intent(inout) :: & + real (kind=dbl_kind), intent(in) :: & hbr_old ! previous timestep brine height (m) real (kind=dbl_kind), dimension (nblyr+1), & @@ -256,7 +259,6 @@ subroutine compute_microS_mushy (n_cat, nilyr, nblyr, & ! map Sin and qin (cice) profiles to bgc grid surface_S = min_salin - hbr_old = min(hbr_old, maxhbr*hice_old) hinc_old = hice_old hbrc_old = hbr_old @@ -447,15 +449,15 @@ end subroutine prepare_hbrine !======================================================================= -! Changes include brine height increases from ice and snow surface melt, +! Changes include brine height increases from ice and snow surface melt, ! congelation growth, and upward pressure driven flow from snow loading. -! -! Decreases arise from downward flushing and bottom melt. ! -! NOTE: In this subroutine, trcrn(nt_fbri) is the volume fraction of ice -! with dynamic salinity or the height ratio == hbr/vicen*aicen, where -! hbr is the height of the brine surface relative to the bottom of the -! ice. This volume fraction may be > 1 in which case there is brine +! Decreases arise from downward flushing and bottom melt. +! +! NOTE: In this subroutine, trcrn(nt_fbri) is the volume fraction of ice +! with dynamic salinity or the height ratio == hbr/vicen*aicen, where +! hbr is the height of the brine surface relative to the bottom of the +! ice. This volume fraction may be > 1 in which case there is brine ! above the ice surface (ponds). subroutine update_hbrine (meltb, meltt, & @@ -475,7 +477,7 @@ subroutine update_hbrine (meltb, meltt, & real (kind=dbl_kind), intent(in) :: & dt ! timestep - + real (kind=dbl_kind), intent(in):: & meltb, & ! bottom melt over dt (m) meltt, & ! true top melt over dt (m) @@ -485,29 +487,29 @@ subroutine update_hbrine (meltb, meltt, & hin_old, & ! past timestep ice thickness (m) hbr_old, & ! previous timestep hbr phi_snow, & ! porosity of snow - kperm, & ! avg ice permeability - bphin, & ! upper brine porosity + kperm, & ! avg ice permeability + bphin, & ! upper brine porosity snoice, & ! snoice change (m) dhS_bottom, & ! change in bottom hbr initially before darcy flow aice0 ! open water area fraction real (kind=dbl_kind), intent(inout):: & darcy_V , & ! Darcy velocity: m/s - darcy_V_chl, & ! Darcy velocity: m/s for bgc + darcy_V_chl, & ! Darcy velocity: m/s for bgc dhS_top , & ! change in top hbr before darcy flow - dh_bot_chl , & ! change in bottom for algae - dh_top_chl , & ! change in bottom for algae - hbr , & ! thickness of brine (m) - fbri , & ! brine height ratio tracer (hbr/hin) - bphi_min ! surface porosity + dh_bot_chl , & ! change in bottom for algae + dh_top_chl , & ! change in bottom for algae + hbr , & ! thickness of brine (m) + fbri , & ! brine height ratio tracer (hbr/hin) + bphi_min ! surface porosity real (kind=dbl_kind), intent(out):: & dh_direct ! surface flooding or runoff (m) - + ! local variables - real (kind=dbl_kind) :: & - hbrmin , & ! thinS or hin + real (kind=dbl_kind) :: & + hbrmin , & ! thinS or hin dhbr_hin , & ! hbr-hin hbrocn , & ! brine height above sea level (m) hbr-h_ocn dhbr , & ! change in brine surface @@ -520,61 +522,61 @@ subroutine update_hbrine (meltb, meltt, & real (kind=dbl_kind), parameter :: & dh_min = p001 ! brine remains within dh_min of sea level ! when ice thickness is less than thinS - + hbrocn = c0 darcy_V = c0 - darcy_V_chl = c0 + darcy_V_chl = c0 hbrocn_new = c0 - h_ocn = rhosi/rhow*hin + rhos/rhow*hsn + h_ocn = rhosi/rhow*hin + rhos/rhow*hsn dh_direct = c0 - + if (hbr_old > thinS .AND. hin_old > thinS .AND. hin > thinS ) then hbrmin = thinS - dhS_top = -max(c0, min(hin_old-hbr_old, meltt)) * rhoi/rhow + dhS_top = -max(c0, min(hin_old-hbr_old, meltt)) * rhoi/rhow dhS_top = dhS_top - max(c0, melts) * rhos/rhow dh_top_chl = dhS_top - dhbr = dhS_bottom - dhS_top - hbr = max(puny, hbr_old+dhbr) + dhbr = dhS_bottom - dhS_top + hbr = max(puny, hbr_old+dhbr) hbrocn = hbr - h_ocn darcy_coeff = max(c0, kperm*gravit/(viscos*hbr_old)) - if (hbrocn > c0 .AND. hbr > thinS ) then - bphi_min = bphin + if (hbrocn > c0 .AND. hbr > thinS ) then + bphi_min = bphin dhrunoff = -dhS_top*aice0 hbrocn = max(c0,hbrocn - dhrunoff) hbrocn_new = hbrocn*exp(-darcy_coeff/bphi_min*dt) hbr = max(hbrmin, h_ocn + hbrocn_new) hbrocn_new = hbr-h_ocn darcy_V = -SIGN((hbrocn-hbrocn_new)/dt*bphi_min, hbrocn) - darcy_V_chl= darcy_V + darcy_V_chl= darcy_V dhS_top = dhS_top - darcy_V*dt/bphi_min + dhrunoff dh_top_chl = dh_top_chl - darcy_V_chl*dt/bphi_min + dhrunoff dh_direct = dhrunoff elseif (hbrocn < c0 .AND. hbr > thinS) then hbrocn_new = hbrocn*exp(-darcy_coeff/bphi_min*dt) - dhflood = max(c0,hbrocn_new - hbrocn)*aice0 + dhflood = max(c0,hbrocn_new - hbrocn)*aice0 hbr = max(hbrmin, h_ocn + hbrocn_new) darcy_V = -SIGN((hbrocn-hbrocn_new + dhflood)/dt*bphi_min, hbrocn) - darcy_V_chl= darcy_V + darcy_V_chl= darcy_V dhS_top = dhS_top - darcy_V*dt/bphi_min - dhflood dh_top_chl = dh_top_chl - darcy_V_chl*dt/bphi_min - dhflood dh_direct = -dhflood endif - - dh_bot_chl = dhS_bottom - - else ! very thin brine height + + dh_bot_chl = dhS_bottom + + else ! very thin brine height hbrmin = min(thinS, hin) hbr = max(hbrmin, hbr_old+dhS_bottom-dhS_top) dhbr_hin = hbr - h_ocn if (abs(dhbr_hin) > dh_min) & - hbr = max(hbrmin, h_ocn + SIGN(dh_min,dhbr_hin)) + hbr = max(hbrmin, h_ocn + SIGN(dh_min,dhbr_hin)) dhS_top = hbr_old - hbr + dhS_bottom dh_top_chl = dhS_top dh_bot_chl = dhS_bottom - endif - - fbri = hbr/hin + endif + + fbri = hbr/hin end subroutine update_hbrine @@ -625,7 +627,7 @@ subroutine compute_microS (n_cat, nilyr, nblyr, & sst ! ocean temperature (oC) real (kind=dbl_kind), dimension(ntrcr), intent(inout) :: & - trcrn + trcrn real (kind=dbl_kind), intent(inout) :: & hbr_old , & ! old brine height diff --git a/src/core_seaice/column/ice_colpkg.F90 b/src/core_seaice/column/ice_colpkg.F90 index 2951fb8156..285d439840 100644 --- a/src/core_seaice/column/ice_colpkg.F90 +++ b/src/core_seaice/column/ice_colpkg.F90 @@ -36,6 +36,7 @@ module ice_colpkg ! time stepping public :: & + colpkg_step_snow, & colpkg_step_therm1, & colpkg_biogeochemistry, & colpkg_step_therm2, & @@ -56,7 +57,9 @@ module ice_colpkg colpkg_snow_temperature, & colpkg_liquidus_temperature, & colpkg_sea_freezing_temperature, & - colpkg_enthalpy_snow + colpkg_enthalpy_ice, & + colpkg_enthalpy_snow, & + colpkg_salinity_profile ! warning messages public :: & @@ -259,6 +262,10 @@ subroutine colpkg_init_itd(ncat, hin_max, l_stop, stop_label) endif ! kcatbound + if (kitd == 1) then + hin_max(ncat) = 999.9_dbl_kind ! arbitrary big number + endif + end subroutine colpkg_init_itd !======================================================================= @@ -379,7 +386,31 @@ subroutine colpkg_init_thermo(nilyr, sprofile) end subroutine colpkg_init_thermo !======================================================================= +! Initial salinity profile +! +! authors: C. M. Bitz, UW +! William H. Lipscomb, LANL + + function colpkg_salinity_profile(zn) result(salinity) + + use ice_colpkg_shared, only: saltmax + use ice_constants_colpkg, only: c1, c2, pi + + real(kind=dbl_kind), intent(in) :: & + zn ! depth + + real(kind=dbl_kind) :: & + salinity ! initial salinity profile + + real (kind=dbl_kind), parameter :: & + nsal = 0.407_dbl_kind, & + msal = 0.573_dbl_kind + + salinity = (saltmax/c2)*(c1-cos(pi*zn**(nsal/(msal+zn)))) + end function colpkg_salinity_profile + +!======================================================================= ! Compute orbital parameters for the specified date. ! ! author: Bruce P. Briegleb, NCAR @@ -389,10 +420,13 @@ subroutine colpkg_init_orbit(l_stop, stop_label) use ice_constants_colpkg, only: iyear_AD, eccen, obliqr, lambm0, & mvelpp, obliq, mvelp, decln, eccf, log_print -#ifndef CCSMCOUPLED +#ifdef CCSMCOUPLED + use shr_orb_mod, only: shr_orb_params +#else use ice_orbital, only: shr_orb_params #endif + logical (kind=log_kind), intent(out) :: & l_stop ! if true, abort the model @@ -403,7 +437,10 @@ subroutine colpkg_init_orbit(l_stop, stop_label) iyear_AD = 1950 log_print = .false. ! if true, write out orbital parameters -#ifndef CCSMCOUPLED +#ifdef CCSMCOUPLED + call shr_orb_params( iyear_AD, eccen , obliq , mvelp , & + obliqr , lambm0, mvelpp, log_print) +#else call shr_orb_params( iyear_AD, eccen , obliq , mvelp , & obliqr , lambm0, mvelpp, log_print, & l_stop, stop_label) @@ -741,7 +778,7 @@ subroutine colpkg_init_zbgc (nblyr, nilyr, nslyr, & f_don_Am_protein ,f_doc_s, f_doc_l, & f_exude_s, f_exude_l, k_bac_s, k_bac_l, & algaltype_diatoms, algaltype_sp, algaltype_phaeo, & - doctype_s, doctype_l, dontype_protein, & + doctype_s, doctype_l, dictype_1, dontype_protein, & fedtype_1, feptype_1, zaerotype_bc1, zaerotype_bc2, & zaerotype_dust1, zaerotype_dust2, zaerotype_dust3, & zaerotype_dust4, & @@ -968,6 +1005,7 @@ subroutine colpkg_init_zbgc (nblyr, nilyr, nslyr, & humtype , & ! doctype_s , & ! doctype_l , & ! + dictype_1 , & ! dontype_protein , & ! fedtype_1 , & ! feptype_1 , & ! @@ -1118,7 +1156,7 @@ subroutine colpkg_init_zbgc (nblyr, nilyr, nslyr, & F_abs_chl(3) = F_abs_chl_phaeo R_Fe2DON(1) = ratio_Fe2DON - R_C2N(1) = ratio_C2N_proteins + R_C2N_DON(1) = ratio_C2N_proteins R_Fe2DOC(1) = ratio_Fe2DOC_s R_Fe2DOC(2) = ratio_Fe2DOC_l @@ -1176,6 +1214,9 @@ subroutine colpkg_init_zbgc (nblyr, nilyr, nslyr, & K_Fe(2) = K_Fe_sp K_Fe(3) = K_Fe_phaeo + f_doc(1) = f_doc_s + f_doc(2) = f_doc_l + f_don(1) = f_don_protein kn_bac(1) = kn_bac_protein f_don_Am(1) = f_don_Am_protein @@ -1192,6 +1233,8 @@ subroutine colpkg_init_zbgc (nblyr, nilyr, nslyr, & doctype(1) = doctype_s doctype(2) = doctype_l + dictype(1) = dictype_1 + dontype(1) = dontype_protein fedtype(1) = fedtype_1 @@ -1622,7 +1665,7 @@ end function colpkg_liquidus_temperature function colpkg_sea_freezing_temperature(sss) result(Tf) use ice_colpkg_shared, only: tfrz_option - use ice_constants_colpkg, only: depressT + use ice_constants_colpkg, only: depressT, Tocnfrz real(dbl_kind), intent(in) :: sss real(dbl_kind) :: Tf @@ -1637,7 +1680,7 @@ function colpkg_sea_freezing_temperature(sss) result(Tf) else - Tf = -1.8_dbl_kind + Tf = Tocnfrz endif @@ -1693,6 +1736,33 @@ function colpkg_snow_temperature(qin) result(Tsn) end function colpkg_snow_temperature +!======================================================================= + + function colpkg_enthalpy_ice(zTin, zSin) result(qin) + + use ice_colpkg_shared, only: ktherm + use ice_mushy_physics, only: enthalpy_mush + use ice_constants_colpkg, only: depressT, rhoi, cp_ice, Lfresh, cp_ocn, c1 + + real(kind=dbl_kind), intent(in) :: zTin + real(kind=dbl_kind), intent(in) :: zSin + real(kind=dbl_kind) :: qin + + real(kind=dbl_kind) :: Tmlt + + if (ktherm == 2) then + + qin = enthalpy_mush(zTin, zSin) + + else + + Tmlt = -zSin*depressT + qin = -(rhoi * (cp_ice*(Tmlt-zTin) + Lfresh*(c1-Tmlt/zTin) - cp_ocn*Tmlt)) + + endif + + end function colpkg_enthalpy_ice + !======================================================================= function colpkg_enthalpy_snow(zTsn) result(qsn) @@ -1717,6 +1787,7 @@ end function colpkg_enthalpy_snow ! Elizabeth C. Hunke, LANL subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & + aice0 , & aicen_init , & vicen_init , vsnon_init , & aice , aicen , & @@ -1725,10 +1796,12 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & uvel , vvel , & Tsfc , zqsn , & zqin , zSin , & + smice , smliq , & alvl , vlvl , & apnd , hpnd , & ipnd , & iage , FY , & + rsnw , use_smliq_pnd,& aerosno , aeroice , & uatm , vatm , & wind , zlvl , & @@ -1753,7 +1826,7 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & fbot , & frzmlt , rside , & fsnow , frain , & - fpond , & + fpond , fsloss , & fsurf , fsurfn , & fcondtop , fcondtopn , & fswsfcn , fswintn , & @@ -1774,6 +1847,7 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & meltb , meltbn , & meltl , & melts , meltsn , & + meltsliq , meltsliqn , & congel , congeln , & snoice , snoicen , & dsnown , frazil , & @@ -1785,16 +1859,18 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & use ice_aerosol, only: update_aerosol use ice_atmo, only: neutral_drag_coeffs use ice_age, only: increment_age - use ice_constants_colpkg, only: rhofresh, rhoi, rhos, c0, c1, puny + use ice_constants_colpkg, only: rhofresh, rhoi, rhos, c0, c1, puny, & + snwlvlfac use ice_firstyear, only: update_FYarea use ice_flux_colpkg, only: set_sfcflux, merge_fluxes use ice_meltpond_cesm, only: compute_ponds_cesm use ice_meltpond_lvl, only: compute_ponds_lvl use ice_meltpond_topo, only: compute_ponds_topo + use ice_snow, only: drain_snow use ice_therm_shared, only: hi_min use ice_therm_vertical, only: frzmlt_bottom_lateral, thermo_vertical use ice_colpkg_tracers, only: tr_iage, tr_FY, tr_aero, tr_pond, & - tr_pond_cesm, tr_pond_lvl, tr_pond_topo + tr_pond_cesm, tr_pond_lvl, tr_pond_topo, tr_snow, tr_rsnw integer (kind=int_kind), intent(in) :: & ncat , & ! number of thickness categories @@ -1812,12 +1888,14 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & logical (kind=log_kind), intent(in) :: & lmask_n , & ! northern hemisphere mask - lmask_s ! southern hemisphere mask + lmask_s , & ! southern hemisphere mask + use_smliq_pnd ! if true, use snow liquid tracer for ponds logical (kind=log_kind), intent(in), optional :: & prescribed_ice ! if .true., use prescribed ice instead of computed real (kind=dbl_kind), intent(inout) :: & + aice0 , & ! open water fraction aice , & ! sea ice concentration vice , & ! volume per unit area of ice (m) vsno , & ! volume per unit area of snow (m) @@ -1831,6 +1909,7 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & rhoa , & ! air density (kg/m^3) frain , & ! rainfall rate (kg/m^2 s) fsnow , & ! snowfall rate (kg/m^2 s) + fsloss , & ! blowing snow loss to leads (kg/m^2/s) fpond , & ! fresh water flux to ponds (kg/m^2/s) fresh , & ! fresh water flux to ocean (kg/m^2/s) fsalt , & ! salt flux to ocean (kg/m^2/s) @@ -1881,6 +1960,7 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & sss , & ! sea surface salinity (ppt) meltt , & ! top ice melt (m/step-->cm/day) melts , & ! snow melt (m/step-->cm/day) + meltsliq , & ! snow melt mass (kg/m^2/step-->kg/m^2/day) meltb , & ! basal ice melt (m/step-->cm/day) meltl , & ! lateral ice melt (m/step-->cm/day) mlt_onset , & ! day of year that sfc melting begins @@ -1917,6 +1997,7 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & dhsn , & ! depth difference for snow on sea ice and pond ice ffracn , & ! fraction of fsurfn used to melt ipond meltsn , & ! snow melt (m) + meltsliqn , & ! snow melt mass (kg/m^2) melttn , & ! top ice melt (m) meltbn , & ! bottom ice melt (m) congeln , & ! congelation ice growth (m) @@ -1927,8 +2008,11 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & zqsn , & ! snow layer enthalpy (J m-3) zqin , & ! ice layer enthalpy (J m-3) zSin , & ! internal ice layer salinities + smice , & ! ice mass tracer in snow (kg/m^3) + smliq , & ! liquid water mass tracer in snow (kg/m^3) Sswabsn , & ! SW radiation absorbed in snow layers (W m-2) - Iswabsn ! SW radiation absorbed in ice layers (W m-2) + Iswabsn , & ! SW radiation absorbed in ice layers (W m-2) + rsnw ! snow grain radius (10^-6 m) in snow layers real (kind=dbl_kind), dimension(:,:,:), intent(inout) :: & aerosno , & ! snow aerosol tracer (kg/m^2) @@ -1971,6 +2055,26 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & raice , & ! 1/aice pond ! water retained in ponds (m) + !--------------------------------------------------------------- + ! Initialize rate of snow loss to leads + !--------------------------------------------------------------- + + fsloss = fsnow*aice0 + + !--------------------------------------------------------------- + ! 30% rule for snow redistribution: precip factor + !--------------------------------------------------------------- + + if (trim(snwredist) == '30percent') then + worka = c0 + do n = 1, ncat + worka = worka + alvl(n) + enddo + worka = worka * snwlvlfac/(c1+snwlvlfac) + fsloss = fsloss + fsnow*(c1-worka) + fsnow = fsnow* worka + endif ! snwredist + !----------------------------------------------------------------- ! Adjust frzmlt to account for ice-ocean heat fluxes since last ! call to coupler. @@ -2015,6 +2119,7 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & do n = 1, ncat meltsn (n) = c0 + meltsliqn(n) = c0 melttn (n) = c0 meltbn (n) = c0 congeln(n) = c0 @@ -2108,13 +2213,15 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & vicen (n), vsnon (n), & Tsfc (n), zSin (:,n), & zqin (:,n), zqsn (:,n), & - apnd (n), hpnd (n), & - iage (n), tr_pond_topo, & + smice (:,n), smliq (:,n), & + tr_snow, apnd (n), & + hpnd (n), iage (n), & + tr_pond_topo, & flw, potT, & Qa, rhoa, & fsnow, fpond, & fbot, Tbot, & - sss, & + sss, rsnw (:,n), & lhcoef, shcoef, & fswsfcn (n), fswintn (n), & Sswabsn(:,n), Iswabsn(:,n), & @@ -2122,12 +2229,13 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & fsensn (n), flatn (n), & flwoutn, evapn, & freshn, fsaltn, & - fhocnn, & + fhocnn, frain, & melttn (n), meltsn (n), & - meltbn (n), & + meltbn (n), meltsliqn(n), & congeln (n), snoicen (n), & mlt_onset, frz_onset, & yday, dsnown (n), & + tr_rsnw, & l_stop, stop_label, & prescribed_ice) @@ -2162,6 +2270,18 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & endif ! aicen_init + !----------------------------------------------------------------- + ! Transport liquid water in snow between layers and + ! compute the meltpond contribution + !----------------------------------------------------------------- + + if (use_smliq_pnd) then + call drain_snow (dt, nslyr, & + vsnon (n) , aicen (n), & + smice (:,n), smliq (:,n), & + meltsliqn(n)) + endif + !----------------------------------------------------------------- ! Melt ponds ! If using tr_pond_cesm, the full calculation is performed here. @@ -2171,7 +2291,6 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & !call ice_timer_start(timer_ponds) if (tr_pond) then - if (tr_pond_cesm) then rfrac = rfracmin + (rfracmax-rfracmin) * aicen(n) call compute_ponds_cesm(dt, hi_min, & @@ -2180,7 +2299,9 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & frain, & aicen (n), vicen (n), & vsnon (n), Tsfc (n), & - apnd (n), hpnd (n)) + apnd (n), hpnd (n), & + meltsliqn(n), & + use_smliq_pnd) elseif (tr_pond_lvl) then rfrac = rfracmin + (rfracmax-rfracmin) * aicen(n) @@ -2198,7 +2319,9 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & zqin(:,n), zSin(:,n), & Tsfc (n), alvl (n), & apnd (n), hpnd (n), & - ipnd (n)) + ipnd (n), & + meltsliqn(n), & + use_smliq_pnd) elseif (tr_pond_topo) then if (aicen_init(n) > puny) then @@ -2206,10 +2329,14 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & ! collect liquid water in ponds ! assume salt still runs off rfrac = rfracmin + (rfracmax-rfracmin) * aicen(n) - pond = rfrac/rhofresh * (melttn(n)*rhoi & - + meltsn(n)*rhos & - + frain *dt) - + if (use_smliq_pnd) then + pond = rfrac/rhofresh * (melttn(n)*rhoi & + + meltsliqn(n)) + else + pond = rfrac/rhofresh * (melttn(n)*rhoi & + + meltsn(n)*rhos & + + frain *dt) + endif ! if pond does not exist, create new pond over full ice area ! otherwise increase pond depth without changing pond area if (apnd(n) < puny) then @@ -2251,10 +2378,10 @@ subroutine colpkg_step_therm1(dt, ncat, nilyr, nslyr, n_aero, & fhocn, fswthru, & melttn (n), meltsn(n), & meltbn (n), congeln(n), & - snoicen(n), & + snoicen(n), meltsliqn(n), & meltt, melts, & meltb, congel, & - snoice, & + snoice, meltsliq, & Uref, Urefn) enddo ! ncat @@ -2287,7 +2414,7 @@ end subroutine colpkg_step_therm1 ! authors: William H. Lipscomb, LANL ! Elizabeth C. Hunke, LANL - subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & + subroutine colpkg_step_therm2 (dt, ncat, n_aero, nbtrcr, & nilyr, nslyr, & hin_max, nblyr, & aicen, & @@ -2313,14 +2440,14 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & frazil_diag, & frz_onset, yday) - use ice_constants_colpkg, only: puny + use ice_constants_colpkg, only: puny, c0 use ice_itd, only: aggregate_area, reduce_area, cleanup_itd use ice_therm_itd, only: linear_itd, add_new_ice, lateral_melt - use ice_colpkg_tracers, only: ntrcr, nbtrcr, tr_aero, tr_pond_topo + use ice_colpkg_tracers, only: ntrcr, tr_aero, tr_pond_topo, tr_brine, nt_fbri, bio_index integer (kind=int_kind), intent(in) :: & ncat , & ! number of thickness categories - nltrcr , & ! number of zbgc tracers + nbtrcr , & ! number of zbgc tracers nblyr , & ! number of bio layers nilyr , & ! number of ice layers nslyr , & ! number of snow layers @@ -2329,7 +2456,7 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & logical (kind=log_kind), intent(in) :: & update_ocn_f ! if true, update fresh water and salt fluxes - real (kind=dbl_kind), dimension(0:ncat), intent(inout) :: & + real (kind=dbl_kind), dimension(0:ncat), intent(in) :: & hin_max ! category boundaries (m) real (kind=dbl_kind), intent(in) :: & @@ -2421,6 +2548,8 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & ! Compute fractional ice area in each grid cell. !----------------------------------------------------------------- + flux_bio(:) = c0 + call aggregate_area (ncat, aicen, aice, aice0) if (kitd == 1) then @@ -2431,21 +2560,21 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & if (aice > puny) then - call linear_itd (ncat, hin_max, & - nilyr, nslyr, & - ntrcr, trcr_depend, & - trcr_base, & - n_trcr_strata, & - nt_strata, & + call linear_itd (ncat, hin_max, & + nilyr, nslyr, & + ntrcr, trcr_depend, & + trcr_base, & + n_trcr_strata, & + nt_strata, Tf, & aicen_init, & vicen_init, & aicen, & - trcrn, & + trcrn, & vicen, & vsnon, & - aice , & - aice0 , & - fpond, l_stop, & + aice, & + aice0, & + fpond, l_stop, & stop_label) if (l_stop) return @@ -2465,7 +2594,7 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & call add_new_ice (ncat, nilyr, & nblyr, & n_aero, dt, & - ntrcr, nltrcr, & + ntrcr, nbtrcr, & hin_max, ktherm, & aicen, trcrn, & vicen, vsnon(1), & @@ -2478,7 +2607,7 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & salinz, phi_init, & dSin0_frazil, bgrid, & cgrid, igrid, & - nbtrcr, flux_bio, & + flux_bio, & ocean_bio, fzsal, & frazil_diag, & l_stop, stop_label) @@ -2517,7 +2646,8 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & ! categories with very small areas. !----------------------------------------------------------------- - call cleanup_itd (dt, ntrcr, & + call cleanup_itd (dt, Tf, & + ntrcr, & nilyr, nslyr, & ncat, hin_max, & aicen, trcrn(1:ntrcr,:), & @@ -2647,7 +2777,7 @@ end subroutine colpkg_prep_radiation ! Elizabeth C. Hunke, LANL subroutine colpkg_step_radiation (dt, ncat, & - n_algae, tr_zaero, & + n_algae, & nblyr, ntrcr, & nbtrcr, nbtrcr_sw, & nilyr, nslyr, & @@ -2661,6 +2791,8 @@ subroutine colpkg_step_radiation (dt, ncat, & vsnon, Tsfcn, & alvln, apndn, & hpndn, ipndn, & + snwredist, & + rsnow, & aeron, & zbion, & trcrn, & @@ -2688,13 +2820,28 @@ subroutine colpkg_step_radiation (dt, ncat, & albpndn, apeffn, & snowfracn, & dhsn, ffracn, & - l_print_point, & - initonly) + l_print_point, & + initonly, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd, & + rsnw_dEddn) use ice_constants_colpkg, only: c0, puny use ice_shortwave, only: run_dEdd, shortwave_ccsm3, compute_shortwave_trcr use ice_colpkg_tracers, only: tr_pond_cesm, tr_pond_lvl, tr_pond_topo, & - tr_bgc_N, tr_aero + tr_bgc_N, tr_aero, tr_rsnw, tr_zaero + use ice_colpkg_shared, only: z_tracers, skl_bgc integer (kind=int_kind), intent(in) :: & @@ -2745,7 +2892,8 @@ subroutine colpkg_step_radiation (dt, ncat, & real (kind=dbl_kind), dimension(:,:), intent(in) :: & kaer_tab, & ! aerosol mass extinction cross section (m2/kg) waer_tab, & ! aerosol single scatter albedo (fraction) - gaer_tab ! aerosol asymmetry parameter (cos(theta)) + gaer_tab, & ! aerosol asymmetry parameter (cos(theta)) + rsnow ! snow grain radius tracer (10^-6 m) real (kind=dbl_kind), dimension(:,:), intent(in) :: & kaer_bc_tab, & ! aerosol mass extinction cross section (m2/kg) @@ -2766,6 +2914,9 @@ subroutine colpkg_step_radiation (dt, ncat, & ipndn , & ! pond refrozen lid thickness (m) fbri ! brine fraction + character(len=char_len), intent(in) :: & + snwredist ! type of snow redistribution + real(kind=dbl_kind), dimension(:,:), intent(in) :: & aeron , & ! aerosols (kg/m^3) trcrn ! tracers @@ -2788,6 +2939,7 @@ subroutine colpkg_step_radiation (dt, ncat, & albicen , & ! bare ice albsnon , & ! snow albpndn , & ! pond + rsnw_dEddn, & ! snow grain radius (um) apeffn ! effective pond area used for radiation calculation real (kind=dbl_kind), dimension(:,:), intent(inout) :: & @@ -2798,12 +2950,35 @@ subroutine colpkg_step_radiation (dt, ncat, & logical (kind=log_kind), intent(in) :: & l_print_point, & ! flag for printing diagnostics dEdd_algae , & ! .true. use prognostic chla in dEdd - modal_aero , & ! .true. use modal aerosol optical treatment - tr_zaero + modal_aero ! .true. use modal aerosol optical treatment logical (kind=log_kind), optional :: & initonly ! flag to indicate init only, default is false + + ! snow grain single-scattering properties for + ! direct (drc) and diffuse (dfs) shortwave incidents + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc, & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs, & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc, & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs, & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc, & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + ! local variables integer (kind=int_kind) :: & @@ -2877,6 +3052,8 @@ subroutine colpkg_step_radiation (dt, ncat, & vsnon, Tsfcn, & alvln, apndn, & hpndn, ipndn, & + snwredist, & + rsnow, tr_rsnw, & aeron, kalg, & zbion, & heat_capacity, & @@ -2907,8 +3084,23 @@ subroutine colpkg_step_radiation (dt, ncat, & albpndn, apeffn, & snowfracn, & dhsn, ffracn, & + rsnw_dEddn, & l_print_point, & - linitonly) + linitonly, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) else ! .not. dEdd @@ -2983,6 +3175,7 @@ subroutine colpkg_step_ridge (dt, ndtd, & nblyr, & ncat, hin_max, & rdg_conv, rdg_shear, & + Tf, & aicen, & trcrn, & vicen, vsnon, & @@ -3010,7 +3203,8 @@ subroutine colpkg_step_ridge (dt, ndtd, & use ice_colpkg_tracers, only: tr_pond_topo, tr_aero, tr_brine, ntrcr, nbtrcr real (kind=dbl_kind), intent(in) :: & - dt ! time step + dt , & ! time step + Tf ! ocean freezing temperature integer (kind=int_kind), intent(in) :: & ncat , & ! number of thickness categories @@ -3125,7 +3319,8 @@ subroutine colpkg_step_ridge (dt, ndtd, & aredistn, vredistn, & dardg1ndt, dardg2ndt, & dvirdgndt, & - araftn, vraftn) + araftn, vraftn, & + Tf) if (l_stop) return @@ -3135,7 +3330,8 @@ subroutine colpkg_step_ridge (dt, ndtd, & !----------------------------------------------------------------- dtt = dt * ndtd ! for proper averaging over thermo timestep - call cleanup_itd (dtt, ntrcr, & + call cleanup_itd (dtt, Tf, & + ntrcr, & nilyr, nslyr, & ncat, hin_max, & aicen, trcrn, & @@ -3167,7 +3363,7 @@ end subroutine colpkg_step_ridge ! authors: C. M. Bitz, UW ! W. H. Lipscomb, LANL - subroutine colpkg_aggregate (ncat, & + subroutine colpkg_aggregate (ncat, Tf, & aicen, trcrn, & vicen, vsnon, & aice, trcr, & @@ -3186,6 +3382,9 @@ subroutine colpkg_aggregate (ncat, & ncat , & ! number of thickness categories ntrcr ! number of tracers in use + real (kind=dbl_kind), intent(in) :: & + Tf ! ocean freezing temperature (Celsius) + real (kind=dbl_kind), dimension (:), intent(in) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) @@ -3273,7 +3472,8 @@ subroutine colpkg_aggregate (ncat, & atrcr, aice, & vice , vsno, & trcr_base, n_trcr_strata, & - nt_strata, trcr) + nt_strata, trcr, & + Tf) deallocate (atrcr) @@ -3474,6 +3674,7 @@ subroutine colpkg_atm_boundary(sfctype, & if (present(uvel)) then worku = uvel endif + ! should this be for vvel,workv? if (present(uvel)) then worku = uvel endif @@ -3610,6 +3811,208 @@ subroutine colpkg_ocn_mixed_layer (alvdr_ocn, swvdr, & end subroutine colpkg_ocn_mixed_layer +!======================================================================= +! +! Updates snow tracers +! +! authors: Elizabeth C. Hunke, LANL +! Nicole Jeffery, LANL + + subroutine colpkg_step_snow (dt, wind, & + nilyr, & + nslyr, ncat, & + aice, aicen, & + vicen, vsnon, & + alvl, vlvl, & + smice, smliq, & + rhos_effn, rhos_eff, & + rhos_cmpn, rhos_cmp, & + rsnw, zqin1, & + zSin1, Tsfc, & + zqsn, & + fresh, fhocn, & + fsloss, fsnow, & + rhosnew, rhosmax, & + windmin, drhosdwind, & + snowage_tau, & + snowage_kappa, & + snowage_drdt0, & + idx_T_max, & + idx_Tgrd_max, & + idx_rhos_max, & + l_stop, & + stop_label) + + use ice_colpkg_tracers, only: tr_snow, tr_rsnw + use ice_constants_colpkg, only: c0, puny, rhos + use ice_snow, only: snow_effective_density, update_snow_radius, & + snow_redist + + integer (kind=int_kind), intent(in) :: & + nslyr, & ! number of snow layers + nilyr, & ! number of ice layers + ncat, & ! number of thickness categories + idx_T_max, & ! dimensions of snow parameter matrix + idx_Tgrd_max, & + idx_rhos_max + + real (kind=dbl_kind), intent(in) :: & + dt , & ! time step + wind , & ! wind speed (m/s) + fsnow , & ! snowfall rate (kg m-2 s-1) + aice , & ! ice area fraction + rhosnew, & ! new snow density (kg/m^3) + rhosmax, & ! maximum snow density (kg/m^3) + windmin, & ! minimum wind speed to compact snow (m/s) + drhosdwind ! wind compaction factor (kg s/m^4) + + real (kind=dbl_kind), dimension(:), intent(in) :: & + aicen, & ! ice area fraction + vicen, & ! ice volume (m) + Tsfc , & ! surface temperature (C) + zqin1, & ! ice upper layer enthalpy + zSin1, & ! ice upper layer salinity + alvl, & ! level ice area tracer + vlvl ! level ice volume tracer + + real (kind=dbl_kind), intent(inout) :: & + fresh , & ! fresh water flux to ocean (kg/m^2/s) + fhocn , & ! net heat flux to ocean (W/m^2) + fsloss ! snow loss to leads (kg/m^2/s) + + real (kind=dbl_kind), dimension(:), intent(inout) :: & + vsnon ! snow volume (m) + + real (kind=dbl_kind), dimension(:,:), intent(inout) :: & + zqsn , & ! snow enthalpy (J/m^3) + smice , & ! mass of ice in snow (kg/m^3) + smliq , & ! mass of liquid in snow (kg/m^3) + rsnw , & ! snow grain radius (10^-6 m) + rhos_effn, & ! effective snow density: content (kg/m^3) + rhos_cmpn ! effective snow density: compaction (kg/m^3) + + real (kind=dbl_kind), intent(inout) :: & + rhos_eff , & ! mean effective snow density: content (kg/m^3) + rhos_cmp ! mean effective snow density: compaction (kg/m^3) + + ! dry snow aging parameters + real (kind=dbl_kind), dimension(idx_rhos_max,idx_Tgrd_max,idx_T_max), intent(in) :: & + snowage_tau, & ! (10^-6 m) + snowage_kappa, & ! + snowage_drdt0 ! (10^-6 m/hr) + + logical (kind=log_kind), intent(inout) :: & + l_stop ! if true, print diagnostics and abort model + + character (len=*), intent(out) :: & + stop_label ! abort error message + + ! local temporary variables + + integer (kind=int_kind) :: n + + real (kind=dbl_kind), dimension(ncat) :: & + zTin, & ! ice upper layer temperature (oC) + hsn , & ! snow thickness (m) + hin ! ice thickness + + real (kind=dbl_kind) :: & + vsno, & ! snow volume (m) + tmp1, tmp2 + + character(len=char_len_long) :: & + warning ! warning message + + l_stop = .false. + stop_label = '' + + if (tr_snow) then + + !----------------------------------------------------------------- + ! Compute effective density of snow + !----------------------------------------------------------------- + + vsno = c0 + do n = 1, ncat + vsno = vsno + vsnon(n) + enddo + + call snow_effective_density(nslyr, ncat, & + vsnon, vsno, & + smice, smliq, & + rhosnew, & + rhos_effn, rhos_eff, & + rhos_cmpn, rhos_cmp) + + !----------------------------------------------------------------- + ! Redistribute snow based on wind + !----------------------------------------------------------------- + + tmp1 = rhos*vsno + fresh*dt + + if (snwredist(1:3) == 'ITD' .and. aice > puny) then + call snow_redist(dt, & + nslyr, ncat, & + wind, aicen(:), & + vicen(:), vsnon(:), & + zqsn(:,:),snwredist, & + alvl(:), vlvl(:), & + fresh, fhocn, & + fsloss, rhos_cmpn, & + fsnow, rhosmax, & + windmin, drhosdwind, & + l_stop, stop_label) + endif + + vsno = c0 + do n = 1, ncat + vsno = vsno + vsnon(n) + enddo + tmp2 = rhos*vsno + fresh*dt + if (abs(tmp1-tmp2)>puny) then + write(warning,*) ' ' + call add_warning(warning) + write(warning,*)'tmp1 ne tmp2',tmp1, tmp2 + call add_warning(warning) + stop_label ='snow redistribution error' + l_stop = .true. + endif + + endif ! tr_snow + + !----------------------------------------------------------------- + ! Adjust snow grain radius + !----------------------------------------------------------------- + + if (tr_rsnw) then + do n = 1, ncat + zTin(n)= c0 + hsn(n) = c0 + hin(n) = c0 + if (aicen(n) > puny) then + zTin(n) = colpkg_ice_temperature(zqin1(n),zSin1(n)) + hsn(n) = vsnon(n)/aicen(n) + hin(n) = vicen(n)/aicen(n) + endif + enddo + + call update_snow_radius (dt, ncat, & + nslyr, nilyr, & + rsnw, hin, & + Tsfc, zTin, & + hsn, zqsn, & + smice, smliq, & + rsnw_fall, rsnw_tmax, & + snowage_tau, & + snowage_kappa, & + snowage_drdt0, & + idx_T_max, & + idx_Tgrd_max, & + idx_rhos_max) + endif + + end subroutine colpkg_step_snow + !======================================================================= ! subroutine to set the column package internal parameters @@ -3626,6 +4029,7 @@ subroutine colpkg_init_parameters(& phi_c_slow_mode_in, & phi_i_mushy_in, & shortwave_in, & + use_snicar_in, & albedo_type_in, & albicev_in, & albicei_in, & @@ -3774,7 +4178,8 @@ subroutine colpkg_init_parameters(& dmspdtype_in, & humtype_in, & doctype_s_in, & - doctype_l_in, & + doctype_l_in, & + dictype_1_in, & dontype_protein_in, & fedtype_1_in, & feptype_1_in, & @@ -3793,7 +4198,15 @@ subroutine colpkg_init_parameters(& F_abs_chl_diatoms_in, & F_abs_chl_sp_in, & F_abs_chl_phaeo_in, & - ratio_C2N_proteins_in) + ratio_C2N_proteins_in, & + snwredist_in, & + use_smliq_pnd_in, & + rsnw_fall_in, & + rsnw_tmax_in, & + rhosnew_in, & + rhosmax_in, & + windmin_in, & + drhosdwind_in) !restore_bgc_in) use ice_colpkg_shared, only: & @@ -3809,6 +4222,7 @@ subroutine colpkg_init_parameters(& phi_c_slow_mode, & phi_i_mushy, & shortwave, & + use_snicar, & albedo_type, & albicev, & albicei, & @@ -3958,6 +4372,7 @@ subroutine colpkg_init_parameters(& humtype , & doctype_s , & doctype_l , & + dictype_1 , & dontype_protein , & fedtype_1 , & feptype_1 , & @@ -3976,7 +4391,15 @@ subroutine colpkg_init_parameters(& F_abs_chl_diatoms , & F_abs_chl_sp , & F_abs_chl_phaeo , & - ratio_C2N_proteins + ratio_C2N_proteins , & + snwredist, & + use_smliq_pnd, & + rsnw_fall, & + rsnw_tmax, & + rhosnew, & + rhosmax, & + windmin, & + drhosdwind !restore_bgc !----------------------------------------------------------------------- @@ -4037,6 +4460,12 @@ subroutine colpkg_init_parameters(& rsnw_mlt_in , & ! maximum melting snow grain radius (10^-6 m) kalg_in ! algae absorption coefficient for 0.5 m thick layer + ! snicar 5 band system, set in namelist + logical (kind=log_kind), intent(in) :: & + use_snicar_in ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + !----------------------------------------------------------------------- ! Parameters for ridging and strength !----------------------------------------------------------------------- @@ -4229,6 +4658,7 @@ subroutine colpkg_init_parameters(& humtype_in , & ! doctype_s_in , & ! doctype_l_in , & ! + dictype_1_in , & ! dontype_protein_in , & ! fedtype_1_in , & ! feptype_1_in , & ! @@ -4274,6 +4704,32 @@ subroutine colpkg_init_parameters(& real (kind=dbl_kind), intent(in) :: & hp1_in ! critical parameter for pond ice thickness +!----------------------------------------------------------------------- +! Parameters for snow +!----------------------------------------------------------------------- + + ! snow metamorphism parameters, set in namelist + real (kind=dbl_kind), intent(in) :: & + rsnw_fall_in , & ! fallen snow grain radius (10^-6 m)) 54.5 um CLM ** + ! 30 um is minimum for defined mie properties + rsnw_tmax_in , & ! maximum dry metamorphism snow grain radius (10^-6 m) + ! 1500 um is maximum for defined mie properties + rhosnew_in , & ! new snow density (kg/m^3) + rhosmax_in , & ! maximum snow density (kg/m^3) + windmin_in , & ! minimum wind speed to compact snow (m/s) + drhosdwind_in ! wind compaction factor (kg s/m^4) + + character(len=char_len), intent(in) :: & + snwredist_in ! type of snow redistribution + ! '30percent' = 30% rule, precip only + ! '30percentsw' = 30% rule with shortwave + ! 'ITDsd' = Lecomte PhD, 2014 + ! 'ITDrdg' = like ITDsd but use level/ridged ice + ! 'default' or 'none' = none + + logical (kind=log_kind), intent(in) :: & + use_smliq_pnd_in ! if true, use snow liquid tracer for ponds + ktherm = ktherm_in conduct = conduct_in fbot_xfer_type = fbot_xfer_type_in @@ -4286,6 +4742,7 @@ subroutine colpkg_init_parameters(& phi_c_slow_mode = phi_c_slow_mode_in phi_i_mushy = phi_i_mushy_in shortwave = shortwave_in + use_snicar = use_snicar_in albedo_type = albedo_type_in albicev = albicev_in albicei = albicei_in @@ -4434,6 +4891,7 @@ subroutine colpkg_init_parameters(& humtype = humtype_in doctype_s = doctype_s_in doctype_l = doctype_l_in + dictype_1 = dictype_1_in dontype_protein = dontype_protein_in fedtype_1 = fedtype_1_in feptype_1 = feptype_1_in @@ -4453,6 +4911,14 @@ subroutine colpkg_init_parameters(& F_abs_chl_sp = F_abs_chl_sp_in F_abs_chl_phaeo = F_abs_chl_phaeo_in ratio_C2N_proteins = ratio_C2N_proteins_in + snwredist = snwredist_in + use_smliq_pnd = use_smliq_pnd_in + rsnw_fall = rsnw_fall_in + rsnw_tmax = rsnw_tmax_in + rhosnew = rhosnew_in + rhosmax = rhosmax_in + windmin = windmin_in + drhosdwind = drhosdwind_in end subroutine colpkg_init_parameters @@ -4467,6 +4933,8 @@ subroutine colpkg_init_tracer_flags(& tr_pond_cesm_in , & ! if .true., use cesm pond tracer tr_pond_lvl_in , & ! if .true., use level-ice pond tracer tr_pond_topo_in , & ! if .true., use explicit topography-based ponds + tr_snow_in , & ! if .true., use snow trcrs (smice, smliq, rhos_cmp) + tr_rsnw_in , & ! if .true., use snow grain radius tracer tr_aero_in , & ! if .true., use aerosol tracers tr_brine_in , & ! if .true., brine height differs from ice thickness tr_bgc_S_in , & ! if .true., use zsalinity @@ -4492,6 +4960,8 @@ subroutine colpkg_init_tracer_flags(& tr_pond_cesm , & ! if .true., use cesm pond tracer tr_pond_lvl , & ! if .true., use level-ice pond tracer tr_pond_topo , & ! if .true., use explicit topography-based ponds + tr_snow , & ! if .true., use snow trcrs (smice, smliq, rhos_cmp) + tr_rsnw , & ! if .true., use snow grain radius tracer tr_aero , & ! if .true., use aerosol tracers tr_brine , & ! if .true., brine height differs from ice thickness tr_bgc_S , & ! if .true., use zsalinity @@ -4517,6 +4987,8 @@ subroutine colpkg_init_tracer_flags(& tr_pond_cesm_in , & ! if .true., use cesm pond tracer tr_pond_lvl_in , & ! if .true., use level-ice pond tracer tr_pond_topo_in , & ! if .true., use explicit topography-based ponds + tr_snow_in , & ! if .true., use snow trcrs (smice, smliq, rhos_cmp) + tr_rsnw_in , & ! if .true., use snow grain radius tracer tr_aero_in , & ! if .true., use aerosol tracers tr_brine_in , & ! if .true., brine height differs from ice thickness tr_bgc_S_in , & ! if .true., use zsalinity @@ -4540,6 +5012,8 @@ subroutine colpkg_init_tracer_flags(& tr_pond_cesm = tr_pond_cesm_in tr_pond_lvl = tr_pond_lvl_in tr_pond_topo = tr_pond_topo_in + tr_snow = tr_snow_in + tr_rsnw = tr_rsnw_in tr_aero = tr_aero_in tr_brine = tr_brine_in tr_bgc_S = tr_bgc_S_in @@ -4574,6 +5048,10 @@ subroutine colpkg_init_tracer_indices(& nt_hpnd_in, & ! melt pond depth nt_ipnd_in, & ! melt pond refrozen lid thickness nt_aero_in, & ! starting index for aerosols in ice + nt_smice_in, & ! snow ice mass + nt_smliq_in, & ! snow liquid mass + nt_rsnw_in, & ! snow grain radius + nt_rhos_in, & ! snow density nt_zaero_in, & ! black carbon and other aerosols nt_bgc_N_in , & ! diatoms, phaeocystis, pico/small nt_bgc_C_in , & ! diatoms, phaeocystis, pico/small @@ -4635,6 +5113,10 @@ subroutine colpkg_init_tracer_indices(& nt_hpnd, & ! melt pond depth nt_ipnd, & ! melt pond refrozen lid thickness nt_aero, & ! starting index for aerosols in ice + nt_smice, & ! snow ice mass + nt_smliq, & ! snow liquid mass + nt_rsnw, & ! snow grain radius + nt_rhos, & ! snow density nt_zaero, & ! black carbon and other aerosols nt_bgc_N , & ! diatoms, phaeocystis, pico/small nt_bgc_C , & ! diatoms, phaeocystis, pico/small @@ -4690,6 +5172,10 @@ subroutine colpkg_init_tracer_indices(& nt_hpnd_in, & ! melt pond depth nt_ipnd_in, & ! melt pond refrozen lid thickness nt_aero_in, & ! starting index for aerosols in ice + nt_smice_in, & ! snow ice mass + nt_smliq_in, & ! snow liquid mass + nt_rsnw_in, & ! snow grain radius + nt_rhos_in, & ! snow density nt_bgc_Nit_in, & ! nutrients nt_bgc_Am_in, & ! nt_bgc_Sil_in, & ! @@ -4773,6 +5259,10 @@ subroutine colpkg_init_tracer_indices(& nt_hpnd = nt_hpnd_in nt_ipnd = nt_ipnd_in nt_aero = nt_aero_in + nt_smice = nt_smice_in + nt_smliq = nt_smliq_in + nt_rsnw = nt_rsnw_in + nt_rhos = nt_rhos_in nt_bgc_Nit = nt_bgc_Nit_in nt_bgc_Am = nt_bgc_Am_in nt_bgc_Sil = nt_bgc_Sil_in @@ -4894,20 +5384,21 @@ subroutine colpkg_biogeochemistry(dt, & nblyr, nilyr, nslyr, n_algae, n_zaero, ncat, & n_doc, n_dic, n_don, n_fed, n_fep, & meltbn, melttn, congeln, snoicen, & - sst, sss, fsnow, meltsn, hmix, salinz, & + sst, sss, Tf, fsnow, meltsn, hmix, salinz, & hin_old, flux_bio, flux_bio_atm, & aicen_init, vicen_init, aicen, vicen, vsnon, & aice0, trcrn, vsnon_init, skl_bgc, & max_algae, max_nbtrcr, & + flux_bion, & l_stop, stop_label) use ice_algae, only: zbio, sklbio use ice_brine, only: preflushing_changes, compute_microS_mushy, & - update_hbrine, compute_microS + update_hbrine, compute_microS use ice_colpkg_shared, only: solve_zsal, z_tracers, phi_snow use ice_colpkg_tracers, only: nt_fbri, tr_brine, & - nt_bgc_S, nt_qice, nt_sice, nt_zbgc_frac, bio_index - use ice_constants_colpkg, only: c0, c1, puny + nt_bgc_S, nt_qice, nt_sice, nt_zbgc_frac, bio_index, bio_index_o + use ice_constants_colpkg, only: c0, c1, puny, p5 use ice_zsalinity, only: zsalinity use ice_zbgc_shared, only: zbgc_frac_init @@ -4928,7 +5419,7 @@ subroutine colpkg_biogeochemistry(dt, & real (kind=dbl_kind), dimension (:), intent(inout) :: & bgrid , & ! biology nondimensional vertical grid points igrid , & ! biology vertical interface points - cgrid , & ! CICE vertical coordinate + cgrid , & ! CICE vertical coordinate icgrid , & ! interface grid for CICE (shortwave variable) ocean_bio , & ! contains all the ocean bgc tracer concentrations fbio_snoice , & ! fluxes from snow to ice @@ -4937,9 +5428,9 @@ subroutine colpkg_biogeochemistry(dt, & dhbr_bot , & ! brine bottom change darcy_V , & ! darcy velocity positive up (m/s) hin_old , & ! old ice thickness - sice_rho , & ! avg sea ice density (kg/m^3) - ice_bio_net , & ! depth integrated tracer (mmol/m^2) - snow_bio_net , & ! depth integrated snow tracer (mmol/m^2) + sice_rho , & ! avg sea ice density (kg/m^3) + ice_bio_net , & ! depth integrated tracer (mmol/m^2) + snow_bio_net , & ! depth integrated snow tracer (mmol/m^2) flux_bio ! all bio fluxes to ocean logical (kind=log_kind), dimension (:), intent(inout) :: & @@ -4948,21 +5439,24 @@ subroutine colpkg_biogeochemistry(dt, & ! during a single time step from ice that was ! there the entire time step (true until ice forms) + real (kind=dbl_kind), dimension (:,:), intent(out) :: & + flux_bion ! per categeory ice to ocean biogeochemistry flux (mmol/m2/s) + real (kind=dbl_kind), dimension (:,:), intent(inout) :: & Zoo , & ! N losses accumulated in timestep (ie. zooplankton/bacteria) ! mmol/m^3 - bphi , & ! porosity of layers + bphi , & ! porosity of layers bTiz , & ! layer temperatures interpolated on bio grid (C) zfswin , & ! Shortwave flux into layers interpolated on bio grid (W/m^2) - iDi , & ! igrid Diffusivity (m^2/s) - iki , & ! Ice permeability (m^2) - trcrn ! tracers + iDi , & ! igrid Diffusivity (m^2/s) + iki , & ! Ice permeability (m^2) + trcrn ! tracers real (kind=dbl_kind), intent(inout) :: & grow_net , & ! Specific growth rate (/s) per grid cell PP_net , & ! Total production (mg C/m^2/s) per grid cell hbri , & ! brine height, area-averaged for comparison with hi (m) - zsal_tot , & ! Total ice salinity in per grid cell (g/m^2) + zsal_tot , & ! Total ice salinity in per grid cell (g/m^2) fzsal , & ! Total flux of salt to ocean at time step for conservation fzsal_g , & ! Total gravity drainage flux upNO , & ! nitrate uptake rate (mmol/m^2/d) times aice @@ -4970,7 +5464,7 @@ subroutine colpkg_biogeochemistry(dt, & totalChla ! ice integrated chla and summed over all algal groups (mg/m^2) logical (kind=log_kind), intent(inout) :: & - Rayleigh_criteria ! .true. means Ra_c was reached + Rayleigh_criteria ! .true. means Ra_c was reached real (kind=dbl_kind), dimension (:,:), intent(in) :: & fswpenln ! visible SW entering ice layers (W m-2) @@ -4982,11 +5476,11 @@ subroutine colpkg_biogeochemistry(dt, & meltbn , & ! bottom melt in category n (m) congeln , & ! congelation ice formation in category n (m) snoicen , & ! snow-ice formation in category n (m) - salinz , & ! initial salinity profile (ppt) - flux_bio_atm, & ! all bio fluxes to ice from atmosphere + salinz , & ! initial salinity profile (ppt) + flux_bio_atm, & ! all bio fluxes to ice from atmosphere aicen_init , & ! initial ice concentration, for linear ITD vicen_init , & ! initial ice volume (m), for linear ITD - vsnon_init , & ! initial snow volume (m), for aerosol + vsnon_init , & ! initial snow volume (m), for aerosol aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) @@ -4996,12 +5490,13 @@ subroutine colpkg_biogeochemistry(dt, & sss , & ! sea surface salinity (ppt) sst , & ! sea surface temperature (C) hmix , & ! mixed layer depth (m) + Tf , & ! basal freezing temperature (C) fsnow ! snowfall rate (kg/m^2 s) logical (kind=log_kind), intent(in) :: & skl_bgc ! if true, solve skeletal biochemistry - logical (kind=log_kind), intent(inout) :: & + logical (kind=log_kind), intent(inout) :: & l_stop ! if true, abort the model character (len=*), intent(inout) :: stop_label @@ -5018,7 +5513,7 @@ subroutine colpkg_biogeochemistry(dt, & hbr_old , & ! old brine thickness before growh/melt dhice , & ! change due to sublimation/condensation (m) kavg , & ! average ice permeability (m^2) - bphi_o , & ! surface ice porosity + bphi_o , & ! surface ice porosity hbrin , & ! brine height dh_direct ! surface flooding or runoff @@ -5030,19 +5525,30 @@ subroutine colpkg_biogeochemistry(dt, & real (kind=dbl_kind), dimension (nblyr+1) :: & ! Defined on Bio Grid interfaces - iphin , & ! porosity + iphin , & ! porosity ibrine_sal , & ! brine salinity (ppt) ibrine_rho , & ! brine_density (kg/m^3) iTin ! Temperature on the interface grid (oC) - real (kind=dbl_kind) :: & + real (kind=dbl_kind) :: & sloss ! brine flux contribution from surface runoff (g/m^2) + real (kind=dbl_kind), dimension (ncat) :: & + hbrnInitial, & ! inital brine height + hbrnFinal ! category initial and final brine heights + ! for bgc sk - real (kind=dbl_kind) :: & + real (kind=dbl_kind) :: & dh_bot_chl , & ! Chlorophyll may or may not flush dh_top_chl , & ! Chlorophyll may or may not flush - darcy_V_chl + darcy_V_chl + + real (kind=dbl_kind), dimension (nblyr+1) :: & + zspace ! vertical grid spacing + + zspace(:) = c1/real(nblyr,kind=dbl_kind) + zspace(1) = p5*zspace(1) + zspace(nblyr+1) = p5*zspace(nblyr+1) l_stop = .false. @@ -5051,11 +5557,16 @@ subroutine colpkg_biogeochemistry(dt, & !----------------------------------------------------------------- ! initialize !----------------------------------------------------------------- + flux_bion(:,n) = c0 hin_old(n) = c0 - if (aicen_init(n) > puny) then + hbrnFinal(n) = c0 + hbrnInitial(n) = c0 + + if (aicen_init(n) > puny) then hin_old(n) = vicen_init(n) & / aicen_init(n) else + first_ice(n) = .true. if (tr_brine) trcrn(nt_fbri,n) = c1 do mm = 1,nbtrcr @@ -5066,7 +5577,7 @@ subroutine colpkg_biogeochemistry(dt, & endif if (aicen(n) > puny) then - + dh_top_chl = c0 dh_bot_chl = c0 darcy_V_chl= c0 @@ -5077,7 +5588,7 @@ subroutine colpkg_biogeochemistry(dt, & kavg = c0 bphi_o = c0 sloss = c0 - + !----------------------------------------------------------------- ! brine dynamics !----------------------------------------------------------------- @@ -5085,24 +5596,25 @@ subroutine colpkg_biogeochemistry(dt, & dhbr_top(n) = c0 dhbr_bot(n) = c0 - if (tr_brine) then - if (trcrn(nt_fbri,n) .le. c0) trcrn(nt_fbri,n) = c1 + if (tr_brine) then dhice = c0 call preflushing_changes (n, aicen (n), & vicen (n), vsnon (n), & meltbn (n), melttn (n), & congeln (n), snoicen(n), & - hin_old (n), dhice, & + hin_old (n), dhice, & trcrn(nt_fbri,n), & dhbr_top(n), dhbr_bot(n), & hbr_old, hin, & hsn, first_ice(n), & l_stop, stop_label) + hbrnInitial(n) = hbr_old + if (l_stop) return - if (solve_zsal) then + if (solve_zsal) then call compute_microS (n, nilyr, nblyr, & bgrid, cgrid, igrid, & @@ -5116,7 +5628,7 @@ subroutine colpkg_biogeochemistry(dt, & salinz(1:nilyr), l_stop, stop_label) if (l_stop) return - else + else ! Requires the average ice permeability = kavg(:) ! and the surface ice porosity = zphi_o(:) @@ -5127,14 +5639,14 @@ subroutine colpkg_biogeochemistry(dt, & call compute_microS_mushy (n, nilyr, nblyr, & bgrid, cgrid, igrid, & trcrn(:,n), hin_old(n), hbr_old, & - sss, sst, bTiz(:,n), & + sss, sst, bTiz(:,n), & iTin(:), bphi(:,n), kavg, & bphi_o, phi_snow, bSin(:), & brine_sal(:), brine_rho(:), iphin(:), & ibrine_rho(:), ibrine_sal(:), sice_rho(n), & iDi(:,n), l_stop, stop_label) - endif ! solve_zsal + endif ! solve_zsal call update_hbrine (meltbn (n), melttn(n), & meltsn (n), dt, & @@ -5145,18 +5657,19 @@ subroutine colpkg_biogeochemistry(dt, & trcrn(nt_fbri,n), & snoicen(n), & dhbr_top(n), dhbr_bot(n), & - dh_top_chl, dh_bot_chl, & + dh_top_chl, dh_bot_chl, & kavg, bphi_o, & - darcy_V (n), darcy_V_chl, & + darcy_V (n), darcy_V_chl, & bphi(2,n), aice0, & dh_direct) - - hbri = hbri + hbrin * aicen(n) - if (solve_zsal) then + hbri = hbri + hbrin * aicen(n) + hbrnFinal(n) = hbrin + + if (solve_zsal) then call zsalinity (n, dt, & - nilyr, bgrid, & + nilyr, bgrid, & cgrid, igrid, & trcrn(nt_bgc_S:nt_bgc_S+nblyr-1,n), & trcrn(nt_qice:nt_qice+nilyr-1,n), & @@ -5167,8 +5680,8 @@ subroutine colpkg_biogeochemistry(dt, & iki(:,n), hbr_old, & hbrin, hin, & hin_old(n), iDi(:,n), & - darcy_V(n), brine_sal, & - brine_rho, ibrine_sal, & + darcy_V(n), brine_sal, & + brine_rho, ibrine_sal, & ibrine_rho, dh_direct, & Rayleigh_criteria, & first_ice(n), sss, & @@ -5176,9 +5689,9 @@ subroutine colpkg_biogeochemistry(dt, & dhbr_bot(n), & l_stop, stop_label, & fzsal, fzsal_g, & - bphi_o, nblyr, & + bphi_o, nblyr, & vicen(n), aicen_init(n), & - zsal_tot) + zsal_tot) if (l_stop) return @@ -5190,26 +5703,27 @@ subroutine colpkg_biogeochemistry(dt, & ! biogeochemistry !----------------------------------------------------------------- - if (z_tracers) then - + if (z_tracers) then + call zbio (dt, nblyr, & nslyr, nilyr, & melttn(n), & meltsn(n), meltbn (n), & - congeln(n), snoicen(n), & + congeln(n), snoicen(n), & nbtrcr, fsnow, & ntrcr, trcrn(1:ntrcr,n), & - bio_index(1:nbtrcr), aicen_init(n), & + bio_index(1:nbtrcr), bio_index_o(:), & + aicen_init(n), & vicen_init(n), vsnon_init(n), & vicen(n), vsnon(n), & - aicen(n), flux_bio_atm(1:nbtrcr), & + aicen(n), flux_bio_atm(:), & n, n_algae, & n_doc, n_dic, & n_don, & n_fed, n_fep, & n_zaero, first_ice(n), & - hin_old(n), ocean_bio(1:nbtrcr), & - bphi(:,n), iphin, & + hin_old(n), ocean_bio(:), & + bphi(:,n), iphin, & iDi(:,n), sss, & fswpenln(:,n), & dhbr_top(n), dhbr_bot(n), & @@ -5222,25 +5736,26 @@ subroutine colpkg_biogeochemistry(dt, & bphi_o, & dhice, iTin, & Zoo(:,n), & - flux_bio(1:nbtrcr), dh_direct, & + flux_bio(:), dh_direct, & upNO, upNH, & fbio_snoice, fbio_atmice, & - PP_net, ice_bio_net (1:nbtrcr), & - snow_bio_net(1:nbtrcr),grow_net, & + PP_net, ice_bio_net (:), & + snow_bio_net(:), grow_net, & totalChla, & + flux_bion(:,n), & l_stop, stop_label) - + if (l_stop) return - + elseif (skl_bgc) then - call sklbio (dt, ntrcr, & - nilyr, & + call sklbio (dt, Tf, & + ntrcr, nilyr, & nbtrcr, n_algae, & n_zaero, n_doc, & n_dic, n_don, & n_fed, n_fep, & - flux_bio (1:nbtrcr), ocean_bio(1:nbtrcr), & + flux_bio (1:nbtrcr), ocean_bio(:), & hmix, aicen (n), & meltbn (n), congeln (n), & fswthrun (n), first_ice(n), & @@ -5255,7 +5770,16 @@ subroutine colpkg_biogeochemistry(dt, & endif ! skl_bgc first_ice(n) = .false. - + else + do mm = 1, nbtrcr + do k = 1, nblyr+1 + flux_bion(mm,n) = flux_bion(mm,n) + trcrn(bio_index(mm) + k-1,n) * & + hin_old(n) * zspace(k)/dt * trcrn(nt_fbri,n) + flux_bio(mm) = flux_bio(mm) + trcrn(bio_index(mm) + k-1,n) * & + vicen_init(n) * zspace(k)/dt * trcrn(nt_fbri,n) + trcrn(bio_index(mm) + k-1,n) = c0 + enddo + enddo endif ! aicen > puny enddo ! ncat @@ -5268,8 +5792,8 @@ end subroutine colpkg_biogeochemistry subroutine colpkg_init_hbrine(bgrid, igrid, cgrid, & icgrid, swgrid, nblyr, nilyr, phi_snow) - use ice_constants_colpkg, only: c1, c1p5, c2, p5, c0, rhoi, rhos - + use ice_constants_colpkg, only: c1, c1p5, c2, p5, c0, rhoi, rhos, p25 + integer (kind=int_kind), intent(in) :: & nilyr, & ! number of ice layers nblyr ! number of bio layers @@ -5282,9 +5806,9 @@ subroutine colpkg_init_hbrine(bgrid, igrid, cgrid, & real (kind=dbl_kind), dimension (nblyr+1), intent(out) :: & igrid ! biology vertical interface points - + real (kind=dbl_kind), dimension (nilyr+1), intent(out) :: & - cgrid , & ! CICE vertical coordinate + cgrid , & ! CICE vertical coordinate icgrid , & ! interface grid for CICE (shortwave variable) swgrid ! grid for ice tracers used in dEdd scheme @@ -5292,65 +5816,65 @@ subroutine colpkg_init_hbrine(bgrid, igrid, cgrid, & k , & ! vertical index n ! thickness category index - real (kind=dbl_kind) :: & + real (kind=dbl_kind) :: & zspace ! grid spacing for CICE vertical grid if (phi_snow .le. c0) phi_snow = c1-rhos/rhoi !----------------------------------------------------------------- - ! Calculate bio gridn: 0 to 1 corresponds to ice top to bottom + ! Calculate bio gridn: 0 to 1 corresponds to ice top to bottom !----------------------------------------------------------------- - bgrid(:) = c0 ! zsalinity grid points + bgrid(:) = c0 ! zsalinity grid points bgrid(nblyr+2) = c1 ! bottom value - igrid(:) = c0 ! bgc interface grid points + igrid(:) = c0 ! bgc interface grid points igrid(1) = c0 ! ice top igrid(nblyr+1) = c1 ! ice bottom - + zspace = c1/max(c1,(real(nblyr,kind=dbl_kind))) do k = 2, nblyr+1 bgrid(k) = zspace*(real(k,kind=dbl_kind) - c1p5) enddo - + do k = 2, nblyr igrid(k) = p5*(bgrid(k+1)+bgrid(k)) enddo !----------------------------------------------------------------- - ! Calculate CICE cgrid for interpolation ice top (0) to bottom (1) + ! Calculate CICE cgrid for interpolation ice top (0) to bottom (1) !----------------------------------------------------------------- - + cgrid(1) = c0 ! CICE vertical grid top point zspace = c1/(real(nilyr,kind=dbl_kind)) ! CICE grid spacing - + do k = 2, nilyr+1 - cgrid(k) = zspace * (real(k,kind=dbl_kind) - c1p5) - enddo + cgrid(k) = zspace * (real(k,kind=dbl_kind) - c1p5) + enddo !----------------------------------------------------------------- ! Calculate CICE icgrid for ishortwave interpolation top(0) , bottom (1) !----------------------------------------------------------------- - - icgrid(1) = c0 + + icgrid(1) = c0 zspace = c1/(real(nilyr,kind=dbl_kind)) ! CICE grid spacing - + do k = 2, nilyr+1 icgrid(k) = zspace * (real(k,kind=dbl_kind)-c1) - enddo + enddo !------------------------------------------------------------------------ ! Calculate CICE swgrid for dEdd ice: top of ice (0) , bottom of ice (1) ! Does not include snow ! see ice_shortwave.F90 ! swgrid represents the layer index of the delta-eddington ice layer index - !------------------------------------------------------------------------ + !------------------------------------------------------------------------ zspace = c1/(real(nilyr,kind=dbl_kind)) ! CICE grid spacing - swgrid(1) = min(c1/60.0_dbl_kind, zspace/c2) + swgrid(1) = min(c1/60.0_dbl_kind, zspace*p25) swgrid(2) = zspace/c2 !+ swgrid(1) do k = 3, nilyr+1 swgrid(k) = zspace * (real(k,kind=dbl_kind)-c1p5) - enddo + enddo end subroutine colpkg_init_hbrine @@ -5416,7 +5940,7 @@ subroutine colpkg_init_ocean_conc (amm, dmsp, dms, algalN, doc, dic, don, & doc(2) = 9.0_dbl_kind ! lipids doc(3) = c1 ! do k = 1, max_dic - dic(k) = c1 + dic(k) = 1950.0_dbl_kind ! 1950-2260 mmol C/m3 (Tynan et al. 2015) enddo do k = 1, max_don don(k) = 12.9_dbl_kind @@ -5629,12 +6153,15 @@ subroutine colpkg_get_warnings(warningsOut) warningsOut integer :: & - iWarning + iWarning, & + nWarnings + + nWarnings = get_number_warnings() if (allocated(warningsOut)) deallocate(warningsOut) - allocate(warningsOut(get_number_warnings())) + allocate(warningsOut(nWarnings)) - do iWarning = 1, get_number_warnings() + do iWarning = 1, nWarnings warningsOut(iWarning) = trim(get_warning(iWarning)) enddo diff --git a/src/core_seaice/column/ice_colpkg_shared.F90 b/src/core_seaice/column/ice_colpkg_shared.F90 index 5e49c598c1..cb8125cbd2 100644 --- a/src/core_seaice/column/ice_colpkg_shared.F90 +++ b/src/core_seaice/column/ice_colpkg_shared.F90 @@ -89,7 +89,14 @@ module ice_colpkg_shared real (kind=dbl_kind), parameter, public :: & hi_ssl = 0.050_dbl_kind, & ! ice surface scattering layer thickness (m) - hs_ssl = 0.040_dbl_kind ! snow surface scattering layer thickness (m) + hs_ssl = 0.040_dbl_kind, & ! snow surface scattering layer thickness (m) + hs_ssl_min = 5.0e-4_dbl_kind ! minimum snow scattering layer thickness for aerosol accumulation (m) + + ! snicar 5 band system, set in namelist + logical (kind=log_kind), public :: & + use_snicar ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice !----------------------------------------------------------------------- ! Parameters for ridging and strength @@ -171,6 +178,47 @@ module ice_colpkg_shared real (kind=dbl_kind), public :: & hp1 ! critical parameter for pond ice thickness +!----------------------------------------------------------------------- +! Parameters for snow +!----------------------------------------------------------------------- + + ! snow metamorphism parameters, set in namelist + real (kind=dbl_kind), public :: & + rsnw_fall , & ! fallen snow grain radius (10^-6 m)) 54.5 um CLM ** + ! 30 um is minimum for defined mie properties + rsnw_tmax , & ! maximum dry metamorphism snow grain radius (10^-6 m) + ! 1500 um is maximum for defined mie properties + rhosnew , & ! new snow density (kg/m^3) + rhosmax , & ! maximum snow density (kg/m^3) + windmin , & ! minimum wind speed to compact snow (m/s) + drhosdwind ! wind compaction factor (kg s/m^4) + + character(len=char_len), public :: & + snwredist ! type of snow redistribution + ! '30percent' = 30% rule, precip only + ! '30percentsw' = 30% rule with shortwave + ! 'ITDsd' = Lecomte PhD, 2014 + ! 'ITDrdg' = like ITDsd but use level/ridged ice + ! 'default' or 'none' = none + + logical (kind=log_kind), public :: & + use_smliq_pnd ! if true, use snow liquid tracer for ponds + + ! indices for aging lookup table [idx] + integer(kind=int_kind), parameter, public :: & + idx_T_max = 11 , & ! maxiumum temperature index + idx_T_min = 1 , & ! minimum temperature index + idx_Tgrd_max = 31 , & ! maxiumum temperature gradient index + idx_Tgrd_min = 1 , & ! minimum temperature gradient index + idx_rhos_max = 8 , & ! maxiumum snow density index + idx_rhos_min = 1 ! minimum snow density index + + ! dry snow aging parameters + real (kind=dbl_kind), dimension(8,31,11), public :: & + snowage_tau, & ! (10^-6 m) + snowage_kappa, & ! + snowage_drdt0 ! (10^-6 m/hr) + !----------------------------------------------------------------------- ! Parameters for biogeochemistry !----------------------------------------------------------------------- @@ -345,6 +393,7 @@ module ice_colpkg_shared humtype , & ! doctype_s , & ! doctype_l , & ! + dictype_1 , & ! dontype_protein , & ! fedtype_1 , & ! feptype_1 , & ! @@ -374,8 +423,8 @@ module ice_colpkg_shared ! Algal types: Diatoms, flagellates, Phaeocycstis ! DOC : Proteins, EPS, Lipids !----------------------------------------------------------------- - real (kind=dbl_kind), parameter, dimension(max_dic), public :: & - dictype = (/-c1/) ! not in namelist + real (kind=dbl_kind), dimension(max_dic), public :: & + dictype ! added to namelist real (kind=dbl_kind), dimension(max_algae), public :: & algaltype ! tau_min for both retention and release @@ -407,9 +456,7 @@ module ice_colpkg_shared !----------------------------------------------------------------- real (kind=dbl_kind), parameter, public :: & - rhosi = 940.0_dbl_kind, & ! average sea ice density - ! Cox and Weeks, 1982: 919-974 kg/m^2 - sk_l = 0.03_dbl_kind ! skeletal layer thickness (m) + rhosi = 940.0_dbl_kind real (kind=dbl_kind), dimension(max_algae), public :: & R_C2N , & ! algal C to N (mole/mole) diff --git a/src/core_seaice/column/ice_colpkg_tracers.F90 b/src/core_seaice/column/ice_colpkg_tracers.F90 index b703e93143..8bb2f5710d 100644 --- a/src/core_seaice/column/ice_colpkg_tracers.F90 +++ b/src/core_seaice/column/ice_colpkg_tracers.F90 @@ -34,6 +34,10 @@ module ice_colpkg_tracers nt_apnd , & ! melt pond area fraction nt_hpnd , & ! melt pond depth nt_ipnd , & ! melt pond refrozen lid thickness + nt_smice , & ! mass of ice in snow + nt_smliq , & ! mass of liquid water in snow + nt_rhos , & ! effective snow density (compaction) + nt_rsnw , & ! effective snow grain radius nt_aero , & ! starting index for aerosols in ice nt_bgc_Nit, & ! nutrients nt_bgc_Am, & ! @@ -54,6 +58,8 @@ module ice_colpkg_tracers tr_pond_cesm, & ! if .true., use cesm pond tracer tr_pond_lvl , & ! if .true., use level-ice pond tracer tr_pond_topo, & ! if .true., use explicit topography-based ponds + tr_snow , & ! if .true., use snow tracers (ice, liquid water mass) + tr_rsnw , & ! if .true., use dynamic snow grain radius tracer tr_aero , & ! if .true., use aerosol tracers tr_brine ! if .true., brine height differs from ice thickness @@ -158,9 +164,10 @@ subroutine colpkg_compute_tracers (ntrcr, trcr_depend, & atrcrn, aicen, & vicen, vsnon, & trcr_base, n_trcr_strata, & - nt_strata, trcrn) + nt_strata, trcrn, & + Tf) - use ice_constants_colpkg, only: c0, c1, puny, Tocnfrz + use ice_constants_colpkg, only: c0, c1, puny integer (kind=int_kind), intent(in) :: & ntrcr ! number of tracers in use @@ -182,7 +189,8 @@ subroutine colpkg_compute_tracers (ntrcr, trcr_depend, & real (kind=dbl_kind), intent(in) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) - vsnon ! volume per unit area of snow (m) + vsnon , & ! volume per unit area of snow (m) + Tf ! ocean freezing temperature (Celsius) real (kind=dbl_kind), dimension (ntrcr), intent(out) :: & trcrn ! ice tracers @@ -215,7 +223,7 @@ subroutine colpkg_compute_tracers (ntrcr, trcr_depend, & trcrn(it) = atrcrn(it) / aicen else trcrn(it) = c0 - if (it == nt_Tsfc) trcrn(it) = Tocnfrz ! surface temperature + if (it == nt_Tsfc) trcrn(it) = Tf ! surface temperature endif else diff --git a/src/core_seaice/column/ice_firstyear.F90 b/src/core_seaice/column/ice_firstyear.F90 index 01b898a00c..d0d259150c 100755 --- a/src/core_seaice/column/ice_firstyear.F90 +++ b/src/core_seaice/column/ice_firstyear.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_firstyear.F90 1012 2015-06-26 12:34:09Z eclare $ +! SVN:$Id: ice_firstyear.F90 1099 2015-12-12 18:12:30Z eclare $ !======================================================================= ! ! First year concentration tracer for sea ice diff --git a/src/core_seaice/column/ice_flux_colpkg.F90 b/src/core_seaice/column/ice_flux_colpkg.F90 index 760f6b87b7..4806fdcecc 100644 --- a/src/core_seaice/column/ice_flux_colpkg.F90 +++ b/src/core_seaice/column/ice_flux_colpkg.F90 @@ -47,10 +47,12 @@ subroutine merge_fluxes (aicen, & Tref, Qref, & fresh, fsalt, & fhocn, fswthru, & - melttn, meltsn, meltbn, congeln, snoicen, & - meltt, melts, & - meltb, & - congel, snoice, & + melttn, meltsn, & + meltbn, congeln, & + snoicen, meltsliqn, & + meltt, melts, & + meltb, congel, & + snoice, meltsliq, & Uref, Urefn ) ! single category fluxes @@ -77,6 +79,7 @@ subroutine merge_fluxes (aicen, & melttn , & ! top ice melt (m) meltbn , & ! bottom ice melt (m) meltsn , & ! snow melt (m) + meltsliqn,& ! snow liquid contribution to meltpond (kg/m^2) congeln , & ! congelation ice growth (m) snoicen ! snow-ice growth (m) @@ -104,6 +107,7 @@ subroutine merge_fluxes (aicen, & meltt , & ! top ice melt (m) meltb , & ! bottom ice melt (m) melts , & ! snow melt (m) + meltsliq, & ! snow liquid contribution to meltponds (kg/m^2) congel , & ! congelation ice growth (m) snoice ! snow-ice growth (m) @@ -151,6 +155,7 @@ subroutine merge_fluxes (aicen, & melts = melts + meltsn * aicen congel = congel + congeln * aicen snoice = snoice + snoicen * aicen + meltsliq = meltsliq + meltsliqn * aicen end subroutine merge_fluxes diff --git a/src/core_seaice/column/ice_itd.F90 b/src/core_seaice/column/ice_itd.F90 index 5fa2b5f14b..a9de8a2ba7 100644 --- a/src/core_seaice/column/ice_itd.F90 +++ b/src/core_seaice/column/ice_itd.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_itd.F90 1182 2017-03-16 19:29:26Z njeffery $ +! SVN:$Id: ice_itd.F90 1196 2017-04-18 13:32:23Z eclare $ !======================================================================= ! Routines to initialize the ice thickness distribution and @@ -28,7 +28,7 @@ module ice_itd use ice_kinds_mod use ice_constants_colpkg, only: c0, c1, c2, p001, puny, p5, & - Lfresh, rhos, ice_ref_salinity, hs_min, cp_ice, Tocnfrz, rhoi + Lfresh, rhos, ice_ref_salinity, hs_min, cp_ice, rhoi use ice_warnings, only: & add_warning @@ -89,7 +89,7 @@ end subroutine aggregate_area subroutine rebin (ntrcr, trcr_depend, & trcr_base, & n_trcr_strata, & - nt_strata, & + nt_strata, Tf, & aicen, trcrn, & vicen, vsnon, & ncat, hin_max, & @@ -103,6 +103,9 @@ subroutine rebin (ntrcr, trcr_depend, & trcr_depend, & ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon n_trcr_strata ! number of underlying tracer layers + real (kind=dbl_kind), intent(in) :: & + Tf ! ocean freezing temperature (C) + real (kind=dbl_kind), dimension (:,:), intent(in) :: & trcr_base ! = 0 or 1 depending on tracer dependency ! argument 2: (1) aice, (2) vice, (3) vsno @@ -207,7 +210,7 @@ subroutine rebin (ntrcr, trcr_depend, & trcr_depend, & trcr_base, & n_trcr_strata, & - nt_strata, & + nt_strata, Tf, & aicen, trcrn, & vicen, vsnon, & hicen, donor, & @@ -255,7 +258,7 @@ subroutine rebin (ntrcr, trcr_depend, & trcr_depend, & trcr_base, & n_trcr_strata, & - nt_strata, & + nt_strata, Tf, & aicen, trcrn, & vicen, vsnon, & hicen, donor, & @@ -345,7 +348,7 @@ subroutine shift_ice (ntrcr, ncat, & trcr_depend, & trcr_base, & n_trcr_strata, & - nt_strata, & + nt_strata, Tf, & aicen, trcrn, & vicen, vsnon, & hicen, donor, & @@ -362,6 +365,9 @@ subroutine shift_ice (ntrcr, ncat, & trcr_depend, & ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon n_trcr_strata ! number of underlying tracer layers + real (kind=dbl_kind), intent(in) :: & + Tf ! ocean freezing temperature (C) + real (kind=dbl_kind), dimension (:,:), intent(in) :: & trcr_base ! = 0 or 1 depending on tracer dependency ! argument 2: (1) aice, (2) vice, (3) vsno @@ -647,11 +653,12 @@ subroutine shift_ice (ntrcr, ncat, & ! Compute new tracers !----------------------------------------------------------------- - call colpkg_compute_tracers (ntrcr, trcr_depend, & - atrcrn(:,n), aicen(n), & - vicen(n), vsnon(n), & + call colpkg_compute_tracers (ntrcr, trcr_depend, & + atrcrn(:,n), aicen(n), & + vicen(n), vsnon(n), & trcr_base, n_trcr_strata, & - nt_strata, trcrn(:,n)) + nt_strata, trcrn(:,n), & + Tf) enddo ! ncat @@ -744,7 +751,8 @@ end subroutine column_conservation_check ! ! author: William H. Lipscomb, LANL - subroutine cleanup_itd (dt, ntrcr, & + subroutine cleanup_itd (dt, Tf, & + ntrcr, & nilyr, nslyr, & ncat, hin_max, & aicen, trcrn, & @@ -774,7 +782,8 @@ subroutine cleanup_itd (dt, ntrcr, & n_aero ! number of aerosol tracers real (kind=dbl_kind), intent(in) :: & - dt ! time step + dt , & ! time step + Tf ! ocean freezing temperature (Celsius) real (kind=dbl_kind), dimension(0:ncat), intent(in) :: & hin_max ! category boundaries (m) @@ -916,7 +925,7 @@ subroutine cleanup_itd (dt, ntrcr, & call rebin (ntrcr, trcr_depend, & trcr_base, & n_trcr_strata, & - nt_strata, & + nt_strata, Tf, & aicen, trcrn, & vicen, vsnon, & ncat, hin_max, & @@ -929,7 +938,8 @@ subroutine cleanup_itd (dt, ntrcr, & !----------------------------------------------------------------- if (limit_aice) then - call zap_small_areas (dt, ntrcr, & + call zap_small_areas (dt, Tf, & + ntrcr, & ncat, n_aero, & nblyr, & nilyr, nslyr, & @@ -1016,7 +1026,8 @@ end subroutine cleanup_itd ! ! author: William H. Lipscomb, LANL - subroutine zap_small_areas (dt, ntrcr, & + subroutine zap_small_areas (dt, Tf, & + ntrcr, & ncat, n_aero, & nblyr, & nilyr, nslyr, & @@ -1035,7 +1046,8 @@ subroutine zap_small_areas (dt, ntrcr, & nt_apnd, nt_hpnd, nt_fbri, tr_brine, nt_bgc_S, & bio_index use ice_colpkg_shared, only: solve_zsal, skl_bgc, z_tracers, min_salin, & - sk_l, rhosi + rhosi + use ice_constants_colpkg, only: sk_l use ice_zbgc_shared, only: zap_small_bgc integer (kind=int_kind), intent(in) :: & @@ -1048,7 +1060,8 @@ subroutine zap_small_areas (dt, ntrcr, & nbtrcr ! number of biology tracers real (kind=dbl_kind), intent(in) :: & - dt ! time step + dt , & ! time step + Tf ! ocean freezing temperature (Celsius) real (kind=dbl_kind), intent(inout) :: & aice , & ! total ice concentration @@ -1188,7 +1201,7 @@ subroutine zap_small_areas (dt, ntrcr, & aice0 = aice0 + aicen(n) aicen(n) = c0 vicen(n) = c0 - trcrn(nt_Tsfc,n) = Tocnfrz + trcrn(nt_Tsfc,n) = Tf !----------------------------------------------------------------- ! Zap snow @@ -1382,7 +1395,7 @@ subroutine zap_snow(dt, nslyr, & endif ! tr_aero if (z_tracers) then - dvssl = min(p5*vsnon, hs_ssl*aicen) !snow surface layer + dvssl = min(p5*vsnon/real(nslyr,kind=dbl_kind), hs_ssl*aicen) !snow surface layer dvint = vsnon- dvssl !snow interior do it = 1, nbtrcr diff --git a/src/core_seaice/column/ice_mechred.F90 b/src/core_seaice/column/ice_mechred.F90 index 668621fee7..2754cd1c2a 100644 --- a/src/core_seaice/column/ice_mechred.F90 +++ b/src/core_seaice/column/ice_mechred.F90 @@ -100,7 +100,8 @@ subroutine ridge_ice (dt, ndtd, & aredistn, vredistn, & dardg1ndt, dardg2ndt, & dvirdgndt, & - araftn, vraftn) + araftn, vraftn, & + Tf) use ice_colpkg_tracers, only: nt_qice, nt_qsno, nt_fbri, nt_sice @@ -114,7 +115,8 @@ subroutine ridge_ice (dt, ndtd, & real (kind=dbl_kind), intent(in) :: & mu_rdg , & ! gives e-folding scale of ridged ice (m^.5) - dt ! time step + dt , & ! time step + Tf ! ocean freezing temperature (C) real (kind=dbl_kind), dimension(0:ncat), intent(inout) :: & hin_max ! category limits (m) @@ -371,7 +373,8 @@ subroutine ridge_ice (dt, ndtd, & msnow_mlt, esnow_mlt, & maero, mpond, & l_stop, stop_label, & - aredistn, vredistn) + aredistn, vredistn, & + Tf) if (l_stop) return @@ -1043,7 +1046,8 @@ subroutine ridge_shift (ntrcr, dt, & msnow_mlt, esnow_mlt, & maero, mpond, & l_stop, stop_label, & - aredistn, vredistn) + aredistn, vredistn, & + Tf) use ice_colpkg_tracers, only: nt_qsno, nt_fbri, & nt_alvl, nt_vlvl, nt_aero, tr_aero, & @@ -1058,7 +1062,8 @@ subroutine ridge_shift (ntrcr, dt, & krdg_redist ! selects redistribution function real (kind=dbl_kind), intent(in) :: & - dt ! time step (s) + dt, & ! time step (s) + Tf ! ocean freezing temperature (C) integer (kind=int_kind), dimension (:), intent(in) :: & trcr_depend, & ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon @@ -1529,7 +1534,8 @@ subroutine ridge_shift (ntrcr, dt, & atrcrn(:,n), aicen(n), & vicen(n), vsnon(n), & trcr_base, n_trcr_strata, & - nt_strata, trcrn(:,n)) + nt_strata, trcrn(:,n), & + Tf) enddo end subroutine ridge_shift diff --git a/src/core_seaice/column/ice_meltpond_cesm.F90 b/src/core_seaice/column/ice_meltpond_cesm.F90 index 815b6b4c4e..47d926d113 100644 --- a/src/core_seaice/column/ice_meltpond_cesm.F90 +++ b/src/core_seaice/column/ice_meltpond_cesm.F90 @@ -35,7 +35,8 @@ subroutine compute_ponds_cesm(dt, hi_min, & rfrac, meltt, & melts, frain, & aicen, vicen, vsnon, & - Tsfcn, apnd, hpnd) + Tsfcn, apnd, hpnd, & + meltsliqn, use_smliq_pnd) real (kind=dbl_kind), intent(in) :: & dt, & ! time step (s) @@ -43,6 +44,7 @@ subroutine compute_ponds_cesm(dt, hi_min, & pndaspect ! ratio of pond depth to pond fraction real (kind=dbl_kind), intent(in) :: & + meltsliqn, & ! liquid input from snow liquid tracer rfrac, & ! water fraction retained for melt ponds meltt, & melts, & @@ -58,6 +60,9 @@ subroutine compute_ponds_cesm(dt, hi_min, & apnd, & hpnd + logical (kind=log_kind), intent(in) :: & + use_smliq_pnd ! use snow liquid and ice tracers + ! local temporary variables real (kind=dbl_kind) :: & @@ -104,11 +109,18 @@ subroutine compute_ponds_cesm(dt, hi_min, & !----------------------------------------------------------- ! Update pond volume !----------------------------------------------------------- - volpn = volpn & + if (use_smliq_pnd) then + volpn = volpn & + + rfrac/rhofresh*(meltt*rhoi & + + meltsliqn) & + * aicen + else + volpn = volpn & + rfrac/rhofresh*(meltt*rhoi & + melts*rhos & + frain* dt)& * aicen + endif !----------------------------------------------------------- ! Shrink pond volume under freezing conditions diff --git a/src/core_seaice/column/ice_meltpond_lvl.F90 b/src/core_seaice/column/ice_meltpond_lvl.F90 index 8a2dbe0b6e..ada156d6be 100644 --- a/src/core_seaice/column/ice_meltpond_lvl.F90 +++ b/src/core_seaice/column/ice_meltpond_lvl.F90 @@ -41,7 +41,8 @@ subroutine compute_ponds_lvl(dt, nilyr, & aicen, vicen, vsnon, & qicen, sicen, & Tsfcn, alvl, & - apnd, hpnd, ipnd) + apnd, hpnd, ipnd, & + meltsliqn, use_smliq_pnd) integer (kind=int_kind), intent(in) :: & nilyr, & ! number of ice layers @@ -68,7 +69,8 @@ subroutine compute_ponds_lvl(dt, nilyr, & fsurfn,& ! atm-ice surface heat flux (W/m2) aicen, & ! ice area fraction vicen, & ! ice volume (m) - vsnon ! snow volume (m) + vsnon, & ! snow volume (m) + meltsliqn ! liquid contribution to meltponds in dt (kg/m^2) real (kind=dbl_kind), & intent(inout) :: & @@ -86,6 +88,9 @@ subroutine compute_ponds_lvl(dt, nilyr, & intent(out) :: & ffrac ! fraction of fsurfn over pond used to melt ipond + logical (kind=log_kind), intent(in) :: & + use_smliq_pnd ! use snow liquid and ice tracers + ! local temporary variables real (kind=dbl_kind) :: & @@ -151,9 +156,14 @@ subroutine compute_ponds_lvl(dt, nilyr, & ! update pond volume !----------------------------------------------------------- ! add melt water - dvn = rfrac/rhofresh*(meltt*rhoi & + if (use_smliq_pnd) then + dvn = rfrac/rhofresh*(meltt*rhoi & + + meltsliqn)*aicen + else + dvn = rfrac/rhofresh*(meltt*rhoi & + melts*rhos & + frain* dt)*aicen + endif ! shrink pond volume under freezing conditions if (trim(frzpnd) == 'cesm') then diff --git a/src/core_seaice/column/ice_mushy_physics.F90 b/src/core_seaice/column/ice_mushy_physics.F90 index 3279de5250..9f2bf32a89 100644 --- a/src/core_seaice/column/ice_mushy_physics.F90 +++ b/src/core_seaice/column/ice_mushy_physics.F90 @@ -4,7 +4,7 @@ module ice_mushy_physics use ice_constants_colpkg, only: c0, c1, c2, c4, c8, c10, c1000, & p001, p01, p05, p1, p2, p5, pi, bignum, puny, ice_ref_salinity, & viscosity_dyn, rhow, rhoi, rhos, cp_ocn, cp_ice, Lfresh, gravit, & - hs_min, ksno + ksno implicit none diff --git a/src/core_seaice/column/ice_shortwave.F90 b/src/core_seaice/column/ice_shortwave.F90 index 5c1d1a1593..506b98505d 100644 --- a/src/core_seaice/column/ice_shortwave.F90 +++ b/src/core_seaice/column/ice_shortwave.F90 @@ -45,8 +45,10 @@ module ice_shortwave use ice_constants_colpkg, only: c0, c1, c1p5, c2, c3, c4, c10, & p01, p1, p15, p25, p5, p75, puny, & albocn, Timelt, snowpatch, awtvdr, awtidr, awtvdf, awtidf, & - kappav, hs_min, rhofresh, rhos, nspint + kappav, hs_min, rhofresh, rhos, nspint, nspint_5bd, snwlvlfac use ice_colpkg_shared, only: hi_ssl, hs_ssl, modal_aero, max_aero + use ice_colpkg_shared, only: hi_ssl, hs_ssl, modal_aero, rsnw_fall, & + rsnw_tmax use ice_warnings, only: add_warning implicit none @@ -684,6 +686,8 @@ subroutine run_dEdd(dt, tr_aero, & vsnon, Tsfcn, & alvln, apndn, & hpndn, ipndn, & + snwredist, & + rsnow, tr_rsnw, & aeron, kalg, & zbion, & heat_capacity, & @@ -715,8 +719,23 @@ subroutine run_dEdd(dt, tr_aero, & albpndn, apeffn, & snowfracn, & dhsn, ffracn, & + rsnw_dEddn, & l_print_point, & - initonly) + initonly, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) use ice_orbital, only: compute_coszen @@ -737,6 +756,7 @@ subroutine run_dEdd(dt, tr_aero, & tr_pond_cesm, & ! if .true., use explicit topography-based ponds tr_pond_lvl , & ! if .true., use explicit topography-based ponds tr_pond_topo, & ! if .true., use explicit topography-based ponds + tr_rsnw, & ! if .true., use snow grain radius tracer dEdd_algae, & ! .true. use prognostic chla in dEdd tr_bgc_N, & ! .true. active bgc (skl or z) tr_zaero, & ! .true. use zaerosols @@ -765,6 +785,27 @@ subroutine run_dEdd(dt, tr_aero, & waer_bc_tab, & ! aerosol single scatter albedo (fraction) gaer_bc_tab ! aerosol asymmetry parameter (cos(theta)) + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc, & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs, & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc, & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs, & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc, & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment bcenh ! BC absorption enhancement factor @@ -800,11 +841,16 @@ subroutine run_dEdd(dt, tr_aero, & hpndn, & ! pond depth (m) ipndn ! pond refrozen lid thickness (m) + character(len=char_len), intent(in) :: & + snwredist ! type of snow redistribution + real(kind=dbl_kind), dimension(:,:), intent(in) :: & + rsnow, & ! snow grain radius tracer (10^-6 m) aeron, & ! aerosols (kg/m^3) zbion ! zaerosols (kg/m^3) + chlorophyll on shorthwave grid real(kind=dbl_kind), dimension(:), intent(inout) :: & + rsnw_dEddn, & ! snow grain radius if .not. tr_rsnw (10^-6 m) dhsn ! depth difference for snow on sea ice and pond ice real(kind=dbl_kind), intent(inout) :: & @@ -829,6 +875,11 @@ subroutine run_dEdd(dt, tr_aero, & Iswabsn , & ! SW radiation absorbed in ice layers (W m-2) fswpenln ! visible SW entering ice layers (W m-2) + logical (kind=log_kind), intent(in) :: & + use_snicar ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + logical (kind=log_kind), intent(in) :: & l_print_point @@ -841,7 +892,10 @@ subroutine run_dEdd(dt, tr_aero, & ! snow variables for Delta-Eddington shortwave real (kind=dbl_kind) :: & fsn , & ! snow horizontal fraction - hsn ! snow depth (m) + hsn , & ! snow depth (m) + hsnlvl , & ! snow depth over level ice (m) + vsn , & ! snow volume + alvl ! area fraction of level ice real (kind=dbl_kind), dimension (nslyr) :: & rhosnwn , & ! snow density (kg/m3) @@ -854,6 +908,7 @@ subroutine run_dEdd(dt, tr_aero, & integer (kind=int_kind) :: & n , & ! thickness category index + k , & ! snow layer index na ! aerosol index real (kind=dbl_kind) :: & @@ -865,6 +920,7 @@ subroutine run_dEdd(dt, tr_aero, & hmx , & ! maximum available snow infiltration equivalent depth dhs , & ! local difference in snow depth on sea ice and pond ice spn , & ! snow depth on refrozen pond (m) + rnslyr , & ! 1/nslyr tmp ! 0 or 1 logical (kind=log_kind) :: & @@ -898,6 +954,7 @@ subroutine run_dEdd(dt, tr_aero, & rsnwn(:) = c0 apeffn(n) = c0 ! for history snowfracn(n) = c0 ! for history + rsnw_dEddn(n) = c0 ! for history if (aicen(n) > puny) then @@ -906,7 +963,8 @@ subroutine run_dEdd(dt, tr_aero, & aicen(n), vsnon(n), & Tsfcn(n), fsn, & hs0, hsn, & - rhosnwn, rsnwn) + rhosnwn, rsnwn, & + rsnow(:,n), tr_rsnw) ! set pond properties if (tr_pond_cesm) then @@ -925,6 +983,26 @@ subroutine run_dEdd(dt, tr_aero, & fsn = min(fsn, c1-fpn) apeffn(n) = fpn ! for history elseif (tr_pond_lvl) then + hsnlvl = hsn ! initialize + if (trim(snwredist) == '30percentsw') then + hsnlvl = hsn / (c1 + snwlvlfac*(c1-alvln(n))) + ! snow volume over level ice + alvl = aicen(n) * alvln(n) + if (alvl > puny) then + vsn = hsnlvl * alvl + else + vsn = vsnon(n) + alvl = aicen(n) + endif + ! set snow properties over level ice + call shortwave_dEdd_set_snow(nslyr, R_snw, & + dT_mlt, rsnw_mlt, & + alvl, vsn, & + Tsfcn(n), fsn, & + hs0, hsnlvl, & + rhosnwn(:), rsnwn(:), & + rsnow(:,n), tr_rsnw) + endif ! snwredist fpn = c0 ! fraction of ice covered in pond hpn = c0 ! pond depth over fpn ! refrozen pond lid thickness avg over ice @@ -933,8 +1011,8 @@ subroutine run_dEdd(dt, tr_aero, & dhs = dhsn(n) ! snow depth difference, sea ice - pond if (.not. linitonly .and. ipn > puny .and. & dhs < puny .and. fsnow*dt > hs_min) & - dhs = hsn - fsnow*dt ! initialize dhs>0 - spn = hsn - dhs ! snow depth on pond ice + dhs = hsnlvl - fsnow*dt ! initialize dhs>0 + spn = hsnlvl - dhs ! snow depth on pond ice if (.not. linitonly .and. ipn*spn < puny) dhs = c0 dhsn(n) = dhs ! save: constant until reset to 0 @@ -959,7 +1037,7 @@ subroutine run_dEdd(dt, tr_aero, & ! infiltrate snow hp = hpn if (hp > puny) then - hs = hsn + hs = hsnlvl ! melt ponds reside on level ice rp = rhofresh*hp/(rhofresh*hp + rhos*hs) if (rp < p15) then fpn = c0 @@ -968,8 +1046,9 @@ subroutine run_dEdd(dt, tr_aero, & hmx = hs*(rhofresh - rhos)/rhofresh tmp = max(c0, sign(c1, hp-hmx)) ! 1 if hp>=hmx, else 0 hp = (rhofresh*hp + rhos*hs*tmp) & - / (rhofresh - rhos*(c1-tmp)) - hsn = hs - hp*fpn*(c1-tmp) + / (rhofresh - rhos*(c1-tmp)) +!echmod hsn = hs - hp*fpn*(c1-tmp) + hsn = hsn - hp*fpn*(c1-tmp) hpn = hp * tmp fpn = fpn * tmp endif @@ -1015,7 +1094,7 @@ subroutine run_dEdd(dt, tr_aero, & fpn = c0 hpn = c0 endif ! pond type - + snowfracn(n) = fsn ! for history call shortwave_dEdd(n_aero, n_zaero, & @@ -1035,7 +1114,7 @@ subroutine run_dEdd(dt, tr_aero, & kaer_bc_tab, & waer_bc_tab, & gaer_bc_tab, & - bcenh, modal_aero, & + bcenh, modal_aero, & kalg, & swvdr, swvdf, & swidr, swidf, & @@ -1048,7 +1127,28 @@ subroutine run_dEdd(dt, tr_aero, & albicen(n), & albsnon(n), albpndn(n), & fswpenln(:,n), zbion(:,n), & - l_print_point) + l_print_point, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) + + if (.not. tr_rsnw) then + rnslyr = c1/max(c1,(real(nslyr,kind=dbl_kind))) + do k = 1,nslyr + rsnw_dEddn(n) = rsnw_dEddn(n) + rsnwn(k)*rnslyr + enddo + endif endif ! aicen > puny @@ -1113,7 +1213,21 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & Iswabs, albice, & albsno, albpnd, & fswpenl, zbio, & - l_print_point) + l_print_point, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) integer (kind=int_kind), intent(in) :: & nilyr , & ! number of ice layers @@ -1189,6 +1303,11 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & albsno , & ! snow albedo, for history albpnd ! pond albedo, for history + logical (kind=log_kind), intent(in) :: & + use_snicar ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + logical (kind=log_kind) , intent(in) :: & l_print_point @@ -1236,6 +1355,29 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & character(len=char_len_long) :: & warning ! warning message + ! snow grain single-scattering properties for + ! direct (drc) and diffuse (dfs) shortwave incidents + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc, & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs, & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc, & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs, & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc, & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + !----------------------------------------------------------------------- klev = nslyr + nilyr + 1 ! number of radiation layers - 1 @@ -1257,6 +1399,7 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & fswsfc = c0 fswint = c0 fswthru = c0 + ! compute fraction of nir down direct to total over all points: fnidr = c0 if( swidr + swidf > puny ) then @@ -1339,22 +1482,52 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & ! calculate snow covered sea ice srftyp = 1 - call compute_dEdd(nilyr, nslyr, klev, klevp, & - n_zaero, zbio, dEdd_algae, & - nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & - tr_zaero, & - heat_capacity, fnidr, coszen, & - n_aero, tr_aero, R_ice, R_pnd, & - kaer_tab, waer_tab, gaer_tab, & - kaer_bc_tab, waer_bc_tab, gaer_bc_tab, & - bcenh, modal_aero, kalg, & - swvdr, swvdf, swidr, swidf, srftyp, & - hs, rhosnw, rsnw, hi, hp, & - fs, aero_mp, avdrl, avdfl, & - aidrl, aidfl, & - fswsfc, fswint, & - fswthru, Sswabs, & - Iswabs, fswpenl) + if (use_snicar) then ! use 5-band snicar IOPs for snow + call compute_dEdd_5bd(nilyr, nslyr, klev, klevp, & + n_zaero, zbio, dEdd_algae, & + nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & + tr_zaero, & + heat_capacity, fnidr, coszen, & + n_aero, tr_aero, R_ice, R_pnd, & + kaer_tab_5bd, waer_tab_5bd, gaer_tab_5bd, & + kaer_bc_tab_5bd, waer_bc_tab_5bd, gaer_bc_tab_5bd,& + bcenh_5bd, modal_aero, kalg, & + swvdr, swvdf, swidr, swidf, srftyp, & + hs, rhosnw, rsnw, hi, hp, & + fs, aero_mp, avdrl, avdfl, & + aidrl, aidfl, & + fswsfc, fswint, & + fswthru, Sswabs, & + Iswabs, fswpenl, & + asm_prm_ice_drc, asm_prm_ice_dfs, & + ss_alb_ice_drc, ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, ext_cff_mss_ice_dfs) + + alvdr = alvdr + avdrl *fs + alvdf = alvdf + avdfl *fs + alidr = alidr + aidrl *fs + alidf = alidf + aidfl *fs + ! for history + albsno = albsno & + + awtvdr*avdrl + awtidr*aidrl & + + awtvdf*avdfl + awtidf*aidfl + else ! use 3 band IOPs for snow + call compute_dEdd(nilyr, nslyr, klev, klevp, & + n_zaero, zbio, dEdd_algae, & + nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & + tr_zaero, & + heat_capacity, fnidr, coszen, & + n_aero, tr_aero, R_ice, R_pnd, & + kaer_tab, waer_tab, gaer_tab, & + kaer_bc_tab, waer_bc_tab, gaer_bc_tab, & + bcenh, modal_aero, kalg, & + swvdr, swvdf, swidr, swidf, srftyp, & + hs, rhosnw, rsnw, hi, hp, & + fs, aero_mp, avdrl, avdfl, & + aidrl, aidfl, & + fswsfc, fswint, & + fswthru, Sswabs, & + Iswabs, fswpenl) alvdr = alvdr + avdrl *fs alvdf = alvdf + avdfl *fs @@ -1364,9 +1537,11 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & albsno = albsno & + awtvdr*avdrl + awtidr*aidrl & + awtvdf*avdfl + awtidf*aidfl + endif ! end if using 5band snicar subroutine + endif endif - + hi = c0 ! sea ice points with sun above horizon @@ -2368,15 +2543,12 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & g(k) = gs enddo ! k - ! aerosol in snow - if (tr_zaero .and. dEdd_algae) then + if (tr_zaero .and. dEdd_algae) then do k = 0,nslyr - gzaer(ns,k) = gzaer(ns,k)/(wzaer(ns,k)+puny) - wzaer(ns,k) = wzaer(ns,k)/(tzaer(ns,k)+puny) - g(k) = (g(k)*w0(k)*tau(k) + gzaer(ns,k)*wzaer(ns,k)*tzaer(ns,k)) / & - (w0(k)*tau(k) + wzaer(ns,k)*tzaer(ns,k)) - w0(k) = (w0(k)*tau(k) + wzaer(ns,k)*tzaer(ns,k)) / & + g(k) = (g(k)*w0(k)*tau(k) + gzaer(ns,k)) / & + (w0(k)*tau(k) + wzaer(ns,k)) + w0(k) = (w0(k)*tau(k) + wzaer(ns,k)) / & (tau(k) + tzaer(ns,k)) tau(k) = tau(k) + tzaer(ns,k) enddo @@ -2389,7 +2561,7 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & do na=1,4*n_aero,4 ! mgf++ if (modal_aero) then - if (na == 1) then + if (na == 1) then !interstitial BC taer = taer + & aero_mp(na)*kaer_bc_tab(ns,k_bcexs(k)) @@ -2399,7 +2571,7 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & gaer = gaer + & aero_mp(na)*kaer_bc_tab(ns,k_bcexs(k))* & waer_bc_tab(ns,k_bcexs(k))*gaer_bc_tab(ns,k_bcexs(k)) - elseif (na == 5)then + elseif (na == 5)then !within-ice BC taer = taer + & aero_mp(na)*kaer_bc_tab(ns,k_bcins(k))* & @@ -2464,7 +2636,7 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & gaer = gaer + & (aero_mp(na+1)/rnslyr)*kaer_bc_tab(ns,k_bcins(k))* & waer_bc_tab(ns,k_bcins(k))*gaer_bc_tab(ns,k_bcins(k)) - + else ! other species (dust) taer = taer + & @@ -2510,9 +2682,9 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & ! no aerosol in pond enddo ! k endif ! srftyp - + ! set optical properties of sea ice - + ! bare or snow-covered sea ice layers if( srftyp <= 1 ) then ! ssl @@ -2542,7 +2714,7 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & if( ns == 1 ) then ! total layer absorption optical depth fixed at value ! of kalg*0.50m, independent of actual layer thickness - kabs = kabs + kabs_chl(ns,k) + kabs = kabs + kabs_chl(ns,k) endif sig = ki_int(ns)*wi_int(ns) tau(k) = (kabs+sig)*dzk(k) @@ -2550,16 +2722,14 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & g(k) = gi_int(ns) ! aerosol in sea ice if (tr_zaero .and. dEdd_algae) then - do k = kii, klev - gzaer(ns,k) = gzaer(ns,k)/(wzaer(ns,k)+puny) - wzaer(ns,k) = wzaer(ns,k)/(tzaer(ns,k)+puny) - g(k) = (g(k)*w0(k)*tau(k) + gzaer(ns,k)*wzaer(ns,k)*tzaer(ns,k)) / & - (w0(k)*tau(k) + wzaer(ns,k)*tzaer(ns,k)) - w0(k) = (w0(k)*tau(k) + wzaer(ns,k)*tzaer(ns,k)) / & + do k = kii, klev + g(k) = (g(k)*w0(k)*tau(k) + gzaer(ns,k))/ & + (w0(k)*tau(k) + wzaer(ns,k)) + w0(k) = (w0(k)*tau(k) + wzaer(ns,k)) / & (tau(k) + tzaer(ns,k)) tau(k) = tau(k) + tzaer(ns,k) enddo - elseif (tr_aero) then + elseif (tr_aero) then k = kii ! sea ice SSL taer = c0 waer = c0 @@ -2587,7 +2757,7 @@ subroutine compute_dEdd (nilyr, nslyr, klev, klevp, & waer_bc_tab(ns,k_bcins(k)) gaer = gaer + & aero_mp(na+2)*kaer_bc_tab(ns,k_bcins(k))* & - waer_bc_tab(ns,k_bcins(k))*gaer_bc_tab(ns,k_bcins(k)) + waer_bc_tab(ns,k_bcins(k))*gaer_bc_tab(ns,k_bcins(k)) else ! other species (dust) taer = taer + & @@ -3452,7 +3622,8 @@ subroutine shortwave_dEdd_set_snow(nslyr, R_snw, & aice, vsno, & Tsfc, fs, & hs0, hs, & - rhosnw, rsnw) + rhosnw, rsnw, & + rsnow, tr_rsnw) integer (kind=int_kind), intent(in) :: & nslyr ! number of snow layers @@ -3472,10 +3643,16 @@ subroutine shortwave_dEdd_set_snow(nslyr, R_snw, & fs , & ! horizontal coverage of snow hs ! snow depth - real (kind=dbl_kind), dimension (:), intent(out) :: & + real (kind=dbl_kind), dimension (:), intent(in) :: & + rsnow ! snow grain radius tracer (micro-meters) + + real (kind=dbl_kind), dimension (:), intent(inout) :: & rhosnw , & ! density in snow layer (kg/m3) rsnw ! grain radius in snow layer (micro-meters) + logical(kind=log_kind), intent(in) :: & + tr_rsnw ! if true, use rsnow + ! local variables integer (kind=int_kind) :: & @@ -3502,23 +3679,36 @@ subroutine shortwave_dEdd_set_snow(nslyr, R_snw, & if (hs0 > puny) fs = min(hs/hs0, c1) endif - ! bare ice, temperature dependence - dTs = Timelt - Tsfc - fT = -min(dTs/dT_mlt-c1,c0) - ! tune nonmelt snow grain radius if desired: note that - ! the sign is negative so that if R_snw is 1, then the - ! snow grain radius is reduced and thus albedo increased. - rsnw_nm = rsnw_nonmelt - R_snw*rsnw_sig - rsnw_nm = max(rsnw_nm, rsnw_fresh) - rsnw_nm = min(rsnw_nm, rsnw_mlt) - do ks = 1, nslyr - ! snow density ccsm3 constant value - rhosnw(ks) = rhos - ! snow grain radius between rsnw_nonmelt and rsnw_mlt - rsnw(ks) = rsnw_nm + (rsnw_mlt-rsnw_nm)*fT - rsnw(ks) = max(rsnw(ks), rsnw_fresh) - rsnw(ks) = min(rsnw(ks), rsnw_mlt) - enddo ! ks + if (tr_rsnw) then !use snow grain tracer + + do ks = 1, nslyr + rsnw(ks) = max(rsnw_fall,rsnow(ks)) + rsnw(ks) = min(rsnw_tmax,rsnw(ks)) + rhosnw(ks) = rhos + enddo + + else + + ! bare ice, temperature dependence + dTs = Timelt - Tsfc + fT = -min(dTs/dT_mlt-c1,c0) + ! tune nonmelt snow grain radius if desired: note that + ! the sign is negative so that if R_snw is 1, then the + ! snow grain radius is reduced and thus albedo increased. + rsnw_nm = rsnw_nonmelt - R_snw*rsnw_sig + rsnw_nm = max(rsnw_nm, rsnw_fresh) + rsnw_nm = min(rsnw_nm, rsnw_mlt) + + do ks = 1, nslyr + ! snow density ccsm3 constant value + rhosnw(ks) = rhos + ! snow grain radius between rsnw_nonmelt and rsnw_mlt + rsnw(ks) = rsnw_nm + (rsnw_mlt-rsnw_nm)*fT + rsnw(ks) = max(rsnw(ks), rsnw_fresh) + rsnw(ks) = min(rsnw(ks), rsnw_mlt) + enddo ! ks + + endif end subroutine shortwave_dEdd_set_snow @@ -3577,23 +3767,23 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & nbtrcr_sw, n_zaero, & skl_bgc, z_tracers, & l_stop, stop_label) - - use ice_constants_colpkg, only: c0, c1, c2, p5 + + use ice_constants_colpkg, only: c0, c1, c2, p5, sk_l use ice_colpkg_tracers, only: nt_bgc_N, nt_zaero, tr_bgc_N, & tr_zaero, nlt_chl_sw, nlt_zaero_sw - use ice_colpkg_shared, only: dEdd_algae, bgc_flux_type, sk_l, & + use ice_colpkg_shared, only: dEdd_algae, bgc_flux_type, & R_chl2N, min_bgc, F_abs_chl, hi_ssl use ice_zbgc_shared, only: remap_zbgc integer (kind=int_kind), intent(in) :: & nslyr, & ! number of snow layers - n_zaero , & ! number of cells with aicen > puny + n_zaero , & ! number of cells with aicen > puny nbtrcr_sw, n_algae, & ! nilyr+nslyr+2 for chlorophyll ntrcr integer (kind=int_kind), intent(in) :: & nblyr , & ! number of bio layers - nilyr ! number of ice layers + nilyr ! number of ice layers real (kind=dbl_kind), dimension (ntrcr), intent(in) :: & trcrn ! aerosol or chlorophyll @@ -3603,20 +3793,20 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & trcrn_sw ! ice on shortwave grid tracers real (kind=dbl_kind), dimension (:), intent(in) :: & - sw_grid , & ! - i_grid ! CICE bio grid - + sw_grid , & ! + i_grid ! CICE bio grid + real(kind=dbl_kind), intent(in) :: & hin , & ! CICE ice thickness - hbri ! brine height + hbri ! brine height logical (kind=log_kind), intent(in) :: & - skl_bgc, & ! skeletal layer bgc - z_tracers ! zbgc + skl_bgc, & ! skeletal layer bgc + z_tracers ! zbgc logical (kind=log_kind), intent(inout) :: & l_stop ! if true, print diagnostics and abort on return - + character (char_len), intent(inout) :: stop_label ! local variables @@ -3631,7 +3821,7 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & icegrid ! correct for large ice surface layers real (kind=dbl_kind):: & - top_conc ! 1% (min_bgc) of surface concentration + top_conc ! 1% (min_bgc) of surface concentration ! when hin > hbri: just used in sw calculation !----------------------------------------------------------------- @@ -3644,11 +3834,11 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & do k = 1,nilyr+1 icegrid(k) = sw_grid(k) - enddo - if (sw_grid(1)*hin*c2 > hi_ssl) then + enddo + if (sw_grid(1)*hin*c2 > hi_ssl .and. hin > puny) then icegrid(1) = hi_ssl/c2/hin endif - + icegrid(2) = c2*sw_grid(1) + (sw_grid(2) - sw_grid(1)) if (z_tracers) then if (tr_bgc_N) then do k = 1, nblyr+1 @@ -3657,7 +3847,7 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & R_chl2N(n)*F_abs_chl(n)*trcrn(nt_bgc_N(n)+k-1) enddo ! n enddo ! k - + top_conc = trtmp0(nt_bgc_N(1))*min_bgc call remap_zbgc (ntrcr, nilyr+1, & nt_bgc_N(1), & @@ -3666,8 +3856,8 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & 1, nblyr+1, & hin, hbri, & icegrid(1:nilyr+1), & - i_grid(1:nblyr+1), top_conc, & - l_stop, stop_label) + i_grid(1:nblyr+1), top_conc, & + l_stop, stop_label) if (l_stop) return @@ -3677,11 +3867,11 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & do n = 1, n_algae ! snow contribution trcrn_sw(nlt_chl_sw)= trcrn_sw(nlt_chl_sw) & - + R_chl2N(n)*F_abs_chl(n)*trcrn(nt_bgc_N(n)+nblyr+1) + + R_chl2N(n)*F_abs_chl(n)*trcrn(nt_bgc_N(n)+nblyr+1) ! snow surface layer trcrn_sw(nlt_chl_sw+1:nlt_chl_sw+nslyr) = & trcrn_sw(nlt_chl_sw+1:nlt_chl_sw+nslyr) & - + R_chl2N(n)*F_abs_chl(n)*trcrn(nt_bgc_N(n)+nblyr+2) + + R_chl2N(n)*F_abs_chl(n)*trcrn(nt_bgc_N(n)+nblyr+2) ! only 1 snow layer in zaero enddo ! n endif ! tr_bgc_N @@ -3728,6 +3918,1494 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & endif end subroutine compute_shortwave_trcr + +!======================================================================= +! --- Begin 5 band dEdd subroutine --- +! Evaluate snow/ice/ponded ice inherent optical properties (IOPs), and +! then calculate the multiple scattering solution by calling solution_dEdd. +! +! author: Bruce P. Briegleb, NCAR +! 2013: E Hunke merged with NCAR version +! 2018: Cheng Dang merged with SNICAR 5-band snow and aersols IOPs, UC Irvine +! +! Note by Cheng Dang 2018: +! This subroutine kept the existing delta-eddington adding-doubling +! method, snow and sea ice layer sturcture, and most of the code structures +! of subroutine compute_dEdd, with major changeds listed below to merge +! current snow treatments in SNICAR Model +! 1. The shortwave radiative transfer properties of snow-covered sea ice are +! calcualted for 5 bands (1 visible and 4 near-IR) defined in SNICAR +! 2. The reflection/absorption/transmission of direct and diffuse shortwave +! incidents are calculated seperately to remove the snow grain adjustment +! in subroutine compute_dEdd +! 3. The albedo and absorption of snow-covered sea ice are adjusted when solar +! zenith angle is above 75 degree +! 4. Comments given in subroutine compute_dEdd are all kepted in this subroutine +! with modifications at where above changes applies to. +! 5. This subroutine can be modified and merged with subroutine compute_dEdd +! to compute shortwave properties of bare and ponded sea ice if requested. +! For now, these two subroutines are seperated for testing new features. +! +! The justification and explaination for above changes can be find in paper: +! Dang, C., Zender, C. S., and Flanner, M. G.: Inter-comparison and improvement +! of 2-stream shortwave radiative transfer models for unified treatment of +! cryospheric surfaces in ESMs, The Cryosphere Discuss., +! https://doi.org/10.5194/tc-2019-22, in review, 2019 + + subroutine compute_dEdd_5bd (nilyr, nslyr, klev, klevp, & + n_zaero, zbio, dEdd_algae, & + nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & + tr_zaero, & + heat_capacity, fnidr, coszen, & + n_aero, tr_aero, R_ice, R_pnd, & + kaer_tab_5bd, waer_tab_5bd, gaer_tab_5bd, & + kaer_bc_tab_5bd, waer_bc_tab_5bd, & + gaer_bc_tab_5bd, bcenh_5bd, modal_aero, kalg, & + swvdr, swvdf, swidr, swidf, srftyp, & + hs, rhosnw, rsnw, hi, hp, & + fi, aero_mp, alvdr, alvdf, & + alidr, alidf, & + fswsfc, fswint, & + fswthru, Sswabs, & + Iswabs, fswpenl, & + asm_prm_ice_drc, asm_prm_ice_dfs, & + ss_alb_ice_drc, ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, ext_cff_mss_ice_dfs) + + integer (kind=int_kind), intent(in) :: & + nilyr , & ! number of ice layers + nslyr , & ! number of snow layers + n_aero , & ! number of aerosol tracers + n_zaero , & ! number of zaerosol tracers in use + nlt_chl_sw , & ! index for chla + klev , & ! number of radiation layers - 1 + klevp ! number of radiation interfaces - 1 + ! (0 layer is included also) + + integer (kind=int_kind), dimension(:), intent(in) :: & + nlt_zaero_sw ! index for zaerosols + + logical (kind=log_kind), intent(in) :: & + heat_capacity , & ! if true, ice has nonzero heat capacity + tr_aero , & ! if .true., use aerosol tracers + dEdd_algae , & ! .true. use prognostic chla in dEdd + tr_bgc_N , & ! .true. active bgc (skl or z) + tr_zaero , & ! .true. use zaerosols + modal_aero ! .true. use modal aerosol treatment + + ! dEdd tuning parameters, set in namelist + real (kind=dbl_kind), intent(in) :: & + R_ice , & ! sea ice tuning parameter; +1 > 1sig increase in albedo + R_pnd ! ponded ice tuning parameter; +1 > 1sig increase in albedo + + real (kind=dbl_kind), intent(in) :: & + kalg , & ! algae absorption coefficient + fnidr , & ! fraction of direct to total down flux in nir + coszen , & ! cosine solar zenith angle + swvdr , & ! shortwave down at surface, visible, direct (W/m^2) + swvdf , & ! shortwave down at surface, visible, diffuse (W/m^2) + swidr , & ! shortwave down at surface, near IR, direct (W/m^2) + swidf ! shortwave down at surface, near IR, diffuse (W/m^2) + + integer (kind=int_kind), intent(in) :: & + srftyp ! surface type over ice: (0=air, 1=snow, 2=pond) + + real (kind=dbl_kind), intent(in) :: & + hs ! snow thickness (m) + + real (kind=dbl_kind), dimension (:), intent(in) :: & + rhosnw , & ! snow density in snow layer (kg/m3) + rsnw , & ! snow grain radius in snow layer (m) + zbio , & ! zaerosol + chla shortwave tracers kg/m^3 + aero_mp ! aerosol mass path in kg/m2 + + real (kind=dbl_kind), intent(in) :: & + hi , & ! ice thickness (m) + hp , & ! pond depth (m) + fi ! snow/bare ice fractional coverage (0 to 1) + + real (kind=dbl_kind), intent(inout) :: & + alvdr , & ! visible, direct, albedo (fraction) + alvdf , & ! visible, diffuse, albedo (fraction) + alidr , & ! near-ir, direct, albedo (fraction) + alidf , & ! near-ir, diffuse, albedo (fraction) + fswsfc , & ! SW absorbed at snow/bare ice/pondedi ice surface (W m-2) + fswint , & ! SW interior absorption (below surface, above ocean,W m-2) + fswthru ! SW through snow/bare ice/ponded ice into ocean (W m-2) + + real (kind=dbl_kind), dimension (:), intent(inout) :: & + fswpenl , & ! visible SW entering ice layers (W m-2) + Sswabs , & ! SW absorbed in snow layer (W m-2) + Iswabs ! SW absorbed in ice layer (W m-2) + + + ! snow grain single-scattering properties for + ! direct (drc) and diffuse (dfs) shortwave incidents + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc , & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs , & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc , & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs , & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc , & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd , & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd , & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd , & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd , & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + +!----------------------------------------------------------------------- +! Set up optical property profiles, based on snow, sea ice and ponded +! ice IOPs from: +! +! Briegleb, B. P., and B. Light (2007): A Delta-Eddington Multiple +! Scattering Parameterization for Solar Radiation in the Sea Ice +! Component of the Community Climate System Model, NCAR Technical +! Note NCAR/TN-472+STR February 2007 +! +! Computes column Delta-Eddington radiation solution for specific +! surface type: either snow over sea ice, bare sea ice, or ponded sea ice. +! +! Divides solar spectrum into 3 intervals: 0.2-0.7, 0.7-1.19, and +! 1.19-5.0 micro-meters. The latter two are added (using an assumed +! partition of incident shortwave in the 0.7-5.0 micro-meter band between +! the 0.7-1.19 and 1.19-5.0 micro-meter band) to give the final output +! of 0.2-0.7 visible and 0.7-5.0 near-infrared albedos and fluxes. +! +! Specifies vertical layer optical properties based on input snow depth, +! density and grain radius, along with ice and pond depths, then computes +! layer by layer Delta-Eddington reflectivity, transmissivity and combines +! layers (done by calling routine solution_dEdd). Finally, surface albedos +! and internal fluxes/flux divergences are evaluated. +! +! Description of the level and layer index conventions. This is +! for the standard case of one snow layer and four sea ice layers. +! +! Please read the following; otherwise, there is 99.9% chance you +! will be confused about indices at some point in time........ :) +! +! CICE4.0 snow treatment has one snow layer above the sea ice. This +! snow layer has finite heat capacity, so that surface absorption must +! be distinguished from internal. The Delta-Eddington solar radiation +! thus adds extra surface scattering layers to both snow and sea ice. +! Note that in the following, we assume a fixed vertical layer structure +! for the radiation calculation. In other words, we always have the +! structure shown below for one snow and four sea ice layers, but for +! ponded ice the pond fills "snow" layer 1 over the sea ice, and for +! bare sea ice the top layers over sea ice are treated as transparent air. +! +! SSL = surface scattering layer for either snow or sea ice +! DL = drained layer for sea ice immediately under sea ice SSL +! INT = interior layers for sea ice below the drained layer. +! +! Notice that the radiation level starts with 0 at the top. Thus, +! the total number radiation layers is klev+1, where klev is the +! sum of nslyr, the number of CCSM snow layers, and nilyr, the +! number of CCSM sea ice layers, plus the sea ice SSL: +! klev = 1 + nslyr + nilyr +! +! For the standard case illustrated below, nslyr=1, nilyr=4, +! and klev=6, with the number of layer interfaces klevp=klev+1. +! Layer interfaces are the surfaces on which reflectivities, +! transmissivities and fluxes are evaluated. +! +! CCSM3 Sea Ice Model Delta-Eddington Solar Radiation +! Layers and Interfaces +! Layer Index Interface Index +! --------------------- --------------------- 0 +! 0 \\\ snow SSL \\\ +! snow layer 1 --------------------- 1 +! 1 rest of snow layer +! +++++++++++++++++++++ +++++++++++++++++++++ 2 +! 2 \\\ sea ice SSL \\\ +! sea ice layer 1 --------------------- 3 +! 3 sea ice DL +! --------------------- --------------------- 4 +! +! sea ice layer 2 4 sea ice INT +! +! --------------------- --------------------- 5 +! +! sea ice layer 3 5 sea ice INT +! +! --------------------- --------------------- 6 +! +! sea ice layer 4 6 sea ice INT +! +! --------------------- --------------------- 7 +! +! When snow lies over sea ice, the radiation absorbed in the +! snow SSL is used for surface heating, and that in the rest +! of the snow layer for its internal heating. For sea ice in +! this case, all of the radiant heat absorbed in both the +! sea ice SSL and the DL are used for sea ice layer 1 heating. +! +! When pond lies over sea ice, and for bare sea ice, all of the +! radiant heat absorbed within and above the sea ice SSL is used +! for surface heating, and that absorbed in the sea ice DL is +! used for sea ice layer 1 heating. +! +! Basically, vertical profiles of the layer extinction optical depth (tau), +! single scattering albedo (w0) and asymmetry parameter (g) are required over +! the klev+1 layers, where klev+1 = 2 + nslyr + nilyr. All of the surface type +! information and snow/ice iop properties are evaulated in this routine, so +! the tau,w0,g profiles can be passed to solution_dEdd for multiple scattering +! evaluation. Snow, bare ice and ponded ice iops are contained in data arrays +! in this routine. +! +!----------------------------------------------------------------------- + + ! local variables + + integer (kind=int_kind) :: & + k , & ! level index + ns , & ! spectral index + nr , & ! index for grain radius tables + ki , & ! index for internal absorption + km , & ! k starting index for snow, sea ice internal absorption + kp , & ! k+1 or k+2 index for snow, sea ice internal absorption + ksrf , & ! level index for surface absorption + ksnow , & ! level index for snow density and grain size + kii ! level starting index for sea ice (nslyr+1) + + integer (kind=int_kind), parameter :: & + nmbrad = 32 ! number of snow grain radii in tables + + real (kind=dbl_kind) :: & + avdr , & ! visible albedo, direct (fraction) + avdf , & ! visible albedo, diffuse (fraction) + aidr , & ! near-ir albedo, direct (fraction) + aidf ! near-ir albedo, diffuse (fraction) + + real (kind=dbl_kind) :: & + fsfc , & ! shortwave absorbed at snow/bare ice/ponded ice surface (W m-2) + fint , & ! shortwave absorbed in interior (W m-2) + fthru ! shortwave through snow/bare ice/ponded ice to ocean (W/m^2) + + real (kind=dbl_kind), dimension(nslyr) :: & + Sabs ! shortwave absorbed in snow layer (W m-2) + + real (kind=dbl_kind), dimension(nilyr) :: & + Iabs ! shortwave absorbed in ice layer (W m-2) + + real (kind=dbl_kind), dimension(nilyr+1) :: & + fthrul ! shortwave through to ice layers (W m-2) + + real (kind=dbl_kind), dimension (nspint) :: & + wghtns ! spectral weights + + real (kind=dbl_kind), parameter :: & + cp67 = 0.67_dbl_kind , & ! nir band weight parameter + cp33 = 0.33_dbl_kind , & ! nir band weight parameter + cp78 = 0.78_dbl_kind , & ! nir band weight parameter + cp22 = 0.22_dbl_kind , & ! nir band weight parameter + cp01 = 0.01_dbl_kind ! for ocean visible albedo + + real (kind=dbl_kind), dimension (0:klev) :: & + tau , & ! layer extinction optical depth + w0 , & ! layer single scattering albedo + g ! layer asymmetry parameter + + ! following arrays are defined at model interfaces; 0 is the top of the + ! layer above the sea ice; klevp is the sea ice/ocean interface. + real (kind=dbl_kind), dimension (0:klevp) :: & + trndir , & ! solar beam down transmission from top + trntdr , & ! total transmission to direct beam for layers above + trndif , & ! diffuse transmission to diffuse beam for layers above + rupdir , & ! reflectivity to direct radiation for layers below + rupdif , & ! reflectivity to diffuse radiation for layers below + rdndif ! reflectivity to diffuse radiation for layers above + + real (kind=dbl_kind), dimension (0:klevp) :: & + dfdir , & ! down-up flux at interface due to direct beam at top surface + dfdif ! down-up flux at interface due to diffuse beam at top surface + + real (kind=dbl_kind) :: & + refk , & ! interface k multiple scattering term + delr , & ! snow grain radius interpolation parameter + ! inherent optical properties (iop) for snow + Qs , & ! Snow extinction efficiency + ks , & ! Snow mass extinction coefficient (m^2/kg) + ws , & ! Snow single scattering albedo + gs ! Snow asymmetry parameter + + real (kind=dbl_kind), dimension(nslyr) :: & + frsnw ! snow grain radius in snow layer * adjustment factor (m) + + ! actual used ice and ponded ice IOPs, allowing for tuning + ! modifications of the above "_mn" value + real (kind=dbl_kind), dimension (nspint) :: & + ki_ssl , & ! Surface-scattering-layer ice extinction coefficient (/m) + wi_ssl , & ! Surface-scattering-layer ice single scattering albedo + gi_ssl , & ! Surface-scattering-layer ice asymmetry parameter + ki_dl , & ! Drained-layer ice extinction coefficient (/m) + wi_dl , & ! Drained-layer ice single scattering albedo + gi_dl , & ! Drained-layer ice asymmetry parameter + ki_int , & ! Interior-layer ice extinction coefficient (/m) + wi_int , & ! Interior-layer ice single scattering albedo + gi_int , & ! Interior-layer ice asymmetry parameter + ki_p_ssl , & ! Ice under pond srf scat layer extinction coefficient (/m) + wi_p_ssl , & ! Ice under pond srf scat layer single scattering albedo + gi_p_ssl , & ! Ice under pond srf scat layer asymmetry parameter + ki_p_int , & ! Ice under pond extinction coefficient (/m) + wi_p_int , & ! Ice under pond single scattering albedo + gi_p_int ! Ice under pond asymmetry parameter + + real (kind=dbl_kind), dimension(0:klev) :: & + dzk ! layer thickness + + real (kind=dbl_kind) :: & + dz , & ! snow, sea ice or pond water layer thickness + dz_ssl , & ! snow or sea ice surface scattering layer thickness + fs ! scaling factor to reduce (nilyr<4) or increase (nilyr>4) DL + ! extinction coefficient to maintain DL optical depth constant + ! with changing number of sea ice layers, to approximately + ! conserve computed albedo for constant physical depth of sea + ! ice when the number of sea ice layers vary + real (kind=dbl_kind) :: & + sig , & ! scattering coefficient for tuning + kabs , & ! absorption coefficient for tuning + sigp ! modified scattering coefficient for tuning + + + real (kind=dbl_kind) :: & + albodr , & ! spectral ocean albedo to direct rad + albodf ! spectral ocean albedo to diffuse rad + + ! for melt pond transition to bare sea ice for small pond depths + real (kind=dbl_kind) :: & + sig_i , & ! ice scattering coefficient (/m) + sig_p , & ! pond scattering coefficient (/m) + kext ! weighted extinction coefficient (/m) + + ! aerosol optical properties from Mark Flanner, 26 June 2008 + ! order assumed: hydrophobic black carbon, hydrophilic black carbon, + ! four dust aerosols by particle size range: + ! dust1(.05-0.5 micron), dust2(0.5-1.25 micron), + ! dust3(1.25-2.5 micron), dust4(2.5-5.0 micron) + ! spectral bands same as snow/sea ice: (0.3-0.7 micron, 0.7-1.19 micron + ! and 1.19-5.0 micron in wavelength) + + integer (kind=int_kind) :: & + na , n ! aerosol index + + real (kind=dbl_kind) :: & + taer , & ! total aerosol extinction optical depth + waer , & ! total aerosol single scatter albedo + gaer , & ! total aerosol asymmetry parameter + swdr , & ! shortwave down at surface, direct (W/m^2) + swdf , & ! shortwave down at surface, diffuse (W/m^2) + rnilyr , & ! real(nilyr) + rnslyr , & ! real(nslyr) + rns , & ! real(ns) + tmp_0, tmp_ks, tmp_kl ! temp variables + + integer(kind=int_kind), dimension(0:klev) :: & + k_bcini , & + k_bcins , & + k_bcexs + + real(kind=dbl_kind):: & + tmp_gs, tmp1 ! temp variables + + ! inherent optical property (iop) arrays for ice and ponded ice + ! mn = specified mean (or base) value + ! ki = extinction coefficient (/m) + ! wi = single scattering albedo + ! gi = asymmetry parameter + + ! ice surface scattering layer (ssl) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_ssl_mn = (/ 1000.1_dbl_kind, 1003.7_dbl_kind, 7042._dbl_kind/), & + wi_ssl_mn = (/ .9999_dbl_kind, .9963_dbl_kind, .9088_dbl_kind/), & + gi_ssl_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind/) + + ! ice drained layer (dl) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_dl_mn = (/ 100.2_dbl_kind, 107.7_dbl_kind, 1309._dbl_kind /), & + wi_dl_mn = (/ .9980_dbl_kind, .9287_dbl_kind, .0305_dbl_kind /), & + gi_dl_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! ice interior layer (int) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_int_mn = (/ 20.2_dbl_kind, 27.7_dbl_kind, 1445._dbl_kind /), & + wi_int_mn = (/ .9901_dbl_kind, .7223_dbl_kind, .0277_dbl_kind /), & + gi_int_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! ponded ice surface scattering layer (ssl) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_p_ssl_mn = (/ 70.2_dbl_kind, 77.7_dbl_kind, 1309._dbl_kind/), & + wi_p_ssl_mn = (/ .9972_dbl_kind, .9009_dbl_kind, .0305_dbl_kind/), & + gi_p_ssl_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! ponded ice interior layer (int) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_p_int_mn = (/ 20.2_dbl_kind, 27.7_dbl_kind, 1445._dbl_kind/), & + wi_p_int_mn = (/ .9901_dbl_kind, .7223_dbl_kind, .0277_dbl_kind/), & + gi_p_int_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! inherent optical property (iop) arrays for pond water and underlying ocean + ! kw = Pond water extinction coefficient (/m) + ! ww = Pond water single scattering albedo + ! gw = Pond water asymmetry parameter + real (kind=dbl_kind), dimension (nspint), parameter :: & + kw = (/ 0.20_dbl_kind, 12.0_dbl_kind, 729._dbl_kind /), & + ww = (/ 0.00_dbl_kind, 0.00_dbl_kind, 0.00_dbl_kind /), & + gw = (/ 0.00_dbl_kind, 0.00_dbl_kind, 0.00_dbl_kind /) + + real (kind=dbl_kind), parameter :: & + rhoi = 917.0_dbl_kind, & ! pure ice mass density (kg/m3) + fr_max = 1.00_dbl_kind, & ! snow grain adjustment factor max + fr_min = 0.80_dbl_kind, & ! snow grain adjustment factor min + ! tuning parameters + ! ice and pond scat coeff fractional change for +- one-sigma in albedo + fp_ice = 0.15_dbl_kind, & ! ice fraction of scat coeff for + stn dev in alb + fm_ice = 0.15_dbl_kind, & ! ice fraction of scat coeff for - stn dev in alb + fp_pnd = 2.00_dbl_kind, & ! ponded ice fraction of scat coeff for + stn dev in alb + fm_pnd = 0.50_dbl_kind ! ponded ice fraction of scat coeff for - stn dev in alb + + real (kind=dbl_kind), parameter :: & !chla-specific absorption coefficient + kchl_tab = 0.01 !0.0023-0.0029 Perovich 1993, also 0.0067 m^2 (mg Chl)^-1 + ! found values of 0.006 to 0.023 m^2/ mg (676 nm) Neukermans 2014 + ! and averages over the 300-700nm of 0.0075 m^2/mg in ice Fritsen (2011) + ! at 440nm values as high as 0.2 m^2/mg in under ice bloom (Balch 2014) + ! Grenfell 1991 uses 0.004 (m^2/mg) which is (0.0078 * spectral weighting) + !chlorophyll mass extinction cross section (m^2/mg chla) + + character(len=char_len_long) :: & + warning ! warning message + + ! SNICAR + ! new inputs + integer (kind=int_kind), parameter :: & + nmbrad_snicar = 1471 , &! number of snow grain radii in SNICAR + ! snow iops table + rsnw_snicar_max = 1500 , & + rsnw_snicar_min = 30 + + real (kind=dbl_kind), dimension (nspint_5bd) :: & + wghtns_5bd_dfs, & ! spectral weights for diffuse incident + wghtns_5bd_drc ! spectral weights for direct incident + + ! FUTURE-WORK: update 5-band sea ice iops when avalible + real (kind=dbl_kind), dimension (nspint_5bd) :: & ! for ice only + ki_ssl_5bd , & ! Surface-scattering-layer ice extinction coefficient (/m) + wi_ssl_5bd , & ! Surface-scattering-layer ice single scattering albedo + gi_ssl_5bd , & ! Surface-scattering-layer ice asymmetry parameter + ki_dl_5bd , & ! Drained-layer ice extinction coefficient (/m) + wi_dl_5bd , & ! Drained-layer ice single scattering albedo + gi_dl_5bd , & ! Drained-layer ice asymmetry parameter + ki_int_5bd , & ! Interior-layer ice extinction coefficient (/m) + wi_int_5bd , & ! Interior-layer ice single scattering albedo + gi_int_5bd ! Interior-layer ice asymmetry parameter + + ! 5-band aersol data + real (kind=dbl_kind), dimension(nspint_5bd, 0:klev) :: & + kabs_chl_5bd , & ! absorption coefficient for chlorophyll (/m) + tzaer_5bd , & ! total aerosol extinction optical depth + wzaer_5bd , & ! total aerosol single scatter albedo + gzaer_5bd ! total aerosol asymmetry parameter + + ! index + integer (kind=int_kind) :: & + nsky !sky = 1 (2) for direct (diffuse) downward SW incident + + ! temporary variables used to assign variables for direct/diffuse incident + ! based on snicar 5 band IOPs + real (kind=dbl_kind), dimension (0:klevp) :: & + dfdir_snicar , & ! down-up flux at interface due to direct beam at top surface + dfdif_snicar , & ! down-up flux at interface due to diffuse beam at top surface + rupdir_snicar , & ! reflectivity to direct radiation for layers below + rupdif_snicar ! reflectivity to diffuse radiation for layers above + + ! solar zenith angle parameterizations + real (kind=dbl_kind), parameter :: & + sza_a0 = 0.085730_dbl_kind , & + sza_a1 = -0.630883_dbl_kind , & + sza_a2 = 1.303723_dbl_kind , & + sza_b0 = 1.467291_dbl_kind , & + sza_b1 = -3.338043_dbl_kind , & + sza_b2 = 6.807489_dbl_kind , & + mu_75 = 0.2588_dbl_kind ! cosine of 75 degree + + real (kind=dbl_kind) :: & + sza_c1 , & ! parameter for high sza adjustment + sza_c0 , & ! parameter for high sza adjustment + sza_factor , & ! parameter for high sza adjustment + mu0 + + ! 5-bands ice surface scattering layer (ssl) iops to match SNICAR calculations + ! note by Cheng Dang: + ! for now these data are not needed since the sea ice layer IOPs can be directly + ! assigned based on the 3 bands data after adjustment based on tuning parameter R_ice + ! In the future, when 5-band sea ice IOPs are available, these data shall be updated + ! and the sea ice layer IOPs shall be calculated based on updated 5band iops* + ! + ! The 5band data given in this section are based on CICE and SNICAR band choice: + ! SNICAR band 1 = CICE band 1 + ! SNICAR band 2 = SNICAR band 3 = CICE band 2 + ! SNICAR band 4 = SNICAR band 5 = CICE band 3 + + ! ice surface scattering layer (ssl) iops + real (kind=dbl_kind), dimension (nspint_5bd), parameter :: & + ki_ssl_mn_5bd = (/ 1000.1_dbl_kind, 1003.7_dbl_kind, 1003.7_dbl_kind, & + 7042._dbl_kind, 7042._dbl_kind /), & + wi_ssl_mn_5bd = (/ .9999_dbl_kind, .9963_dbl_kind, .9963_dbl_kind, & + .9088_dbl_kind, .9088_dbl_kind /), & + gi_ssl_mn_5bd = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind, & + .94_dbl_kind, .94_dbl_kind /) + + ! ice drained layer (dl) iops + real (kind=dbl_kind), dimension (nspint_5bd), parameter :: & + ki_dl_mn_5bd = (/ 100.2_dbl_kind, 107.7_dbl_kind, 107.7_dbl_kind, & + 1309._dbl_kind, 1309._dbl_kind /), & + wi_dl_mn_5bd = (/ .9980_dbl_kind, .9287_dbl_kind, .9287_dbl_kind, & + .0305_dbl_kind, .0305_dbl_kind /), & + gi_dl_mn_5bd = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind, & + .94_dbl_kind, .94_dbl_kind /) + + ! ice interior layer (int) iops + real (kind=dbl_kind), dimension (nspint_5bd), parameter :: & + ki_int_mn_5bd = (/ 20.2_dbl_kind, 27.7_dbl_kind, 27.7_dbl_kind, & + 1445._dbl_kind, 1445._dbl_kind/), & + wi_int_mn_5bd = (/ .9901_dbl_kind, .7223_dbl_kind, .7223_dbl_kind, & + .0277_dbl_kind, .0277_dbl_kind /), & + gi_int_mn_5bd = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind, & + .94_dbl_kind, .94_dbl_kind /) + +!----------------------------------------------------------------------- +! Initialize and tune bare ice/ponded ice iops + + k_bcini(:) = c0 + k_bcins(:) = c0 + k_bcexs(:) = c0 + + rnilyr = c1/real(nilyr,kind=dbl_kind) + rnslyr = c1/real(nslyr,kind=dbl_kind) + kii = nslyr + 1 + + ! initialize albedos and fluxes to 0 + fthrul = c0 + Iabs = c0 + kabs_chl_5bd(:,:) = c0 + tzaer_5bd(:,:) = c0 + wzaer_5bd(:,:) = c0 + gzaer_5bd(:,:) = c0 + + avdr = c0 + avdf = c0 + aidr = c0 + aidf = c0 + fsfc = c0 + fint = c0 + fthru = c0 + + ! spectral weights - 3 bands + ! this section of code is kept for future mearge between 5band and 3 band + ! subroutines + ! weights 2 (0.7-1.19 micro-meters) and 3 (1.19-5.0 micro-meters) + ! are chosen based on 1D calculations using ratio of direct to total + ! near-infrared solar (0.7-5.0 micro-meter) which indicates clear/cloudy + ! conditions: more cloud, the less 1.19-5.0 relative to the + ! 0.7-1.19 micro-meter due to cloud absorption. + !wghtns(1) = c1 + !wghtns(2) = cp67 + (cp78-cp67)*(c1-fnidr) +! wghtns(3) = cp33 + (cp22-cp33)*(c1-fnidr) + !wghtns(3) = c1 - wghtns(2) + + ! spectral weights - 5 bands + ! direct beam incident + ! add-local-variable + wghtns_5bd_drc(1) = 1._dbl_kind + wghtns_5bd_drc(2) = 0.49352158521175_dbl_kind!0.49352_dbl_kind!0.50_dbl_kind + wghtns_5bd_drc(3) = 0.18099494230665_dbl_kind!0.18100_dbl_kind!0.18_dbl_kind + wghtns_5bd_drc(4) = 0.12094898498813_dbl_kind!0.12095_dbl_kind!0.12_dbl_kind ! + wghtns_5bd_drc(5) = c1-(wghtns_5bd_drc(2)+wghtns_5bd_drc(3)+wghtns_5bd_drc(4)) + !wghtns_5bd_drc(5) = 0.20453448749347_dbl_kind!0.20453_dbl_kind!0.20_dbl_kind ! + + ! diffuse incident + wghtns_5bd_dfs(1) = 1._dbl_kind + wghtns_5bd_dfs(2) = 0.58581507618433_dbl_kind!0.58582_dbl_kind!0.59_dbl_kind ! + wghtns_5bd_dfs(3) = 0.20156903770812_dbl_kind!0.20157_dbl_kind!0.20_dbl_kind ! + wghtns_5bd_dfs(4) = 0.10917889346386_dbl_kind!0.10918_dbl_kind!0.11_dbl_kind ! + wghtns_5bd_dfs(5) = c1-(wghtns_5bd_dfs(2)+wghtns_5bd_dfs(3)+wghtns_5bd_dfs(4)) + !wghtns_5bd_dfs(5) = 0.10343699264369_dbl_kind!0.10343_dbl_kind!0.10_dbl_kind ! + + + do k = 1, nslyr + !frsnw(k) = (fr_max*fnidr + fr_min*(c1-fnidr))*rsnw(k) + Sabs(k) = c0 + enddo + + ! layer thicknesses + ! snow + dz = hs*rnslyr + ! for small enough snow thickness, ssl thickness half of top snow layer +!ech: note this is highly resolution dependent! + dzk(0) = min(hs_ssl, dz/c2) + dzk(1) = dz - dzk(0) + if (nslyr > 1) then + do k = 2, nslyr + dzk(k) = dz + enddo + endif + + ! ice + dz = hi*rnilyr + ! empirical reduction in sea ice ssl thickness for ice thinner than 1.5m; + ! factor of 30 gives best albedo comparison with limited observations + dz_ssl = hi_ssl +!ech: note hardwired parameters +! if( hi < 1.5_dbl_kind ) dz_ssl = hi/30._dbl_kind + dz_ssl = min(hi_ssl, hi/30._dbl_kind) + ! set sea ice ssl thickness to half top layer if sea ice thin enough +!ech: note this is highly resolution dependent! + dz_ssl = min(dz_ssl, dz/c2) + + dzk(kii) = dz_ssl + dzk(kii+1) = dz - dz_ssl + if (kii+2 <= klev) then + do k = kii+2, klev + dzk(k) = dz + enddo + endif + + ! adjust sea ice iops with tuning parameters; tune only the + ! scattering coefficient by factors of R_ice, R_pnd, where + ! R values of +1 correspond approximately to +1 sigma changes in albedo, and + ! R values of -1 correspond approximately to -1 sigma changes in albedo + ! Note: the albedo change becomes non-linear for R values > +1 or < -1 + if( R_ice >= c0 ) then + do ns = 1, nspint_5bd + sigp = ki_ssl_mn_5bd(ns)*wi_ssl_mn_5bd(ns)*(c1+fp_ice*R_ice) + ki_ssl_5bd(ns) = sigp+ki_ssl_mn_5bd(ns)*(c1-wi_ssl_mn_5bd(ns)) + wi_ssl_5bd(ns) = sigp/ki_ssl_5bd(ns) + gi_ssl_5bd(ns) = gi_ssl_mn_5bd(ns) + + sigp = ki_dl_mn_5bd(ns)*wi_dl_mn_5bd(ns)*(c1+fp_ice*R_ice) + ki_dl_5bd(ns) = sigp+ki_dl_mn_5bd(ns)*(c1-wi_dl_mn_5bd(ns)) + wi_dl_5bd(ns) = sigp/ki_dl_5bd(ns) + gi_dl_5bd(ns) = gi_dl_mn_5bd(ns) + + sigp = ki_int_mn_5bd(ns)*wi_int_mn_5bd(ns)*(c1+fp_ice*R_ice) + ki_int_5bd(ns) = sigp+ki_int_mn_5bd(ns)*(c1-wi_int_mn_5bd(ns)) + wi_int_5bd(ns) = sigp/ki_int_5bd(ns) + gi_int_5bd(ns) = gi_int_mn_5bd(ns) + enddo + else !if( R_ice < c0 ) then + do ns = 1, nspint_5bd + sigp = ki_ssl_mn_5bd(ns)*wi_ssl_mn_5bd(ns)*(c1+fm_ice*R_ice) + sigp = max(sigp, c0) + ki_ssl_5bd(ns) = sigp+ki_ssl_mn_5bd(ns)*(c1-wi_ssl_mn_5bd(ns)) + wi_ssl_5bd(ns) = sigp/ki_ssl_5bd(ns) + gi_ssl_5bd(ns) = gi_ssl_mn_5bd(ns) + + sigp = ki_dl_mn_5bd(ns)*wi_dl_mn_5bd(ns)*(c1+fm_ice*R_ice) + sigp = max(sigp, c0) + ki_dl_5bd(ns) = sigp+ki_dl_mn_5bd(ns)*(c1-wi_dl_mn_5bd(ns)) + wi_dl_5bd(ns) = sigp/ki_dl_5bd(ns) + gi_dl_5bd(ns) = gi_dl_mn_5bd(ns) + + sigp = ki_int_mn_5bd(ns)*wi_int_mn_5bd(ns)*(c1+fm_ice*R_ice) + sigp = max(sigp, c0) + ki_int_5bd(ns) = sigp+ki_int_mn_5bd(ns)*(c1-wi_int_mn_5bd(ns)) + wi_int_5bd(ns) = sigp/ki_int_5bd(ns) + gi_int_5bd(ns) = gi_int_mn_5bd(ns) + enddo + endif ! adjust ice iops + + ! use srftyp to determine interface index of surface absorption + ksrf = 1 ! snow covered sea ice + + if (tr_bgc_N .and. dEdd_algae) then ! compute kabs_chl for chlorophyll + do k = 0, klev + kabs_chl_5bd(1,k) = kchl_tab*zbio(nlt_chl_sw+k) + enddo + else + k = klev + kabs_chl_5bd(1,k) = kalg*(0.50_dbl_kind/dzk(k)) + !print *, 'aerosol, k, kabs_chl_5bd(1,k)', k, kabs_chl_5bd(1,k) + endif + +!mgf++ + if (modal_aero) then + do k=0,klev + if (k < nslyr+1) then ! define indices for snow layer + ! use top rsnw, rhosnw for snow ssl and rest of top layer + ! Cheng: note that aerosol IOPs are related to snow grain radius. + ! CICE adjusted snow grain radius rsnw to frsnw, while for + ! SNICAR there is no need, the tmp_gs is therefore calculated + ! differently from code in subroutine compute_dEdd + ksnow = k - min(k-1,0) + tmp_gs = rsnw(ksnow) ! use rsnw not frsnw + + ! get grain size index: + ! works for 25 < snw_rds < 1625 um: + if (tmp_gs < 125) then + tmp1 = tmp_gs/50 + k_bcini(k) = nint(tmp1) + elseif (tmp_gs < 175) then + k_bcini(k) = 2 + else + tmp1 = (tmp_gs/250)+2 + k_bcini(k) = nint(tmp1) + endif + else ! use the largest snow grain size for ice + k_bcini(k) = 8 + endif + ! Set index corresponding to BC effective radius. Here, + ! asssume constant BC effective radius of 100nm + ! (corresponding to index 2) + k_bcins(k) = 2 + k_bcexs(k) = 2 + + ! check bounds: + if (k_bcini(k) < 1) k_bcini(k) = 1 + if (k_bcini(k) > 8) k_bcini(k) = 8 + if (k_bcins(k) < 1) k_bcins(k) = 1 + if (k_bcins(k) > 10) k_bcins(k) = 10 + if (k_bcexs(k) < 1) k_bcexs(k) = 1 + if (k_bcexs(k) > 10) k_bcexs(k) = 10 + + ! print ice radius index: + ! write(warning,*) "MGFICE2:k, ice index= ",k, k_bcini(k) + ! call add_warning(warning) + enddo ! k + ! assign the aerosol index + + if (tr_zaero .and. dEdd_algae) then ! compute kzaero for chlorophyll + do n = 1,n_zaero + if (n == 1) then ! interstitial BC + do k = 0, klev + do ns = 1,nspint_5bd ! not weighted by aice + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcexs(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))* & + gaer_bc_tab_5bd(ns,k_bcexs(k))*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + elseif (n==2) then ! within-ice BC + do k = 0, klev + do ns = 1,nspint_5bd + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcins(k)) * & + bcenh_5bd(ns,k_bcins(k),k_bcini(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))* & + gaer_bc_tab_5bd(ns,k_bcins(k))*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + else ! dust + do k = 0, klev + do ns = 1,nspint_5bd ! not weighted by aice + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + gaer_tab_5bd(ns,n)*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + endif !(n=1) + enddo ! n_zaero + endif ! tr_zaero and dEdd_algae + + else ! Bulk aerosol treatment + if (tr_zaero .and. dEdd_algae) then ! compute kzaero for chlorophyll + do n = 1,n_zaero ! multiply by aice? + do k = 0, klev + do ns = 1,nspint_5bd ! not weighted by aice + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + gaer_tab_5bd(ns,n)*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + enddo + endif !tr_zaero + + endif ! modal_aero + + +!----------------------------------------------------------------------- + ! begin spectral loop + do ns = 1, nspint_5bd + ! for snow-covered sea ice, comput 5 bands + !if( srftyp == 1 ) then + ! SNICAR-AD major changes + ! 1. loop through 5bands: do ns = 1, nspint_5bd based on nsky + ! 2. use snow grain size rsnow, not scaled frsnw + ! 3. replace $IOPs_tab with $IOPs_snicar + ! 4. replace wghtns with wghtns_5bd + do nsky = 1,2 ! loop for both direct beam and diffuse beam + if (nsky == 1) then ! direc incident + do k=0,nslyr + ! use top rsnw, rhosnw for snow ssl and rest of top layer + ksnow = k - min(k-1,0) + if (rsnw(ksnow) <= rsnw_snicar_min) then + ks = ext_cff_mss_ice_drc(ns,1) + ws = ss_alb_ice_drc(ns,1) + gs = asm_prm_ice_drc(ns,1) + elseif (rsnw(ksnow) >= rsnw_snicar_max) then + ks = ext_cff_mss_ice_drc(ns,nmbrad_snicar) + ws = ss_alb_ice_drc(ns,nmbrad_snicar) + gs = asm_prm_ice_drc(ns,nmbrad_snicar) + elseif (ceiling(rsnw(ksnow)) - rsnw(ksnow) < 1.0e-3_dbl_kind) then + nr = ceiling(rsnw(ksnow)) - 30 + 1 + ks = ext_cff_mss_ice_drc(ns,nr) + ws = ss_alb_ice_drc(ns,nr) + gs = asm_prm_ice_drc(ns,nr) + else ! linear interpolation in rsnw + ! radius = 30 --> nr = 1 in SNICAR table + nr = ceiling(rsnw(ksnow)) - 30 + 1 + delr = (rsnw(ksnow) - floor(rsnw(ksnow))) / & + (ceiling(rsnw(ksnow)) - floor(rsnw(ksnow))) + ks = ext_cff_mss_ice_drc(ns,nr-1)*(delr) + & + ext_cff_mss_ice_drc(ns,nr)*(c1-delr) + ws = ss_alb_ice_drc(ns,nr-1)*(delr) + & + ss_alb_ice_drc(ns,nr)*(c1-delr) + gs = asm_prm_ice_drc(ns,nr-1)*(delr) + & + asm_prm_ice_drc(ns,nr)*(c1-delr) + endif + ! ks = Qs*((rhosnw(ksnow)/rhoi)*3._dbl_kind / & + ! (4._dbl_kind*rsnw(ksnow)*1.0e-6_dbl_kind)) + tau(k) = (ks*rhosnw(ksnow) + kabs_chl_5bd(ns,k))*dzk(k) + !w0(k) = ks/(ks + kabs_chl_5bd(ns,k))*ws + w0(k) = (ks*rhosnw(ksnow))/(ks*rhosnw(ksnow) + kabs_chl_5bd(ns,k)) * ws + g(k) = gs + + !write(warning, *) "sky, k, tau, w0, g =", nsky, k, tau(k), w0(k), g(k) + !write(warning, *) "ns, ks, kabs_chl_5bd(ns,k), ", ns, ks, kabs_chl_5bd(ns,k) + !call add_warning(warning) + !print *, "rsnw(ksnow)", rsnw(ksnow) + !print *, "sky, k, tau, w0, g =", nsky, k, tau(k), w0(k), g(k) + !print *, "ns, ks, kabs_chl_5bd(ns,k), ",ns, ks, kabs_chl_5bd(ns,k) + + enddo ! k + elseif (nsky == 2) then ! diffuse incident + do k=0,nslyr + ! use top rsnw, rhosnw for snow ssl and rest of top layer + ksnow = k - min(k-1,0) + if (rsnw(ksnow) < rsnw_snicar_min) then + ks = ext_cff_mss_ice_dfs(ns,1) + ws = ss_alb_ice_dfs(ns,1) + gs = asm_prm_ice_dfs(ns,1) + elseif (rsnw(ksnow) > rsnw_snicar_max) then + ks = ext_cff_mss_ice_dfs(ns,nmbrad_snicar) + ws = ss_alb_ice_dfs(ns,nmbrad_snicar) + gs = asm_prm_ice_dfs(ns,nmbrad_snicar) + elseif (ceiling(rsnw(ksnow)) - rsnw(ksnow) < 1.0e-3_dbl_kind) then + nr = ceiling(rsnw(ksnow)) - 30 + 1 + ks = ext_cff_mss_ice_dfs(ns,nr) + ws = ss_alb_ice_dfs(ns,nr) + gs = asm_prm_ice_dfs(ns,nr) + else ! linear interpolation in rsnw + ! radius = 30 --> nr = 1 in SNICAR table + nr = ceiling(rsnw(ksnow)) - 30 + 1 + delr = (rsnw(ksnow) - floor(rsnw(ksnow))) / & + (ceiling(rsnw(ksnow)) - floor(rsnw(ksnow))) + ks = ext_cff_mss_ice_dfs(ns,nr-1)*(c1-delr) + & + ext_cff_mss_ice_dfs(ns,nr)*delr + ws = ss_alb_ice_dfs(ns,nr-1)*(c1-delr) + & + ss_alb_ice_dfs(ns,nr)*delr + gs = asm_prm_ice_dfs(ns,nr-1)*(c1-delr) + & + asm_prm_ice_dfs(ns,nr)*delr + endif + ! ks = Qs*((rhosnw(ksnow)/rhoi)*3._dbl_kind / & + ! (4._dbl_kind*rsnw(ksnow)*1.0e-6_dbl_kind)) + tau(k) = (ks*rhosnw(ksnow) + kabs_chl_5bd(ns,k))*dzk(k) + !w0(k) = ks/(ks + kabs_chl_5bd(ns,k)) *ws + w0(k) = (ks*rhosnw(ksnow))/(ks*rhosnw(ksnow) + kabs_chl_5bd(ns,k)) * ws + g(k) = gs + + !write(warning, *) "sky, k, tau, w0, g =", nsky, k, tau(k), w0(k), g(k) + !write(warning, *) "ns, ks, kabs_chl_5bd(ns,k), ", ns, ks, kabs_chl_5bd(ns,k) + !call add_warning(warning) + enddo ! k + endif ! end if nsky for snow IOPs assignment + !------------------------------------------------------------------------------ + + !aerosol in snow + if (tr_zaero .and. dEdd_algae) then + do k = 0,nslyr + g(k) = (g(k)*w0(k)*tau(k) + gzaer_5bd(ns,k)) / & + (w0(k)*tau(k) + wzaer_5bd(ns,k)) + w0(k) = (w0(k)*tau(k) + wzaer_5bd(ns,k)) / & + (tau(k) + tzaer_5bd(ns,k)) + tau(k) = tau(k) + tzaer_5bd(ns,k) + enddo + elseif (tr_aero) then + k = 0 ! snow SSL + taer = c0 + waer = c0 + gaer = c0 + + do na=1,4*n_aero,4 +! mgf++ + if (modal_aero) then + if (na == 1) then + !interstitial BC + taer = taer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na == 5)then + !within-ice BC + taer = taer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + else + ! other species (dust) + taer = taer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif + else + taer = taer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif !modal_aero +!mgf-- + enddo ! na + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + + do k=1,nslyr + taer = c0 + waer = c0 + gaer = c0 + do na=1,4*n_aero,4 + if (modal_aero) then +!mgf++ + if (na==1) then + ! interstitial BC + taer = taer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na==5) then + ! within-ice BC + taer = taer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcins(k))*& + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + + else + ! other species (dust) + taer = taer + & + (aero_mp(na+1)/rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+1)/rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+1)/rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif !(na==1) + + else + taer = taer + & + (aero_mp(na+1)*rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+1)*rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+1)*rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! modal_aero +!mgf-- + enddo ! na + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + g(k) = (g(k)*w0(k)*tau(k) + gaer*waer*taer) / & + (w0(k)*tau(k) + waer*taer) + w0(k) = (w0(k)*tau(k) + waer*taer) / & + (tau(k) + taer) + tau(k) = tau(k) + taer + enddo ! k + endif ! tr_aero + + ! set optical properties of sea ice + + ! bare or snow-covered sea ice layers + !if( srftyp <= 1 ) then + ! ssl + k = kii + tau(k) = (ki_ssl_5bd(ns)+kabs_chl_5bd(ns,k))*dzk(k) + w0(k) = ki_ssl_5bd(ns)/(ki_ssl_5bd(ns) + kabs_chl_5bd(ns,k))*wi_ssl_5bd(ns) + g(k) = gi_ssl_5bd(ns) + ! dl + k = kii + 1 + ! scale dz for dl relative to 4 even-layer-thickness 1.5m case + fs = p25/rnilyr + tau(k) = (ki_dl_5bd(ns) + kabs_chl_5bd(ns,k)) *dzk(k)*fs + w0(k) = ki_dl_5bd(ns)/(ki_dl_5bd(ns) + kabs_chl_5bd(ns,k)) *wi_dl_5bd(ns) + g(k) = gi_dl_5bd(ns) + ! int above lowest layer + if (kii+2 <= klev-1) then + do k = kii+2, klev-1 + tau(k) = (ki_int_5bd(ns) + kabs_chl_5bd(ns,k))*dzk(k) + w0(k) = ki_int_5bd(ns)/(ki_int_5bd(ns) + kabs_chl_5bd(ns,k)) *wi_int_5bd(ns) + g(k) = gi_int_5bd(ns) + enddo + endif + ! lowest layer + k = klev + ! add algae to lowest sea ice layer, visible only: + kabs = ki_int_5bd(ns)*(c1-wi_int_5bd(ns)) + if( ns == 1 ) then + ! total layer absorption optical depth fixed at value + ! of kalg*0.50m, independent of actual layer thickness + kabs = kabs + kabs_chl_5bd(ns,k) + endif + sig = ki_int_5bd(ns)*wi_int_5bd(ns) + tau(k) = (kabs+sig)*dzk(k) + w0(k) = (sig/(sig+kabs)) + g(k) = gi_int_5bd(ns) + ! aerosol in sea ice + if (tr_zaero .and. dEdd_algae) then + do k = kii, klev + g(k) = (g(k)*w0(k)*tau(k) + gzaer_5bd(ns,k)) / & + (w0(k)*tau(k) + wzaer_5bd(ns,k)) + w0(k) = (w0(k)*tau(k) + wzaer_5bd(ns,k)) / & + (tau(k) + tzaer_5bd(ns,k)) + tau(k) = tau(k) + tzaer_5bd(ns,k) + enddo + elseif (tr_aero) then + k = kii ! sea ice SSL + taer = c0 + waer = c0 + gaer = c0 + do na=1,4*n_aero,4 + !mgf++ + if (modal_aero) then + if (na==1) then + ! interstitial BC + taer = taer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na==5) then + ! within-ice BC + taer = taer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + else + ! other species (dust) + taer = taer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif + else !bulk + taer = taer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! modal_aero + !mgf-- + enddo ! na + + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + g(k) = (g(k)*w0(k)*tau(k) + gaer*waer*taer) / & + (w0(k)*tau(k) + waer*taer) + w0(k) = (w0(k)*tau(k) + waer*taer) / & + (tau(k) + taer) + tau(k) = tau(k) + taer + do k = kii+1, klev + taer = c0 + waer = c0 + gaer = c0 + do na=1,4*n_aero,4 + !mgf++ + if (modal_aero) then + if (na==1) then + ! interstitial BC + taer = taer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na==5) then + ! within-ice BC + taer = taer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + + else + ! other species (dust) + taer = taer + & + (aero_mp(na+3)/rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+3)/rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+3)/rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif + else !bulk + + taer = taer + & + (aero_mp(na+3)*rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+3)*rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+3)*rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! modal_aero + !mgf-- + enddo ! na + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + g(k) = (g(k)*w0(k)*tau(k) + gaer*waer*taer) / & + (w0(k)*tau(k) + waer*taer) + w0(k) = (w0(k)*tau(k) + waer*taer) / & + (tau(k) + taer) + tau(k) = tau(k) + taer + enddo ! k + endif ! tr_aero +! --------------------------------------------------------------------------- + + ! set reflectivities for ocean underlying sea ice + ! if ns == 1 (visible), albedo is 0.1, else, albedo is zero + rns = real(ns-1, kind=dbl_kind) + albodr = cp01 * (c1 - min(rns, c1)) + albodf = cp01 * (c1 - min(rns, c1)) + + ! layer input properties now completely specified: tau, w0, g, + ! albodr, albodf; now compute the Delta-Eddington solution + ! reflectivities and transmissivities for each layer; then, + ! combine the layers going downwards accounting for multiple + ! scattering between layers, and finally start from the + ! underlying ocean and combine successive layers upwards to + ! the surface; see comments in solution_dEdd for more details. + call solution_dEdd & + (coszen, srftyp, klev, klevp, nslyr, & + tau, w0, g, albodr, albodf, & + trndir, trntdr, trndif, rupdir, rupdif, & + rdndif) + ! the interface reflectivities and transmissivities required + ! to evaluate interface fluxes are returned from solution_dEdd; + ! now compute up and down fluxes for each interface, using the + ! combined layer properties at each interface: + ! + ! layers interface + ! + ! --------------------- k + ! k + ! --------------------- + + do k = 0, klevp + ! interface scattering + refk = c1/(c1 - rdndif(k)*rupdif(k)) + ! dir tran ref from below times interface scattering, plus diff + ! tran and ref from below times interface scattering + ! fdirup(k) = (trndir(k)*rupdir(k) + & + ! (trntdr(k)-trndir(k)) & + ! *rupdif(k))*refk + ! dir tran plus total diff trans times interface scattering plus + ! dir tran with up dir ref and down dif ref times interface scattering + ! fdirdn(k) = trndir(k) + (trntdr(k) & + ! - trndir(k) + trndir(k) & + ! *rupdir(k)*rdndif(k))*refk + ! diffuse tran ref from below times interface scattering + ! fdifup(k) = trndif(k)*rupdif(k)*refk + ! diffuse tran times interface scattering + ! fdifdn(k) = trndif(k)*refk + + ! dfdir = fdirdn - fdirup + dfdir(k) = trndir(k) & + + (trntdr(k)-trndir(k)) * (c1 - rupdif(k)) * refk & + - trndir(k)*rupdir(k) * (c1 - rdndif(k)) * refk + if (dfdir(k) < puny) dfdir(k) = c0 !echmod necessary? + ! dfdif = fdifdn - fdifup + dfdif(k) = trndif(k) * (c1 - rupdif(k)) * refk + if (dfdif(k) < puny) dfdif(k) = c0 !echmod necessary? + enddo ! k + + ! note that because the snow IOPs for diffuse and direct incidents + ! are different, the snow albedo needs to be calculated twice for + ! direct incident and diffuse incident respectively + if (nsky == 1) then ! direc beam (keep the direct beam results) + do k = 0, klevp + dfdir_snicar(k) = dfdir(k) + rupdir_snicar(k) = rupdir(k) + enddo + elseif (nsky == 2) then ! diffuse (keep the diffuse incident results) + do k = 0, klevp + dfdif_snicar(k) = dfdif(k) + rupdif_snicar(k) = rupdif(k) + enddo + endif + enddo ! end direct/diffuse incident nsky + + ! calculate final surface albedos and fluxes- + ! all absorbed flux above ksrf is included in surface absorption + if( ns == 1) then ! visible + swdr = swvdr + swdf = swvdf + avdr = rupdir_snicar(0) + avdf = rupdif_snicar(0) + tmp_0 = dfdir_snicar(0 )*swdr + dfdif_snicar(0 )*swdf + tmp_ks = dfdir_snicar(ksrf )*swdr + dfdif_snicar(ksrf )*swdf + tmp_kl = dfdir_snicar(klevp)*swdr + dfdif_snicar(klevp)*swdf + + ! for layer biology: save visible only + do k = nslyr+2, klevp ! Start at DL layer of ice after SSL scattering + fthrul(k-nslyr-1) = dfdir_snicar(k)*swdr + dfdif_snicar(k)*swdf + enddo + + fsfc = fsfc + tmp_0 - tmp_ks + fint = fint + tmp_ks - tmp_kl + fthru = fthru + tmp_kl + + ! if snow covered ice, set snow internal absorption; else, Sabs=0 + if( srftyp == 1 ) then + ki = 0 + do k=1,nslyr + ! skip snow SSL, since SSL absorption included in the surface + ! absorption fsfc above + km = k + kp = km + 1 + ki = ki + 1 + Sabs(ki) = Sabs(ki) & + + dfdir_snicar(km)*swdr + dfdif_snicar(km)*swdf & + - (dfdir_snicar(kp)*swdr + dfdif_snicar(kp)*swdf) + enddo ! k + endif + + ! complex indexing to insure proper absorptions for sea ice + ki = 0 + do k=nslyr+2,nslyr+1+nilyr + ! for bare ice, DL absorption for sea ice layer 1 + km = k + kp = km + 1 + ! modify for top sea ice layer for snow over sea ice + if( srftyp == 1 ) then + ! must add SSL and DL absorption for sea ice layer 1 + if( k == nslyr+2 ) then + km = k - 1 + kp = km + 2 + endif + endif + ki = ki + 1 + Iabs(ki) = Iabs(ki) & + + dfdir_snicar(km)*swdr + dfdif_snicar(km)*swdf & + - (dfdir_snicar(kp)*swdr + dfdif_snicar(kp)*swdf) + enddo ! k + + else !if(ns > 1) then ! near IR + + swdr = swidr + swdf = swidf + + ! let fr2(3,4,5) = alb_2(3,4,5)*swd*wght2(3,4,5) + ! the ns=2(3,4,5) reflected fluxes respectively, + ! where alb_2(3,4,5) are the band + ! albedos, swd = nir incident shortwave flux, and wght2(3,4,5) are + ! the 2(3,4,5) band weights. thus, the total reflected flux is: + ! fr = fr2 + fr3 + fr4 + fr5 + ! = alb_2*swd*wght2 + alb_3*swd*wght3 + alb_4*swd*wght4 + alb_5*swd*wght5 + ! hence, the 2,3,4,5 nir band albedo is + ! alb = fr/swd = alb_2*wght2 + alb_3*wght3 + alb_4*wght4 + alb_5*wght5 + + aidr = aidr + rupdir_snicar(0)*wghtns_5bd_drc(ns) + aidf = aidf + rupdif_snicar(0)*wghtns_5bd_dfs(ns) + + tmp_0 = dfdir_snicar(0 )*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(0 )*swdf*wghtns_5bd_dfs(ns) + tmp_ks = dfdir_snicar(ksrf )*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(ksrf )*swdf*wghtns_5bd_dfs(ns) + tmp_kl = dfdir_snicar(klevp)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(klevp)*swdf*wghtns_5bd_dfs(ns) + + fsfc = fsfc + tmp_0 - tmp_ks + fint = fint + tmp_ks - tmp_kl + fthru = fthru + tmp_kl + + ! if snow covered ice, set snow internal absorption; else, Sabs=0 + if( srftyp == 1 ) then + ki = 0 + do k=1,nslyr + ! skip snow SSL, since SSL absorption included in the surface + ! absorption fsfc above + km = k + kp = km + 1 + ki = ki + 1 + Sabs(ki) = Sabs(ki) & + + dfdir_snicar(km)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(km)*swdf*wghtns_5bd_dfs(ns) & + -(dfdir_snicar(kp)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(kp)*swdf*wghtns_5bd_dfs(ns)) + + enddo ! k + endif + + ! complex indexing to insure proper absorptions for sea ice + ki = 0 + do k=nslyr+2,nslyr+1+nilyr + ! for bare ice, DL absorption for sea ice layer 1 + km = k + kp = km + 1 + ! modify for top sea ice layer for snow over sea ice + if( srftyp == 1 ) then + ! must add SSL and DL absorption for sea ice layer 1 + if( k == nslyr+2 ) then + km = k - 1 + kp = km + 2 + endif + endif + ki = ki + 1 + Iabs(ki) = Iabs(ki) & + + dfdir_snicar(km)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(km)*swdf*wghtns_5bd_dfs(ns) & + -(dfdir_snicar(kp)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(kp)*swdf*wghtns_5bd_dfs(ns)) + enddo ! k + endif ! ns = 1, ns > 1 + enddo ! end spectral loop ns + + + ! accumulate fluxes over bare sea ice + + ! solar zenith angle parameterization + ! calculate the scaling factor for NIR direct albedo if SZA>75 degree + sza_factor = c1 + if( srftyp == 1 ) then + mu0 = max(coszen,p01) + if (mu0 < mu_75) then + sza_c1 = sza_a0 + sza_a1 * mu0 + sza_a2 * mu0**2 + sza_c0 = sza_b0 + sza_b1 * mu0 + sza_b2 * mu0**2 + sza_factor = sza_c1 * (log10(rsnw(1)) - 6.0) + sza_c0 + endif + endif + + alvdr = avdr + alvdf = avdf + alidr = aidr * sza_factor !sza factor is always larger than or equal to 1 + alidf = aidf + + ! note that we assume the reduced NIR energy absorption by snow + ! due to corrected snow albedo is absorbed by the snow single + ! scattering layer only - this is generally true if snow SSL >= 2 cm + ! by the default model set up: + ! if snow_depth >= 8 cm, SSL = 4 cm, satisfy + ! esle if snow_depth >= 4 cm, SSL = snow_depth/2 >= 2 cm, satisfy + ! esle snow_depth < 4 cm, SSL = snow_depth/2, may overcool SSL layer + fswsfc = fswsfc + (fsfc- (sza_factor-c1)*aidr*swidr)*fi + fswint = fswint + fint *fi + fswthru = fswthru + fthru*fi + + + do k = 1, nslyr + Sswabs(k) = Sswabs(k) + Sabs(k)*fi + enddo ! k + + do k = 1, nilyr + Iswabs(k) = Iswabs(k) + Iabs(k)*fi + + ! bgc layer + fswpenl(k) = fswpenl(k) + fthrul(k)* fi + + if (k == nilyr) then + fswpenl(k+1) = fswpenl(k+1) + fthrul(k+1)*fi + endif + enddo ! k + + !---------------------------------------------------------------- + ! if ice has zero heat capacity, no SW can be absorbed + ! in the ice/snow interior, so add to surface absorption. + ! Note: nilyr = nslyr = 1 for this case + !---------------------------------------------------------------- + + if (.not. heat_capacity) then + + ! SW absorbed at snow/ice surface + fswsfc = fswsfc + Iswabs(1) + Sswabs(1) + + ! SW absorbed in ice interior + fswint = c0 + Iswabs(1) = c0 + Sswabs(1) = c0 + + endif ! heat_capacity + + end subroutine compute_dEdd_5bd + !======================================================================= end module ice_shortwave diff --git a/src/core_seaice/column/ice_snow.F90 b/src/core_seaice/column/ice_snow.F90 new file mode 100644 index 0000000000..ec437eab9d --- /dev/null +++ b/src/core_seaice/column/ice_snow.F90 @@ -0,0 +1,952 @@ +! SVN:$Id: ice_snow.F90 972 2015-04-15 19:44:20Z njeffery $ +!======================================================================= +! +! authors Elizabeth Hunke, LANL +! Nicole Jeffery, LANL + + module ice_snow + + use ice_kinds_mod + use ice_constants_colpkg, only: puny, c0, c1, c10, rhos, Lfresh, & + rhow, rhoi, rhofresh, snwlvlfac, & + rhosmin + use ice_warnings, only: add_warning + + implicit none + save + + private + public :: snow_effective_density, update_snow_radius, snow_redist,& + drain_snow + + real (kind=dbl_kind), parameter, public :: & + S_r = 0.033_dbl_kind, & ! irreducible saturation (Anderson 1976) + S_wet= 0.422_dbl_kind ! (um^3/s) wet metamorphism parameters + +!======================================================================= + + contains + +!======================================================================= + +! Compute effective density of snow layers from ice, liquid water mass + + subroutine snow_effective_density(nslyr, ncat, & + vsnon, vsno, & + smice, smliq, & + rhosnew, & + rhos_effn, rhos_eff, & + rhos_cmpn, rhos_cmp) + + integer (kind=int_kind), intent(in) :: & + nslyr, & ! number of snow layers + ncat ! number of thickness categories + + real (kind=dbl_kind), dimension(:), intent(in) :: & + vsnon ! snow volume (m) + + real (kind=dbl_kind), intent(in) :: & + vsno , & ! total snow volume (m) + rhosnew ! new snow density (kg/m^3) + + real (kind=dbl_kind), dimension(:,:), & + intent(inout) :: & + smice , & ! mass of ice in snow (kg/m^3) + smliq , & ! mass of liquid in snow (kg/m^3) + rhos_effn, & ! effective snow density: content (kg/m^3) + rhos_cmpn ! effective snow density: compaction (kg/m^3) + + real (kind=dbl_kind), intent(inout) :: & + rhos_eff , & ! mean effective snow density: content (kg/m^3) + rhos_cmp ! mean effective snow density: compaction (kg/m^3) + + integer (kind=int_kind) :: & + k , & ! snow layer index + n , & ! ice thickness category index + cnt ! counter for snow presence + + rhos_eff = c0 + rhos_cmp = c0 + + if (vsno > puny) then + + !----------------------------------------------------------------- + ! Initialize effective snow density (compaction) for new snow + !----------------------------------------------------------------- + + do n = 1, ncat + do k = 1, nslyr + if (rhos_cmpn(k,n) < rhosmin) rhos_cmpn(k,n) = rhosnew + enddo + enddo + + !----------------------------------------------------------------- + ! Compute average effective density of snow + !----------------------------------------------------------------- + + do n = 1, ncat + if (vsnon(n) > c0) then + do k = 1, nslyr + rhos_effn(k,n) = rhos_effn(k,n) + smice(k,n) + smliq(k,n) + rhos_eff = rhos_eff + vsnon(n)*rhos_effn(k,n) + rhos_cmp = rhos_cmp + vsnon(n)*rhos_cmpn(k,n) + enddo + endif + enddo + rhos_eff = rhos_eff/(vsno*real(nslyr,kind=dbl_kind)) + rhos_cmp = rhos_cmp/(vsno*real(nslyr,kind=dbl_kind)) + + endif ! vsno + + end subroutine snow_effective_density + +!======================================================================= + +! Snow redistribution by wind, based on O. Lecomte Ph.D. (2014). +! Namelist option snwredist = 'ITDsd': +! Snow in suspension depends on wind speed, density and the standard +! deviation of the ice thickness distribution. Snow is redistributed +! among ice categories proportionally to the category areas. +! Namelist option snwredist = 'ITDrdg': +! As above, but use the standard deviation of the level and ridged +! ice thickness distribution for snow in suspension, and redistribute +! based on ridged ice area. + +! convention: +! volume, mass and energy include factor of ain +! thickness does not + + subroutine snow_redist(dt, nslyr, ncat, wind, ain, vin, vsn, zqsn, & + snwredist, alvl, vlvl, fresh, fhocn, fsloss, rhos_cmpn, & + fsnow, rhosmax, windmin, drhosdwind, l_stop, stop_label) + + use ice_therm_vertical, only: adjust_enthalpy + + integer (kind=int_kind), intent(in) :: & + nslyr , & ! number of snow layers + ncat ! number of thickness categories + + real (kind=dbl_kind), intent(in) :: & + dt , & ! time step (s) + wind , & ! wind speed (m/s) + fsnow , & ! snowfall rate (kg m-2 s-1) + rhosmax , & ! maximum snow density (kg/m^3) + windmin , & ! minimum wind speed to compact snow (m/s) + drhosdwind ! wind compaction factor (kg s/m^4) + + real (kind=dbl_kind), dimension(:), intent(in) :: & + ain , & ! ice area fraction + vin , & ! ice volume (m) + alvl , & ! level ice area tracer + vlvl ! level ice volume tracer + + real (kind=dbl_kind), intent(inout) :: & + fresh , & ! fresh water flux to ocean (kg/m^2/s) + fhocn , & ! net heat flux to ocean (W/m^2) + fsloss ! snow loss to leads (kg/m^2/s) + + real (kind=dbl_kind), dimension(:), intent(inout) :: & + vsn ! snow volume (m) + + real (kind=dbl_kind), dimension(:,:), intent(inout) :: & + zqsn , & ! snow enthalpy (J/m^3) + rhos_cmpn ! effective snow density: compaction (kg/m^3) + + character(len=char_len), intent(in) :: & + snwredist ! type of snow redistribution + + logical (kind=log_kind), intent(out) :: & + l_stop ! if true, print diagnostics and abort on return + + character (len=*), intent(out) :: stop_label + + ! local variables + + integer (kind=int_kind) :: & + n , & ! category index + k ! layer index + + integer (kind=int_kind), dimension(ncat) :: & + klyr ! layer index + + real (kind=dbl_kind), parameter :: & + refsd = c1 , & ! standard deviation reference + gamma = 1.e-5_dbl_kind ! tuning coefficient + + real (kind=dbl_kind) :: & + Vseas , & ! critical seasonal wind speed (m/s) + ITDsd , & ! standard deviation of ITD + flost , & ! fraction of snow lost in leads + alost , & ! effective lead area for snow lost in leads + suma , & ! sum of ice area over categories + sumv , & ! sum of ice volume over categories (m) + summ , & ! sum of snow mass over categories (kg/m^2) + sumq , & ! sum of snow enthalpy over categories (kg/m^2) + msusp , & ! potential mass of snow in suspension (kg/m^2) + msnw_susp , & ! mass of snow in suspension (kg/m^2) + esnw_susp , & ! energy of snow in suspension (J/m^2) + asnw_lvl , & ! mass of snow redeposited on level ice (kg/m^2) + e_redeptmp, & ! redeposited energy (J/m^2) + dhsn , & ! change in snow depth (m) + dmp , & ! mass difference in previous layer (kg/m^2) + hslyr , & ! snow layer thickness (m) + hslab , & ! new snow thickness (m) + drhos , & ! change in snow density due to compaction (kg/m^3) + mlost , & ! mass of suspended snow lost in leads (kg/m^2) + elost , & ! energy of suspended snow lost in leads (J/m^2) + de , & ! change in energy (J/m^2) + al, ar , & ! areas of level and ridged ice + hlvl, hrdg, & ! thicknesses of level and ridged ice + tmp1, tmp2, & ! temporary values + tmp3, tmp4, & ! temporary values + tmp5 , & ! temporary values + work ! temporary value + + real (kind=dbl_kind), dimension(ncat) :: & + sfac , & ! temporary for snwlvlfac + ardg , & ! ridged ice area tracer + m_erosion , & ! eroded mass (kg/m^2) + e_erosion , & ! eroded energy (J/m^2) + m_redep , & ! redeposited mass (kg/m^2) + e_redep , & ! redeposited energy (J/m^2) + vsn_init , & ! initial volume (m) + esn_init , & ! initial energy (J/m^2) + esn_final , & ! final energy (J/m^2) + atmp , & ! temporary variable for ain, for debugging convenience + hin , & ! ice thickness (m) + hsn , & ! snow depth (m) + hsn_new ! new snow depth (m) + + real (kind=dbl_kind), dimension (nslyr) :: & + dzs ! snow layer thickness after redistribution (m) + + real (kind=dbl_kind), dimension (nslyr+1) :: & + zs1 , & ! depth of snow layer boundaries (m) + zs2 ! adjusted depths, with equal hslyr (m) + + character(len=char_len_long) :: & + warning + + !----------------------------------------------------------------- + ! Conservation checks + !----------------------------------------------------------------- + + l_stop = .false. + stop_label = '' + tmp1 = c0 + tmp3 = c0 + do n = 1, ncat + ! mass conservation check + tmp1 = tmp1 + vsn(n) + vsn_init(n) = vsn(n) + esn_init(n) = c0 + ! energy conservation check + do k = 1, nslyr + tmp3 = tmp3 + vsn(n)*zqsn(k,n)/nslyr + esn_init(n) = esn_init(n) + vsn(n)*zqsn(k,n)/nslyr + enddo + enddo + + !----------------------------------------------------------------- + ! category thickness and sums + !----------------------------------------------------------------- + + hin(:) = c0 + hsn(:) = c0 + suma = c0 + sumv = c0 + do n = 1, ncat + atmp(n) = ain(n) + if (atmp(n) > puny) then + hin(n) = vin(n)/atmp(n) + hsn(n) = vsn(n)/atmp(n) + endif + hsn_new(n) = hsn(n) + suma = suma + atmp(n) + sumv = sumv + vin(n) + ! maintain positive definite enthalpy + do k = 1, nslyr + zqsn(k,n) = min(zqsn(k,n) + Lfresh*rhos, c0) + enddo + enddo ! ncat + + !----------------------------------------------------------------- + ! standard deviation of ice thickness distribution + !----------------------------------------------------------------- + + work = c0 + asnw_lvl = c0 + if (trim(snwredist) == 'ITDrdg') then ! use level and ridged ice + do n = 1, ncat + ardg(n) = c1 - alvl(n) ! ridged ice tracer + al = alvl(n) * atmp(n) ! level + ar = ardg(n) * atmp(n) ! ridged + hlvl = c0 + hrdg = c0 + if (al > puny) hlvl = vin(n)*vlvl(n)/al + if (ar > puny) hrdg = vin(n)*(c1-vlvl(n))/ar + work = work + al*(hlvl - sumv)**2 + ar*(hrdg - sumv)**2 + + ! for redeposition of snow on level ice + sfac(n) = snwlvlfac + if (ardg(n) > c0) sfac(n) = min(snwlvlfac, alvl(n)/ardg(n)) + asnw_lvl = asnw_lvl + al - sfac(n)*ar + enddo + asnw_lvl = asnw_lvl/suma + else ! snwredist = 'ITDsd' ! use standard ITD + do n = 1, ncat + work = work + atmp(n)*(hin(n) - sumv)**2 + enddo + endif + ITDsd = sqrt(work) + + !----------------------------------------------------------------- + ! fraction of suspended snow lost in leads + !----------------------------------------------------------------- + + flost = (c1 - suma) * exp(-ITDsd/refsd) +!echmod flost = c0 + alost = c1 - suma * (c1-flost) + + !----------------------------------------------------------------- + ! suspended snow + !----------------------------------------------------------------- + + msusp = c0 + do n = 1, ncat + ! critical seasonal wind speed needed to compact snow to density rhos + Vseas = (rhos_cmpn(1,n) - 44.6_dbl_kind)/174.0_dbl_kind ! use top layer + Vseas = max(Vseas, c0) + ! maximum mass per unit area of snow in suspension (kg/m^2) + if (ITDsd > puny) & + msusp = msusp + atmp(n)*gamma*dt*max(wind-Vseas,c0) & + * (rhosmax-rhos_cmpn(1,n))/(rhosmax*ITDsd) + enddo + + !----------------------------------------------------------------- + ! erosion + !----------------------------------------------------------------- + + msnw_susp = c0 + esnw_susp = c0 + klyr(:) = 1 + do n = 1, ncat + m_erosion(n) = c0 ! mass + e_erosion(n) = c0 ! energy + if (atmp(n) > puny) then + m_erosion(n) = min(msusp, rhos*vsn(n)) + if (m_erosion(n) > puny) then + summ = c0 + dmp = m_erosion(n) + do k = 1, nslyr + if (dmp > c0) then + dhsn = min(hsn(n)/nslyr, dmp/(rhos*atmp(n))) + msnw_susp = msnw_susp + dhsn*rhos*atmp(n) ! total mass in suspension + hsn_new(n) = hsn_new(n) - dhsn + e_erosion(n) = e_erosion(n) + dhsn*zqsn(k,n)*atmp(n) + klyr(n) = k ! number of affected layers + summ = summ + rhos*vsn(n)/nslyr ! mass, partial sum + dmp = max(m_erosion(n) - summ, c0) + endif ! dmp + enddo + esnw_susp = esnw_susp + e_erosion(n) ! total energy in suspension + endif + endif + enddo + + !----------------------------------------------------------------- + ! redeposition + !----------------------------------------------------------------- + + do n = 1, ncat + if (trim(snwredist) == 'ITDrdg') then ! use level and ridged ice + work = atmp(n)*(c1-flost)*(ardg(n)*(c1+sfac(n)) + asnw_lvl) + else ! use standard ITD + work = atmp(n)*(c1-flost) + endif + m_redep(n) = msnw_susp*work ! mass + e_redep(n) = c0 + e_redeptmp = esnw_susp*work ! energy + + ! change in snow depth + dhsn = c0 + if (atmp(n) > puny) then + dhsn = m_redep(n) / (rhos*atmp(n)) + + if (abs(dhsn) > c0) then + + e_redep(n) = e_redeptmp + vsn(n) = (hsn_new(n)+dhsn)*atmp(n) + + ! change in snow energy + de = e_redeptmp / klyr(n) + ! spread among affected layers + sumq = c0 + do k = 1, klyr(n) + zqsn(k,n) = (atmp(n)*hsn_new(n)*zqsn(k,n) + de) & + / (vsn(n)) ! factor of nslyr cancels out + + if (zqsn(k,n) > c0) then + sumq = sumq + zqsn(k,n) + zqsn(k,n) = c0 + endif + + enddo ! klyr + zqsn(klyr(n),n) = min(zqsn(klyr(n),n) + sumq, c0) ! may lose energy here + + !----------------------------------------------------------------- + ! Conserving energy, compute the enthalpy of the new equal layers + !----------------------------------------------------------------- + + if (nslyr > 1) then + + dzs(:) = hsn(n) / real(nslyr,kind=dbl_kind) ! old layer thickness + do k = 1, klyr(n) + dzs(k) = dzs(k) + dhsn / klyr(n) ! old layer thickness (updated) + enddo + hsn_new(n) = hsn_new(n) + dhsn + hslyr = hsn_new(n) / real(nslyr,kind=dbl_kind) ! new layer thickness + + zs1(1) = c0 + zs1(1+nslyr) = hsn_new(n) + + zs2(1) = c0 + zs2(1+nslyr) = hsn_new(n) + + do k = 1, nslyr-1 + zs1(k+1) = zs1(k) + dzs(k) ! old layer depths (unequal thickness) + zs2(k+1) = zs2(k) + hslyr ! new layer depths (equal thickness) + enddo + + call adjust_enthalpy (nslyr, & + zs1(:), zs2(:), & + hslyr, hsn_new(n), & + zqsn(:,n)) + endif ! nslyr > 1 + endif ! |dhsn| > puny + endif ! ain > puny + + ! maintain positive definite enthalpy + do k = 1, nslyr + zqsn(k,n) = zqsn(k,n) - Lfresh*rhos + enddo + enddo ! ncat + + !----------------------------------------------------------------- + ! mass of suspended snow lost in leads + !----------------------------------------------------------------- + mlost = msnw_susp*alost + fsloss = fsloss + mlost / dt + + !----------------------------------------------------------------- + ! mass conservation check + !----------------------------------------------------------------- + + tmp2 = c0 + do n = 1, ncat + tmp2 = tmp2 + vsn(n) + enddo + + if (tmp2 > tmp1) then ! correct roundoff error + vsn(:) = vsn(:) * tmp1/tmp2 + tmp2 = c0 + do n = 1, ncat + tmp2 = tmp2 + vsn(n) + enddo + endif + + if (tmp2 < tmp1) fresh = fresh + rhos*(tmp1-tmp2)/dt + + tmp2 = tmp2 + (mlost/rhos) + + if (abs(tmp1-tmp2) > puny) then + write(warning,*)'mass conservation error in snow_redist', tmp1, tmp2 + call add_warning(warning) + write(warning,*)'klyr',klyr + call add_warning(warning) + write(warning,*)'ain',atmp(:) + call add_warning(warning) + write(warning,*)'vsn final',vsn(:) + call add_warning(warning) + write(warning,*)'vsn init',vsn_init(:) + call add_warning(warning) + write(warning,*)'rhos*vsn init',rhos*vsn_init(:) + call add_warning(warning) + write(warning,*)'m_erosion',m_erosion(:) + call add_warning(warning) + write(warning,*)'m_redep',m_redep(:) + call add_warning(warning) + write(warning,*)'mlost',mlost + call add_warning(warning) + write(warning,*)'v_erosion',m_erosion(:)/rhos + call add_warning(warning) + write(warning,*)'v_redep',m_redep(:)/rhos + call add_warning(warning) + write(warning,*)'v lost',mlost/rhos + call add_warning(warning) + write(warning,*)'hsn',hsn(:) + call add_warning(warning) + write(warning,*)'hsn_new',hsn_new(:) + call add_warning(warning) + write(warning,*)'vsn_new',hsn_new(:)*atmp(:) + call add_warning(warning) + write(warning,*)'lost',suma,flost,alost,msnw_susp + call add_warning(warning) + stop_label = 'snow redistribution mass conservation error' + l_stop = .true. + endif + + !----------------------------------------------------------------- + ! energy conservation check + !----------------------------------------------------------------- + + tmp4 = c0 + tmp5 = c0 + esn_final(:) = c0 + do n = 1, ncat + do k = 1, nslyr + tmp4 = tmp4 + vsn(n)*zqsn(k,n)/nslyr + esn_final(n) = esn_final(n) + vsn(n)*zqsn(k,n)/nslyr + enddo + tmp5 = tmp5 - e_erosion(n) + e_redep(n) + enddo + tmp5 = tmp5 + esnw_susp*alost + + !----------------------------------------------------------------- + ! energy of suspended snow lost in leads + !----------------------------------------------------------------- + elost = tmp3 - tmp4 + fhocn = fhocn + elost / dt + + if (abs(tmp5) > nslyr*Lfresh*puny) then + write(warning,*)'energy conservation error in snow_redist', tmp3, tmp4, tmp5 + call add_warning(warning) + write(warning,*)'klyr',klyr + call add_warning(warning) + write(warning,*)'ain',atmp(:) + call add_warning(warning) + write(warning,*)'vsn final',vsn(:) + call add_warning(warning) + write(warning,*)'vsn init',vsn_init(:) + call add_warning(warning) + write(warning,*)'rhos*vsn init',rhos*vsn_init(:) + call add_warning(warning) + write(warning,*)'m_erosion',m_erosion(:) + call add_warning(warning) + write(warning,*)'m_redep',m_redep(:) + call add_warning(warning) + write(warning,*)'mlost',mlost + call add_warning(warning) + write(warning,*)'v_erosion',m_erosion(:)/rhos + call add_warning(warning) + write(warning,*)'v_redep',m_redep(:)/rhos + call add_warning(warning) + write(warning,*)'v lost',mlost/rhos + call add_warning(warning) + write(warning,*)'hsn',hsn(:) + call add_warning(warning) + write(warning,*)'hsn_new',hsn_new(:) + call add_warning(warning) + write(warning,*)'vsn_new',hsn_new(:)*atmp(:) + call add_warning(warning) + write(warning,*)'lost',suma,flost,alost,msnw_susp + call add_warning(warning) + write(warning,*)'tmp3(1)', (vsn(1)*zqsn(k,1)/nslyr,k=1,nslyr) + call add_warning(warning) + write(warning,*)'esn init',esn_init(:) + call add_warning(warning) + write(warning,*)'esn final',esn_final(:) + call add_warning(warning) + write(warning,*)'e_erosion',e_erosion(:) + call add_warning(warning) + write(warning,*)'e_redep',e_redep(:) + call add_warning(warning) + write(warning,*)'elost',elost,esnw_susp*alost,Lfresh*mlost + call add_warning(warning) + write(warning,*)'esnw_susp',esnw_susp + call add_warning(warning) + stop_label = 'snow redistribution energy conservation error' + l_stop = .true. + endif + + !----------------------------------------------------------------- + ! wind compaction + !----------------------------------------------------------------- + + do n = 1, ncat + if (vsn(n) > puny) then + ! compact freshly fallen or redistributed snow + drhos = drhosdwind * max(wind - windmin, c0) + hslab = c0 + if (fsnow > c0) & + hslab = max(min(fsnow*dt/(rhos+drhos), hsn_new(n)-hsn(n)), c0) + hslyr = hsn_new(n) / real(nslyr,kind=dbl_kind) + do k = 1, nslyr + work = hslab - hslyr * real(k-1,kind=dbl_kind) + work = max(c0, min(hslyr, work)) + rhos_cmpn(k,n) = rhos_cmpn(k,n) + drhos*work/hslyr + rhos_cmpn(k,n) = min(rhos_cmpn(k,n), rhosmax) + enddo + endif + enddo + + end subroutine snow_redist + +!======================================================================= + +! Snow grain metamorphism driver + + subroutine update_snow_radius (dt, ncat, nslyr, nilyr, rsnw, hin, & + Tsfc, zTin, & + hsn, zqsn, smice, smliq, & + rsnw_fall, rsnw_tmax, & + snowage_tau, & + snowage_kappa, & + snowage_drdt0, & + idx_T_max, & + idx_Tgrd_max, & + idx_rhos_max) + + integer (kind=int_kind), intent(in) :: & + ncat, & ! number of categories + nslyr, & ! number of snow layers + nilyr, & ! number of ice layers + idx_T_max, & ! dimensions of snow parameter matrix + idx_Tgrd_max, & + idx_rhos_max + + real (kind=dbl_kind), intent(in) :: & + dt ! time step + + real (kind=dbl_kind), dimension(ncat), intent(in) :: & + zTin , & ! surface ice temperature (oC) + Tsfc , & ! surface temperature (oC) + hin , & ! ice thickness (m) + hsn ! snow thickness (m) + + real (kind=dbl_kind), dimension(nslyr,ncat), intent(in) :: & + zqsn ! enthalpy of snow (J m-3) + + real (kind=dbl_kind), dimension(nslyr,ncat), intent(inout) :: & + rsnw + + real (kind=dbl_kind), dimension(nslyr,ncat), & + intent(inout) :: & + smice, & ! mass of ice in snow (kg/m^2) + smliq ! mass of liquid in snow (kg/m^2) + + real (kind=dbl_kind), intent(in) :: & + rsnw_fall, & ! radius of newly fallen snow (10^-6 m) + rsnw_tmax ! maximum grain radius from dry metamorphism (10^-6 m) + + ! dry snow aging parameters + real (kind=dbl_kind), dimension(idx_rhos_max,idx_Tgrd_max,idx_T_max), intent(in) :: & + snowage_tau, & ! (10^-6 m) + snowage_kappa, & ! + snowage_drdt0 ! (10^-6 m/hr) + + ! local temporary variables + + integer (kind=int_kind) :: k, n + + real (kind=dbl_kind), dimension(nslyr) :: & + drsnw_wet, & ! wet metamorphism (10^-6 m) + drsnw_dry ! dry (temperature gradient) metamorphism (10^-6 m) + + !----------------------------------------------------------------- + ! dry metamorphism + !----------------------------------------------------------------- + do n = 1, ncat + + if (hsn(n) > puny .and. hin(n) > puny) then + + drsnw_dry(:) = c0 + drsnw_wet(:) = c0 + + call snow_dry_metamorph (nslyr, nilyr, dt, rsnw(:,n), drsnw_dry, zqsn(:,n), Tsfc(n), & + zTin(n), hsn(n), hin(n), smice(:,n),smliq(:,n), rsnw_fall, & + snowage_tau, snowage_kappa, snowage_drdt0, & + idx_T_max, idx_Tgrd_max, idx_rhos_max) + + !----------------------------------------------------------------- + ! wet metamorphism + !----------------------------------------------------------------- + + + do k = 1,nslyr + call snow_wet_metamorph (dt, drsnw_wet(k), rsnw(k,n), smice(k,n),smliq(k,n)) + rsnw(k,n) = min(rsnw_tmax, rsnw(k,n) + drsnw_dry(k) + drsnw_wet(k)) + enddo + else + do k = 1,nslyr + rsnw(k,n) = max(rsnw_fall,min(rsnw_tmax, rsnw(k,n))) + smice(k,n) = rhos + smliq(k,n) = c0 + enddo + + endif + enddo + + end subroutine update_snow_radius + +!======================================================================= + +! Snow grain metamorphism + + subroutine snow_dry_metamorph (nslyr,nilyr, dt, rsnw, drsnw_dry, zqsn, & + Tsfc, zTin1, hsn, hin, smice, smliq, rsnw_fall, & + snowage_tau, snowage_kappa, snowage_drdt0, & + idx_T_max, idx_Tgrd_max, idx_rhos_max) + + use ice_constants_colpkg, only: c0, rhos, Tffresh, Lfresh, cp_ice, p5, puny, c10 + use ice_colpkg_shared, only: idx_T_min, idx_Tgrd_min, idx_rhos_min + + ! Vapor redistribution: Method is to retrieve 3 best-bit parameters that + ! depend on snow temperature, temperature gradient, and density, + ! that are derived from the microphysical model described in: + ! Flanner and Zender (2006), Linking snowpack microphysics and albedo + ! evolution, J. Geophys. Res., 111, D12208, doi:10.1029/2005JD006834. + ! The parametric equation has the form: + ! dr/dt = drdt_0*(tau/(dr_fresh+tau))^(1/kappa), where: + ! r is the effective radius, + ! tau and kappa are best-fit parameters, + ! drdt_0 is the initial rate of change of effective radius, and + ! dr_fresh is the difference between the current and fresh snow states + ! (r_current - r_fresh). + + integer (kind=int_kind), intent(in) :: & + nslyr, & ! number of snow layers + nilyr, & ! number of ice layers + idx_T_max, & ! dimensions of snow parameter matrix + idx_Tgrd_max, & + idx_rhos_max + + real (kind=dbl_kind), intent(in) :: & + dt ! time step (s) + + real (kind=dbl_kind), dimension(nslyr), & + intent(in) :: & + smice , & ! mass of ice in snow (kg/m^3) + smliq , & ! mass of liquid in snow (kg/m^3) + rsnw, & ! snow grain radius (10^-6 m) + zqsn ! snow enthalpy (J m-3) + + real (kind=dbl_kind), dimension(nslyr), & + intent(inout) :: & + drsnw_dry ! change due to snow aging (10^-6 m) + + real (kind=dbl_kind), intent(in) :: & + Tsfc, & ! surface temperature (oC) + zTin1, & ! top ice layer temperature (oC) + hsn, & ! snow thickness (m) + hin, & ! ice thickness (m) + rsnw_fall + + ! dry snow aging parameters + real (kind=dbl_kind), dimension(idx_rhos_max,idx_Tgrd_max,idx_T_max), intent(in) :: & + snowage_tau, & ! (10^-6 m) + snowage_kappa, & ! + snowage_drdt0 ! (10^-6 m/hr) + + ! local temporary variables + + integer (kind=int_kind) :: k + + integer (kind=int_kind) :: & + T_idx, & ! temperature index + Tgrd_idx, & ! temperature gradient index + rhos_idx ! density index + + real (kind=dbl_kind), dimension(nslyr):: & + zrhos, & ! snow density (kg/m^3) ! for variable snow density + zdTdz, & ! temperature gradient (K/s) + zTsn ! snow temperature (oC) + + real (kind=dbl_kind) :: & + bst_tau, & ! snow aging parameter retrieved from lookup table [hour] + bst_kappa, & ! snow aging parameter retrieved from lookup table [unitless] + bst_drdt0, & ! snow aging parameter retrieved from lookup table [um hr-1] + dr_fresh, & ! change in snow radius from fresh (10^-6 m) + dzs, & ! snow layer thickness (m) + dzi ! ice layer thickness (m) + + character(len=char_len_long) :: & + warning ! warning message + +! Needed for variable snow density not currently modeled +! calculate density based on liquid and ice content of snow + + drsnw_dry(:) = c0 + zTsn(:) = c0 + zdTdz(:) = c0 + zrhos(:) = rhos + + dzs = hsn/real(nslyr,kind=dbl_kind) + dzi = hin/real(nilyr,kind=dbl_kind) + + if (nslyr == 1) then + zTsn(1) =(Lfresh + zqsn(1)/rhos)/cp_ice + zdTdz(1) = min(c10*idx_Tgrd_max,abs((zTsn(1)*dzi + zTin1*dzs)/(dzs + dzi+puny)- Tsfc)/(hsn+puny)) + else + zTsn(1) =(Lfresh + zqsn(1)/rhos)/cp_ice + do k = 2, nslyr + zTsn(k) = (Lfresh + zqsn(k)/rhos)/cp_ice + if (k == 2) then + zdTdz(k-1) = abs((zTsn(k-1)+zTsn(k))*p5 - Tsfc)/(dzs+puny) + zdTdz(k-1) = min(c10*idx_Tgrd_max,zdTdz(k-1)) + else + zdTdz(k-1) = abs(zTsn(k-2)-zTsn(k))*p5/(dzs+puny) + zdTdz(k-1) = min(c10*idx_Tgrd_max,zdTdz(k-1)) + endif + enddo + + zdTdz(nslyr) = abs((zTsn(nslyr)*dzi + zTin1*dzs)/(dzs + dzi+puny)- & + (zTsn(nslyr) + zTsn(nslyr-1))*p5)/(dzs+puny) + zdTdz(nslyr) = min(c10*idx_Tgrd_max,zdTdz(nslyr)) + endif + + ! best-fit parameters are read from a table + ! 11 temperatures from 225 to 273 K + ! 31 temperature gradients from 0 to 300 K/m + ! 8 snow densities from 0 to 350 kg/m3 + ! pointer snowage_tau, snowage_kappa, snowage_drdt0 + + do k = 1, nslyr + zrhos(k) = smice(k) + smliq(k) + + ! best-fit table indecies: + T_idx = nint(abs(zTsn(k)+ Tffresh - 223.0_dbl_kind) / 5.0_dbl_kind, kind=int_kind) + Tgrd_idx = nint(zdTdz(k) / 10.0_dbl_kind, kind=int_kind) + !rhos_idx = nint(zrhos(k)-50.0_dbl_kind) / 50.0_dbl_kind, kind=int_kind) ! variable density + rhos_idx = nint((rhos-50.0_dbl_kind) / 50.0_dbl_kind, kind=int_kind) ! fixed density + + ! boundary check: + T_idx = min(idx_T_max, max(1,T_idx+1))!min(idx_T_max, max(idx_T_min,T_idx)) + Tgrd_idx = min(idx_Tgrd_max, max(1,Tgrd_idx+1))!min(idx_Tgrd_max, max(idx_Tgrd_min,Tgrd_idx)) + rhos_idx = min(idx_rhos_max, max(1,rhos_idx+1)) !min(idx_rhos_max, max(idx_rhos_min,rhos_idx)) + + bst_tau = snowage_tau(rhos_idx,Tgrd_idx,T_idx) + bst_kappa = snowage_kappa(rhos_idx,Tgrd_idx,T_idx) + bst_drdt0 = snowage_drdt0(rhos_idx,Tgrd_idx,T_idx) + + ! change in snow effective radius, using best-fit parameters + dr_fresh = max(c0,rsnw(k)-rsnw_fall) + drsnw_dry(k) = (bst_drdt0*(bst_tau/(dr_fresh+bst_tau))**(1/bst_kappa))& + * (dt/3600.0_dbl_kind) + enddo + + end subroutine snow_dry_metamorph + +!======================================================================= + +! Snow grain metamorphism + + subroutine snow_wet_metamorph (dt, dr_wet, rsnw, smice, smliq) + + use ice_constants_colpkg, only: c0, c1, c4, pi, p1, c100 + ! + ! Liquid water redistribution: Apply the grain growth function from: + ! Brun, E. (1989), Investigation of wet-snow metamorphism in respect of + ! liquid-water content, Annals of Glaciology, 13, 22-26. + ! There are two parameters that describe the grain growth rate as + ! a function of snow liquid water content (LWC). The "LWC=0" parameter + ! is zeroed here because we are accounting for dry snowing with a + ! different representation + ! + real (kind=dbl_kind), intent(in) :: & + dt ! time step + + real (kind=dbl_kind), & + intent(in) :: & + rsnw , & ! snow grain radius (10^-6 m) + smice, & ! snow ice density (kg/m^3) + smliq ! snow liquid density (kg/m^3) + + real (kind=dbl_kind), & + intent(inout) :: & + dr_wet + + real (kind=dbl_kind) :: & + fliq ! liquid mass fraction + + dr_wet = c0 + fliq = c1 + if (smice + smliq > c0 .and. rsnw > c0) then + fliq = min(smliq/(smice + smliq),p1)*c100 + dr_wet = S_wet * fliq**3*dt/(c4*pi*rsnw**2) + endif + + end subroutine snow_wet_metamorph + +!======================================================================= + +! Conversions between ice mass, liquid water mass in snow + + subroutine drain_snow (dt, nslyr, vsnon, aicen, & + smice, smliq, meltsliq) + + integer (kind=int_kind), intent(in) :: & + nslyr ! number of snow layers + + real (kind=dbl_kind), intent(in) :: & + dt, & ! time step + vsnon, & ! snow volume (m) + aicen ! aice area + + real (kind=dbl_kind), intent(inout) :: & + meltsliq ! total liquid content + + real (kind=dbl_kind), dimension(nslyr), & + intent(in) :: & + smice ! mass of ice in snow (kg/m^2) + + real (kind=dbl_kind), dimension(nslyr), & + intent(inout) :: & + smliq ! mass of liquid in snow (kg/m^2) + + ! local temporary variables + + integer (kind=int_kind) :: k + + real (kind=dbl_kind) :: & + hslyr, & ! snow layer thickness (m) + hsn ! snow thickness (m) + + real (kind=dbl_kind), dimension(nslyr) :: & + dlin , & ! liquid into the layer from above (kg/m^2) + dlout , & ! liquid out of the layer (kg/m^2) + phi_liq , & ! volumetric liquid fraction + phi_ice , & ! volumetric ice fraction + w_drain ! flow between layers + + hsn = c0 + if (aicen > c0) hsn = vsnon/aicen + if (hsn > puny) then + dlin(:) = c0 + dlout(:) = c0 + hslyr = hsn / real(nslyr,kind=dbl_kind) + meltsliq = c0 + do k = 1,nslyr + smliq(k) = smliq(k) + dlin(k) / hslyr ! liquid in from above layer + phi_ice(k) = min(c1, smice(k) / rhoi) + phi_liq(k) = smliq(k)/rhofresh + w_drain(k) = max(c0, (phi_liq(k) - S_r*(c1-phi_ice(k))) / dt * rhofresh * hslyr) + dlout(k) = w_drain(k) * dt + smliq(k) = smliq(k) - dlout(k)/ hslyr + if (k < nslyr) then + dlin(k+1) = dlout(k) + else + meltsliq = dlout(nslyr) + endif + enddo + else + meltsliq = meltsliq ! computed in thickness_changes + endif + + end subroutine drain_snow + +!======================================================================= + + end module ice_snow + +!======================================================================= diff --git a/src/core_seaice/column/ice_therm_0layer.F90 b/src/core_seaice/column/ice_therm_0layer.F90 index 4ebb968c47..0cb42522f2 100644 --- a/src/core_seaice/column/ice_therm_0layer.F90 +++ b/src/core_seaice/column/ice_therm_0layer.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_therm_0layer.F90 1182 2017-03-16 19:29:26Z njeffery $ +! SVN:$Id: ice_therm_0layer.F90 1196 2017-04-18 13:32:23Z eclare $ !========================================================================= ! ! Update ice and snow internal temperatures diff --git a/src/core_seaice/column/ice_therm_bl99.F90 b/src/core_seaice/column/ice_therm_bl99.F90 index a6dc588dc9..fea2d8494d 100644 --- a/src/core_seaice/column/ice_therm_bl99.F90 +++ b/src/core_seaice/column/ice_therm_bl99.F90 @@ -1,4 +1,4 @@ - ! SVN:$Id: ice_therm_bl99.F90 1182 2017-03-16 19:29:26Z njeffery $ + ! SVN:$Id: ice_therm_bl99.F90 1196 2017-04-18 13:32:23Z eclare $ !========================================================================= ! ! Update ice and snow internal temperatures diff --git a/src/core_seaice/column/ice_therm_itd.F90 b/src/core_seaice/column/ice_therm_itd.F90 index d9b617841d..0c8fac0928 100644 --- a/src/core_seaice/column/ice_therm_itd.F90 +++ b/src/core_seaice/column/ice_therm_itd.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_therm_itd.F90 1182 2017-03-16 19:29:26Z njeffery $ +! SVN:$Id: ice_therm_itd.F90 1196 2017-04-18 13:32:23Z eclare $ !======================================================================= ! ! Thermo calculations after call to coupler, related to ITD: @@ -23,7 +23,7 @@ module ice_therm_itd use ice_kinds_mod use ice_constants_colpkg, only: c0, c1, c2, c3, c4, c6, c10, & p001, p1, p333, p5, p666, puny, bignum, & - rhos, rhoi, Lfresh, ice_ref_salinity + rhos, rhoi, Lfresh, ice_ref_salinity, rhosmin use ice_warnings, only: add_warning @@ -66,22 +66,23 @@ module ice_therm_itd ! authors: William H. Lipscomb, LANL ! Elizabeth C. Hunke, LANL - subroutine linear_itd (ncat, hin_max, & - nilyr, nslyr, & - ntrcr, trcr_depend, & + subroutine linear_itd (ncat, hin_max, & + nilyr, nslyr, & + ntrcr, trcr_depend, & trcr_base, n_trcr_strata,& - nt_strata, & - aicen_init, vicen_init, & - aicen, trcrn, & - vicen, vsnon, & - aice, aice0, & - fpond, l_stop, & + nt_strata, Tf, & + aicen_init, vicen_init, & + aicen, trcrn, & + vicen, vsnon, & + aice, aice0, & + fpond, l_stop, & stop_label) use ice_itd, only: aggregate_area, shift_ice, & column_sum, column_conservation_check use ice_colpkg_tracers, only: nt_qice, nt_qsno, nt_fbri, nt_sice, & - tr_pond_topo, nt_apnd, nt_hpnd, tr_brine + tr_pond_topo, nt_apnd, nt_hpnd, tr_brine, & + nt_rhos, tr_snow use ice_therm_shared, only: hi_min integer (kind=int_kind), intent(in) :: & @@ -90,7 +91,10 @@ subroutine linear_itd (ncat, hin_max, & nslyr , & ! number of snow layers ntrcr ! number of tracers in use - real (kind=dbl_kind), dimension(0:ncat), intent(inout) :: & + real (kind=dbl_kind), intent(in) :: & + Tf ! ocean freezing temperature (C) + + real (kind=dbl_kind), dimension(0:ncat), intent(in) :: & hin_max ! category boundaries (m) integer (kind=int_kind), dimension (:), intent(in) :: & @@ -197,8 +201,6 @@ subroutine linear_itd (ncat, hin_max, & l_stop = .false. - hin_max(ncat) = 999.9_dbl_kind ! arbitrary big number - do n = 1, ncat donor(n) = 0 daice(n) = c0 @@ -230,7 +232,7 @@ subroutine linear_itd (ncat, hin_max, & if (tr_brine) then vbrin(n) = vbrin(n) + trcrn(nt_fbri,n) & - * vicen(n)/real(nilyr,kind=dbl_kind) + * vicen(n) endif do k = 1, nilyr @@ -542,12 +544,21 @@ subroutine linear_itd (ncat, hin_max, & trcrn(k,n) = trcrn(k,n) + rhos*Lfresh enddo enddo - + ! maintain rhos_cmp positive definiteness + if (tr_snow) then + do n = 1, ncat + do k = nt_rhos, nt_rhos+nslyr-1 + trcrn(k,n) = max(trcrn(k,n)-rhosmin, c0) +! trcrn(k,n) = trcrn(k,n) - rhosmin + enddo + enddo + endif + call shift_ice (ntrcr, ncat, & trcr_depend, & trcr_base, & n_trcr_strata, & - nt_strata, & + nt_strata, Tf, & aicen, trcrn, & vicen, vsnon, & hicen, donor, & @@ -561,6 +572,14 @@ subroutine linear_itd (ncat, hin_max, & trcrn(k,n) = trcrn(k,n) - rhos*Lfresh enddo enddo + ! maintain rhos_cmp positive definiteness + if (tr_snow) then + do n = 1, ncat + do k = nt_rhos, nt_rhos+nslyr-1 + trcrn(k,n) = trcrn(k,n) + rhosmin + enddo + enddo + endif !----------------------------------------------------------------- ! Make sure hice(1) >= minimum ice thickness hi_min. @@ -609,7 +628,7 @@ subroutine linear_itd (ncat, hin_max, & if (tr_brine) then vbrin(n) = vbrin(n) + trcrn(nt_fbri,n) & - * vicen(n)/real(nilyr,kind=dbl_kind) + * vicen(n) endif do k = 1, nilyr @@ -840,7 +859,7 @@ subroutine lateral_melt (dt, ncat, & use ice_colpkg_tracers, only: nt_qice, nt_qsno, nt_aero, tr_aero, & tr_pond_topo, nt_apnd, nt_hpnd, bio_index use ice_colpkg_shared, only: z_tracers , hs_ssl, solve_zsal - use ice_zbgc, only: lateral_melt_bgc + use ice_zbgc, only: lateral_melt_bgc real (kind=dbl_kind), intent(in) :: & dt ! time step (s) @@ -874,7 +893,7 @@ subroutine lateral_melt (dt, ncat, & real (kind=dbl_kind), dimension(nbtrcr), & intent(inout) :: & - flux_bio ! biology tracer flux from layer bgc (mmol/m^2/s) + flux_bio ! biology tracer flux from layer bgc (mmol/m^2/s) real (kind=dbl_kind), dimension(:), intent(inout) :: & faero_ocn ! aerosol flux to ocean (kg/m^2/s) @@ -958,10 +977,10 @@ subroutine lateral_melt (dt, ncat, & !----------------------------------------------------------------- ! Biogeochemistry - !----------------------------------------------------------------- + !----------------------------------------------------------------- if (z_tracers) then ! snow tracers - dvssl = min(p5*vsnon(n), hs_ssl*aicen(n)) !snow surface layer + dvssl = min(p5*vsnon(n)/real(nslyr,kind=dbl_kind), hs_ssl*aicen(n)) !snow surface layer dvint = vsnon(n)- dvssl !snow interior do k = 1, nbtrcr flux_bio(k) = flux_bio(k) & @@ -976,9 +995,10 @@ subroutine lateral_melt (dt, ncat, & if (solve_zsal .or. z_tracers) & call lateral_melt_bgc(dt, & ncat, nblyr, & - rside, vicen_init, & + rside, vicen, & trcrn, fzsal, & - flux_bio, nbtrcr) + flux_bio, nbtrcr, & + vicen_init) endif ! rside @@ -1006,7 +1026,7 @@ end subroutine lateral_melt ! subroutine add_new_ice (ncat, nilyr, nblyr, & n_aero, dt, & - ntrcr, nltrcr, & + ntrcr, nbtrcr, & hin_max, ktherm, & aicen, trcrn, & vicen, vsnon1, & @@ -1018,8 +1038,8 @@ subroutine add_new_ice (ncat, nilyr, nblyr, & Tf, sss, & salinz, phi_init, & dSin0_frazil, & - bgrid, cgrid, igrid, & - nbtrcr, flux_bio, & + bgrid, cgrid, & + igrid, flux_bio, & ocean_bio, fzsal, & frazil_diag, & l_stop, stop_label) @@ -1041,7 +1061,7 @@ subroutine add_new_ice (ncat, nilyr, nblyr, & nilyr , & ! number of ice layers nblyr , & ! number of bio layers ntrcr , & ! number of tracers - nltrcr, & ! number of zbgc tracers + nbtrcr, & ! number of bio tracer types n_aero, & ! number of aerosol tracers ktherm ! type of thermodynamics (0 0-layer, 1 BL99, 2 mushy) @@ -1103,9 +1123,6 @@ subroutine add_new_ice (ncat, nilyr, nblyr, & real (kind=dbl_kind), dimension (nilyr+1), intent(in) :: & cgrid ! CICE vertical coordinate - integer (kind=int_kind), intent(in) :: & - nbtrcr ! number of biology tracers - real (kind=dbl_kind), dimension (:), intent(inout) :: & flux_bio ! tracer flux to ocean from biology (mmol/m^2/s) @@ -1165,7 +1182,10 @@ subroutine add_new_ice (ncat, nilyr, nblyr, & vbri_final ! brine volume summed over categories real (kind=dbl_kind), dimension (ncat) :: & - vbrin ! trcrn(nt_fbri,n)*vicen(n) + vbrin ! trcrn(nt_fbri,n)*vicen(n) + + character(len=char_len_long) :: & + warning ! warning message !----------------------------------------------------------------- ! initialize @@ -1490,12 +1510,12 @@ subroutine add_new_ice (ncat, nilyr, nblyr, & !----------------------------------------------------------------- if (tr_brine .or. nbtrcr > 0) & call add_new_ice_bgc(dt, nblyr, & - ncat, nilyr, nltrcr, & + ncat, nilyr, nbtrcr, & bgrid, cgrid, igrid, & aicen_init, vicen_init, vi0_init, & aicen, vicen, vsnon1, & vi0new, ntrcr, trcrn, & - nbtrcr, sss, ocean_bio,& + sss, ocean_bio, & flux_bio, hsurp, & l_stop, stop_label, & l_conservation_check) diff --git a/src/core_seaice/column/ice_therm_mushy.F90 b/src/core_seaice/column/ice_therm_mushy.F90 index 78401ca716..769e11c819 100644 --- a/src/core_seaice/column/ice_therm_mushy.F90 +++ b/src/core_seaice/column/ice_therm_mushy.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_therm_mushy.F90 1182 2017-03-16 19:29:26Z njeffery $ +! SVN:$Id: ice_therm_mushy.F90 1196 2017-04-18 13:32:23Z eclare $ !======================================================================= module ice_therm_mushy @@ -19,7 +19,8 @@ module ice_therm_mushy private public :: & temperature_changes_salinity, & - permeability + permeability, & + update_vertical_tracers_snow real(kind=dbl_kind), parameter :: & dTemp_errmax = 5.0e-4_dbl_kind ! max allowed change in temperature @@ -50,6 +51,8 @@ subroutine temperature_changes_salinity(dt, & fcondtop, fcondbot, & fadvheat, snoice, & einit_old, & + smice, smliq, & + tr_snow, & lstop, stop_label) ! solve the enthalpy and bulk salinity of the ice for a single column @@ -96,7 +99,9 @@ subroutine temperature_changes_salinity(dt, & real (kind=dbl_kind), dimension (:), intent(inout) :: & Sswabs , & ! SW radiation absorbed in snow layers (W m-2) - Iswabs ! SW radiation absorbed in ice layers (W m-2) + Iswabs , & ! SW radiation absorbed in ice layers (W m-2) + smice , & ! ice mass tracer in snow (kg/m^3) + smliq ! liquid water mass tracer in snow (kg/m^3) real (kind=dbl_kind), intent(inout):: & fsurfn , & ! net flux to top surface, excluding fcondtopn @@ -120,6 +125,9 @@ subroutine temperature_changes_salinity(dt, & zqsn , & ! snow layer enthalpy (J m-3) zTsn ! internal snow layer temperatures + logical (kind=log_kind), intent(in) :: & + tr_snow ! if .true., use snow tracers + logical (kind=log_kind), intent(inout) :: & lstop ! solver failure flag @@ -326,6 +334,8 @@ subroutine temperature_changes_salinity(dt, & phi, dt, & zSin, Sbr, & sss, qocn, & + smice, smliq, & + tr_snow, & snoice, fadvheat) end subroutine temperature_changes_salinity @@ -523,7 +533,7 @@ subroutine two_stage_solver_snow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -576,7 +586,7 @@ subroutine two_stage_solver_snow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -842,7 +852,7 @@ subroutine two_stage_solver_nosnow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -895,7 +905,7 @@ subroutine two_stage_solver_nosnow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -1364,13 +1374,30 @@ subroutine picard_solver(nilyr, nslyr, & ! if not converged if (.not. lconverged) then - call picard_nonconvergence(nilyr, nslyr,& - Tsf0, Tsf, & - zTsn0, zTsn, & - zTin0, zTin, & - zSin0, zSin, & - zqsn0, zqsn, & - zqin0, phi) + call picard_nonconvergence(nilyr, nslyr, & + Tsf0, Tsf, & + zTsn0, zTsn, & + zTin0, zTin, & + zSin0, zSin, & + zqsn0, zqsn, & + zqin0, phi, & + dt, & + hilyr, hslyr, & + km, ks, & + Iswabs, Sswabs, & + Tbot, & + fswint, fswsfc, & + rhoa, flw, & + potT, Qa, & + shcoef, lhcoef, & + fcondtop, fcondbot, & + fadvheat, & + flwoutn, fsensn, & + flatn, fsurfn, & + qpond, qocn, & + Spond, sss, & + q, dSdt, & + w) lstop = .true. stop_label = "picard_solver: Picard solver non-convergence" @@ -1380,13 +1407,30 @@ end subroutine picard_solver !======================================================================= - subroutine picard_nonconvergence(nilyr, nslyr,& - Tsf0, Tsf, & - zTsn0, zTsn, & - zTin0, zTin, & - zSin0, zSin, & - zqsn0, zqsn, & - zqin0, phi) + subroutine picard_nonconvergence(nilyr, nslyr, & + Tsf0, Tsf, & + zTsn0, zTsn, & + zTin0, zTin, & + zSin0, zSin, & + zqsn0, zqsn, & + zqin0, phi, & + dt, & + hilyr, hslyr, & + km, ks, & + Iswabs, Sswabs, & + Tbot, & + fswint, fswsfc, & + rhoa, flw, & + potT, Qa, & + shcoef, lhcoef, & + fcondtop, fcondbot, & + fadvheat, & + flwoutn, fsensn, & + flatn, fsurfn, & + qpond, qocn, & + Spond, sss, & + q, dSdt, & + w) integer (kind=int_kind), intent(in) :: & nilyr , & ! number of ice layers @@ -1408,29 +1452,150 @@ subroutine picard_nonconvergence(nilyr, nslyr,& phi , & ! ice layer liquid fraction zqin0 + real(kind=dbl_kind), intent(in) :: & + dt , & ! time step (s) + hilyr , & ! ice layer thickness (m) + hslyr , & ! snow layer thickness (m) + Tbot , & ! ice bottom surfce temperature (deg C) + fswint , & ! SW absorbed in ice interior below surface (W m-2) + fswsfc , & ! SW absorbed at ice/snow surface (W m-2) + rhoa , & ! air density (kg/m^3) + flw , & ! incoming longwave radiation (W/m^2) + potT , & ! air potential temperature (K) + Qa , & ! specific humidity (kg/kg) + shcoef , & ! transfer coefficient for sensible heat + lhcoef , & ! transfer coefficient for latent heat + qpond , & ! melt pond brine enthalpy (J m-3) + qocn , & ! ocean brine enthalpy (J m-3) + Spond , & ! melt pond salinity (ppt) + sss , & ! sea surface salinity (ppt) + w ! vertical flushing Darcy velocity (m/s) + + real(kind=dbl_kind), dimension(:), intent(in) :: & + km , & ! ice conductivity (W m-1 K-1) + Iswabs , & ! SW radiation absorbed in ice layers (W m-2) + dSdt ! gravity drainage desalination rate for slow mode (ppt s-1) + + real(kind=dbl_kind), dimension(0:nilyr), intent(in) :: & + q ! upward interface vertical Darcy flow (m s-1) + + real(kind=dbl_kind), dimension(:), intent(in) :: & + ks , & ! snow conductivity (W m-1 K-1) + Sswabs ! SW radiation absorbed in snow layers (W m-2) + + real(kind=dbl_kind), intent(in) :: & + flwoutn , & ! upward LW at surface (W m-2) + fsensn , & ! surface downward sensible heat (W m-2) + flatn , & ! surface downward latent heat (W m-2) + fsurfn ! net flux to top surface, excluding fcondtop + + real(kind=dbl_kind), intent(in) :: & + fcondtop , & ! downward cond flux at top surface (W m-2) + fcondbot , & ! downward cond flux at bottom surface (W m-2) + fadvheat ! flow of heat to ocean due to advection (W m-2) + integer :: & k ! vertical layer index character(len=char_len_long) :: & warning ! warning message - + write(warning,*) "-------------------------------------" call add_warning(warning) + write(warning,*) + call add_warning(warning) write(warning,*) "picard convergence failed!" call add_warning(warning) + write(warning,*) "==========================" + call add_warning(warning) + write(warning,*) + call add_warning(warning) + + write(warning,*) "Surface: Tsf0, Tsf" + call add_warning(warning) write(warning,*) 0, Tsf0, Tsf call add_warning(warning) - + write(warning,*) + call add_warning(warning) + + write(warning,*) "Snow: zTsn0(k), zTsn(k), zqsn0(k), ks(k), Sswabs(k)" + call add_warning(warning) do k = 1, nslyr - write(warning,*) k, zTsn0(k), zTsn(k), zqsn0(k) + write(warning,*) k, zTsn0(k), zTsn(k), zqsn0(k), ks(k), Sswabs(k) call add_warning(warning) - enddo ! k - + enddo ! k + write(warning,*) + call add_warning(warning) + + write(warning,*) "Ice: zTin0(k), zTin(k), zSin0(k), zSin(k), phi(k), zqin0(k), km(k), Iswabs(k), dSdt(k)" + call add_warning(warning) do k = 1, nilyr - write(warning,*) k, zTin0(k), zTin(k), zSin0(k), zSin(k), phi(k), zqin0(k) + write(warning,*) k, zTin0(k), zTin(k), zSin0(k), zSin(k), phi(k), zqin0(k), km(k), Iswabs(k), dSdt(k) call add_warning(warning) enddo ! k + write(warning,*) + call add_warning(warning) + + write(warning,*) "Ice boundary: q(k)" + call add_warning(warning) + do k = 0, nilyr + write(warning,*) k, q(k) + call add_warning(warning) + enddo ! k + write(warning,*) + call add_warning(warning) + + write(warning,*) "dt: ", dt + call add_warning(warning) + write(warning,*) "hilyr: ", hilyr + call add_warning(warning) + write(warning,*) "hslyr: ", hslyr + call add_warning(warning) + write(warning,*) "Tbot: ", Tbot + call add_warning(warning) + write(warning,*) "fswint: ", fswint + call add_warning(warning) + write(warning,*) "fswsfc: ", fswsfc + call add_warning(warning) + write(warning,*) "rhoa: ", rhoa + call add_warning(warning) + write(warning,*) "flw: ", flw + call add_warning(warning) + write(warning,*) "potT: ", potT + call add_warning(warning) + write(warning,*) "Qa: ", Qa + call add_warning(warning) + write(warning,*) "shcoef: ", shcoef + call add_warning(warning) + write(warning,*) "lhcoef: ", lhcoef + call add_warning(warning) + write(warning,*) "qpond: ", qpond + call add_warning(warning) + write(warning,*) "qocn: ", qocn + call add_warning(warning) + write(warning,*) "Spond: ", Spond + call add_warning(warning) + write(warning,*) "sss: ", sss + call add_warning(warning) + write(warning,*) "w: ", w + call add_warning(warning) + write(warning,*) "flwoutn: ", flwoutn + call add_warning(warning) + write(warning,*) "fsensn: ", fsensn + call add_warning(warning) + write(warning,*) "flatn: ", flatn + call add_warning(warning) + write(warning,*) "fsurfn: ", fsurfn + call add_warning(warning) + write(warning,*) "fcondtop: ", fcondtop + call add_warning(warning) + write(warning,*) "fcondbot: ", fcondbot + call add_warning(warning) + write(warning,*) "fadvheat: ", fadvheat + call add_warning(warning) + write(warning,*) + call add_warning(warning) write(warning,*) "-------------------------------------" call add_warning(warning) @@ -3181,6 +3346,8 @@ subroutine flood_ice(hsn, hin, & phi, dt, & zSin, Sbr, & sss, qocn, & + smice, smliq, & + tr_snow, & snoice, fadvheat) ! given upwards flushing brine flow calculate amount of snow ice and @@ -3204,7 +3371,9 @@ subroutine flood_ice(hsn, hin, & zqsn , & ! snow layer enthalpy (J m-2) zqin , & ! ice layer enthalpy (J m-2) zSin , & ! ice layer bulk salinity (ppt) - phi ! ice liquid fraction + phi , & ! ice liquid fraction + smice , & ! ice mass tracer in snow (kg/m^3) + smliq ! liquid water mass tracer in snow (kg/m^3) real(kind=dbl_kind), dimension(:), intent(in) :: & Sbr ! ice layer brine salinity (ppt) @@ -3216,9 +3385,12 @@ subroutine flood_ice(hsn, hin, & real(kind=dbl_kind), intent(out) :: & snoice ! snow ice formation - real(kind=dbl_kind), intent(inout) :: & + real(kind=dbl_kind), intent(inout) :: & fadvheat ! advection heat flux to ocean + logical (kind=log_kind), intent(in) :: & + tr_snow ! if .true., use snow tracers + real(kind=dbl_kind) :: & hin2 , & ! new ice thickness (m) hsn2 , & ! new snow thickness (m) @@ -3232,6 +3404,7 @@ subroutine flood_ice(hsn, hin, & zqsn_snowice , & ! snow enthalpy of snow thats becoming snowice (J m-2) freeboard_density , & ! negative of ice surface freeboard times the ocean density (kg m-2) ice_mass , & ! mass of the ice (kg m-2) + snow_mass , & ! mass of the ice (kg m-2) rho_ocn , & ! density of the ocean (kg m-3) ice_density , & ! density of ice layer (kg m-3) hadded , & ! thickness rate of water used from ocean (m/s) @@ -3257,16 +3430,32 @@ subroutine flood_ice(hsn, hin, & enddo ! k ice_mass = ice_mass * hilyr +! for now, do not use variable snow density +! snow_mass = c0 +! if (tr_snow) then +! do k = 1,nslyr +! snow_mass = snow_mass + (smice(k) + smliq(k)) * hslyr +! enddo +! else + snow_mass = rhos * hsn +! endif + ! negative freeboard times ocean density - freeboard_density = max(ice_mass + hsn * rhos - hin * rho_ocn, c0) + freeboard_density = max(ice_mass + snow_mass - hin * rho_ocn, c0) ! check if have flooded ice if (freeboard_density > c0) then ! sea ice fraction of newly formed snow ice - phi_snowice = (c1 - rhos / rhoi) +! phi_snowice = (c1 - snow_mass / hsn / rhoi) ! non-BFB + phi_snowice = (c1 - rhos / rhoi) ! for now, do not use variable snow density - ! density of newly formed snowice +! njeffery: changed to rhos instead of (c1-phi_snowice)*rhoi +! to conserve ice and liquid snow tracers when rhos = smice + smliq +! eclare: this change seems to be BFB + + ! density of newly formed snowice +! rho_snowice = phi_snowice * rho_ocn + rhos rho_snowice = phi_snowice * rho_ocn + (c1 - phi_snowice) * rhoi ! calculate thickness of new ice added @@ -3290,6 +3479,11 @@ subroutine flood_ice(hsn, hin, & ! change snow properties call update_vertical_tracers_snow(nslyr, zqsn, hslyr, hslyr2) + if (tr_snow .and. hslyr2 > puny) then + call update_vertical_tracers_snow(nslyr, smice, hslyr, hslyr2) + call update_vertical_tracers_snow(nslyr, smliq, hslyr, hslyr2) + endif + ! change ice properties call update_vertical_tracers_ice(nilyr, zqin, hilyr, hilyr2, & hin, hin2, zqin_snowice) diff --git a/src/core_seaice/column/ice_therm_shared.F90 b/src/core_seaice/column/ice_therm_shared.F90 index 1f59e28b29..09bf6b7593 100644 --- a/src/core_seaice/column/ice_therm_shared.F90 +++ b/src/core_seaice/column/ice_therm_shared.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_therm_shared.F90 1182 2017-03-16 19:29:26Z njeffery $ +! SVN:$Id: ice_therm_shared.F90 1196 2017-04-18 13:32:23Z eclare $ !========================================================================= ! ! Shared thermo variables, subroutines diff --git a/src/core_seaice/column/ice_therm_vertical.F90 b/src/core_seaice/column/ice_therm_vertical.F90 index f28d24962c..d2ad23be29 100644 --- a/src/core_seaice/column/ice_therm_vertical.F90 +++ b/src/core_seaice/column/ice_therm_vertical.F90 @@ -1,4 +1,4 @@ -! SVN:$Id: ice_therm_vertical.F90 1182 2017-03-16 19:29:26Z njeffery $ +! SVN:$Id: ice_therm_vertical.F90 1196 2017-04-18 13:32:23Z eclare $ !========================================================================= ! ! Update ice and snow internal temperatures and compute @@ -24,7 +24,8 @@ module ice_therm_vertical use ice_constants_colpkg, only: c0, c1, c3, p001, p5, puny, & pi, depressT, Lvap, hs_min, cp_ice, & cp_ocn, rhow, rhoi, rhos, Lfresh, rhofresh, ice_ref_salinity - use ice_colpkg_shared, only: ktherm, heat_capacity, calc_Tsfc, min_salin + use ice_colpkg_shared, only: ktherm, heat_capacity, calc_Tsfc, & + min_salin, rsnw_fall, rsnw_tmax use ice_therm_shared, only: ferrmax, l_brine, & calculate_tin_from_qin, Tmin use ice_therm_bl99, only: temperature_changes @@ -35,7 +36,7 @@ module ice_therm_vertical save private - public :: frzmlt_bottom_lateral, thermo_vertical + public :: frzmlt_bottom_lateral, thermo_vertical, adjust_enthalpy !======================================================================= @@ -54,13 +55,15 @@ subroutine thermo_vertical (nilyr, nslyr, & vicen, vsnon, & Tsf, zSin, & zqin, zqsn, & - apond, hpond, & - iage, tr_pond_topo,& + smice, smliq, & + tr_snow, apond, & + hpond, iage, & + tr_pond_topo, & flw, potT, & Qa, rhoa, & fsnow, fpond, & fbot, Tbot, & - sss, & + sss, rsnw, & lhcoef, shcoef, & fswsfc, fswint, & Sswabs, Iswabs, & @@ -68,11 +71,14 @@ subroutine thermo_vertical (nilyr, nslyr, & fsensn, flatn, & flwoutn, evapn, & freshn, fsaltn, & - fhocnn, meltt, & + fhocnn, frain, & + meltt, & melts, meltb, & + meltsliq, & congel, snoice, & mlt_onset, frz_onset, & yday, dsnow, & + tr_rsnw, & l_stop, stop_label,& prescribed_ice) @@ -83,7 +89,8 @@ subroutine thermo_vertical (nilyr, nslyr, & nslyr ! number of snow layers real (kind=dbl_kind), intent(in) :: & - dt ! time step + dt , & ! time step + frain ! rainfall rate (kg/m2/s) ! ice state variables real (kind=dbl_kind), intent(inout) :: & @@ -107,7 +114,10 @@ subroutine thermo_vertical (nilyr, nslyr, & real (kind=dbl_kind), dimension (:), intent(inout) :: & zqsn , & ! snow layer enthalpy, zqsn < 0 (J m-3) zqin , & ! ice layer enthalpy, zqin < 0 (J m-3) - zSin ! internal ice layer salinities + zSin , & ! internal ice layer salinities + rsnw , & ! snow grain radius (10^-6 m) + smice , & ! ice mass tracer in snow (kg/m^3) + smliq ! liquid water mass tracer in snow (kg/m^3) ! input from atmosphere real (kind=dbl_kind), & @@ -159,6 +169,7 @@ subroutine thermo_vertical (nilyr, nslyr, & intent(inout):: & meltt , & ! top ice melt (m/step-->cm/day) melts , & ! snow melt (m/step-->cm/day) + meltsliq , & ! snow melt mass (kg/m^2/step-->kg/m^2/day) meltb , & ! basal ice melt (m/step-->cm/day) congel , & ! basal ice growth (m/step-->cm/day) snoice , & ! snow-ice formation (m/step-->cm/day) @@ -169,6 +180,10 @@ subroutine thermo_vertical (nilyr, nslyr, & real (kind=dbl_kind), intent(in) :: & yday ! day of year + logical (kind=log_kind), intent(in) :: & + tr_snow , & ! if .true., use snow tracers + tr_rsnw ! if .true., use dynamic snow grain radius + logical (kind=log_kind), intent(out) :: & l_stop ! if true, print diagnostics and abort on return @@ -231,6 +246,7 @@ subroutine thermo_vertical (nilyr, nslyr, & congel = c0 snoice = c0 dsnow = c0 + meltsliq= c0 if (calc_Tsfc) then fsensn = c0 @@ -288,6 +304,8 @@ subroutine thermo_vertical (nilyr, nslyr, & fcondtopn, fcondbot, & fadvocn, snoice, & einit, & + smice, smliq, & + tr_snow, & l_stop, stop_label) if (l_stop) return @@ -372,17 +390,20 @@ subroutine thermo_vertical (nilyr, nslyr, & hin, hilyr, & hsn, hslyr, & zqin, zqsn, & + smice, smliq, & fbot, Tbot, & flatn, fsurfn, & fcondtopn, fcondbot, & fsnow, hsn_new, & fhocnn, evapn, & meltt, melts, & + meltsliq, frain, & meltb, iage, & congel, snoice, & mlt_onset, frz_onset, & zSin, sss, & - dsnow) + dsnow, tr_snow, & + rsnw, tr_rsnw) !----------------------------------------------------------------- ! Check for energy conservation by comparing the change in energy @@ -675,8 +696,11 @@ subroutine init_vertical_profile(nilyr, nslyr, & real (kind=dbl_kind), dimension (:), & intent(out) :: & - zqsn , & ! snow enthalpy zTsn ! snow temperature + + real (kind=dbl_kind), dimension (:), & + intent(inout) :: & + zqsn ! snow enthalpy logical (kind=log_kind), intent(inout) :: & l_stop ! if true, print diagnostics and abort model @@ -785,7 +809,7 @@ subroutine init_vertical_profile(nilyr, nslyr, & if (zTsn(k) > Tmax) then write(warning,*) ' ' call add_warning(warning) - write(warning,*) 'Starting thermo, zTsn > Tmax' + write(warning,*) 'Starting thermo, zTsn > Tmax, k = ', k call add_warning(warning) write(warning,*) 'zTsn=',zTsn(k) call add_warning(warning) @@ -807,7 +831,7 @@ subroutine init_vertical_profile(nilyr, nslyr, & if (zTsn(k) < Tmin) then ! allowing for roundoff error write(warning,*) ' ' call add_warning(warning) - write(warning,*) 'Starting thermo, zTsn < Tmin' + write(warning,*) 'Starting thermo, zTsn < Tmin, k = ',k call add_warning(warning) write(warning,*) 'zTsn=', zTsn(k) call add_warning(warning) @@ -815,9 +839,9 @@ subroutine init_vertical_profile(nilyr, nslyr, & call add_warning(warning) write(warning,*) 'zqsn', zqsn(k) call add_warning(warning) - write(warning,*) hin + write(warning,*) 'hin', hin call add_warning(warning) - write(warning,*) hsn + write(warning,*) 'hsn', hsn call add_warning(warning) l_stop = .true. stop_label = "init_vertical_profile: Starting thermo, zTsn < Tmin" @@ -904,16 +928,9 @@ subroutine init_vertical_profile(nilyr, nslyr, & if (tice_high .and. heat_capacity) then - if (l_brine) then - Tmax = Tmlts(k) - else ! fresh ice - Tmax = -zqin(k)*puny/(rhos*cp_ice*vicen) - endif - - if (zTin(k) > Tmax) then write(warning,*) ' ' call add_warning(warning) - write(warning,*) 'Starting thermo, T > Tmax, layer', k + write(warning,*) 'Starting thermo, zTin > Tmax, layer', k call add_warning(warning) write(warning,*) 'k:', k call add_warning(warning) @@ -941,27 +958,30 @@ subroutine init_vertical_profile(nilyr, nslyr, & call add_warning(warning) else l_stop = .true. - stop_label = "init_vertical_profile: Starting thermo, T > Tmax, layer" + stop_label = "init_vertical_profile: Starting thermo, zTin > Tmax, layer" return endif - endif endif ! tice_high if (tice_low .and. heat_capacity) then - if (zTin(k) < Tmin) then write(warning,*) ' ' call add_warning(warning) - write(warning,*) 'Starting thermo T < Tmin, layer', k + write(warning,*) 'Starting thermo, zTin < Tmin, layer', k call add_warning(warning) - write(warning,*) 'zTin =', zTin(k) + write(warning,*) 'k:', k + call add_warning(warning) + write(warning,*) 'zTin =',zTin(k),', Tmin=',Tmin + call add_warning(warning) + write(warning,*) 'zSin =',zSin(k) call add_warning(warning) - write(warning,*) 'Tmin =', Tmin + write(warning,*) 'hin =',hin + call add_warning(warning) + write(warning,*) 'zqin =',zqin(k) call add_warning(warning) l_stop = .true. - stop_label = "init_vertical_profile: Starting thermo, T < Tmin, layer" + stop_label = "init_vertical_profile: Starting thermo, zTin < Tmin, layer" return - endif endif ! tice_low !----------------------------------------------------------------- @@ -1009,17 +1029,20 @@ subroutine thickness_changes (nilyr, nslyr, & hin, hilyr, & hsn, hslyr, & zqin, zqsn, & + smice, smliq, & fbot, Tbot, & flatn, fsurfn, & fcondtopn, fcondbot, & fsnow, hsn_new, & fhocnn, evapn, & meltt, melts, & + meltsliq, frain, & meltb, iage, & congel, snoice, & mlt_onset, frz_onset,& zSin, sss, & - dsnow) + dsnow, tr_snow, & + rsnw, tr_rsnw) use ice_colpkg_shared, only: phi_i_mushy use ice_mushy_physics, only: enthalpy_mush, enthalpy_of_melting, & @@ -1040,14 +1063,18 @@ subroutine thickness_changes (nilyr, nslyr, & fsnow , & ! snowfall rate (kg m-2 s-1) flatn , & ! surface downward latent heat (W m-2) fsurfn , & ! net flux to top surface, excluding fcondtopn - fcondtopn ! downward cond flux at top surface (W m-2) + fcondtopn , & ! downward cond flux at top surface (W m-2) + frain ! rainfall rate (kg/m2/s) real (kind=dbl_kind), intent(inout) :: & fcondbot ! downward cond flux at bottom surface (W m-2) real (kind=dbl_kind), dimension (:), intent(inout) :: & zqin , & ! ice layer enthalpy (J m-3) - zqsn ! snow layer enthalpy (J m-3) + zqsn , & ! snow layer enthalpy (J m-3) + rsnw , & ! snow grain radius (10^-6 m) + smice , & ! ice mass tracer in snow (kg/m^3) + smliq ! liquid water mass tracer in snow (kg/m^3) real (kind=dbl_kind), intent(inout) :: & hilyr , & ! ice layer thickness (m) @@ -1056,6 +1083,7 @@ subroutine thickness_changes (nilyr, nslyr, & real (kind=dbl_kind), intent(inout) :: & meltt , & ! top ice melt (m/step-->cm/day) melts , & ! snow melt (m/step-->cm/day) + meltsliq , & ! snow melt mass (kg/m^2/step-->kg/m^2/day) meltb , & ! basal ice melt (m/step-->cm/day) congel , & ! basal ice growth (m/step-->cm/day) snoice , & ! snow-ice formation (m/step-->cm/day) @@ -1086,6 +1114,10 @@ subroutine thickness_changes (nilyr, nslyr, & real (kind=dbl_kind), intent(in) :: & sss ! ocean salinity (PSU) + logical (kind=log_kind), intent(in) :: & + tr_snow , & ! if .true., use snow tracers + tr_rsnw ! if .true., use snow dynamic snow grain radius + ! local variables real (kind=dbl_kind), parameter :: & @@ -1114,7 +1146,9 @@ subroutine thickness_changes (nilyr, nslyr, & wk1 , & ! temporary variable zqsnew , & ! enthalpy of new snow (J m-3) hstot , & ! snow thickness including new snow (m) - Tmlts ! melting temperature + Tmlts , & ! melting temperature + smtot , & ! total ice + liquid mass of snow + smice_precs ! ice mass added to snow due to snowfall (kg/m^2) real (kind=dbl_kind), dimension (nilyr+1) :: & zi1 , & ! depth of ice layer boundaries (m) @@ -1128,7 +1162,9 @@ subroutine thickness_changes (nilyr, nslyr, & dzi ! ice layer thickness after growth/melting real (kind=dbl_kind), dimension (nslyr) :: & - dzs ! snow layer thickness after growth/melting + dzs , & ! snow layer thickness after growth/melting + smicetot , & ! total ice mass of snow in each layer (kg/m^2) + smliqtot ! total liquid mass of snow in each layer (kg/m^2) real (kind=dbl_kind), dimension (nilyr) :: & qm , & ! energy of melting (J m-3) = zqin in BL99 formulation @@ -1151,6 +1187,12 @@ subroutine thickness_changes (nilyr, nslyr, & do k = 1, nslyr dzs(k) = hslyr + smicetot(k) = c0 + smliqtot(k) = c0 + if (tr_snow) then + smicetot(k) = dzs(k) * smice(k) + smliqtot(k) = dzs(k) * smliq(k) + endif enddo do k = 1, nilyr @@ -1175,8 +1217,12 @@ subroutine thickness_changes (nilyr, nslyr, & do k = 1, nslyr Ts = (Lfresh + zqsn(k)/rhos) / cp_ice if (Ts > c0) then - dhs = cp_ice*Ts*dzs(k) / Lfresh - dzs(k) = dzs(k) - dhs + dhs = cp_ice*Ts*dzs(k) / Lfresh ! melt + smice_precs = c0 + if (abs(dzs(k)) > puny) smice_precs = smicetot(k)/dzs(k) * dhs + smicetot(k) = max(c0,smicetot(k) - smice_precs) ! dhs << dzs + smliqtot(k) = max(c0,smliqtot(k) + smice_precs) + dzs (k) = dzs(k) - dhs zqsn(k) = -rhos*Lfresh endif enddo @@ -1219,6 +1265,7 @@ subroutine thickness_changes (nilyr, nslyr, & if (hsn > puny) then ! add snow with enthalpy zqsn(1) dhs = econ / (zqsn(1) - rhos*Lvap) ! econ < 0, dhs > 0 + smicetot(1) = dhs*rhos + smicetot(1) ! new snow ice dzs(1) = dzs(1) + dhs evapn = evapn + dhs*rhos else ! add ice with enthalpy zqin(1) @@ -1299,8 +1346,12 @@ subroutine thickness_changes (nilyr, nslyr, & if (ktherm == 2 .and. zqsn(k) > -rhos * Lfresh) then dhs = max(-dzs(k), & - -((zqsn(k) + rhos*Lfresh) / (rhos*Lfresh)) * dzs(k)) - dzs(k) = dzs(k) + dhs + -((zqsn(k) + rhos*Lfresh) / (rhos*Lfresh)) * dzs(k)) ! dhs < 0 + smice_precs = c0 + if (abs(dzs(k)) > puny) smice_precs = smicetot(k)/dzs(k) * dhs + smicetot(k) = max(c0,smicetot(k) + smice_precs) ! -dhs <= dzs + smliqtot(k) = max(c0,smliqtot(k) - smice_precs) + dzs (k) = dzs(k) + dhs zqsn(k) = -rhos * Lfresh melts = melts - dhs ! delta E = zqsn(k) + rhos * Lfresh @@ -1313,6 +1364,9 @@ subroutine thickness_changes (nilyr, nslyr, & qsub = zqsn(k) - rhos*Lvap ! qsub < 0 dhs = max (-dzs(k), esub/qsub) ! esub > 0, dhs < 0 + smice_precs = c0 + if (abs(dzs(k)) > puny) smice_precs = dhs * smicetot(k)/dzs(k) + smicetot(k) = max(c0,smicetot(k) + smice_precs) dzs(k) = dzs(k) + dhs esub = esub - dhs*qsub esub = max(esub, c0) ! in case of roundoff error @@ -1323,6 +1377,10 @@ subroutine thickness_changes (nilyr, nslyr, & !-------------------------------------------------------------- dhs = max(-dzs(k), etop_mlt/zqsn(k)) + smice_precs = c0 + if (abs(dzs(k)) > puny) smice_precs = smicetot(k)/dzs(k) * dhs + smicetot(k) = max(c0,smicetot(k) + smice_precs) + smliqtot(k) = max(c0,smliqtot(k) - smice_precs) dzs(k) = dzs(k) + dhs ! zqsn < 0, dhs < 0 etop_mlt = etop_mlt - dhs*zqsn(k) etop_mlt = max(etop_mlt, c0) ! in case of roundoff error @@ -1403,6 +1461,9 @@ subroutine thickness_changes (nilyr, nslyr, & ebot_mlt = ebot_mlt - dhs*zqsn(k) ebot_mlt = max(ebot_mlt, c0) + ! bug fix added by Andrew Roberts, August 5, 2020 + melts = melts - dhs + enddo ! nslyr !----------------------------------------------------------------- @@ -1435,10 +1496,44 @@ subroutine thickness_changes (nilyr, nslyr, & ! avoid roundoff errors zqsn(1) = min(zqsn(1), -rhos*Lfresh) + if (tr_snow) then + + smtot = c0 + if (abs(dzs(1)) > c0) smtot = smicetot(1)/dzs(1) !smice(1) ! save for now + + ! ice mass in snow due to snowfall (precs) + ! new snow density = rhos for now + smice_precs = hsn_new * rhos ! kg/m^2 + + ! update ice mass tracer due to snowfall + smicetot(1) = smicetot(1) + smice_precs + ! smice(1) = (dzs(1)*smice(1) + smice_precs) / hstot + + ! mass fraction of ice due to snowfall + smtot = c0 + do k = 1, nslyr + ! smtot = smtot + smice(k) + smliq(k) + smtot = smtot + smicetot(k) + smliqtot(k) + enddo + if (smtot > c0) then + smice_precs = smice_precs / smtot + else + smice_precs = c1 + endif + + endif + dzs(1) = hstot + endif endif +!---!----------------------------------------------------------------- +!---! Add rain at top surface (only to liquid tracer) +!---!----------------------------------------------------------------- + + smliqtot(1) = smliqtot(1) + frain*dt + !----------------------------------------------------------------- ! Find the new ice and snow thicknesses. !----------------------------------------------------------------- @@ -1455,6 +1550,14 @@ subroutine thickness_changes (nilyr, nslyr, & dsnow = dsnow + dzs(k) - hslyr enddo ! k + !------------------------------------------------------------------- + ! Incorporate new snow for snow grain radius + !------------------------------------------------------------------- + if (tr_rsnw .and. hsn_new > c0) & + call add_new_snow_radius (nslyr, dzs(1), & + hsn_new, rsnw(1), & + rsnw_fall, rsnw_tmax) + !------------------------------------------------------------------- ! Convert snow to ice if snow lies below freeboard. !------------------------------------------------------------------- @@ -1465,8 +1568,26 @@ subroutine thickness_changes (nilyr, nslyr, & hin, hsn, & zqin, zqsn, & dzi, dzs, & - dsnow) + dsnow, & + smicetot(:), & + smliqtot(:)) + !------------------------------------------------------------------- + ! Update snow mass tracers, smice and smliq, for uneven layers + !------------------------------------------------------------------- + if (tr_snow) then + do k = 1, nslyr + meltsliq = meltsliq + smliqtot(k) ! total liquid (in case all snow melted) + if (dzs(k) > c0) then + smice(k) = smicetot(k) / dzs(k) + smliq(k) = smliqtot(k) / dzs(k) + else + smice(k) = c0 + smliq(k) = c0 + endif + enddo + endif + !---!------------------------------------------------------------------- !---! Repartition the ice and snow into equal-thickness layers, !---! conserving energy. @@ -1556,6 +1677,23 @@ subroutine thickness_changes (nilyr, nslyr, & hslyr, hsn, & zqsn) + if (tr_rsnw) & + call adjust_enthalpy (nslyr, & + zs1(:), zs2(:), & + hslyr, hsn, & + rsnw(:)) + + if (tr_snow) then + call adjust_enthalpy (nslyr, & + zs1(:), zs2(:), & + hslyr, hsn, & + smice(:)) + call adjust_enthalpy (nslyr, & + zs1(:), zs2(:), & + hslyr, hsn, & + smliq(:)) + endif + endif ! nslyr > 1 !----------------------------------------------------------------- @@ -1568,6 +1706,11 @@ subroutine thickness_changes (nilyr, nslyr, & fhocnn = fhocnn & + zqsn(k)*hsn/(real(nslyr,kind=dbl_kind)*dt) zqsn(k) = -rhos*Lfresh + if (tr_snow) then + meltsliq = meltsliq + smicetot(k) ! add to meltponds + smice(k) = c0 + smliq(k) = c0 + endif hslyr = c0 endif enddo @@ -1615,7 +1758,8 @@ subroutine freeboard (nslyr, dt, & hin, hsn, & zqin, zqsn, & dzi, dzs, & - dsnow) + dsnow, smicetot, & + smliqtot) integer (kind=int_kind), intent(in) :: & nslyr ! number of snow layers @@ -1640,7 +1784,9 @@ subroutine freeboard (nslyr, dt, & real (kind=dbl_kind), dimension (:), intent(inout) :: & zqin , & ! ice layer enthalpy (J m-3) dzi , & ! ice layer thicknesses (m) - dzs ! snow layer thicknesses (m) + dzs , & ! snow layer thicknesses (m) + smicetot, & ! snow ice mass per layer (kg/m^2) + smliqtot ! snow liquid mass per layer (kg/m^2) ! local variables @@ -1664,7 +1810,7 @@ subroutine freeboard (nslyr, dt, & dhsn = c0 hqs = c0 - wk1 = hsn - hin*(rhow-rhoi)/rhos + wk1 = hsn - hin*(rhow-rhoi)/rhos ! not yet consistent with smice/smliq if (wk1 > puny .and. hsn > puny) then ! snow below freeboard dhsn = min(wk1*rhoi/rhow, hsn) ! snow to remove @@ -1679,6 +1825,8 @@ subroutine freeboard (nslyr, dt, & do k = nslyr, 1, -1 if (dhin > puny) then dhs = min(dhsn, dzs(k)) ! snow to remove from layer + smicetot(k) = max(c0,smicetot(k) - dhs * smicetot(k) / dzs(k)) !smice(k) + smliqtot(k) = max(c0,smliqtot(k) - dhs * smliqtot(k) / dzs(k)) !smliq(k) hsn = hsn - dhs dsnow = dsnow -dhs !new snow addition term dzs(k) = dzs(k) - dhs @@ -1837,7 +1985,8 @@ subroutine conservation_check_vthermo(dt, & real (kind=dbl_kind) :: & einp , & ! energy input during timestep (J m-2) - ferr ! energy conservation error (W m-2) + ferr , & ! energy conservation error (W m-2) + ftop ! surface flux error: fcondtopn-fsurfn character(len=char_len_long) :: & warning ! warning message @@ -1883,6 +2032,10 @@ subroutine conservation_check_vthermo(dt, & call add_warning(warning) write(warning,*) fbot,fcondbot call add_warning(warning) + write(warning,*) 'fsurfn,fcondtopn:' + call add_warning(warning) + write(warning,*) fsurfn,fcondtopn + call add_warning(warning) ! if (ktherm == 2) then write(warning,*) 'Intermediate energy =', einter @@ -1893,11 +2046,15 @@ subroutine conservation_check_vthermo(dt, & write(warning,*) 'einter - einit =', & einter-einit call add_warning(warning) + ftop = c0 + if (ktherm == 2) then + if (fcondtopn > fsurfn) ftop = (fcondtopn-fsurfn) + end if write(warning,*) 'Conduction Error =', (einter-einit) & - - (fcondtopn*dt - fcondbot*dt + fswint*dt) + - (fcondtopn*dt - fcondbot*dt + fswint*dt) + ftop*dt call add_warning(warning) write(warning,*) 'Melt/Growth Error =', (einter-einit) & - + ferr*dt - (fcondtopn*dt - fcondbot*dt + fswint*dt) + + ferr*dt - (fcondtopn*dt - fcondbot*dt + fswint*dt)-ftop*dt call add_warning(warning) write(warning,*) 'Advection Error =', fadvocn*dt call add_warning(warning) @@ -1988,6 +2145,35 @@ subroutine update_state_vthermo(nilyr, nslyr, & end subroutine update_state_vthermo +!======================================================================= + +! Modify snow grain radius in upper layer due to fallen snow + + subroutine add_new_snow_radius (nslyr, dzs, hsn_new, rsnw, & + rsnw_fall, rsnw_tmax) + + use ice_constants_colpkg, only: c0, puny + + integer (kind=int_kind), intent(in) :: & + nslyr ! number of snow layers + + real (kind=dbl_kind), intent(in) :: & + dzs , & ! upper snow layer thickness (m) + hsn_new , & ! new snow fall thickness (m) + rsnw_fall , & ! radius of new snow (10^-6 m) + rsnw_tmax ! maximum radius (10^-6 m) + + real (kind=dbl_kind), & + intent(inout) :: & + rsnw ! upper layer snow radius (10^-6 m) + + rsnw = (hsn_new * rsnw_fall + max(c0,dzs-hsn_new) * rsnw)/ & + (max(hsn_new + max(c0,dzs-hsn_new),puny)) + + rsnw = max(rsnw_fall,min(rsnw_tmax, rsnw)) + + end subroutine add_new_snow_radius + !======================================================================= end module ice_therm_vertical diff --git a/src/core_seaice/column/ice_warnings.F90 b/src/core_seaice/column/ice_warnings.F90 index 15ab61abac..6747269623 100644 --- a/src/core_seaice/column/ice_warnings.F90 +++ b/src/core_seaice/column/ice_warnings.F90 @@ -33,7 +33,7 @@ subroutine add_warning(warning) ! number of array elements to increase size of warnings array if that array has run out of space integer, parameter :: & - nWarningsBuffer = 10 + nWarningsBuffer = 100 ! temporary array to store previous warnings while warning array is increased in size character(len=char_len_long), dimension(:), allocatable :: & diff --git a/src/core_seaice/column/ice_zbgc.F90 b/src/core_seaice/column/ice_zbgc.F90 index 642ba659b0..bb45c96d1c 100644 --- a/src/core_seaice/column/ice_zbgc.F90 +++ b/src/core_seaice/column/ice_zbgc.F90 @@ -27,20 +27,20 @@ module ice_zbgc ! Adjust biogeochemical tracers when new frazil ice forms subroutine add_new_ice_bgc (dt, nblyr, & - ncat, nilyr, nltrcr, & + ncat, nilyr, nbtrcr, & bgrid, cgrid, igrid, & aicen_init, vicen_init, vi0_init, & aicen, vicen, vsnon1, & vi0new, & - ntrcr, trcrn, nbtrcr, & + ntrcr, trcrn, & sss, ocean_bio, flux_bio, & - hsurp, l_stop, & + hsurp, l_stop, & stop_label, l_conservation_check) - use ice_constants_colpkg, only: c0, c1, puny, depressT + use ice_constants_colpkg, only: c0, c1, puny, depressT, p5 use ice_itd, only: column_sum, & column_conservation_check - use ice_colpkg_tracers, only: tr_brine, nt_fbri, nt_sice, nt_qice, nt_Tsfc + use ice_colpkg_tracers, only: tr_brine, nt_fbri, nt_sice, nt_qice, nt_Tsfc, bio_index use ice_colpkg_shared, only: solve_zsal use ice_therm_shared, only: calculate_Tin_from_qin @@ -48,7 +48,6 @@ subroutine add_new_ice_bgc (dt, nblyr, & nblyr , & ! number of bio layers ncat , & ! number of thickness categories nilyr , & ! number of ice layers - nltrcr, & ! number of zbgc tracers nbtrcr , & ! number of biology tracers ntrcr ! number of tracers in use @@ -91,7 +90,7 @@ subroutine add_new_ice_bgc (dt, nblyr, & real (kind=dbl_kind), dimension (:), & intent(inout) :: & flux_bio ! tracer flux to ocean from biology (mmol/m^2/s) - + real (kind=dbl_kind), dimension (:), & intent(in) :: & ocean_bio ! ocean concentration of biological tracer @@ -109,7 +108,8 @@ subroutine add_new_ice_bgc (dt, nblyr, & integer (kind=int_kind) :: & location , & ! 1 (add frazil to bottom), 0 (add frazil throughout) n , & ! ice category index - k ! ice layer index + k , & ! ice layer index + m real (kind=dbl_kind) :: & vbri1 , & ! starting volume of existing brine @@ -121,8 +121,8 @@ subroutine add_new_ice_bgc (dt, nblyr, & vtmp ! total volume of new and old ice real (kind=dbl_kind), dimension (ncat) :: & - vbrin ! trcrn(nt_fbri,n)*vicen(n) - + vbrin ! trcrn(nt_fbri,n)*vicen(n) + real (kind=dbl_kind) :: & vice_new ! vicen_init + vsurp @@ -132,7 +132,18 @@ subroutine add_new_ice_bgc (dt, nblyr, & character (len=char_len) :: & fieldid ! field identifier - !----------------------------------------------------------------- + real (kind=dbl_kind), dimension (nbtrcr) :: & + total_bio_initial, & ! Initial column bio concentration (mmol/m2) + total_bio_final ! final column bio concentration (mmol/m2) + + real (kind=dbl_kind), dimension (nblyr+1) :: & + zspace ! vertical grid spacing + + zspace(:) = c1/real(nblyr,kind=dbl_kind) + zspace(1) = p5*zspace(1) + zspace(nblyr+1) = p5*zspace(nblyr+1) + + !----------------------------------------------------------------- ! brine !----------------------------------------------------------------- vbrin(:) = c0 @@ -140,22 +151,28 @@ subroutine add_new_ice_bgc (dt, nblyr, & vbrin(n) = vicen_init(n) if (tr_brine) vbrin(n) = trcrn(nt_fbri,n)*vicen_init(n) enddo - + + do m = 1, nbtrcr + total_bio_initial(m) = c0 + do n = 1, ncat + do k = 1, nblyr+1 + total_bio_initial(m) = total_bio_initial(m) + vbrin(n) * zspace(k)*trcrn(bio_index(m)+k-1,n) + enddo + enddo + enddo + call column_sum (ncat, vbrin, vbri_init) vbri_init = vbri_init + vi0_init - do k = 1, nbtrcr - flux_bio(k) = flux_bio(k) & - - vi0_init/dt*ocean_bio(k)*zbgc_init_frac(k) - enddo + !----------------------------------------------------------------- - ! Distribute bgc in new ice volume among all ice categories by + ! Distribute bgc in new ice volume among all ice categories by ! increasing ice thickness, leaving ice area unchanged. !----------------------------------------------------------------- ! Diffuse_bio handles concentration changes from ice growth/melt ! ice area does not change - ! add salt to the bottom , location = 1 + ! add salt to the bottom , location = 1 vsurp = c0 vtmp = c0 @@ -174,7 +191,7 @@ subroutine add_new_ice_bgc (dt, nblyr, & trcrn(nt_fbri,n) = c1 endif - if (nltrcr > 0) then + if (nbtrcr > 0) then location = 1 call adjust_tracer_profile(nbtrcr, dt, ntrcr, & aicen_init(n), & @@ -190,7 +207,7 @@ subroutine add_new_ice_bgc (dt, nblyr, & location, & l_stop, stop_label) if (l_stop) return - endif ! nltrcr + endif ! nbtrcr endif ! hsurp > 0 enddo ! n @@ -212,8 +229,8 @@ subroutine add_new_ice_bgc (dt, nblyr, & ! ice area changes ! add salt throughout, location = 0 - if (nltrcr > 0) then - location = 0 + if (nbtrcr > 0) then + location = 0 call adjust_tracer_profile(nbtrcr, dt, ntrcr, & aicen(1), & vbrin(1), & @@ -233,9 +250,19 @@ subroutine add_new_ice_bgc (dt, nblyr, & Tmlts = -trcrn(nt_sice,1)*depressT trcrn(nt_Tsfc,1) = calculate_Tin_from_qin(trcrn(nt_qice,1),Tmlts) endif ! solve_zsal - endif ! nltrcr > 0 + endif ! nbtrcr > 0 endif ! vi0new > 0 + do m = 1, nbtrcr + total_bio_final(m) = c0 + do n = 1, ncat + do k = 1, nblyr+1 + total_bio_final(m) = total_bio_final(m) + trcrn(nt_fbri,n) * vicen(n) *zspace(k)*trcrn(bio_index(m)+k-1,n) + enddo + enddo + flux_bio(m) = flux_bio(m) + (total_bio_initial(m) - total_bio_final(m))/dt + enddo + if (tr_brine .and. l_conservation_check) then call column_sum (ncat, vbrin, vbri_final) @@ -260,22 +287,24 @@ subroutine lateral_melt_bgc (dt, & ncat, nblyr, & rside, vicen, & trcrn, fzsal, & - flux_bio, nbltrcr) + flux_bio, nbtrcr, & + vicen_init) use ice_colpkg_tracers, only: nt_fbri, nt_bgc_S, bio_index use ice_colpkg_shared, only: solve_zsal, rhosi - use ice_constants_colpkg, only: c1, p001 + use ice_constants_colpkg, only: c1, p001, p5, c0 integer (kind=int_kind), intent(in) :: & ncat , & ! number of thickness categories nblyr , & ! number of bio layers - nbltrcr ! number of biology tracers + nbtrcr ! number of biology tracers real (kind=dbl_kind), intent(in) :: & dt ! time step (s) real (kind=dbl_kind), dimension(:), intent(in) :: & - vicen ! volume per unit area of ice (m) + vicen , & ! volume per unit area of ice (m) + vicen_init real (kind=dbl_kind), dimension (:,:), intent(in) :: & trcrn ! tracer array @@ -287,41 +316,49 @@ subroutine lateral_melt_bgc (dt, & fzsal ! salt flux from layer Salinity (kg/m^2/s) real (kind=dbl_kind), dimension(:), intent(inout) :: & - flux_bio ! biology tracer flux from layer bgc (mmol/m^2/s) + flux_bio ! biology tracer flux from layer bgc (mmol/m^2/s) ! local variables + real (kind=dbl_kind) :: & + total_bio_initial, & ! initial column tracer concentration (mmol/m2) + total_bio_final ! final column tracer concentration (mmol/m20 + integer (kind=int_kind) :: & k , & ! layer index m , & ! n ! category index - real (kind=dbl_kind) :: & - zspace ! bio grid spacing + real (kind=dbl_kind), dimension (nblyr+1) :: & + zspace ! vertical grid spacing - zspace = c1/(real(nblyr,kind=dbl_kind)) + zspace(:) = c1/real(nblyr,kind=dbl_kind) + zspace(1) = p5*zspace(1) + zspace(nblyr+1) = p5*zspace(nblyr+1) if (solve_zsal) then do n = 1, ncat do k = 1,nblyr fzsal = fzsal + rhosi*trcrn(nt_fbri,n) & - * vicen(n)*p001*zspace*trcrn(nt_bgc_S+k-1,n) & + * vicen(n)*p001*zspace(2)*trcrn(nt_bgc_S+k-1,n) & * rside/dt enddo enddo endif - do m = 1, nbltrcr + do m = 1, nbtrcr + total_bio_initial = c0 + total_bio_final = c0 do n = 1, ncat do k = 1, nblyr+1 - flux_bio(m) = flux_bio(m) + trcrn(nt_fbri,n) & - * vicen(n)*zspace*trcrn(bio_index(m)+k-1,n) & - * rside/dt + total_bio_initial = total_bio_initial + trcrn(nt_fbri,n) * vicen_init(n) *zspace(k)*trcrn(bio_index(m)+k-1,n) + total_bio_final = total_bio_final + trcrn(nt_fbri,n) * vicen(n) *zspace(k)*trcrn(bio_index(m)+k-1,n) enddo enddo + flux_bio(m) = flux_bio(m) + (total_bio_initial - total_bio_final)/dt enddo - end subroutine lateral_melt_bgc + end subroutine lateral_melt_bgc !======================================================================= ! @@ -538,7 +575,8 @@ subroutine merge_bgc_fluxes (dt, nblyr, & zbgc_snow, zbgc_atm, & PP_net, ice_bio_net,& snow_bio_net, grow_alg, & - grow_net, totalChla) + grow_net, totalChla, & + nslyr) use ice_constants_colpkg, only: c1, c0, p5, secday, puny use ice_colpkg_shared, only: solve_zbgc, max_nbtrcr, hs_ssl, R_C2N, & @@ -550,6 +588,7 @@ subroutine merge_bgc_fluxes (dt, nblyr, & integer (kind=int_kind), intent(in) :: & nblyr, & + nslyr, & ! number of snow layers n_algae, & ! ntrcr, & ! number of tracers nbtrcr ! number of biology tracer tracers @@ -625,7 +664,7 @@ subroutine merge_bgc_fluxes (dt, nblyr, & !----------------------------------------------------------------- ! Merge fluxes !----------------------------------------------------------------- - dvssl = min(p5*vsnon, hs_ssl*aicen) ! snow surface layer + dvssl = min(p5*vsnon/real(nslyr,kind=dbl_kind), hs_ssl*aicen) ! snow surface layer dvint = vsnon - dvssl ! snow interior snow_bio_net(mm) = snow_bio_net(mm) & + trcrn(bio_index(mm)+nblyr+1)*dvssl & @@ -668,9 +707,9 @@ subroutine merge_bgc_fluxes_skl (ntrcr, & upNH, grow_net, & grow_alg, totalChla) - use ice_constants_colpkg, only: c1, secday, puny + use ice_constants_colpkg, only: c1, secday, puny, sk_l use ice_colpkg_tracers, only: nt_bgc_N - use ice_colpkg_shared, only: sk_l, R_C2N, fr_resp, R_chl2N + use ice_colpkg_shared, only: R_C2N, fr_resp, R_chl2N integer (kind=int_kind), intent(in) :: & ntrcr , & ! number of cells with aicen > puny diff --git a/src/core_seaice/model_forward/mpas_seaice_core.F b/src/core_seaice/model_forward/mpas_seaice_core.F index 2dceb8a9cb..f703942101 100644 --- a/src/core_seaice/model_forward/mpas_seaice_core.F +++ b/src/core_seaice/model_forward/mpas_seaice_core.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! module seaice_core use mpas_framework @@ -12,6 +5,7 @@ module seaice_core use seaice_analysis_driver use seaice_column use mpas_threading + use mpas_timer, only: mpas_timer_start, mpas_timer_stop use mpas_log, only: mpas_log_write private @@ -131,7 +125,10 @@ function seaice_core_init(domain, startTimeStamp) result(iErr) ! bootstrap analysis has used config_do_restart: now modify for rest of code if (trim(config_initial_condition_type) == "restart") config_do_restart = .true. + call mpas_timer_start("seaice_init") call mpas_init_block(domain, dt) + call mpas_timer_stop("seaice_init") + end function seaice_core_init @@ -262,7 +259,6 @@ function seaice_core_run(domain) result(iErr) use mpas_derived_types use mpas_kind_types use mpas_stream_manager - use mpas_timer use seaice_time_integration, only: seaice_timestep_finalize use seaice_forcing, only: & seaice_forcing_get, & @@ -391,8 +387,7 @@ end function seaice_core_run subroutine mpas_timestep(domain, itimestep, timeStamp) use mpas_derived_types - use seaice_time_integration - use seaice_error, only: seaice_check_critical_error + use seaice_time_integration, only: seaice_timestep implicit none @@ -400,12 +395,7 @@ subroutine mpas_timestep(domain, itimestep, timeStamp) integer, intent(in) :: itimestep character(len=*), intent(in) :: timeStamp - integer :: ierr - - ierr = 0 - call seaice_timestep(domain, clock, itimestep, ierr) - - call seaice_check_critical_error(domain, ierr) + call seaice_timestep(domain, clock, itimestep) end subroutine mpas_timestep @@ -427,6 +417,8 @@ function seaice_core_finalize(domain) result(iErr) use mpas_decomp use seaice_column, only: & seaice_column_finalize + use seaice_mesh_pool, only: & + seaice_mesh_pool_destroy implicit none @@ -435,6 +427,9 @@ function seaice_core_finalize(domain) result(iErr) iErr = 0 + call mpas_log_write(" Destruct mesh pool...") + call seaice_mesh_pool_destroy(iErr) + ! finalize column call seaice_column_finalize(domain) diff --git a/src/core_seaice/model_forward/mpas_seaice_core_interface.F b/src/core_seaice/model_forward/mpas_seaice_core_interface.F index ef8ade111e..2668263502 100644 --- a/src/core_seaice/model_forward/mpas_seaice_core_interface.F +++ b/src/core_seaice/model_forward/mpas_seaice_core_interface.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! module seaice_core_interface use mpas_derived_types @@ -111,6 +104,9 @@ function seaice_setup_packages(configPool, packagePool, iocontext) result(ierr)! ! set up analysis member packages call seaice_analysis_setup_packages(configPool, packagePool, iocontext, ierr) + ! icebergs + call setup_packages_bergs(configPool, packagePool, ierr) + ! testing system test call setup_packages_other(configPool, packagePool, ierr) @@ -138,69 +134,52 @@ subroutine setup_packages_dynamics(configPool, packagePool, ierr)!{{{ config_use_velocity_solver character(len=strKIND), pointer :: & - config_stress_divergence_scheme, & - config_variational_basis + config_strain_scheme, & + config_stress_divergence_scheme logical, pointer :: & pkgWeakActive, & pkgVariationalActive, & - pkgWachspressActive, & - pkgPieceWiseLinearActive + pkgWeakVariationalActive !pkgWeak !pkgVariational - !pkgWachspress - !pkgPieceWiseLinear call MPAS_pool_get_config(configPool, "config_use_velocity_solver", config_use_velocity_solver) + call MPAS_pool_get_config(configPool, "config_strain_scheme", config_strain_scheme) call MPAS_pool_get_config(configPool, "config_stress_divergence_scheme", config_stress_divergence_scheme) - call MPAS_pool_get_config(configPool, "config_variational_basis", config_variational_basis) call MPAS_pool_get_package(packagePool, "pkgWeakActive", pkgWeakActive) call MPAS_pool_get_package(packagePool, "pkgVariationalActive", pkgVariationalActive) - call MPAS_pool_get_package(packagePool, "pkgWachspressActive", pkgWachspressActive) - call MPAS_pool_get_package(packagePool, "pkgPieceWiseLinearActive", pkgPieceWiseLinearActive) + call MPAS_pool_get_package(packagePool, "pkgWeakVariationalActive", pkgWeakVariationalActive) ! is the velocity solver on if (config_use_velocity_solver) then ! stress divergence scheme - if (trim(config_stress_divergence_scheme) == "weak") then + if (trim(config_strain_scheme) == "weak" .or. & + trim(config_stress_divergence_scheme) == "weak") then ! weak stress divergence scheme pkgWeakActive = .true. - else if (trim(config_stress_divergence_scheme) == "variational") then + endif + + if (trim(config_strain_scheme) == "variational" .or. & + trim(config_stress_divergence_scheme) == "variational") then ! variational stress divergence scheme pkgVariationalActive = .true. - ! variational basis function scheme - if (trim(config_variational_basis) == "wachspress") then - - ! wachspress basis functions - pkgWachspressActive = .true. - - else if (trim(config_variational_basis) == "pwl") then - - ! piecewise linear basis functions - pkgPieceWiseLinearActive = .true. + endif - else + if (trim(config_strain_scheme) == "weak" .and. & + trim(config_stress_divergence_scheme) == "variational") then - ! unknown value of config_variational_basis - call mpas_log_write("Unknown variational scheme: "//trim(config_variational_basis)) - ierr = 1 + ! weak strain / variational stress divergence + pkgWeakVariationalActive = .true. - endif ! config_variational_basis - - else - - ! unknown value of config_stress_divergence_scheme - call mpas_log_write("Unknown stress divergence scheme: "//trim(config_stress_divergence_scheme)) - ierr = 1 - - endif ! config_stress_divergence_scheme + endif endif ! config_use_velocity_solver @@ -257,7 +236,9 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ config_use_humics, & config_use_DON, & config_use_iron, & - config_use_zaerosols + config_use_zaerosols, & + config_use_snow_grain_radius, & + config_use_effective_snow_density logical, pointer :: & pkgColumnTracerIceAgeActive, & @@ -289,7 +270,9 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ pkgTracerVerticalDONActive, & pkgTracerVerticalIronActive, & pkgTracerZAerosolsActive, & - pkgTracerZSalinityActive + pkgTracerZSalinityActive, & + pkgColumnTracerEffectiveSnowDensityActive, & + pkgColumnTracerSnowGrainRadiusActive ! other packages logical, pointer :: & @@ -356,6 +339,8 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ !pkgTracerVerticalIron !pkgTracerZAerosols !pkgTracerZSalinity + !pkgColumnTracerEffectiveSnowDensity + !pkgColumnTracerSnowGrainRadius call MPAS_pool_get_config(configPool, "config_use_ice_age", config_use_ice_age) call MPAS_pool_get_config(configPool, "config_use_first_year_ice", config_use_first_year_ice) @@ -380,6 +365,8 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ call MPAS_pool_get_config(configPool, "config_use_DON", config_use_DON) call MPAS_pool_get_config(configPool, "config_use_iron", config_use_iron) call MPAS_pool_get_config(configPool, "config_use_zaerosols", config_use_zaerosols) + call MPAS_pool_get_config(configPool, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(configPool, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_package(packagePool, "pkgColumnTracerIceAgeActive", pkgColumnTracerIceAgeActive) call MPAS_pool_get_package(packagePool, "pkgColumnTracerFirstYearIceActive", pkgColumnTracerFirstYearIceActive) @@ -411,6 +398,8 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ call MPAS_pool_get_package(packagePool, "pkgTracerVerticalIronActive", pkgTracerVerticalIronActive) call MPAS_pool_get_package(packagePool, "pkgTracerZAerosolsActive", pkgTracerZAerosolsActive) call MPAS_pool_get_package(packagePool, "pkgTracerZSalinityActive", pkgTracerZSalinityActive) + call MPAS_pool_get_package(packagePool, "pkgColumnTracerEffectiveSnowDensityActive", pkgColumnTracerEffectiveSnowDensityActive) + call MPAS_pool_get_package(packagePool, "pkgColumnTracerSnowGrainRadiusActive", pkgColumnTracerSnowGrainRadiusActive) use_meltponds = (config_use_cesm_meltponds .or. config_use_level_meltponds .or. config_use_topo_meltponds) @@ -446,6 +435,9 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ pkgTracerZAerosolsActive = config_use_zaerosols pkgTracerZSalinityActive = config_use_vertical_zsalinity + pkgColumnTracerEffectiveSnowDensityActive = config_use_effective_snow_density + pkgColumnTracerSnowGrainRadiusActive = config_use_snow_grain_radius + if (.not. config_use_column_package) then pkgColumnTracerIceAgeActive = .false. pkgColumnTracerFirstYearIceActive = .false. @@ -477,6 +469,8 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ pkgTracerVerticalIronActive = .false. pkgTracerZAerosolsActive = .false. pkgTracerZSalinityActive = .false. + pkgColumnTracerEffectiveSnowDensityActive = .false. + pkgColumnTracerSnowGrainRadiusActive = .false. endif if (.not. config_use_column_biogeochemistry .and. config_use_column_package) then @@ -529,6 +523,43 @@ subroutine setup_packages_column_physics(configPool, packagePool, ierr)!{{{ end subroutine setup_packages_column_physics!}}} + !*********************************************************************** + ! + ! routine setup_packages_bergs + ! + !> \brief Setup icebergs package + !> \author Darin Comeau + !> \date 19 May 2017 + !> \details This routine is intended to set the icebergs package PkgBergs + !> as active/deactive based on the namelist option config_use_bergs. + ! + !----------------------------------------------------------------------- + + subroutine setup_packages_bergs(configPool, packagePool, ierr)!{{{ + + type (mpas_pool_type), intent(in) :: configPool + type (mpas_pool_type), intent(in) :: packagePool + integer, intent(out) :: ierr + + ! icebergs package + logical, pointer :: & + config_use_data_icebergs + + logical, pointer :: & + pkgBergsActive + + ierr = 0 + + !----------------------------------------------------------------------- + ! iceberg routines + !----------------------------------------------------------------------- + + call MPAS_pool_get_config(configPool, "config_use_data_icebergs", config_use_data_icebergs) + call MPAS_pool_get_package(packagePool, "pkgBergsActive", pkgBergsActive) + pkgBergsActive = config_use_data_icebergs + + end subroutine setup_packages_bergs!}}} + !*********************************************************************** ! ! routine setup_packages_other @@ -549,22 +580,32 @@ subroutine setup_packages_other(configPool, packagePool, ierr)!{{{ logical, pointer :: & config_use_forcing, & - config_testing_system_test + config_use_data_icebergs, & + config_testing_system_test, & + config_use_snicar_ad, & + config_use_prescribed_ice, & + config_use_special_boundaries_velocity, & + config_use_special_boundaries_velocity_masks, & + config_use_special_boundaries_zero_tracers logical, pointer :: & pkgForcingActive, & - pkgTestingSystemTestActive + pkgTestingSystemTestActive, & + pkgSnicarActive, & + pkgPrescribedActive, & + pkgSpecialBoundariesActive ierr = 0 ! pkgForcing call MPAS_pool_get_config(configPool, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(configPool, "config_use_data_icebergs", config_use_data_icebergs) call MPAS_pool_get_package(packagePool, "pkgForcingActive", pkgForcingActive) ! see if we are using the forcing system - if (config_use_forcing) then + if (config_use_forcing .or. config_use_data_icebergs) then pkgForcingActive = .true. @@ -578,9 +619,31 @@ subroutine setup_packages_other(configPool, packagePool, ierr)!{{{ ! see if we are testing the testing system if (config_testing_system_test) then - pkgTestingSystemTestActive = .true. + endif + + ! pkgSnicar + call MPAS_pool_get_config(configPool, "config_use_snicar_ad", config_use_snicar_ad) + call MPAS_pool_get_package(packagePool, "pkgSnicarActive", pkgSnicarActive) + ! see if we are using the snicar_ad system + pkgSnicarActive = .true. + + ! pkgPrescribed + call MPAS_pool_get_config(configPool, "config_use_prescribed_ice", config_use_prescribed_ice) + call MPAS_pool_get_package(packagePool, "pkgPrescribedActive", pkgPrescribedActive) + if (config_use_prescribed_ice) then + pkgPrescribedActive = .true. + endif + ! pkgSpecialBoundaries + call MPAS_pool_get_config(configPool, "config_use_special_boundaries_velocity", config_use_special_boundaries_velocity) + call MPAS_pool_get_config(configPool, "config_use_special_boundaries_velocity_masks", config_use_special_boundaries_velocity_masks) + call MPAS_pool_get_config(configPool, "config_use_special_boundaries_zero_tracers", config_use_special_boundaries_zero_tracers) + call MPAS_pool_get_package(packagePool, "pkgSpecialBoundariesActive", pkgSpecialBoundariesActive) + if (config_use_special_boundaries_velocity .or. & + config_use_special_boundaries_velocity_masks .or. & + config_use_special_boundaries_zero_tracers) then + pkgSpecialBoundariesActive = .true. endif end subroutine setup_packages_other!}}} diff --git a/src/core_seaice/seaice.cmake b/src/core_seaice/seaice.cmake index 0ac2b0dd49..9a880f4d9c 100644 --- a/src/core_seaice/seaice.cmake +++ b/src/core_seaice/seaice.cmake @@ -53,7 +53,6 @@ list(APPEND RAW_SOURCES core_seaice/shared/mpas_seaice_advection_incremental_remap.F core_seaice/shared/mpas_seaice_advection_upwind.F core_seaice/shared/mpas_seaice_advection.F - core_seaice/shared/mpas_seaice_velocity_solver_unit_tests.F core_seaice/shared/mpas_seaice_velocity_solver.F core_seaice/shared/mpas_seaice_velocity_solver_weak.F core_seaice/shared/mpas_seaice_velocity_solver_variational.F @@ -64,7 +63,6 @@ list(APPEND RAW_SOURCES core_seaice/shared/mpas_seaice_forcing.F core_seaice/shared/mpas_seaice_initialize.F core_seaice/shared/mpas_seaice_testing.F - core_seaice/shared/mpas_seaice_unit_test.F core_seaice/shared/mpas_seaice_mesh.F core_seaice/shared/mpas_seaice_diagnostics.F core_seaice/shared/mpas_seaice_numerics.F @@ -72,6 +70,9 @@ list(APPEND RAW_SOURCES core_seaice/shared/mpas_seaice_column.F core_seaice/shared/mpas_seaice_diagnostics.F core_seaice/shared/mpas_seaice_error.F + core_seaice/shared/mpas_seaice_mesh_pool.F + core_seaice/shared/mpas_seaice_prescribed.F + core_seaice/shared/mpas_seaice_special_boundaries.F ) # analysis members diff --git a/src/core_seaice/shared/Makefile b/src/core_seaice/shared/Makefile index 0f0cf3827e..52bcb75710 100644 --- a/src/core_seaice/shared/Makefile +++ b/src/core_seaice/shared/Makefile @@ -5,7 +5,6 @@ OBJS = mpas_seaice_time_integration.o \ mpas_seaice_advection_incremental_remap.o \ mpas_seaice_advection_upwind.o \ mpas_seaice_advection.o \ - mpas_seaice_velocity_solver_unit_tests.o \ mpas_seaice_velocity_solver.o \ mpas_seaice_velocity_solver_weak.o \ mpas_seaice_velocity_solver_variational.o \ @@ -16,14 +15,16 @@ OBJS = mpas_seaice_time_integration.o \ mpas_seaice_forcing.o \ mpas_seaice_initialize.o \ mpas_seaice_testing.o \ - mpas_seaice_unit_test.o \ mpas_seaice_mesh.o \ mpas_seaice_diagnostics.o \ mpas_seaice_numerics.o \ mpas_seaice_constants.o \ mpas_seaice_column.o \ mpas_seaice_diagnostics.o \ - mpas_seaice_error.o + mpas_seaice_error.o \ + mpas_seaice_mesh_pool.o \ + mpas_seaice_prescribed.o \ + mpas_seaice_special_boundaries.o all: $(OBJS) @@ -31,6 +32,8 @@ mpas_seaice_constants.o: mpas_seaice_error.o: +mpas_seaice_mesh_pool.o: + mpas_seaice_column.o: mpas_seaice_error.o mpas_seaice_diagnostics.o: mpas_seaice_constants.o @@ -53,11 +56,9 @@ mpas_seaice_velocity_solver_wachspress.o: mpas_seaice_constants.o mpas_seaice_nu mpas_seaice_velocity_solver_pwl.o: mpas_seaice_constants.o mpas_seaice_numerics.o mpas_seaice_mesh.o mpas_seaice_testing.o mpas_seaice_velocity_solver_variational_shared.o -mpas_seaice_velocity_solver_variational.o: mpas_seaice_constants.o mpas_seaice_velocity_solver_constitutive_relation.o mpas_seaice_velocity_solver_wachspress.o mpas_seaice_velocity_solver_pwl.o - -mpas_seaice_velocity_solver.o: mpas_seaice_constants.o mpas_seaice_mesh.o mpas_seaice_testing.o mpas_seaice_velocity_solver_weak.o mpas_seaice_velocity_solver_constitutive_relation.o mpas_seaice_velocity_solver_variational.o mpas_seaice_diagnostics.o +mpas_seaice_velocity_solver_variational.o: mpas_seaice_constants.o mpas_seaice_velocity_solver_constitutive_relation.o mpas_seaice_velocity_solver_wachspress.o mpas_seaice_velocity_solver_pwl.o mpas_seaice_mesh_pool.o -mpas_seaice_velocity_solver_unit_tests.o: mpas_seaice_constants.o mpas_seaice_mesh.o mpas_seaice_velocity_solver_weak.o mpas_seaice_velocity_solver_variational.o +mpas_seaice_velocity_solver.o: mpas_seaice_constants.o mpas_seaice_mesh.o mpas_seaice_testing.o mpas_seaice_velocity_solver_weak.o mpas_seaice_velocity_solver_constitutive_relation.o mpas_seaice_velocity_solver_variational.o mpas_seaice_diagnostics.o mpas_seaice_mesh_pool.o mpas_seaice_special_boundaries.o mpas_seaice_advection_upwind.o: mpas_seaice_constants.o mpas_seaice_mesh.o @@ -67,13 +68,13 @@ mpas_seaice_advection_incremental_remap.o: mpas_seaice_constants.o mpas_seaice_m mpas_seaice_advection.o: mpas_seaice_advection_upwind.o mpas_seaice_advection_incremental_remap.o -mpas_seaice_unit_test.o: mpas_seaice_constants.o mpas_seaice_velocity_solver_unit_tests.o +mpas_seaice_prescribed.o: mpas_seaice_constants.o mpas_seaice_column.o -mpas_seaice_time_integration.o: mpas_seaice_constants.o mpas_seaice_velocity_solver.o mpas_seaice_forcing.o mpas_seaice_unit_test.o mpas_seaice_advection.o mpas_seaice_diagnostics.o mpas_seaice_column.o +mpas_seaice_time_integration.o: mpas_seaice_constants.o mpas_seaice_velocity_solver.o mpas_seaice_forcing.o mpas_seaice_advection.o mpas_seaice_diagnostics.o mpas_seaice_column.o mpas_seaice_prescribed.o mpas_seaice_special_boundaries.o -mpas_seaice_initialize.o: mpas_seaice_constants.o mpas_seaice_mesh.o mpas_seaice_velocity_solver.o mpas_seaice_testing.o mpas_seaice_forcing.o mpas_seaice_advection.o mpas_seaice_column.o mpas_seaice_forcing.o +mpas_seaice_initialize.o: mpas_seaice_constants.o mpas_seaice_mesh.o mpas_seaice_velocity_solver.o mpas_seaice_testing.o mpas_seaice_forcing.o mpas_seaice_advection.o mpas_seaice_column.o mpas_seaice_forcing.o mpas_seaice_mesh_pool.o mpas_seaice_special_boundaries.o -mpas_seaice_core.o: mpas_seaice_constants.o mpas_seaice_time_integration.o mpas_seaice_velocity_solver.o mpas_seaice_forcing.o mpas_seaice_initialize.o mpas_seaice_column.o +mpas_seaice_core.o: mpas_seaice_constants.o mpas_seaice_time_integration.o mpas_seaice_velocity_solver.o mpas_seaice_forcing.o mpas_seaice_initialize.o mpas_seaice_column.o mpas_seaice_mesh_pool.o mpas_seaice_core_interface.o: mpas_seaice_core.o diff --git a/src/core_seaice/shared/gpu_macros.inc b/src/core_seaice/shared/gpu_macros.inc new file mode 100644 index 0000000000..a12feee71c --- /dev/null +++ b/src/core_seaice/shared/gpu_macros.inc @@ -0,0 +1,36 @@ +#ifdef MPAS_OPENACC + +#define GPU acc +#define GPUC acc /* continuation line macro */ +#define GPUF acc ) /* final line of GPU directive */ +#define ENTER_DATA enter data +#define EXIT_DATA exit data +#define DATA data +#define DATA_END end data +#define COPY_IN_LP copyin( /* for multi-line variable lists */ +#define COPY_OUT_LP copyout( /* for multi-line variable lists */ +#define COPY_DEL_LP delete( +#define UPDATE_D(v) update device v +#define UPDATE_D_LP update device( +#define UPDATE_H(v) update host v /* !$GPU UPDATE_H((stressDivergenceU, stressDivergenceV)) -> !$acc update host (stressDivergenceU, stressDivergenceV) */ +#define UPDATE_H_LP update host( /* for multi-line variable lists */ + +#else + +#define GPU omp +#define GPUC omp& +#define GPUF omp& ) +#define ENTER_DATA target enter data +#define EXIT_DATA target exit data +#define DATA target data +#define DATA_END end target data +#define COPY_IN_LP map(to: +#define COPY_OUT_LP map(from: +#define COPY_DEL_LP map(delete: +#define UPDATE_D(v) target update to v +#define UPDATE_D_LP target update to( +#define UPDATE_H(v) target update from v +#define UPDATE_H_LP target update from( + +#endif + diff --git a/src/core_seaice/shared/mpas_seaice_advection.F b/src/core_seaice/shared/mpas_seaice_advection.F index 38452d4c59..20c58c8b43 100644 --- a/src/core_seaice/shared/mpas_seaice_advection.F +++ b/src/core_seaice/shared/mpas_seaice_advection.F @@ -87,7 +87,7 @@ end subroutine seaice_init_advection ! !----------------------------------------------------------------------- - subroutine seaice_run_advection(domain, clock, ierr) + subroutine seaice_run_advection(domain, clock) use seaice_advection_upwind, only: & seaice_run_advection_upwind @@ -101,9 +101,6 @@ subroutine seaice_run_advection(domain, clock, ierr) type (MPAS_Clock_type), intent(in) :: & clock !< Input: - integer, intent(inout) :: & - ierr !< Input/Output: - logical, pointer :: & config_use_advection @@ -127,7 +124,7 @@ subroutine seaice_run_advection(domain, clock, ierr) ! (Later, change volume to thickness throughout code?) call mpas_timer_start("advection incr remap") - call seaice_run_advection_incremental_remap(domain, clock, ierr) + call seaice_run_advection_incremental_remap(domain, clock) call mpas_timer_stop("advection incr remap") !TODO WHL - Convert ice/snow thickness to volume here diff --git a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F index f8bbcc391b..f55a43e549 100644 --- a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F +++ b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F @@ -1,10 +1,3 @@ -! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! mpas_seaice_advection_incremental_remap @@ -46,20 +39,20 @@ module seaice_advection_incremental_remap ! variables private to this module type :: geometric_avg_cell_type - real(kind=RKIND), dimension(:), pointer :: x => null() ! grid cell average of x - real(kind=RKIND), dimension(:), pointer :: y => null() ! grid cell average of y - real(kind=RKIND), dimension(:), pointer :: xx => null() ! grid cell average of x^2 - real(kind=RKIND), dimension(:), pointer :: xy => null() ! grid cell average of x*y - real(kind=RKIND), dimension(:), pointer :: yy => null() ! grid cell average of y^2 - real(kind=RKIND), dimension(:), pointer :: xxx => null() ! grid cell average of x^3 - real(kind=RKIND), dimension(:), pointer :: xxy => null() ! grid cell average of x^2*y - real(kind=RKIND), dimension(:), pointer :: xyy => null() ! grid cell average of x*y^2 - real(kind=RKIND), dimension(:), pointer :: yyy => null() ! grid cell average of y^3 - real(kind=RKIND), dimension(:), pointer :: xxxx => null() ! grid cell average of x^4 - real(kind=RKIND), dimension(:), pointer :: xxxy => null() ! grid cell average of x^3*y - real(kind=RKIND), dimension(:), pointer :: xxyy => null() ! grid cell average of x^2*y^2 - real(kind=RKIND), dimension(:), pointer :: xyyy => null() ! grid cell average of x*y^3 - real(kind=RKIND), dimension(:), pointer :: yyyy => null() ! grid cell average of y^4 + real(kind=RKIND), dimension(:), contiguous, pointer :: x => null() ! grid cell average of x + real(kind=RKIND), dimension(:), contiguous, pointer :: y => null() ! grid cell average of y + real(kind=RKIND), dimension(:), contiguous, pointer :: xx => null() ! grid cell average of x^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: xy => null() ! grid cell average of x*y + real(kind=RKIND), dimension(:), contiguous, pointer :: yy => null() ! grid cell average of y^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxx => null() ! grid cell average of x^3 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxy => null() ! grid cell average of x^2*y + real(kind=RKIND), dimension(:), contiguous, pointer :: xyy => null() ! grid cell average of x*y^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: yyy => null() ! grid cell average of y^3 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxxx => null() ! grid cell average of x^4 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxxy => null() ! grid cell average of x^3*y + real(kind=RKIND), dimension(:), contiguous, pointer :: xxyy => null() ! grid cell average of x^2*y^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: xyyy => null() ! grid cell average of x*y^3 + real(kind=RKIND), dimension(:), contiguous, pointer :: yyyy => null() ! grid cell average of y^4 end type geometric_avg_cell_type ! parameters private to this module @@ -2345,7 +2338,6 @@ end subroutine compute_geometric_cell_averages subroutine seaice_run_advection_incremental_remap(& domain, & clock, & - ierr, & timeLevelIn, & updateHaloInitIn, & updateHaloFinalIn) @@ -2361,9 +2353,6 @@ subroutine seaice_run_advection_incremental_remap(& type (MPAS_Clock_type), intent(in) :: & clock !< Input: clock - integer, intent(inout) :: & - ierr !< Input/Output: error code - integer, intent(in), optional :: & timeLevelIn !< Input: time level of input fields (1 or 2) @@ -2405,6 +2394,9 @@ subroutine seaice_run_advection_incremental_remap(& real(kind=RKIND), pointer :: & dynamicsTimeStep + logical, pointer :: & + abortFlag ! abort flag + ! assign pointers dminfo => domain % dminfo @@ -2469,8 +2461,8 @@ subroutine seaice_run_advection_incremental_remap(& else ! with reuse - call mpas_dmpar_exch_group_reuse_halo_exch(domain, 'velocityHaloExchangeGroup', iErr=ierr) - if (ierr /= MPAS_DMPAR_NOERR) then + call mpas_dmpar_exch_group_reuse_halo_exch(domain, 'velocityHaloExchangeGroup', iErr=ierrHalo) + if (ierrHalo /= MPAS_DMPAR_NOERR) then call MPAS_log_write("failure to perform reuse halo exchange for velocityHaloExchangeGroup", MPAS_LOG_CRIT) endif @@ -2569,15 +2561,12 @@ subroutine seaice_run_advection_incremental_remap(& domain, & block, & dynamicsTimeStep, & - tracersHead, & - ierr) - if (ierr > 0) exit + tracersHead) block => block % next enddo ! associated(block) call mpas_timer_stop("incr remap blocks") - if (ierr > 0) return ! Optional check for conservation of mass and mass*tracer ! Note: This check must be done outside the block loop because it requires global sums @@ -2588,9 +2577,9 @@ subroutine seaice_run_advection_incremental_remap(& if (verboseRun) call mpas_log_write('Check conservation') call mpas_timer_start("incr remap tracer cons check") - call check_tracer_conservation(dminfo, tracersHead, ierr) + call check_tracer_conservation(dminfo, tracersHead, abortFlag) call mpas_timer_stop("incr remap tracer cons check") - if (ierr > 0) return + call seaice_check_critical_error(domain, abortFlag) endif @@ -2604,9 +2593,9 @@ subroutine seaice_run_advection_incremental_remap(& if (verboseRun) call mpas_log_write('Check monotonicity') call mpas_timer_start("incr remap tracer mono check") - call check_tracer_monotonicity(domain, tracersHead, ierr) + call check_tracer_monotonicity(domain, tracersHead, abortFlag) call mpas_timer_stop("incr remap tracer mono check") - if (ierr > 0) return + call seaice_check_critical_error(domain, abortFlag) endif @@ -2753,7 +2742,6 @@ subroutine incremental_remap_block(& block, & dt, & tracersHead, & - ierr, & timeLevelIn) ! in/out arguments @@ -2766,9 +2754,6 @@ subroutine incremental_remap_block(& real(kind=RKIND), intent(in) :: & dt !< Input: time step - integer, intent(inout) :: & - ierr !< Input/Output: error code - type(tracer_type), pointer :: & tracersHead !< Input/output: pointer to first element of linked list of tracers ! The pointer stays attached to the first tracer, but all tracers are updated @@ -2890,6 +2875,9 @@ subroutine incremental_remap_block(& integer :: n, m, iEdge, iCat, iLayer, iCell integer :: nCategories, nLayers, count + logical :: & + abortFlag ! flag if code aborting + logical, pointer :: & configConservationCheck, & ! namelist configuration whether perform conservation check configMonotonicityCheck, & ! namelist configuration whether perform monotonicity check @@ -3176,6 +3164,7 @@ subroutine incremental_remap_block(& !------------------------------------------------------------------- call mpas_timer_start("incr remap integrate fluxes") + abortFlag = .false. call integrate_fluxes_over_triangles(& tracersHead, & nCells, & @@ -3188,12 +3177,10 @@ subroutine incremental_remap_block(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) call mpas_timer_stop("incr remap integrate fluxes") - if (ierr > 0) then - call seaice_critical_error_write_block(domain, block) - return - endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) if (verboseFluxes .and. etestOnProc .and. block % localBlockID == etestBlockID) then iEdge = etest @@ -3288,6 +3275,7 @@ subroutine incremental_remap_block(& !------------------------------------------------------------------- call mpas_timer_start("incr remap update tracers") + abortFlag = .false. call update_mass_and_tracers(& nCellsSolve, & nEdgesOnCell, & @@ -3298,12 +3286,10 @@ subroutine incremental_remap_block(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) call mpas_timer_stop("incr remap update tracers") - if (ierr > 0) then - call seaice_critical_error_write_block(domain, block) - return - endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) !------------------------------------------------------------------- ! Compute final sums of mass*tracer over the locally owned cells @@ -4272,7 +4258,7 @@ subroutine compute_gradient_2d(& mask !< Input: integer mask for parent tracer; ! = 1 where field values for this tracer are physically meaningful, else = 0 - real(kind=RKIND), dimension(:,:), intent(out) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(out) :: & xGrad, yGrad !< Output: x and y components of the gradient integer, dimension(:), intent(in) :: & @@ -4289,29 +4275,30 @@ subroutine compute_gradient_2d(& integer :: iCell, iEdge, iEdgeOnCell, iCellNeighbor real(kind=RKIND) :: & - signGradient ! = 1 or -1, depending on which direction is taken as positive at a given edge + signGradient, & ! = 1 or -1, depending on which direction is taken as positive at a given edge + tempGrad real(kind=RKIND), dimension(:,:), allocatable :: & normalGrad, & ! normal components of the gradient, defined on cell edges - globalGrad, & ! gradient at cell center, in global x/y/z coordinates - globalGradRotate ! rotated globalGrad vector + globalGrad ! gradient at cell center, in global x/y/z coordinates - real(kind=RKIND), dimension(:), allocatable :: & - zGrad ! diagnostic only; should be much smaller than xGrad and yGrad + !real(kind=RKIND), dimension(:), allocatable :: & + ! zGrad ! diagnostic only; should be much smaller than xGrad and yGrad ! find dimensions and allocate arrays nCategories = size(field,1) - allocate(zGrad(nCategories)) + !allocate(zGrad(nCategories)) allocate(normalGrad(nCategories,maxEdges)) allocate(globalGrad(nCategories,3)) - if (config_rotate_cartesian_grid) allocate(globalGradRotate(nCategories,3)) ! initialize the gradient xGrad(:,:) = 0.0_RKIND yGrad(:,:) = 0.0_RKIND - zGrad(:) = 0.0_RKIND ! diagnostic only + !zGrad(:) = 0.0_RKIND ! diagnostic only - ! loop over cells +#if !defined(__GFORTRAN__) && !defined(CPRGNU) + !$omp parallel do default(shared) firstprivate(normalGrad,globalGrad) private(iEdgeOnCell,iCellNeighbor,iEdge,iCat,signGradient,tempGrad) +#endif do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present in the cell @@ -4343,19 +4330,10 @@ subroutine compute_gradient_2d(& normalGrad(iCat,iEdgeOnCell) = signGradient * (field(iCat,iCellNeighbor) - field(iCat,iCell)) / dcEdge(iEdge) - else ! either or both field values do not have physical meaning; set gradient component = 0 - - normalGrad(iCat,iEdgeOnCell) = 0.0_RKIND - endif enddo ! iCat - else ! there is no cell neighbor on this edge - - ! set gradient component = 0 - normalGrad(:,iEdgeOnCell) = 0.0_RKIND - endif ! add the contribution of this normal component to the reconstructed @@ -4373,10 +4351,11 @@ subroutine compute_gradient_2d(& !TODO - Rotate the gradient vector if on a plane? if (config_rotate_cartesian_grid .and. on_a_sphere) then - globalGradRotate(:,1) = -globalGrad(:,3) ! xR = -z - globalGradRotate(:,2) = globalGrad(:,2) ! yR = y - globalGradRotate(:,3) = globalGrad(:,1) ! zR = x - globalGrad(:,:) = globalGradRotate(:,:) + do iCat = 1, nCategories + tempGrad = globalGrad(iCat,1) + globalGrad(iCat,1) = -globalGrad(iCat,3) ! xR = -z + globalGrad(iCat,3) = tempGrad ! zR = x + enddo ! iCat endif ! transform from global x/y/z coordinates to local east/west coordinates @@ -4392,10 +4371,9 @@ subroutine compute_gradient_2d(& + transGlobalToCell(2,3,iCell) * globalGrad(:,3) ! Note: The zGrad component is never used; it is simply computed as a diagnostic - !TODO - Comment out zGrad computation? - zGrad(:) = transGlobalToCell(3,1,iCell) * globalGrad(:,1) & - + transGlobalToCell(3,2,iCell) * globalGrad(:,2) & - + transGlobalToCell(3,3,iCell) * globalGrad(:,3) + !zGrad(:) = transGlobalToCell(3,1,iCell) * globalGrad(:,1) & + ! + transGlobalToCell(3,2,iCell) * globalGrad(:,2) & + ! + transGlobalToCell(3,3,iCell) * globalGrad(:,3) else ! on a plane; do a simple copy @@ -4419,18 +4397,19 @@ subroutine compute_gradient_2d(& realArgs=(/field(iCatTest,iCellNeighbor), normalGrad(iCatTest,iEdgeOnCell)/)) enddo call mpas_log_write(' ') - call mpas_log_write('Unlimited center gradient: $r $r $r', & - realArgs=(/xGrad(iCatTest,iCell), yGrad(iCatTest,iCell), zGrad(iCatTest)/)) + call mpas_log_write('Unlimited center gradient: $r $r', & + realArgs=(/xGrad(iCatTest,iCell), yGrad(iCatTest,iCell)/)) + !call mpas_log_write('Unlimited center gradient: $r $r $r', & + ! realArgs=(/xGrad(iCatTest,iCell), yGrad(iCatTest,iCell), zGrad(iCatTest)/)) endif endif enddo ! iCell ! cleanup - deallocate(zGrad) + !deallocate(zGrad) deallocate(normalGrad) deallocate(globalGrad) - if (config_rotate_cartesian_grid) deallocate(globalGradRotate) end subroutine compute_gradient_2d @@ -4504,7 +4483,7 @@ subroutine compute_gradient_3d(& mask !< Input: integer mask for parent tracer; ! = 1 where field values for this tracer are physically meaningful, else = 0 - real(kind=RKIND), dimension(:,:,:), intent(out) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(out) :: & xGrad, yGrad !< Output: x and y components of the gradient integer, dimension(:), intent(in) :: & @@ -4522,29 +4501,29 @@ subroutine compute_gradient_3d(& real(kind=RKIND), dimension(:,:,:), allocatable :: & normalGrad, & ! normal components of the gradient, defined on cell edges - globalGrad, & ! gradient at cell center, in global x/y/z coordinates - globalGradRotate ! rotated globalGrad vector + globalGrad ! gradient at cell center, in global x/y/z coordinates real(kind=RKIND) :: & signGradient ! = 1 or -1, depending on which direction is taken as positive at a given edge - real(kind=RKIND), dimension(:,:), allocatable :: & - zGrad ! diagnostic only; should be much smaller than xGrad and yGrad + !real(kind=RKIND), dimension(:,:), allocatable :: & + ! zGrad ! diagnostic only; should be much smaller than xGrad and yGrad ! find dimensions and allocate arrays nLayers = size(field,1) nCategories = size(field,2) - allocate(zGrad(nLayers,nCategories)) + !allocate(zGrad(nLayers,nCategories)) allocate(normalGrad(nLayers,nCategories,maxEdges)) allocate(globalGrad(nLayers,nCategories,3)) - if (config_rotate_cartesian_grid) allocate(globalGradRotate(nLayers,nCategories,3)) ! initialize the gradient xGrad(:,:,:) = 0.0_RKIND yGrad(:,:,:) = 0.0_RKIND - zGrad(:,:) = 0.0_RKIND ! diagnostic only + !zGrad(:,:) = 0.0_RKIND ! diagnostic only - ! loop over cells +#if !defined(__GFORTRAN__) && !defined(CPRGNU) + !$omp parallel do default(shared) firstprivate(normalGrad,globalGrad) private(iEdgeOnCell,iCellNeighbor,iEdge,iCat,iLayer,signGradient) +#endif do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present in the cell @@ -4579,20 +4558,11 @@ subroutine compute_gradient_3d(& normalGrad(iLayer,iCat,iEdgeOnCell) = & signGradient * (field(iLayer,iCat,iCellNeighbor) - field(iLayer,iCat,iCell)) / dcEdge(iEdge) - else ! either or both field values do not have physical meaning; set gradient component = 0 - - normalGrad(iLayer,iCat,iEdgeOnCell) = 0.0_RKIND - endif enddo ! iCat enddo ! iLayer - else ! there is no cell neighbor on this edge - - ! set gradient component = 0 - normalGrad(:,:,iEdgeOnCell) = 0.0_RKIND - endif ! add the contribution of this normal component to the reconstructed gradient @@ -4610,10 +4580,10 @@ subroutine compute_gradient_3d(& !TODO - Rotate the gradient vector if on a plane? if (config_rotate_cartesian_grid .and. on_a_sphere) then - globalGradRotate(:,:,1) = -globalGrad(:,:,3) ! xR = -z - globalGradRotate(:,:,2) = globalGrad(:,:,2) ! yR = y - globalGradRotate(:,:,3) = globalGrad(:,:,1) ! zR = x - globalGrad(:,:,:) = globalGradRotate(:,:,:) + ! reusing normalGrad as a temporary variable to rotate globalGrad + normalGrad(:,:,1) = globalGrad(:,:,1) ! temp + globalGrad(:,:,1) = -globalGrad(:,:,3) ! xR = -z + globalGrad(:,:,3) = normalGrad(:,:,1) ! zR = x endif ! transform from global x/y/z coordinates to local east/west coordinates @@ -4629,10 +4599,9 @@ subroutine compute_gradient_3d(& + transGlobalToCell(2,3,iCell) * globalGrad(:,:,3) ! Note: The zGrad component is never used; it is simply computed as a diagnostic - !TODO - Comment out zGrad computation? - zGrad(:,:) = transGlobalToCell(3,1,iCell) * globalGrad(:,:,1) & - + transGlobalToCell(3,2,iCell) * globalGrad(:,:,2) & - + transGlobalToCell(3,3,iCell) * globalGrad(:,:,3) + !zGrad(:,:) = transGlobalToCell(3,1,iCell) * globalGrad(:,:,1) & + ! + transGlobalToCell(3,2,iCell) * globalGrad(:,:,2) & + ! + transGlobalToCell(3,3,iCell) * globalGrad(:,:,3) else ! on a plane; do a simple copy @@ -4656,18 +4625,19 @@ subroutine compute_gradient_3d(& realArgs=(/field(iLayerTest,iCatTest,iCellNeighbor), normalGrad(iLayerTest,iCatTest,iEdgeOnCell)/)) enddo call mpas_log_write(' ') - call mpas_log_write('Unlimited center gradient: $r $r $r', & - realArgs=(/xGrad(iLayerTest,iCatTest,iCell), yGrad(iLayerTest,iCatTest,iCell), zGrad(iLayerTest,iCatTest)/)) + call mpas_log_write('Unlimited center gradient: $r $r', & + realArgs=(/xGrad(iLayerTest,iCatTest,iCell), yGrad(iLayerTest,iCatTest,iCell)/)) + !call mpas_log_write('Unlimited center gradient: $r $r $r', & + ! realArgs=(/xGrad(iLayerTest,iCatTest,iCell), yGrad(iLayerTest,iCatTest,iCell), zGrad(iLayerTest,iCatTest)/)) endif endif enddo ! iCell ! cleanup - deallocate(zGrad) + !deallocate(zGrad) deallocate(normalGrad) deallocate(globalGrad) - if (config_rotate_cartesian_grid) deallocate(globalGradRotate) end subroutine compute_gradient_3d @@ -4684,6 +4654,7 @@ end subroutine compute_gradient_3d ! !----------------------------------------------------------------------- +!DIR$ ATTRIBUTES FORCEINLINE :: compute_barycenter_coordinates subroutine compute_barycenter_coordinates(& geomAvgCell, & iCell, & @@ -4842,30 +4813,30 @@ subroutine limit_tracer_gradient_2d(& integer, intent(in) :: & nCells !< Input: number of cells - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & nEdgesOnCell !< Input: number of edges per cell - integer, dimension(:,:), intent(in) :: & + integer, dimension(:,:), contiguous, intent(in) :: & cellsOnCell !< Input: cell index for each edge neighbor of a given cell - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & field !< Input: 2d field for which we are limiting the gradient - integer, dimension(:,:), intent(in) :: & + integer, dimension(:,:), contiguous, intent(in) :: & fieldMask !< Input: mask = 1 where field value is physically meaningful, = 0 elsewhere - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & xVertexOnCell, & !< Input: x (east) coordinate of vertex relative to cell center in local tangent plane yVertexOnCell !< Input: y (north) coordinate of vertex relative to cell center in local tangent plane - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & maskCell !< Input: = 1 for cells with ice, else = 0 - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & xBarycenter, & !< Input: x (east) coordinate of barycenter of this tracer's parent yBarycenter !< Input: y (north) coordinate of barycenter of this tracer's parent - real(kind=RKIND), dimension(:,:), intent(inout) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(inout) :: & xGrad, & !< Input/output: x (east) component of gradient vector yGrad !< Input/output: y (north) coordinate of barycenter of this tracer's parent ! Gradient components are unlimited on input, limited on output @@ -4901,7 +4872,9 @@ subroutine limit_tracer_gradient_2d(& allocate(maxLocal(nCategories)) allocate(minLocal(nCategories)) - ! loop over cells + !$omp parallel do default(shared) private(iEdgeOnCell,iCellNeighbor,& + !$omp& iCat,iVertex,deviationAtVertex,gradFactor,gradFactor1,gradFactor2) & + !$omp& firstprivate(maxNeighbor,minNeighbor,maxLocal,minLocal) do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present @@ -5038,35 +5011,35 @@ subroutine limit_tracer_gradient_3d(& integer, intent(in) :: & nCells !< Input: number of cells - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & nEdgesOnCell !< Input: number of edges per cell - integer, dimension(:,:), intent(in) :: & + integer, dimension(:,:), contiguous, intent(in) :: & cellsOnCell !< Input: cell index for each edge neighbor of a given cell - real(kind=RKIND), dimension(:,:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in) :: & field !< Input: 3d field for which we are limiting the gradient - integer, dimension(:,:,:), intent(in) :: & + integer, dimension(:,:,:), contiguous, intent(in) :: & fieldMask !< Input: mask = 1 where field value is physically meaningful, = 0 elsewhere - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & xVertexOnCell, & !< Input: x (east) coordinate of vertex relative to cell center in local tangent plane yVertexOnCell !< Input: y (north) coordinate of vertex relative to cell center in local tangent plane - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & maskCell !< Input: = 1 for cells with ice, else = 0 - real(kind=RKIND), dimension(:,:,:), intent(inout) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(inout) :: & xGrad, & !< Input/output: x (east) component of gradient vector yGrad !< Input/output: y (north) coordinate of barycenter of this tracer's parent ! Gradient components are unlimited on input, limited on output - real(kind=RKIND), dimension(:,:), intent(in), optional :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in), optional :: & xBarycenter2D, & !< Input: x (east) coordinate of barycenter of this tracer's 2Dparent yBarycenter2D !< Input: y (north) coordinate of barycenter of this tracer's 2D parent - real(kind=RKIND), dimension(:,:,:), intent(in), optional :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in), optional :: & xBarycenter3D, & !< Input: x (east) coordinate of barycenter of this tracer's 3D parent yBarycenter3D !< Input: y (north) coordinate of barycenter of this tracer's 3D parent @@ -5113,7 +5086,9 @@ subroutine limit_tracer_gradient_3d(& allocate(maxLocal(nLayers,nCategories)) allocate(minLocal(nLayers,nCategories)) - ! loop over cells + !$omp parallel do default(shared) private(iEdgeOnCell,iCellNeighbor,& + !$omp& iCat,iLayer,iVertex,deviationAtVertex,gradFactor,gradFactor1,gradFactor2) & + !$omp& firstprivate(maxNeighbor,minNeighbor,maxLocal,minLocal) do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present @@ -6440,8 +6415,11 @@ subroutine shift_vertices_of_departure_triangle(& crossProduct) if (abs(crossProduct) < eps11) then ! the two vectors are (nearly) parallel; something is wrong - call mpas_log_write('IR: basis vectors for IR coordinate transformations must not be parallel: iEdge, iCell =', & - MPAS_LOG_CRIT, intArgs=(/iEdge, iCell/)) + call mpas_log_write('IR: basis vectors for IR coordinate transformations must not be parallel: iEdge, iCell = $i, $i', & + MPAS_LOG_ERR, intArgs=(/iEdge, iCell/)) + call mpas_log_write('IR: edgeVector1: $r, $r', MPAS_LOG_ERR, realArgs=edgeVector1) + call mpas_log_write('IR: edgeVector2: $r, $r', MPAS_LOG_ERR, realArgs=edgeVector2) + call mpas_log_write('IR: Critical error', MPAS_LOG_CRIT) endif if (crossProduct > 0.0_RKIND) then @@ -6698,7 +6676,7 @@ subroutine integrate_fluxes_over_triangles(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) type(tracer_type), pointer :: & tracersHead !< Input/output: pointer to first element of linked list of tracers @@ -6731,8 +6709,8 @@ subroutine integrate_fluxes_over_triangles(& type(block_type), intent(in) :: & block !< Input: local block (diagnostic only) - integer, intent(inout) :: & - ierr !< Input: error code + logical, intent(inout) :: & + abortFlag !< Input: error code ! local variables @@ -6742,12 +6720,10 @@ subroutine integrate_fluxes_over_triangles(& dummyTracer ! dummy tracer with value of 1 everywhere integer :: & - iEdge, iCell, iCat, iLayer, iTri, iqp - - integer :: & nTriPerEdgeRemap, & ! number of triangles per edge nCategories, & ! number of ice thickness categories - nLayers ! number of layers + nLayers, & ! number of layers + iEdge, iCell, iCat, iLayer, iTri, iqp real(kind=RKIND), dimension(:), allocatable :: & tracerIntegral2D ! integral over a triangle of mass, mass*tracer, etc. @@ -6815,13 +6791,10 @@ subroutine integrate_fluxes_over_triangles(& else ! nParents = 1, 2 or 3 parentTracer => thisTracer % parent - - endif - - if (verboseFluxes) then - if (thisTracer % nParents > 0) then + if (verboseFluxes) then call mpas_log_write('Parent: '//trim(parentTracer % tracerName)) endif + endif ! Integrate the fluxes of this tracer (for each category and layer) over each triangle of each edge. @@ -6829,6 +6802,7 @@ subroutine integrate_fluxes_over_triangles(& if (thisTracer % ndims == 2) then + !$omp parallel do default(shared) firstprivate(tracerIntegral2D) private(iTri,iCell,iqp,iCat) do iEdge = 1, nEdges if (maskEdge(iEdge) == 1) then @@ -6836,86 +6810,42 @@ subroutine integrate_fluxes_over_triangles(& if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - ! In the following arrays: - ! 1st index of triangleValue2D = category; 2nd index = QP - ! 1st index of center/xGrad/yGrad = category - ! 1st index of xTriangle/yTriangle = QP - ! 1st index of tracerIntegral2D = category - ! 1st index of edgeFlux2D = category - ! identify the cell where the triangle is located iCell = iCellTriangle(iTri,iEdge) - ! evaluate the tracer at each quadrature point - do iqp = 1, nQuadPoints - thisTracer % triangleValue2D(:,iqp,iTri,iEdge) = & - thisTracer % center2D(:,iCell) & - + thisTracer % xGrad2D(:,iCell) * xTriangle(iqp,iTri,iEdge) & - + thisTracer % yGrad2D(:,iCell) * yTriangle(iqp,iTri,iEdge) - enddo - - ! evaluate the product mass*tracer at each quadrature point (using parent tracer info computed already) - ! In nParents = 0, this is mass - ! If nParents = 1, this is mass*tracer1 - ! If nParents = 2, this is mass*tracer1*tracer2 - ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 - ! Note: Parent tracer must have ndims = 2 - thisTracer % triangleValue2D(:,:,iTri,iEdge) = parentTracer % triangleValue2D(:,:,iTri,iEdge) & - * thisTracer % triangleValue2D(:,:,iTri,iEdge) - - ! integrate over the triangle by summing over quadrature points tracerIntegral2D(:) = 0.0_RKIND + ! evaluate the tracer at each quadrature point do iqp = 1, nQuadPoints - tracerIntegral2D(:) = tracerIntegral2D(:) & - + weightQuadPoint(iqp) * thisTracer % triangleValue2D(:,iqp,iTri,iEdge) - enddo - + do iCat = 1, nCategories + ! eval the product mass*tracer at each quadrature point (using parent tracer info computed already) + ! In nParents = 0, this is mass + ! If nParents = 1, this is mass*tracer1 + ! If nParents = 2, this is mass*tracer1*tracer2 + ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 + ! Note: Parent tracer must have ndims = 2 + thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) = & + parentTracer % triangleValue2D(iCat,iqp,iTri,iEdge) * & + ( thisTracer % center2D(iCat,iCell) & + + thisTracer % xGrad2D(iCat,iCell) * xTriangle(iqp,iTri,iEdge) & + + thisTracer % yGrad2D(iCat,iCell) * yTriangle(iqp,iTri,iEdge) & + ) + ! integrate over the triangle by summing over quadrature points + tracerIntegral2D(iCat) = tracerIntegral2D(iCat) & + + weightQuadPoint(iqp) * thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) + enddo ! iCat + enddo ! iqp ! increment the area-weighted flux across the edge thisTracer % edgeFlux2D(:,iEdge) = thisTracer % edgeFlux2D(:,iEdge) & + triangleArea(iTri,iEdge) * tracerIntegral2D(:) - endif ! triangleArea /= 0 - enddo ! nTriPerEdgeRemap - endif ! maskEdge = 1 enddo ! nEdges - - ! Check for negative reconstructed ice area - if (trim(thisTracer % tracerName) == 'iceAreaCategory') then - do iEdge = 1, nEdges - do iTri = 1, nTriPerEdgeRemap - if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - iCell = iCellTriangle(iTri,iEdge) - do iqp = 1, nQuadPoints - do iCat = 1, nCategories - if (thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) < 0.0_RKIND) then - call mpas_log_write('Negative reconstructed ice area', MPAS_LOG_ERR) - call mpas_log_write('nCells = $i', MPAS_LOG_ERR, intArgs=(/nCells/)) - call mpas_log_write('iCat, iCell, global iCell: $i $i $i', MPAS_LOG_ERR, & - intArgs=(/iCat, iCell, indexToCellID(iCell)/)) - call mpas_log_write('iEdge, global iEdge, iTri, iqp: $i $i $i $i', MPAS_LOG_ERR, & - intArgs=(/iEdge, indexToEdgeID(iEdge), iTri, iqp/)) - call mpas_log_write('triangle area: $r', MPAS_LOG_ERR, realArgs=(/triangleArea(iTri,iEdge)/)) - call mpas_log_write('tracer val: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge)/)) - call mpas_log_write('center val: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % center2D(iCat,iCell)/)) - call mpas_log_write('x gradient: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % xGrad2D(iCat,iCell)/)) - call mpas_log_write('y gradient: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % yGrad2D(iCat,iCell)/)) - call mpas_log_write('IR negative reconstructed ice area (nDims == 2)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_NEG_AREA - return - endif ! negative area - enddo ! iCat - enddo ! iqp - endif ! triangleArea > 0 - enddo ! iTri - enddo ! iEdge - endif ! iceAreaCategory - deallocate(tracerIntegral2D) elseif (thisTracer % ndims == 3) then + !$omp parallel do default(shared) firstprivate(tracerIntegral3D) private(iTri,iCell,iqp,iCat,iLayer) do iEdge = 1, nEdges if (maskEdge(iEdge) == 1) then @@ -6923,106 +6853,104 @@ subroutine integrate_fluxes_over_triangles(& if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - ! In the following arrays: - ! 1st index of triangleValue3D = layer; 2nd index = category; 3rd index = QP - ! 1st index of center/xGrad/yGrad = layer; 2nd index = category - ! 1st index of xTriangle/yTriangle = QP - ! 1st index of tracerIntegral = layer; 2nd index = category - ! 1st index of edgeFlux = layer; 2nd index = category - ! identify the cell where the triangle is located iCell = iCellTriangle(iTri,iEdge) - ! evaluate the tracer at each quadrature point - do iqp = 1, nQuadPoints - thisTracer % triangleValue3D(:,:,iqp,iTri,iEdge) = & - thisTracer % center3D(:,:,iCell) & - + thisTracer % xGrad3D(:,:,iCell) * xTriangle(iqp,iTri,iEdge) & - + thisTracer % yGrad3D(:,:,iCell) * yTriangle(iqp,iTri,iEdge) - enddo - - ! evaluate the product mass*tracer at each quadrature point (using parent tracer info computed already) - ! In nParents = 0, this is mass - ! If nParents = 1, this is mass*tracer1 - ! If nParents = 2, this is mass*tracer1*tracer2 - ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 - ! Note: Parent tracer can have ndims = 2 or 3 - if (parentTracer % ndims == 2) then - do iLayer = 1, nLayers - thisTracer % triangleValue3D(iLayer,:,:,iTri,iEdge) = & - parentTracer % triangleValue2D(:,:,iTri,iEdge) * & - thisTracer % triangleValue3D(iLayer,:,:,iTri,iEdge) - enddo - else ! parents has ndims = 3 - thisTracer % triangleValue3D(:,:,:,iTri,iEdge) = parentTracer % triangleValue3D(:,:,:,iTri,iEdge) & - * thisTracer % triangleValue3D(:,:,:,iTri,iEdge) - endif - - ! integrate over the triangle by summing over quadrature points tracerIntegral3D(:,:) = 0.0_RKIND + ! evaluate the tracer at each quadrature point do iqp = 1, nQuadPoints - tracerIntegral3D(:,:) = tracerIntegral3D(:,:) & - + weightQuadPoint(iqp) * thisTracer % triangleValue3D(:,:,iqp,iTri,iEdge) - enddo - + do iCat = 1, nCategories + do iLayer = 1, nLayers + if (parentTracer % ndims == 2) then + ! eval the product mass*tracer at each quadrature point (using parent tracer info + ! computed already) + ! In nParents = 0, this is mass + ! If nParents = 1, this is mass*tracer1 + ! If nParents = 2, this is mass*tracer1*tracer2 + ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 + ! Note: Parent tracer can have ndims = 2 or 3 + thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) = & + parentTracer % triangleValue2D(iCat,iqp,iTri,iEdge) * & + ( thisTracer % center3D(iLayer,iCat,iCell) & + + thisTracer % xGrad3D(iLayer,iCat,iCell) * xTriangle(iqp,iTri,iEdge) & + + thisTracer % yGrad3D(iLayer,iCat,iCell) * yTriangle(iqp,iTri,iEdge) & + ) + else ! parents has ndims = 3 + thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) = & + parentTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) * & + ( thisTracer % center3D(iLayer,iCat,iCell) & + + thisTracer % xGrad3D(iLayer,iCat,iCell) * xTriangle(iqp,iTri,iEdge) & + + thisTracer % yGrad3D(iLayer,iCat,iCell) * yTriangle(iqp,iTri,iEdge) & + ) + endif + ! integrate over the triangle by summing over quadrature points + tracerIntegral3D(iLayer,iCat) = tracerIntegral3D(iLayer,iCat) & + + weightQuadPoint(iqp) * thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) + enddo ! iLayer + enddo ! iCat + enddo ! iqp ! increment the area-weighted flux across the edge thisTracer % edgeFlux3D(:,:,iEdge) = thisTracer % edgeFlux3D(:,:,iEdge) & + triangleArea(iTri,iEdge) * tracerIntegral3D(:,:) - endif ! triangleArea /= 0 - enddo ! nTriPerEdgeRemap - endif ! maskEdge = 1 enddo ! nEdges + deallocate(tracerIntegral3D) - ! Check for negative reconstructed ice area - if (trim(thisTracer % tracerName) == 'iceAreaCategory') then - do iEdge = 1, nEdges - do iTri = 1, nTriPerEdgeRemap - if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - iCell = iCellTriangle(iTri,iEdge) - do iqp = 1, nQuadPoints - do iCat = 1, nCategories + endif ! ndims + + ! Check for negative reconstructed ice area + if (trim(thisTracer % tracerName) == 'iceAreaCategory') then + do iEdge = 1, nEdges + do iTri = 1, nTriPerEdgeRemap + if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then + iCell = iCellTriangle(iTri,iEdge) + do iqp = 1, nQuadPoints + do iCat = 1, nCategories + if (thisTracer % ndims == 2) then + if (thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) < 0.0_RKIND) then + call mpas_log_write('MPAS-seaice: IR negative reconstructed ice area', messageType=MPAS_LOG_ERR) + call mpas_log_write('nCells, iCat, iCell, global iCell: $i $i $i $i', messageType=MPAS_LOG_ERR, & + intArgs=(/nCells, iCat, iCell, indexToCellID(iCell)/)) + call mpas_log_write('iEdge, global iEdge, iTri, iqp: $i $i $i $i', messageType=MPAS_LOG_ERR, & + intArgs=(/iEdge, indexToEdgeID(iEdge), iTri, iqp/)) + call mpas_log_write('triangle area, tracer, center, xGrad2D, yGrad2D: $r $r $r $r $r', & + messageType=MPAS_LOG_CRIT, & ! abort + realArgs=(/triangleArea(iTri,iEdge), & + thisTracer%triangleValue2D(iCat,iqp,iTri,iEdge), & + thisTracer%center2D(iCat,iCell), & + thisTracer% xGrad2D(iCat,iCell), & + thisTracer% yGrad2D(iCat,iCell) /)) + endif ! negative 2D area + elseif (thisTracer % ndims == 3) then do iLayer = 1, nLayers if (thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) < 0.0_RKIND) then - call mpas_log_write('Negative reconstructed ice area', messageType=MPAS_LOG_ERR) - call mpas_log_write('nCells = $i', messageType=MPAS_LOG_ERR, intArgs=(/nCells/)) - call mpas_log_write('iLayer, iCat, iCell, global iCell: $i $i $i $i', & + call mpas_log_write('MPAS-seaice: IR negative reconstructed ice area', messageType=MPAS_LOG_ERR) + call mpas_log_write('nCells, iLayer, iCat, iCell, global iCell: $i $i $i $i $i', & messageType=MPAS_LOG_ERR, & - intArgs=(/iLayer, iCat, iCell, indexToCellID(iCell)/)) + intArgs=(/nCells, iLayer, iCat, iCell, indexToCellID(iCell)/)) call mpas_log_write('iEdge, global iEdge, iTri, iqp: $i $i $i $i', & messageType=MPAS_LOG_ERR, & intArgs=(/iEdge, indexToEdgeID(iEdge), iTri, iqp/)) - call mpas_log_write('triangle area: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/triangleArea(iTri,iEdge)/)) - call mpas_log_write('tracer val: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge)/)) - call mpas_log_write('center val: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % center3D(iLayer,iCat,iCell)/)) - call mpas_log_write('x gradient: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % xGrad3D(iLayer,iCat,iCell)/)) - call mpas_log_write('y gradient: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % yGrad3D(iLayer,iCat,iCell)/)) - call mpas_log_write('MPAS-seaice: IR negative reconstructed ice area (nDims == 3)', & - messageType=MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_NEG_AREA - return - endif ! negative area + call mpas_log_write('triangle area, tracer, center, xGrad3D, yGrad3D: $r $r $r $r $r', & + messageType=MPAS_LOG_CRIT, & ! abort + realArgs=(/triangleArea(iTri,iEdge), & + thisTracer%triangleValue3D(iLayer,iCat,iqp,iTri,iEdge), & + thisTracer%center3D(iLayer,iCat,iCell), & + thisTracer% xGrad3D(iLayer,iCat,iCell), & + thisTracer% yGrad3D(iLayer,iCat,iCell) /)) + endif ! negative 3D area enddo ! iLayer - enddo ! iCat - enddo ! iqp - endif ! triangleArea > 0 - enddo ! iTri - enddo ! iEdge - endif ! iceAreaCategory - - deallocate(tracerIntegral3D) - - endif ! ndims + endif ! ndims + enddo ! iCat + enddo ! iqp + endif ! triangleArea > 0 + enddo ! iTri + enddo ! iEdge + endif ! iceAreaCategory ! clean up - if (associated(dummyTracer)) then if (dummyTracer % ndims == 2) then if (associated(dummyTracer % triangleValue2D)) deallocate(dummyTracer % triangleValue2D) @@ -7204,7 +7132,10 @@ subroutine update_mass_and_tracers(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) + + use seaice_constants, only: & + seaicePuny integer, intent(in) :: & nCellsSolve !< Input: number of locally owned cells to be updated @@ -7230,8 +7161,8 @@ subroutine update_mass_and_tracers(& type(block_type), intent(in) :: & block !< Input: local block (diagnostic only) - integer, intent(inout) :: & - ierr !< Input/Output: error code + logical, intent(inout) :: & + abortFlag !< Input/Output: error flag ! local variables @@ -7332,6 +7263,7 @@ subroutine update_mass_and_tracers(& allocate (fluxFromCell2D(nCategories)) + !$omp parallel do default(shared) firstprivate(fluxFromCell2D) private(iEdgeOnCell,iEdge,edgeSignOnCell,iCat) do iCell = 1, nCellsSolve fluxFromCell2D = 0.0_RKIND @@ -7413,6 +7345,7 @@ subroutine update_mass_and_tracers(& allocate (fluxFromCell3D(nLayers,nCategories)) + !$omp parallel do default(shared) firstprivate(fluxFromCell3D) private(iEdgeOnCell,iEdge,edgeSignOnCell,iCat,iLayer) do iCell = 1, nCellsSolve fluxFromCell3D = 0.0_RKIND @@ -7535,13 +7468,16 @@ subroutine update_mass_and_tracers(& do iCell = 1, nCellsSolve do iCat = 1, nCategories - if (thisTracer % array2D(iCat,iCell) < 0.0_RKIND) then + if (thisTracer % array2D(iCat,iCell) < -seaicePuny*seaicePuny) then call mpas_log_write('IR: Negative mass in IR: iCat, iCell, global iCell, value: $i $i $i $r', & messageType=MPAS_LOG_ERR, & intArgs=(/iCat, iCell, indexToCellID(iCell)/), & realArgs=(/thisTracer % array2D(iCat,iCell)/)) - ierr = SEAICE_ERROR_IR_NEG_MASS + abortFlag = .true. return + else if (thisTracer % array2D(iCat,iCell) >= -seaicePuny*seaicePuny .and. & + thisTracer % array2D(iCat,iCell) < 0.0_RKIND) then + thisTracer % array2D(iCat,iCell) = 0.0_RKIND endif enddo enddo @@ -7551,13 +7487,16 @@ subroutine update_mass_and_tracers(& do iCell = 1, nCellsSolve do iCat = 1, nCategories do iLayer = 1, nLayers - if (thisTracer % array3D(iLayer,iCat,iCell) < 0.0_RKIND) then + if (thisTracer % array3D(iLayer,iCat,iCell) < -seaicePuny*seaicePuny) then call mpas_log_write('IR: Negative mass in IR: iLayer, iCat, iCell, global iCell, value: $i $i $i $i $r', & messageType=MPAS_LOG_ERR, & intArgs=(/iLayer, iCat, iCell, indexToCellID(iCell)/), & realArgs=(/thisTracer % array3D(iLayer,iCat,iCell)/)) - ierr = SEAICE_ERROR_IR_NEG_MASS + abortFlag = .true. return + else if (thisTracer % array3D(iLayer,iCat,iCell) >= -seaicePuny*seaicePuny .and. & + thisTracer % array3D(iLayer,iCat,iCell) < 0.0_RKIND) then + thisTracer % array3D(iLayer,iCat,iCell) = 0.0_RKIND endif enddo enddo @@ -8184,7 +8123,7 @@ end subroutine sum_tracers ! !----------------------------------------------------------------------- - subroutine check_tracer_conservation(dminfo, tracersHead, ierr) + subroutine check_tracer_conservation(dminfo, tracersHead, abortFlag) type (dm_info), intent(in) :: dminfo !< Input: domain info @@ -8192,8 +8131,8 @@ subroutine check_tracer_conservation(dminfo, tracersHead, ierr) tracersHead !< Input/output: pointer to first element of linked list of tracers ! The pointer stays attached to the first tracer, but tracer sums are updated - integer, intent(inout) :: & - ierr ! thisTracer % localMax2D(iCat,iCell) + toleranceMax) then @@ -8650,8 +8589,8 @@ subroutine check_tracer_monotonicity(domain, tracersHead, ierr) call mpas_log_write('Tolerance, difference: $r $r', MPAS_LOG_ERR, & realArgs=(/toleranceMax, thisTracer % array2D(iCat,iCell) - thisTracer % localMax2D(iCat,iCell)/)) call mpas_log_write('IR advection, monotonicity violation (max, nDims == 2)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_MONO - call seaice_critical_error_write_block(domain, block) + abortFlag = .true. + call seaice_critical_error_write_block(domain, block, abortFlag) return endif @@ -8765,8 +8704,8 @@ subroutine check_tracer_monotonicity(domain, tracersHead, ierr) call mpas_log_write('Tolerance, difference: $r $r', MPAS_LOG_ERR, & realArgs=(/toleranceMin, thisTracer % localMin3D(iLayer,iCat,iCell) - thisTracer % array3D(iLayer,iCat,iCell)/)) call mpas_log_write('IR advection, monotonicity violation (min, nDims == 3)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_MONO - call seaice_critical_error_write_block(domain, block) + abortFlag = .true. + call seaice_critical_error_write_block(domain, block, abortFlag) return elseif (thisTracer % array3D(iLayer,iCat,iCell) > & @@ -8784,8 +8723,8 @@ subroutine check_tracer_monotonicity(domain, tracersHead, ierr) call mpas_log_write('Tolerance, difference: $r $r', MPAS_LOG_ERR, & realArgs=(/toleranceMax, thisTracer % array3D(iLayer,iCat,iCell) - thisTracer % localMax3D(iLayer,iCat,iCell)/)) call mpas_log_write('IR advection, monotonicity violation (max, nDims == 3)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_MONO - call seaice_critical_error_write_block(domain, block) + abortFlag = .true. + call seaice_critical_error_write_block(domain, block, abortFlag) return endif diff --git a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F index b1f30702d1..398338a9a7 100644 --- a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F +++ b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F @@ -1,10 +1,3 @@ -! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! mpas_seaice_advection_incremental_remap_tracers @@ -45,16 +38,16 @@ module seaice_advection_incremental_remap_tracers ! (nLayer1, nLayer2, nCategories, nCells) for 3D ! tracer arrays - real(kind=RKIND), dimension(:,:), pointer :: array2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: array3D => null() + real(kind=RKIND), dimension(:,:), pointer :: array2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: array3D => null() ! mass*tracer products ! = mass for mass field ! = mass*tracer1 for tracers with one parent ! = mass*tracer1*tracer2 for tracers with two parents ! = mass*tracer1*tracer2*tracer3 for tracers with three parents - real(kind=RKIND), dimension(:,:), pointer :: massTracerProduct2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: massTracerProduct3D => null() + real(kind=RKIND), dimension(:,:), pointer :: massTracerProduct2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: massTracerProduct3D => null() !TODO - Replace with one field, 2 time levels? ! global sums (over all cells) @@ -77,14 +70,14 @@ module seaice_advection_incremental_remap_tracers ! fluxes across cell edges ! (nCategories, nEdges) for 2D ! (nLayers, nCategories, nEdges) for 3D - real(kind=RKIND), dimension(:,:), pointer :: edgeFlux2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: edgeFlux3D => null() + real(kind=RKIND), dimension(:,:), pointer :: edgeFlux2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: edgeFlux3D => null() ! tracer values at each quadrature point of departure triangles ! (nCategories, nQuadPoints, nTriPerEdge, nEdges) for 2D ! (nLayers,nCategories, nQuadPoints, nTriPerEdge, nEdges) for 3D - real(kind=RKIND), dimension(:,:,:,:), pointer :: triangleValue2D => null() - real(kind=RKIND), dimension(:,:,:,:,:), pointer :: triangleValue3D => null() + real(kind=RKIND), dimension(:,:,:,:), pointer :: triangleValue2D => null() + real(kind=RKIND), dimension(:,:,:,:,:), pointer :: triangleValue3D => null() ! coordinates of barycenter associated with this tracer ! The term 'barycenter' refers to a center of mass or related quantity, as distinct from the geometric center. @@ -95,25 +88,25 @@ module seaice_advection_incremental_remap_tracers ! The barycenter for this tracer is the location where the child tracer value (array2D or array3D) is located. ! Only mass-type fields (nParents = 0) are located at the geometric cell center. - real(kind=RKIND), dimension(:,:), pointer :: xBarycenter2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: xBarycenter3D => null() + real(kind=RKIND), dimension(:,:), pointer :: xBarycenter2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: xBarycenter3D => null() - real(kind=RKIND), dimension(:,:), pointer :: yBarycenter2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: yBarycenter3D => null() + real(kind=RKIND), dimension(:,:), pointer :: yBarycenter2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: yBarycenter3D => null() ! quantities needed for linear reconstruction (value at cell center plus x and y gradients) ! Note: The center value is the value at the geometric cell center and generally is ! difference from the value at the barycenter. ! xGrad and yGrad are the gradient components defined at the cell center - real(kind=RKIND), dimension(:,:), pointer :: center2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: center3D => null() + real(kind=RKIND), dimension(:,:), pointer :: center2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: center3D => null() - real(kind=RKIND), dimension(:,:), pointer :: xGrad2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: xGrad3D => null() + real(kind=RKIND), dimension(:,:), pointer :: xGrad2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: xGrad3D => null() - real(kind=RKIND), dimension(:,:), pointer :: yGrad2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: yGrad3D => null() + real(kind=RKIND), dimension(:,:), pointer :: yGrad2D => null() + real(kind=RKIND), dimension(:,:,:), pointer :: yGrad3D => null() ! pointer to parent tracer type(tracer_type), pointer :: parent => null() @@ -173,6 +166,8 @@ subroutine seaice_add_tracers_to_linked_list(domain) pkgColumnTracerLevelIceActive, & pkgColumnTracerPondsActive, & pkgColumnTracerLidThicknessActive, & + pkgColumnTracerEffectiveSnowDensityActive, & + pkgColumnTracerSnowGrainRadiusActive, & pkgColumnTracerAerosolsActive, & pkgColumnBiogeochemistryActive, & pkgTracerBrineActive, & @@ -240,6 +235,10 @@ subroutine seaice_add_tracers_to_linked_list(domain) pkgColumnTracerPondsActive) call MPAS_pool_get_package(domain % blocklist % packages, 'pkgColumnTracerLidThicknessActive', & pkgColumnTracerLidThicknessActive) + call MPAS_pool_get_package(domain % blocklist % packages, 'pkgColumnTracerEffectiveSnowDensityActive', & + pkgColumnTracerEffectiveSnowDensityActive) + call MPAS_pool_get_package(domain % blocklist % packages, 'pkgColumnTracerSnowGrainRadiusActive', & + pkgColumnTracerSnowGrainRadiusActive) call MPAS_pool_get_package(domain % blocklist % packages, 'pkgColumnTracerAerosolsActive', & pkgColumnTracerAerosolsActive) call MPAS_pool_get_package(domain % blocklist % packages, 'pkgColumnBiogeochemistryActive', & @@ -322,6 +321,18 @@ subroutine seaice_add_tracers_to_linked_list(domain) call add_tracer_to_tracer_linked_list(tracersHead, 'pondLidThickness', 'pondArea') endif + ! snow density + if (pkgColumnTracerEffectiveSnowDensityActive) then + call add_tracer_to_tracer_linked_list(tracersHead, 'snowIceMass', 'snowVolumeCategory') + call add_tracer_to_tracer_linked_list(tracersHead, 'snowLiquidMass', 'snowVolumeCategory') + call add_tracer_to_tracer_linked_list(tracersHead, 'snowDensity', 'snowVolumeCategory') + endif + + ! snow grain radius + if (pkgColumnTracerSnowGrainRadiusActive) then + call add_tracer_to_tracer_linked_list(tracersHead, 'snowGrainRadius','snowVolumeCategory') + endif + if (pkgColumnTracerAerosolsActive) then call add_tracer_to_tracer_linked_list(tracersHead, 'snowScatteringAerosol','snowVolumeCategory') call add_tracer_to_tracer_linked_list(tracersHead, 'snowBodyAerosol', 'snowVolumeCategory') diff --git a/src/core_seaice/shared/mpas_seaice_advection_upwind.F b/src/core_seaice/shared/mpas_seaice_advection_upwind.F index da73a3f230..f64efc6e1d 100644 --- a/src/core_seaice/shared/mpas_seaice_advection_upwind.F +++ b/src/core_seaice/shared/mpas_seaice_advection_upwind.F @@ -53,6 +53,11 @@ module seaice_advection_upwind type(tracerConnectivity), dimension(nTracerVariables), private :: & tracerConnectivities + logical, parameter :: & + config_convert_volume_to_thickness = .true., & + config_limit_ice_concentration = .false., & + config_clean_tracers = .false. + contains !----------------------------------------------------------------------- @@ -73,12 +78,6 @@ module seaice_advection_upwind subroutine seaice_init_advection_upwind(domain)!{{{ - !use mpas_tracer_advection_helpers, only: & - ! mpas_tracer_advection_coefficients - - !use mpas_tracer_advection_mono, only: & - ! mpas_tracer_advection_mono_init - use seaice_mesh, only: & seaice_normal_vectors_polygon @@ -93,36 +92,13 @@ subroutine seaice_init_advection_upwind(domain)!{{{ configs, & velocity_solver - integer, pointer :: & - config_horiz_tracer_adv_order, & - config_vert_tracer_adv_order - - real(kind=RKIND), pointer :: & - config_coef_3rd_order - logical, pointer :: & config_rotate_cartesian_grid, & config_use_velocity_solver - real(kind=RKIND), dimension(:,:,:), pointer :: & - derivTwo - real(kind=RKIND), dimension(:,:,:), pointer :: & normalVectorEdge - real(kind=RKIND), dimension(:,:), pointer :: & - advCoefs, & - advCoefs3rd - - integer, dimension(:), pointer :: & - nAdvCellsForEdge, & - maxLevelCell - - integer, dimension(:,:), pointer :: & - advCellsForEdge, & - highOrderAdvectionMask, & - boundaryCell - integer :: & err @@ -134,44 +110,12 @@ subroutine seaice_init_advection_upwind(domain)!{{{ configs => block % configs call MPAS_pool_get_config(configs, "config_use_velocity_solver", config_use_velocity_solver) - call MPAS_pool_get_config(configs, "config_horiz_tracer_adv_order", config_horiz_tracer_adv_order) - call MPAS_pool_get_config(configs, "config_vert_tracer_adv_order", config_vert_tracer_adv_order) call MPAS_pool_get_config(configs, "config_rotate_cartesian_grid", config_rotate_cartesian_grid) - call MPAS_pool_get_config(configs, "config_coef_3rd_order", config_coef_3rd_order) - - call MPAS_pool_get_array(mesh, "derivTwo", derivTwo) - call MPAS_pool_get_array(mesh, "advCoefs", advCoefs) - call MPAS_pool_get_array(mesh, "advCoefs3rd", advCoefs3rd) - call MPAS_pool_get_array(mesh, "nAdvCellsForEdge", nAdvCellsForEdge) - call MPAS_pool_get_array(mesh, "advCellsForEdge", advCellsForEdge) - call MPAS_pool_get_array(mesh, "maxLevelCell", maxLevelCell) - call MPAS_pool_get_array(mesh, "highOrderAdvectionMask", highOrderAdvectionMask) - call MPAS_pool_get_array(mesh, "boundaryCell", boundaryCell) call MPAS_pool_get_array(velocity_solver, "normalVectorEdge", normalVectorEdge) err = 0 - !call mpas_tracer_advection_coefficients(& - ! mesh, & - ! config_horiz_tracer_adv_order, & - ! derivTwo, & - ! advCoefs, & - ! advCoefs3rd, & - ! nAdvCellsForEdge, & - ! advCellsForEdge, & - ! err, & - ! maxLevelCell, & - ! highOrderAdvectionMask, & - ! boundaryCell) - - !call mpas_tracer_advection_mono_init(& - ! 3, & - ! config_horiz_tracer_adv_order, & - ! config_vert_tracer_adv_order, & - ! config_coef_3rd_order, & - ! .false., .true., err) - call define_tracer_connectivities(& config_use_velocity_solver) @@ -1264,6 +1208,12 @@ subroutine run_advection_subvariable(& childTracerOld(iTracerDimension,iCategory,iCell) * parentTracerOld(1,iCategory,iCell) + & childTendency(iTracerDimension,iCategory,iCell) * dt + !write(*,*) trim(childTracerName), iCell, iCategory, iTracerDimension, & + ! childTracerOld(iTracerDimension,iCategory,iCell), & + ! parentTracerOld(1,iCategory,iCell), & + ! childTendency(iTracerDimension,iCategory,iCell), & + ! childTracerNew(iTracerDimension,iCategory,iCell) + ! store the old child tracer multiplied by the old parent. This will be the parent tracer of the next child childTracerOld(iTracerDimension,iCategory,iCell) = childTracerOld(iTracerDimension,iCategory,iCell) * & parentTracerOld(iTracerDimension,iCategory,iCell) @@ -1975,7 +1925,6 @@ subroutine prepare_tracers(& iCategory logical, pointer :: & - config_convert_volume_to_thickness, & config_conservation_check integer, dimension(:), pointer :: & @@ -1995,7 +1944,6 @@ subroutine prepare_tracers(& call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) call MPAS_pool_get_array(tracers, "surfaceTemperature", surfaceTemperature, 1) - call MPAS_pool_get_config(configs, "config_convert_volume_to_thickness", config_convert_volume_to_thickness) call MPAS_pool_get_config(configs, "config_conservation_check", config_conservation_check) if (config_convert_volume_to_thickness) then @@ -2061,6 +2009,7 @@ subroutine finalize_tracers(& real(kind=RKIND), dimension(:,:,:), pointer :: & iceAreaCategory, & + iceAreaCategory2, & surfaceTemperature, & iceVolumeCategory, & snowVolumeCategory, & @@ -2075,10 +2024,7 @@ subroutine finalize_tracers(& iCategory logical, pointer :: & - config_convert_volume_to_thickness, & - config_limit_ice_concentration, & - config_conservation_check, & - config_clean_tracers + config_conservation_check integer, dimension(:), pointer :: & itimestep @@ -2094,14 +2040,14 @@ subroutine finalize_tracers(& call MPAS_pool_get_array(mesh, "areaCell", areaCell) call MPAS_pool_get_array(tracers, "iceAreaCategory", iceAreaCategory, 1) + call MPAS_pool_get_array(tracers, "iceAreaCategory", iceAreaCategory2, 2) call MPAS_pool_get_array(tracers, "surfaceTemperature", surfaceTemperature, 1) call MPAS_pool_get_array(tracers, "iceVolumeCategory", iceVolumeCategory, 1) call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) - call MPAS_pool_get_config(configs, "config_convert_volume_to_thickness", config_convert_volume_to_thickness) - call MPAS_pool_get_config(configs, "config_limit_ice_concentration", config_limit_ice_concentration) call MPAS_pool_get_config(configs, "config_conservation_check", config_conservation_check) - call MPAS_pool_get_config(configs, "config_clean_tracers", config_clean_tracers) + + !iceAreaCategory(:,:,:) = iceAreaCategory2(:,:,:) if (config_conservation_check) & call final_conservation(& diff --git a/src/core_seaice/shared/mpas_seaice_column.F b/src/core_seaice/shared/mpas_seaice_column.F index b96ee96598..b0e3498b7a 100644 --- a/src/core_seaice/shared/mpas_seaice_column.F +++ b/src/core_seaice/shared/mpas_seaice_column.F @@ -60,12 +60,6 @@ module seaice_column ! maximum number of ancestor tracers integer :: nMaxAncestorTracers = 2 - ! category tracer array - real(kind=RKIND), dimension(:,:), allocatable :: tracerArrayCategory ! trcrn - - ! cell tracer array - real(kind=RKIND), dimension(:), allocatable :: tracerArrayCell ! trcr - ! index of the parent tracer integer, dimension(:), allocatable :: parentIndex ! trcr_depend @@ -95,7 +89,11 @@ module seaice_column index_pondArea, & ! nt_apnd index_pondDepth, & ! nt_hpnd index_pondLidThickness, & ! nt_ipnd - index_aerosols ! nt_aero + index_aerosols, & ! nt_aero + index_snowIceMass, & ! nt_smice + index_snowLiquidMass, & ! nt_smliq + index_snowGrainRadius, & ! nt_rsnw + index_snowDensity ! nt_rhos !----------------------------------------------------------------------- ! biogeochemistry @@ -176,6 +174,13 @@ module seaice_column type(ciceTracerObjectType), private :: ciceTracerObject + real(kind=RKIND), dimension(:,:), allocatable :: & + tracerArrayCategory +!$omp threadprivate(tracerArrayCategory) + + real(kind=RKIND), dimension(:), allocatable :: & + tracerArrayCell + ! warnings string kind integer, parameter :: strKINDWarnings = char_len_long @@ -243,7 +248,8 @@ subroutine seaice_init_column_physics_package_variables(domain, clock) config_use_column_package, & config_do_restart, & config_use_column_biogeochemistry, & - config_use_column_shortwave + config_use_column_shortwave, & + config_use_column_snow_tracers call MPAS_pool_get_config(domain % configs, "config_use_column_package", config_use_column_package) call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) @@ -266,6 +272,11 @@ subroutine seaice_init_column_physics_package_variables(domain, clock) ! history variables call init_column_history_variables(domain) + ! snow + call MPAS_pool_get_config(domain % configs, "config_use_column_snow_tracers", config_use_column_snow_tracers) + if (config_use_column_snow_tracers) & + call init_column_snow_tracers(domain) + ! shortwave call MPAS_pool_get_config(domain % configs, "config_do_restart", config_do_restart) call MPAS_pool_get_config(domain % configs, "config_use_column_shortwave", config_use_column_shortwave) @@ -420,6 +431,199 @@ subroutine init_column_thermodynamic_profiles(domain) end subroutine init_column_thermodynamic_profiles +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! init_column_snow_tracers +! +!> \brief Initializes snow physics tracers +!> +!> \author Nicole Jeffery, LANL +!> \date 1 April 2017 +!> \details +!> +!> The following snow tracers are initialized: +!> 1) Snow liquid content (used to compute wet metamorphism of snow grain, modifies +!> liquid content of ponds, used in calculation of effective snow density due to content) +!> 2) Snow ice content (Used in calculation of effective snow density due to content) +!> 3) Effective snow density (both content and compaction are included. May be used for snow grain aging) +!> 4) Snow grain radius (used in radiative transfer calculations) +! +!----------------------------------------------------------------------- + + subroutine init_column_snow_tracers(domain) + + use seaice_constants, only: & + seaicePuny, & + seaiceDensitySnow + + type(domain_type), intent(in) :: & + domain + + type(block_type), pointer :: & + block + + type(MPAS_pool_type), pointer :: & + mesh, & + tracers, & + tracers_aggregate, & + snow + + logical, pointer :: & + config_use_effective_snow_density, & + config_use_snow_grain_radius, & + config_do_restart_snow_density, & + config_do_restart_snow_grain_radius + + real(kind=RKIND), dimension(:,:,:), pointer :: & + snowIceMass, & + snowLiquidMass, & + snowDensity, & + snowVolumeCategory, & + snowGrainRadius + + real(kind=RKIND), dimension(:,:), pointer :: & + snowMeltMassCategory + + real(kind=RKIND), dimension(:), pointer :: & + snowDensityViaContent, & + snowDensityViaCompaction, & + snowMeltMassCell, & + snowVolumeCell + + real(kind=RKIND), pointer :: & + config_fallen_snow_radius + + integer, pointer :: & + nCellsSolve, & + nSnowLayers, & + nCategories + + integer :: & + iCell, & + iSnowLayer, & + iCategory + + call MPAS_pool_get_config(domain % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(domain % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) + call MPAS_pool_get_config(domain % configs, "config_fallen_snow_radius", config_fallen_snow_radius) + call MPAS_pool_get_config(domain % configs, "config_do_restart_snow_density", config_do_restart_snow_density) + call MPAS_pool_get_config(domain % configs, "config_do_restart_snow_grain_radius", config_do_restart_snow_grain_radius) + + block => domain % blocklist + do while (associated(block)) + + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + call MPAS_pool_get_subpool(block % structs, "tracers", tracers) + call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracers_aggregate) + call MPAS_pool_get_subpool(block % structs, "snow", snow) + + call MPAS_pool_get_dimension(block % dimensions, "nCellsSolve", nCellsSolve) + call MPAS_pool_get_dimension(block % dimensions, "nCategories", nCategories) + call MPAS_pool_get_dimension(block % dimensions, "nSnowLayers", nSnowLayers) + + call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) + call MPAS_pool_get_array(tracers_aggregate, "snowVolumeCell", snowVolumeCell) + + if (config_use_effective_snow_density) then + + call MPAS_pool_get_array(snow, "snowDensityViaContent", snowDensityViaContent) + call MPAS_pool_get_array(snow, "snowDensityViaCompaction", snowDensityViaCompaction) + call MPAS_pool_get_array(snow, "snowMeltMassCategory", snowMeltMassCategory) + call MPAS_pool_get_array(snow, "snowMeltMassCell", snowMeltMassCell) + + call MPAS_pool_get_array(tracers, "snowIceMass", snowIceMass, 1) + call MPAS_pool_get_array(tracers, "snowLiquidMass", snowLiquidMass, 1) + call MPAS_pool_get_array(tracers, "snowDensity", snowDensity, 1) + + if (.not. config_do_restart_snow_density) then + + snowIceMass(:,:,:) = 0.0_RKIND + snowLiquidMass(:,:,:) = 0.0_RKIND + snowDensity(:,:,:) = 0.0_RKIND + snowDensityViaContent(:) = 0.0_RKIND + snowDensityViaCompaction(:) = 0.0_RKIND + snowMeltMassCategory(:,:) = 0.0_RKIND + snowMeltMassCell(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + do iCategory = 1, nCategories + if (snowVolumeCategory(1,iCategory,iCell) .gt. 0.0_RKIND) then + do iSnowLayer = 1, nSnowLayers + snowIceMass(iSnowLayer,iCategory,iCell) = seaiceDensitySnow + snowDensity(iSnowLayer,iCategory,iCell) = seaiceDensitySnow + snowDensityViaContent(iCell) = snowDensityViaContent(iCell) & + + snowVolumeCategory(1,iCategory,iCell) * & + (snowIceMass(iSnowLayer,iCategory,iCell) + & + snowLiquidMass(iSnowLayer,iCategory,iCell)) + snowDensityViaCompaction(iCell) = snowDensityViaCompaction(iCell) & + + snowVolumeCategory(1,iCategory,iCell) * & + snowDensity(iSnowLayer,iCategory,iCell) + enddo !iSnowLayer + endif !snowVolumeCategory + enddo !iCategory + if (snowVolumeCell(iCell) .gt. seaicePuny) then + snowDensityViaContent(iCell) = snowDensityViaContent(iCell)/ & + (snowVolumeCell(iCell) * real(nSnowLayers,kind=RKIND)) !!!CHECK THIS!!! + snowDensityViaCompaction(iCell) = snowDensityViaCompaction(iCell)/ & + (snowVolumeCell(iCell) * real(nSnowLayers,kind=RKIND)) + else + snowDensityViaContent(iCell) = 0.0_RKIND + snowDensityViaCompaction(iCell) = 0.0_RKIND + endif !snowVolumeCell + + enddo ! iCell + else + + snowDensityViaContent(:) = 0.0_RKIND + snowDensityViaCompaction(:) = 0.0_RKIND + snowMeltMassCategory(:,:) = 0.0_RKIND + snowMeltMassCell(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + do iCategory = 1, nCategories + if (snowVolumeCategory(1,iCategory,iCell) .gt. 0.0_RKIND) then + do iSnowLayer = 1, nSnowLayers + snowDensityViaContent(iCell) = snowDensityViaContent(iCell) & + + snowVolumeCategory(1,iCategory,iCell) * & + (snowIceMass(iSnowLayer,iCategory,iCell) + & + snowLiquidMass(iSnowLayer,iCategory,iCell)) + snowDensityViaCompaction(iCell) = snowDensityViaCompaction(iCell) & + + snowVolumeCategory(1,iCategory,iCell) * & + snowDensity(iSnowLayer,iCategory,iCell) + enddo !iSnowLayer + endif !snowVolumeCategory + enddo !iCategory + if (snowVolumeCell(iCell) .gt. seaicePuny) then + snowDensityViaContent(iCell) = snowDensityViaContent(iCell)/ & + (snowVolumeCell(iCell) * real(nSnowLayers,kind=RKIND)) !!!CHECK THIS!!! + snowDensityViaCompaction(iCell) = snowDensityViaCompaction(iCell)/ & + (snowVolumeCell(iCell) * real(nSnowLayers,kind=RKIND)) + else + snowDensityViaContent(iCell) = 0.0_RKIND + snowDensityViaCompaction(iCell) = 0.0_RKIND + endif !snowVolumeCell + + enddo ! iCell + endif ! config_do_restart_snow_density + endif !config_use_effective_snow_density + + if (config_use_snow_grain_radius) then + if (.not. config_do_restart_snow_grain_radius) then + + call MPAS_pool_get_array(tracers, "snowGrainRadius", snowGrainRadius, 1) + + snowGrainRadius(:,:,:) = config_fallen_snow_radius + + endif ! config_do_restart_snow_grain_radius + endif ! config_use_snow_grain_radius + + block => block % next + end do + + end subroutine init_column_snow_tracers + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! init_column_shortwave @@ -436,7 +640,6 @@ subroutine seaice_init_column_shortwave(domain, clock) use ice_colpkg, only: & colpkg_init_orbit, & - colpkg_get_warnings, & colpkg_clear_warnings use seaice_constants, only: & @@ -506,13 +709,12 @@ subroutine seaice_init_column_shortwave(domain, clock) config_shortwave_type logical, pointer :: & - config_do_restart - - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings + config_do_restart, & + config_use_snicar_ad call MPAS_pool_get_config(domain % configs, "config_shortwave_type", config_shortwave_type) call MPAS_pool_get_config(domain % configs, "config_do_restart", config_do_restart) + call MPAS_pool_get_config(domain % configs, "config_use_snicar_ad", config_use_snicar_ad) if (trim(config_shortwave_type) == "dEdd") then @@ -523,8 +725,7 @@ subroutine seaice_init_column_shortwave(domain, clock) call colpkg_init_orbit(& abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) if (abortFlag) then call mpas_log_write("colpkg_init_orbit: "//trim(abortMessage), messageType=MPAS_LOG_CRIT) @@ -834,14 +1035,12 @@ end subroutine seaice_column_finalize ! !----------------------------------------------------------------------- - subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) + subroutine seaice_column_predynamics_time_integration(domain, clock) type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - logical, pointer :: & config_use_column_package, & config_use_column_shortwave, & @@ -883,9 +1082,8 @@ subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column vertical thermodynamics") if (config_use_column_vertical_thermodynamics) & - call column_vertical_thermodynamics(domain, clock, ierr) + call column_vertical_thermodynamics(domain, clock) call mpas_timer_stop("Column vertical thermodynamics") - if (config_use_column_vertical_thermodynamics .and. ierr > 0) return !----------------------------------------------------------------- ! Biogeochemistry @@ -893,9 +1091,8 @@ subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column biogeochemistry") if (config_use_column_biogeochemistry) & - call column_biogeochemistry(domain, ierr) + call column_biogeochemistry(domain) call mpas_timer_stop("Column biogeochemistry") - if (config_use_column_biogeochemistry .and. ierr > 0) return !----------------------------------------------------------------- ! ITD thermodynamics @@ -903,9 +1100,8 @@ subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column ITD thermodynamics") if (config_use_column_itd_thermodynamics) & - call column_itd_thermodynamics(domain, clock, ierr) + call column_itd_thermodynamics(domain, clock) call mpas_timer_stop("Column ITD thermodynamics") - if (config_use_column_itd_thermodynamics .and. ierr > 0) return !----------------------------------------------------------------- ! Update the aggregated state variables @@ -940,14 +1136,12 @@ end subroutine seaice_column_predynamics_time_integration ! !----------------------------------------------------------------------- - subroutine seaice_column_dynamics_time_integration(domain, clock, ierr) + subroutine seaice_column_dynamics_time_integration(domain, clock) type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - logical, pointer :: & config_use_column_package, & config_use_column_ridging, & @@ -984,9 +1178,8 @@ subroutine seaice_column_dynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column ridging") if (config_use_column_ridging) & - call column_ridging(domain, ierr) + call column_ridging(domain) call mpas_timer_stop("Column ridging") - if (config_use_column_ridging .and. ierr > 0) return !----------------------------------------------------------------- ! Update the aggregated state variables @@ -1020,13 +1213,27 @@ subroutine seaice_column_postdynamics_time_integration(domain, clock) logical, pointer :: & config_use_column_package, & - config_use_column_shortwave + config_use_column_shortwave, & + config_use_column_snow_tracers + + type(block_type), pointer :: & + block call MPAS_pool_get_config(domain % configs, "config_use_column_package", config_use_column_package) if (config_use_column_package) then call MPAS_pool_get_config(domain % configs, "config_use_column_shortwave", config_use_column_shortwave) + call MPAS_pool_get_config(domain % configs, "config_use_column_snow_tracers", config_use_column_snow_tracers) + + !----------------------------------------------------------------- + ! snow + !----------------------------------------------------------------- + + call mpas_timer_start("Column snow") + if (config_use_column_snow_tracers) & + call column_snow(domain) + call mpas_timer_stop("Column snow") !----------------------------------------------------------------- ! Shortwave radiation @@ -1061,22 +1268,22 @@ end subroutine seaice_column_postdynamics_time_integration ! !----------------------------------------------------------------------- - subroutine column_vertical_thermodynamics(domain, clock, ierr) + subroutine column_vertical_thermodynamics(domain, clock) use ice_colpkg, only: & colpkg_step_therm1, & - colpkg_clear_warnings, & - colpkg_get_warnings + colpkg_clear_warnings use seaice_constants, only: & seaicePuny + use seaice_mesh, only: & + seaice_interpolate_vertex_to_cell + type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -1096,14 +1303,19 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) shortwave, & ponds, & aerosols, & - diagnostics + diagnostics, & + snow, & + boundary ! configs real(kind=RKIND), pointer :: & config_dt logical, pointer :: & - config_use_aerosols + config_use_aerosols, & + config_use_prescribed_ice, & + config_use_snow_liquid_ponds, & + config_use_high_frequency_coupling ! dimensions integer, pointer :: & @@ -1122,6 +1334,8 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) snowVolumeCell, & uVelocity, & vvelocity, & + uVelocityCell, & + vvelocityCell, & uAirVelocity, & vAirVelocity, & windSpeed, & @@ -1187,7 +1401,10 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) frazilFormation, & meltOnset, & freezeOnset, & - oceanHeatFluxIceBottom + oceanHeatFluxIceBottom, & + openWaterArea, & + snowLossToLeads, & + snowMeltMassCell real(kind=RKIND), dimension(:,:), pointer :: & iceAreaCategoryInitial, & @@ -1213,7 +1430,9 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) sensibleHeatFluxCouple, & surfaceHeatFluxCouple, & surfaceConductiveFluxCouple, & - snowThicknessChangeCategory + snowThicknessChangeCategory, & + snowMeltMassCategory, & + snowRadiusInStandardRadiationSchemeCategory real(kind=RKIND), dimension(:,:,:), pointer :: & iceAreaCategory, & @@ -1235,7 +1454,10 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) snowScatteringAerosol, & snowBodyAerosol, & iceScatteringAerosol, & - iceBodyAerosol + iceBodyAerosol, & + snowIceMass, & + snowLiquidMass, & + snowGrainRadius integer, dimension(:), pointer :: & indexToCellID @@ -1261,9 +1483,6 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) real(kind=RKIND) :: & dayOfYear - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - ! day of year call get_day_of_year(clock, dayOfYear) @@ -1287,9 +1506,14 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) call MPAS_pool_get_subpool(block % structs, "ponds", ponds) call MPAS_pool_get_subpool(block % structs, "aerosols", aerosols) call MPAS_pool_get_subpool(block % structs, "diagnostics", diagnostics) + call MPAS_pool_get_subpool(block % structs, "snow", snow) + call MPAS_pool_get_subpool(block % structs, "boundary", boundary) call MPAS_pool_get_config(block % configs, "config_dt", config_dt) call MPAS_pool_get_config(block % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(block % configs, "config_use_prescribed_ice", config_use_prescribed_ice) + call MPAS_pool_get_config(block % configs, "config_use_snow_liquid_ponds", config_use_snow_liquid_ponds) + call MPAS_pool_get_config(block % configs, "config_use_high_frequency_coupling", config_use_high_frequency_coupling) call MPAS_pool_get_dimension(mesh, "nCellsSolve", nCellsSolve) call MPAS_pool_get_dimension(mesh, "nCategories", nCategories) @@ -1304,6 +1528,7 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) call MPAS_pool_get_array(icestate, "iceAreaCategoryInitial", iceAreaCategoryInitial) call MPAS_pool_get_array(icestate, "iceVolumeCategoryInitial", iceVolumeCategoryInitial) call MPAS_pool_get_array(icestate, "snowVolumeCategoryInitial", snowVolumeCategoryInitial) + call MPAS_pool_get_array(icestate, "openWaterArea", openWaterArea) call MPAS_pool_get_array(tracers_aggregate, "iceAreaCell", iceAreaCell) call MPAS_pool_get_array(tracers_aggregate, "iceVolumeCell", iceVolumeCell) @@ -1327,9 +1552,14 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) call MPAS_pool_get_array(tracers, "snowBodyAerosol", snowBodyAerosol, 1) call MPAS_pool_get_array(tracers, "iceScatteringAerosol", iceScatteringAerosol, 1) call MPAS_pool_get_array(tracers, "iceBodyAerosol", iceBodyAerosol, 1) + call MPAS_pool_get_array(tracers, "snowIceMass", snowIceMass, 1) + call MPAS_pool_get_array(tracers, "snowLiquidMass", snowLiquidMass, 1) + call MPAS_pool_get_array(tracers, "snowGrainRadius", snowGrainRadius, 1) call MPAS_pool_get_array(velocity_solver, "uVelocity", uVelocity) call MPAS_pool_get_array(velocity_solver, "vVelocity", vVelocity) + call MPAS_pool_get_array(velocity_solver, "uVelocityCell", uVelocityCell) + call MPAS_pool_get_array(velocity_solver, "vVelocityCell", vVelocityCell) call MPAS_pool_get_array(velocity_solver, "airStressCellU", airStressCellU) call MPAS_pool_get_array(velocity_solver, "airStressCellV", airStressCellV) call MPAS_pool_get_array(velocity_solver, "oceanStressCellU", oceanStressCellU) @@ -1432,6 +1662,16 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) call MPAS_pool_get_array(diagnostics, "meltOnset", meltOnset) call MPAS_pool_get_array(diagnostics, "freezeOnset", freezeOnset) + call MPAS_pool_get_array(snow, "snowLossToLeads", snowLossToLeads) + call MPAS_pool_get_array(snow, "snowMeltMassCell", snowMeltMassCell) + call MPAS_pool_get_array(snow, "snowMeltMassCategory", snowMeltMassCategory) + + ! high frequency coupling needs to cell center velocity + if (config_use_high_frequency_coupling) then + call seaice_interpolate_vertex_to_cell(mesh, boundary, uVelocityCell, uVelocity) + call seaice_interpolate_vertex_to_cell(mesh, boundary, vVelocityCell, vVelocity) + endif + ! aerosols if (config_use_aerosols) then @@ -1447,7 +1687,13 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) endif - ! loop over cells + ! code abort + abortFlag = .false. + abortMessage = "" + + !$omp parallel do default(shared) private(iCategory,iAerosol,northernHemisphereMask,& + !$omp& abortMessage) firstprivate(specificSnowAerosol,specificIceAerosol) & + !$omp& reduction(.or.:abortFlag) do iCell = 1, nCellsSolve ! initial state values @@ -1489,9 +1735,6 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) northernHemisphereMask = .false. endif - abortFlag = .false. - abortMessage = "" - call colpkg_clear_warnings() call colpkg_step_therm1(& config_dt, & @@ -1499,6 +1742,7 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) nIceLayers, & nSnowLayers, & nAerosols, & + openWaterArea(iCell), & iceAreaCategoryInitial(:,iCell), & iceVolumeCategoryInitial(:,iCell), & snowVolumeCategoryInitial(:,iCell), & @@ -1508,12 +1752,14 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) iceVolumeCategory(1,:,iCell), & snowVolumeCell(iCell), & snowVolumeCategory(1,:,iCell), & - uVelocity(iCell), & - vVelocity(iCell), & + uVelocityCell(iCell), & + vVelocityCell(iCell), & surfaceTemperature(1,:,iCell), & snowEnthalpy(:,:,iCell), & iceEnthalpy(:,:,iCell), & iceSalinity(:,:,iCell), & + snowIceMass(:,:,iCell), & + snowLiquidMass(:,:,iCell), & levelIceArea(1,:,iCell), & levelIceVolume(1,:,iCell), & pondArea(1,:,iCell), & @@ -1521,6 +1767,8 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) pondLidThickness(1,:,iCell), & iceAge(1,:,iCell), & firstYearIceArea(1,:,iCell), & + snowGrainRadius(:,:,iCell), & + config_use_snow_liquid_ponds, & specificSnowAerosol(:,:,:), & specificIceAerosol(:,:,:), & uAirVelocity(iCell), & @@ -1567,6 +1815,7 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) snowfallRate(iCell), & rainfallRate(iCell), & pondFreshWaterFlux(iCell), & + snowLossToLeads(iCell), & surfaceHeatFlux(iCell), & surfaceHeatFluxCategory(:,iCell), & surfaceConductiveFlux(iCell), & @@ -1604,6 +1853,8 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) lateralIceMelt(iCell), & snowMelt(iCell), & snowMeltCategory(:,iCell), & + snowMeltMassCell(iCell), & + snowMeltMassCategory(:,iCell), & congelation(iCell), & congelationCategory(:,iCell), & snowiceFormation(iCell), & @@ -1616,17 +1867,168 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) freezeOnset(iCell), & dayOfYear, & abortFlag, & - abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + abortMessage, & + config_use_prescribed_ice) + call column_write_warnings(abortFlag) - ! code abort + ! cell-specific abort message if (abortFlag) then call mpas_log_write("column_vertical_thermodynamics: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_VERT_THERM - call seaice_critical_error_write_block(domain, block) - return + + call mpas_log_write("config_dt: $r", messageType=MPAS_LOG_ERR, realArgs=(/config_dt/)) + call mpas_log_write("nCategories: $i", messageType=MPAS_LOG_ERR, intArgs=(/nCategories/)) + call mpas_log_write("nIceLayers: $i", messageType=MPAS_LOG_ERR, intArgs=(/nIceLayers/)) + call mpas_log_write("nSnowLayers: $i", messageType=MPAS_LOG_ERR, intArgs=(/nSnowLayers/)) + call mpas_log_write("nAerosols: $i", messageType=MPAS_LOG_ERR, intArgs=(/nAerosols/)) + call mpas_log_write("openWaterArea: $r", messageType=MPAS_LOG_ERR, realArgs=(/openWaterArea(iCell)/)) + call mpas_log_write("iceAreaCategoryInitial: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/iceAreaCategoryInitial(:,iCell)/)) + call mpas_log_write("iceVolumeCategoryInitial: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/iceVolumeCategoryInitial(:,iCell)/)) + call mpas_log_write("snowVolumeCategoryInitial: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/snowVolumeCategoryInitial(:,iCell)/)) + call mpas_log_write("iceAreaCell: $r", messageType=MPAS_LOG_ERR, realArgs=(/iceAreaCell(iCell)/)) + call mpas_log_write("iceAreaCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/iceAreaCategory(1,:,iCell)/)) + call mpas_log_write("iceVolumeCell: $r", messageType=MPAS_LOG_ERR, realArgs=(/iceVolumeCell(iCell)/)) + call mpas_log_write("iceVolumeCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/iceVolumeCategory(1,:,iCell)/)) + call mpas_log_write("snowVolumeCell: $r", messageType=MPAS_LOG_ERR, realArgs=(/snowVolumeCell(iCell)/)) + call mpas_log_write("snowVolumeCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/snowVolumeCategory(1,:,iCell)/)) + call mpas_log_write("uVelocityCell: $r", messageType=MPAS_LOG_ERR, realArgs=(/uVelocityCell(iCell)/)) + call mpas_log_write("vVelocityCell: $r", messageType=MPAS_LOG_ERR, realArgs=(/vVelocityCell(iCell)/)) + call mpas_log_write("surfaceTemperature: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceTemperature(1,:,iCell)/)) + do iCategory = 1, nCategories + call mpas_log_write("snowEnthalpy: $i "//repeat("$r ", nSnowLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/snowEnthalpy(:,iCategory,iCell)/)) + enddo ! iCategory + do iCategory = 1, nCategories + call mpas_log_write("iceEnthalpy: $i "//repeat("$r ", nIceLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/iceEnthalpy(:,iCategory,iCell)/)) + enddo ! iCategory + do iCategory = 1, nCategories + call mpas_log_write("iceSalinity: $i "//repeat("$r ", nIceLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/iceSalinity(:,iCategory,iCell)/)) + enddo ! iCategory + do iCategory = 1, nCategories + call mpas_log_write("snowIceMass: $i "//repeat("$r ", nSnowLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/snowIceMass(:,iCategory,iCell)/)) + enddo ! iCategory + do iCategory = 1, nCategories + call mpas_log_write("snowLiquidMass: $i "//repeat("$r ", nSnowLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/snowLiquidMass(:,iCategory,iCell)/)) + enddo ! iCategory + call mpas_log_write("levelIceArea: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/levelIceArea(1,:,iCell)/)) + call mpas_log_write("levelIceVolume: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/levelIceVolume(1,:,iCell)/)) + call mpas_log_write("pondArea: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/pondArea(1,:,iCell)/)) + call mpas_log_write("pondDepth: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/pondDepth(1,:,iCell)/)) + call mpas_log_write("pondLidThickness: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/pondLidThickness(1,:,iCell)/)) + call mpas_log_write("iceAge: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/iceAge(1,:,iCell)/)) + call mpas_log_write("firstYearIceArea: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/firstYearIceArea(1,:,iCell)/)) + do iCategory = 1, nCategories + call mpas_log_write("snowGrainRadius: $i "//repeat("$r ", nSnowLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/snowGrainRadius(:,iCategory,iCell)/)) + enddo ! iCategory + call mpas_log_write("config_use_snow_liquid_ponds: $l", messageType=MPAS_LOG_ERR, logicArgs=(/config_use_snow_liquid_ponds/)) + if (config_use_aerosols) then + do iCategory = 1, nCategories + call mpas_log_write("specificSnowAerosol $i 1: "//repeat("$r ", nAerosols), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/specificSnowAerosol(:,1,iCategory)/)) + call mpas_log_write("specificSnowAerosol $i 2: "//repeat("$r ", nAerosols), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/specificSnowAerosol(:,2,iCategory)/)) + enddo ! iCategory + do iCategory = 1, nCategories + call mpas_log_write("specificIceAerosol $i 1: "//repeat("$r ", nAerosols), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/specificIceAerosol(:,1,iCategory)/)) + call mpas_log_write("specificIceAerosol $i 2: "//repeat("$r ", nAerosols), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/specificIceAerosol(:,2,iCategory)/)) + enddo ! iCategory + endif + call mpas_log_write("uAirVelocity: $r", messageType=MPAS_LOG_ERR, realArgs=(/uAirVelocity(iCell)/)) + call mpas_log_write("vAirVelocity: $r", messageType=MPAS_LOG_ERR, realArgs=(/vAirVelocity(iCell)/)) + call mpas_log_write("windSpeed: $r", messageType=MPAS_LOG_ERR, realArgs=(/windSpeed(iCell)/)) + call mpas_log_write("airLevelHeight: $r", messageType=MPAS_LOG_ERR, realArgs=(/airLevelHeight(iCell)/)) + call mpas_log_write("airSpecificHumidity: $r", messageType=MPAS_LOG_ERR, realArgs=(/airSpecificHumidity(iCell)/)) + call mpas_log_write("airDensity: $r", messageType=MPAS_LOG_ERR, realArgs=(/airDensity(iCell)/)) + call mpas_log_write("airTemperature: $r", messageType=MPAS_LOG_ERR, realArgs=(/airTemperature(iCell)/)) + call mpas_log_write("atmosReferenceTemperature2m: $r", messageType=MPAS_LOG_ERR, realArgs=(/atmosReferenceTemperature2m(iCell)/)) + call mpas_log_write("atmosReferenceHumidity2m: $r", messageType=MPAS_LOG_ERR, realArgs=(/atmosReferenceHumidity2m(iCell)/)) + call mpas_log_write("atmosReferenceSpeed10m: $r", messageType=MPAS_LOG_ERR, realArgs=(/atmosReferenceSpeed10m(iCell)/)) + call mpas_log_write("airOceanDragCoefficientRatio: $r", messageType=MPAS_LOG_ERR, realArgs=(/airOceanDragCoefficientRatio(iCell)/)) + call mpas_log_write("oceanDragCoefficient: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanDragCoefficient(iCell)/)) + call mpas_log_write("oceanDragCoefficientSkin: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanDragCoefficientSkin(iCell)/)) + call mpas_log_write("oceanDragCoefficientFloe: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanDragCoefficientFloe(iCell)/)) + call mpas_log_write("oceanDragCoefficientKeel: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanDragCoefficientKeel(iCell)/)) + call mpas_log_write("airDragCoefficient: $r", messageType=MPAS_LOG_ERR, realArgs=(/airDragCoefficient(iCell)/)) + call mpas_log_write("airDragCoefficientSkin: $r", messageType=MPAS_LOG_ERR, realArgs=(/airDragCoefficientSkin(iCell)/)) + call mpas_log_write("airDragCoefficientFloe: $r", messageType=MPAS_LOG_ERR, realArgs=(/airDragCoefficientFloe(iCell)/)) + call mpas_log_write("airDragCoefficientPond: $r", messageType=MPAS_LOG_ERR, realArgs=(/airDragCoefficientPond(iCell)/)) + call mpas_log_write("airDragCoefficientRidge: $r", messageType=MPAS_LOG_ERR, realArgs=(/airDragCoefficientRidge(iCell)/)) + call mpas_log_write("dragFreeboard: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragFreeboard(iCell)/)) + call mpas_log_write("dragIceSnowDraft: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragIceSnowDraft(iCell)/)) + call mpas_log_write("dragRidgeHeight: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragRidgeHeight(iCell)/)) + call mpas_log_write("dragRidgeSeparation: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragRidgeSeparation(iCell)/)) + call mpas_log_write("dragKeelDepth: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragKeelDepth(iCell)/)) + call mpas_log_write("dragKeelSeparation: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragKeelSeparation(iCell)/)) + call mpas_log_write("dragFloeLength: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragFloeLength(iCell)/)) + call mpas_log_write("dragFloeSeparation: $r", messageType=MPAS_LOG_ERR, realArgs=(/dragFloeSeparation(iCell)/)) + call mpas_log_write("airStressForcingU: $r", messageType=MPAS_LOG_ERR, realArgs=(/airStressForcingU(iCell)/)) + call mpas_log_write("airStressForcingV: $r", messageType=MPAS_LOG_ERR, realArgs=(/airStressForcingV(iCell)/)) + call mpas_log_write("airStressCellU: $r", messageType=MPAS_LOG_ERR, realArgs=(/airStressCellU(iCell)/)) + call mpas_log_write("airStressCellV: $r", messageType=MPAS_LOG_ERR, realArgs=(/airStressCellV(iCell)/)) + call mpas_log_write("airPotentialTemperature: $r", messageType=MPAS_LOG_ERR, realArgs=(/airPotentialTemperature(iCell)/)) + call mpas_log_write("seaSurfaceTemperature: $r", messageType=MPAS_LOG_ERR, realArgs=(/seaSurfaceTemperature(iCell)/)) + call mpas_log_write("seaSurfaceSalinity: $r", messageType=MPAS_LOG_ERR, realArgs=(/seaSurfaceSalinity(iCell)/)) + call mpas_log_write("seaFreezingTemperature: $r", messageType=MPAS_LOG_ERR, realArgs=(/seaFreezingTemperature(iCell)/)) + call mpas_log_write("oceanStressCellU: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanStressCellU(iCell)/)) + call mpas_log_write("oceanStressCellV: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanStressCellV(iCell)/)) + call mpas_log_write("oceanHeatFluxIceBottom: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanHeatFluxIceBottom(iCell)/)) + call mpas_log_write("freezingMeltingPotential: $r", messageType=MPAS_LOG_ERR, realArgs=(/freezingMeltingPotential(iCell)/)) + call mpas_log_write("lateralIceMeltFraction: $r", messageType=MPAS_LOG_ERR, realArgs=(/lateralIceMeltFraction(iCell)/)) + call mpas_log_write("snowfallRate: $r", messageType=MPAS_LOG_ERR, realArgs=(/snowfallRate(iCell)/)) + call mpas_log_write("rainfallRate: $r", messageType=MPAS_LOG_ERR, realArgs=(/rainfallRate(iCell)/)) + call mpas_log_write("pondFreshWaterFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/pondFreshWaterFlux(iCell)/)) + call mpas_log_write("snowLossToLeads: $r", messageType=MPAS_LOG_ERR, realArgs=(/snowLossToLeads(iCell)/)) + call mpas_log_write("surfaceHeatFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/surfaceHeatFlux(iCell)/)) + call mpas_log_write("surfaceHeatFluxCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceHeatFluxCategory(:,iCell)/)) + call mpas_log_write("surfaceConductiveFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/surfaceConductiveFlux(iCell)/)) + call mpas_log_write("surfaceConductiveFluxCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceConductiveFluxCategory(:,iCell)/)) + call mpas_log_write("surfaceShortwaveFlux: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceShortwaveFlux(:,iCell)/)) + call mpas_log_write("interiorShortwaveFlux: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/interiorShortwaveFlux(:,iCell)/)) + call mpas_log_write("penetratingShortwaveFlux: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/penetratingShortwaveFlux(:,iCell)/)) + call mpas_log_write("absorbedShortwaveFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/absorbedShortwaveFlux(iCell)/)) + call mpas_log_write("longwaveUp: $r", messageType=MPAS_LOG_ERR, realArgs=(/longwaveUp(iCell)/)) + do iCategory = 1, nCategories + call mpas_log_write("absorbedShortwaveSnowLayer: $i "//repeat("$r ", nSnowLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/absorbedShortwaveSnowLayer(:,iCategory,iCell)/)) + enddo ! iCategory + do iCategory = 1, nCategories + call mpas_log_write("absorbedShortwaveIceLayer: $i "//repeat("$r ", nIceLayers), messageType=MPAS_LOG_ERR, intArgs=(/iCategory/), realArgs=(/absorbedShortwaveIceLayer(:,iCategory,iCell)/)) + enddo ! iCategory + call mpas_log_write("longwaveDown: $r", messageType=MPAS_LOG_ERR, realArgs=(/longwaveDown(iCell)/)) + call mpas_log_write("solarZenithAngleCosine: $r", messageType=MPAS_LOG_ERR, realArgs=(/solarZenithAngleCosine(iCell)/)) + call mpas_log_write("sensibleHeatFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/sensibleHeatFlux(iCell)/)) + call mpas_log_write("sensibleHeatFluxCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/sensibleHeatFluxCategory(:,iCell)/)) + call mpas_log_write("latentHeatFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/latentHeatFlux(iCell)/)) + call mpas_log_write("latentHeatFluxCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/latentHeatFluxCategory(:,iCell)/)) + call mpas_log_write("evaporativeWaterFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/evaporativeWaterFlux(iCell)/)) + call mpas_log_write("oceanFreshWaterFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanFreshWaterFlux(iCell)/)) + call mpas_log_write("oceanSaltFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanSaltFlux(iCell)/)) + call mpas_log_write("oceanHeatFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanHeatFlux(iCell)/)) + call mpas_log_write("oceanShortwaveFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanShortwaveFlux(iCell)/)) + call mpas_log_write("latentHeatFluxCouple: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/latentHeatFluxCouple(:,iCell)/)) + call mpas_log_write("sensibleHeatFluxCouple: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/sensibleHeatFluxCouple(:,iCell)/)) + call mpas_log_write("surfaceHeatFluxCouple: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceHeatFluxCouple(:,iCell)/)) + call mpas_log_write("surfaceConductiveFluxCouple: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceConductiveFluxCouple(:,iCell)/)) + call mpas_log_write("atmosAerosolFlux: "//repeat("$r ", nAerosols), messageType=MPAS_LOG_ERR, realArgs=(/atmosAerosolFlux(:,iCell)/)) + call mpas_log_write("oceanAerosolFlux: "//repeat("$r ", nAerosols), messageType=MPAS_LOG_ERR, realArgs=(/oceanAerosolFlux(:,iCell)/)) + call mpas_log_write("pondSnowDepthDifference: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/pondSnowDepthDifference(:,iCell)/)) + call mpas_log_write("pondLidMeltFluxFraction: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/pondLidMeltFluxFraction(:,iCell)/)) + call mpas_log_write("surfaceIceMelt: $r", messageType=MPAS_LOG_ERR, realArgs=(/surfaceIceMelt(iCell)/)) + call mpas_log_write("surfaceIceMeltCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/surfaceIceMeltCategory(:,iCell)/)) + call mpas_log_write("basalIceMelt: $r", messageType=MPAS_LOG_ERR, realArgs=(/basalIceMelt(iCell)/)) + call mpas_log_write("basalIceMeltCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/basalIceMeltCategory(:,iCell)/)) + call mpas_log_write("lateralIceMelt: $r", messageType=MPAS_LOG_ERR, realArgs=(/lateralIceMelt(iCell)/)) + call mpas_log_write("snowMelt: $r", messageType=MPAS_LOG_ERR, realArgs=(/snowMelt(iCell)/)) + call mpas_log_write("snowMeltCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/snowMeltCategory(:,iCell)/)) + call mpas_log_write("snowMeltMassCell: $r", messageType=MPAS_LOG_ERR, realArgs=(/snowMeltMassCell(iCell)/)) + call mpas_log_write("snowMeltMassCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/snowMeltMassCategory(:,iCell)/)) + call mpas_log_write("congelation: $r", messageType=MPAS_LOG_ERR, realArgs=(/congelation(iCell)/)) + call mpas_log_write("congelationCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/congelationCategory(:,iCell)/)) + call mpas_log_write("snowiceFormation: $r", messageType=MPAS_LOG_ERR, realArgs=(/snowiceFormation(iCell)/)) + call mpas_log_write("snowiceFormationCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/snowiceFormationCategory(:,iCell)/)) + call mpas_log_write("snowThicknessChangeCategory: "//repeat("$r ", nCategories), messageType=MPAS_LOG_ERR, realArgs=(/snowThicknessChangeCategory(:,iCell)/)) + call mpas_log_write("frazilFormation: $r", messageType=MPAS_LOG_ERR, realArgs=(/frazilFormation(iCell)/)) + call mpas_log_write("northernHemisphereMask: $l", messageType=MPAS_LOG_ERR, logicArgs=(/northernHemisphereMask/)) + call mpas_log_write("meltOnset: $r", messageType=MPAS_LOG_ERR, realArgs=(/meltOnset(iCell)/)) + call mpas_log_write("freezeOnset: $r", messageType=MPAS_LOG_ERR, realArgs=(/freezeOnset(iCell)/)) + call mpas_log_write("dayOfYear: $r", messageType=MPAS_LOG_ERR, realArgs=(/dayOfYear/)) + call mpas_log_write("config_use_prescribed_ice: $l", messageType=MPAS_LOG_ERR, logicArgs=(/config_use_prescribed_ice/)) endif ! aerosol @@ -1656,6 +2058,10 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) enddo ! iCell + ! error-checking + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + ! aerosols deallocate(specificSnowAerosol) deallocate(specificIceAerosol) @@ -1677,19 +2083,16 @@ end subroutine column_vertical_thermodynamics ! !----------------------------------------------------------------------- - subroutine column_itd_thermodynamics(domain, clock, ierr) + subroutine column_itd_thermodynamics(domain, clock) use ice_colpkg, only: & colpkg_step_therm2, & - colpkg_get_warnings, & colpkg_clear_warnings type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -1722,7 +2125,8 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) nIceLayers, & nSnowLayers, & nAerosols, & - nBioLayers + nBioLayers, & + nBioLayersP1 ! variables real(kind=RKIND), dimension(:), pointer :: & @@ -1758,7 +2162,8 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) real(kind=RKIND), dimension(:,:,:), pointer :: & iceAreaCategory, & iceVolumeCategory, & - snowVolumeCategory + snowVolumeCategory, & + brineFraction integer, dimension(:,:), pointer :: & newlyFormedIce @@ -1771,18 +2176,33 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) iCell, & iCategory, & iBioTracers, & - iBioData + iBioData, & + iBioLayers - logical, dimension(:), allocatable :: & - newlyFormedIceLogical + ! test carbon conservation + real(kind=RKIND), dimension(:), allocatable :: & + totalCarbonCatFinal, & + totalCarbonCatInitial, & + oceanBioFluxesTemp, & + verticalGridSpace + + real(kind=RKIND) :: & + oceanCarbonFlux, & + totalCarbonFinal, & + totalCarbonInitial, & + carbonError real(kind=RKIND), dimension(:), allocatable :: & oceanBioConcentrationsUsed + logical, dimension(:), allocatable :: & + newlyFormedIceLogical + logical :: & abortFlag, & setGetPhysicsTracers, & - setGetBGCTracers + setGetBGCTracers, & + checkCarbon character(len=strKIND) :: & abortMessage, & @@ -1791,12 +2211,11 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) real(kind=RKIND) :: & dayOfYear - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - ! day of year call get_day_of_year(clock, dayOfYear) + checkCarbon = .false. + block => domain % blocklist do while (associated(block)) @@ -1825,6 +2244,7 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) call MPAS_pool_get_dimension(mesh, "nAerosols", nAerosols) call MPAS_pool_get_dimension(block % dimensions, "nBioLayers", nBioLayers) + call MPAS_pool_get_dimension(block % dimensions, "nBioLayersP1", nBioLayersP1) call MPAS_pool_get_array(mesh, "indexToCellID", indexToCellID) call MPAS_pool_get_array(icestate, "iceAreaCategoryInitial", iceAreaCategoryInitial) @@ -1836,6 +2256,7 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) call MPAS_pool_get_array(tracers, "iceAreaCategory", iceAreaCategory, 1) call MPAS_pool_get_array(tracers, "iceVolumeCategory", iceVolumeCategory, 1) call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) + call MPAS_pool_get_array(tracers, "brineFraction", brineFraction, 1) call MPAS_pool_get_array(atmos_coupling, "rainfallRate", rainfallRate) @@ -1872,10 +2293,28 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) ! newly formed ice allocate(newlyFormedIceLogical(nCategories)) allocate(oceanBioConcentrationsUsed(ciceTracerObject % nBioTracers)) + allocate(oceanBioFluxesTemp(ciceTracerObject % nBioTracers)) + allocate(verticalGridSpace(nBioLayersP1)) + if (checkCarbon) then + allocate(totalCarbonCatFinal(nCategories)) + allocate(totalCarbonCatInitial(nCategories)) + endif + + verticalGridSpace(:) = 1.0_RKIND/real(nBioLayers,kind=RKIND) + verticalGridSpace(1) = verticalGridSpace(1)/2.0_RKIND + verticalGridSpace(nBioLayersP1) = verticalGridSpace(1) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + ! code abort + abortFlag = .false. + abortMessage = "" + + !$omp parallel do default(shared) private(iCategory,iBioTracers,iBioData,& + !$omp& totalCarbonInitial,abortMessage,oceanBioFluxesTemp,totalCarbonFinal,& + !$omp& carbonError) firstprivate(newlyFormedIceLogical,oceanBioConcentrationsUsed) & + !$omp& reduction(.or.:abortFlag) do iCell = 1, nCellsSolve ! newly formed ice @@ -1890,33 +2329,41 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) enddo ! iBioTracers ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject,& + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) + + if (checkCarbon) then + totalCarbonInitial = 0.0_RKIND + call seaice_total_carbon_content_category(block,totalCarbonCatInitial,iceAreaCategory(1,:,:),iceVolumeCategory(1,:,:),iCell) + do iCategory = 1,nCategories + totalCarbonInitial = totalCarbonInitial + totalCarbonCatInitial(iCategory)*iceAreaCategory(1,iCategory,iCell) + enddo + endif - abortFlag = .false. - abortMessage = "" + oceanBioFluxesTemp(:) = 0.0_RKIND call colpkg_clear_warnings() call colpkg_step_therm2(& config_dt, & nCategories, & nAerosols, & - ciceTracerObject % nBioTracersLayer, & + ciceTracerObject % nBioTracers, & !nbtrcr, intent(in) nIcelayers, & nSnowLayers, & - categoryThicknessLimits(:), & + categoryThicknessLimits(:), & !hin_max, intent(inout), dimension(0:ncat) nBioLayers, & iceAreaCategory(1,:,iCell), & iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & iceAreaCategoryInitial(:,iCell), & iceVolumeCategoryInitial(:,iCell), & - ciceTracerObject % tracerArrayCategory, & !trcrn + tracerArrayCategory, & !trcrn, intent(inout) openWaterArea(iCell), & iceAreaCell(iCell), & - ciceTracerObject % parentIndex, & !trcr_depend - ciceTracerObject % firstAncestorMask, & !trcr_base - ciceTracerObject % ancestorNumber, & !n_trcr_strata - ciceTracerObject % ancestorIndices, & !nt_strata + ciceTracerObject % parentIndex, & !trcr_depend, intent(in) + ciceTracerObject % firstAncestorMask, & !trcr_base, intent(in) + ciceTracerObject % ancestorNumber, & !n_trcr_strata,intent(in) + ciceTracerObject % ancestorIndices, & !nt_strata, intent(in) seaFreezingTemperature(iCell), & seaSurfaceSalinity(iCell), & initialSalinityProfile(:,iCell), & @@ -1929,22 +2376,26 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) oceanFreshWaterFlux(iCell), & oceanSaltFlux(iCell), & oceanHeatFlux(iCell), & - config_update_ocean_fluxes, & - biologyGrid(:), & - verticalGrid(:), & - interfaceBiologyGrid(:), & + config_update_ocean_fluxes, & !update_ocn_f, intent(in) + biologyGrid(:), & !bgrid, intent(in) + verticalGrid(:), & !cgrid, intent(in) + interfaceBiologyGrid(:), & !igrid, intent(in) oceanAerosolFlux(:,iCell), & - newlyFormedIceLogical(:), & + newlyFormedIceLogical(:), & !first_ice, intent(inout) zSalinityFlux(iCell), & - oceanBioFluxes(:,iCell), & - oceanBioConcentrationsUsed(:), & + oceanBioFluxesTemp(:), & + oceanBioConcentrationsUsed(:), & !ocean_bio, intent(in) abortFlag, & abortMessage, & frazilGrowthDiagnostic(iCell), & freezeOnset(iCell), & dayOfYear) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + + do iBioTracers = 1, ciceTracerObject % nBioTracers + oceanBioFluxes(iBioTracers,iCell) = oceanBioFluxes(iBioTracers,iCell) + oceanBioFluxesTemp(iBioTracers) + enddo + + call column_write_warnings(abortFlag) ! update do iCategory = 1, nCategories @@ -1953,22 +2404,56 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) enddo ! iCategory ! get category tracer array - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) + + if (checkCarbon) then + totalCarbonFinal = 0.0_RKIND + call seaice_total_carbon_content_category(block,totalCarbonCatFinal,iceAreaCategory(1,:,:),iceVolumeCategory(1,:,:),iCell) + call seaice_ocean_carbon_flux_cell(block,oceanCarbonFlux,oceanBioFluxesTemp,iCell) + do iCategory = 1,nCategories + totalCarbonFinal = totalCarbonFinal + totalCarbonCatFinal(iCategory)*iceAreaCategory(1,iCategory,iCell) + enddo + carbonError = totalCarbonInitial - oceanCarbonFlux*config_dt - totalCarbonFinal + + if (abs(carbonError) > 1.0e-14_RKIND*MAXVAL((/totalCarbonInitial,totalCarbonFinal/))) then + call mpas_log_write("column_step_therm2, carbon conservation error", messageType=MPAS_LOG_ERR) + call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write("carbonError: $r", messageType=MPAS_LOG_ERR, realArgs=(/carbonError/)) + call mpas_log_write("totalCarbonInitial: $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonInitial/)) + call mpas_log_write("totalCarbonFinal: $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonFinal/)) + call mpas_log_write("oceanCarbonFlux: $r", messageType=MPAS_LOG_ERR, realArgs=(/oceanCarbonFlux/)) + + do iCategory = 1, nCategories + call mpas_log_write("iCategory: $i", messageType=MPAS_LOG_ERR, intArgs=(/iCategory/)) + call mpas_log_write("totalCarbonCatFinal(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonCatFinal(iCategory)/)) + call mpas_log_write("totalCarbonCatInitial(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonCatFinal(iCategory)/)) + enddo + endif + endif - ! code abort + ! cell-specific abort message if (abortFlag) then call mpas_log_write("column_itd_thermodynamics: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_ITD_THERM - call seaice_critical_error_write_block(domain, block) - return endif enddo ! iCell + ! error checking + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + + if (checkCarbon) then + deallocate(totalCarbonCatFinal) + deallocate(totalCarbonCatInitial) + endif + ! newly formed ice deallocate(newlyFormedIceLogical) deallocate(oceanBioConcentrationsUsed) + deallocate(oceanBioFluxesTemp) + deallocate(verticalGridSpace) block => block % next end do @@ -2105,84 +2590,332 @@ end subroutine column_prep_radiation !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! column_radiation +! column_snow ! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 21th January 2015 +!> \brief Enable snow grain aging, effective snow density, wind compaction and redistribution +!> +!> \author Nicole Jeffery, LANL +!> \date 3rd April 2017 !> \details !> +!> Snow physics improvements include: +!> 1) Snow redistribution by wind (multiple options available). +!> Includes parametrizations for snow compaction, redistribution among categories/level ice +!> and loss to leads. +!> 2) Tracking of snow liquid and ice content. Liquid is stored in snow before passing to ponds. +!> Effective snow density is also tracked. +!> 3) Snow grain radius aging based on wet (liquid content) and dry (temperature gradient) metamorphism. +!> 4) Effective snow density (based on snow liquid/ice content and compaction) ! !----------------------------------------------------------------------- - subroutine column_radiation(domain, clock, lInitialization) + subroutine column_snow(domain) use ice_colpkg, only: & - colpkg_step_radiation, & - colpkg_get_warnings, & - colpkg_clear_warnings + colpkg_step_snow, & + colpkg_clear_warnings, & + colpkg_get_warnings use seaice_constants, only: & - pii + seaicePuny type(domain_type), intent(inout) :: domain - type(MPAS_clock_type), intent(in) :: clock - - logical, intent(in) :: & - lInitialization - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & mesh, & tracers, & - atmos_coupling, & - shortwave, & - ponds, & - aerosols, & - biogeochemistry - - ! configs - real(kind=RKIND), pointer :: & - config_dt + tracers_aggregate, & + atmos_forcing, & + snow, & + ocean_fluxes, & + atmos_coupling logical, pointer :: & - config_use_shortwave_bioabsorption, & - config_use_brine, & - config_use_modal_aerosols, & - config_use_zaerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius, & config_use_column_biogeochemistry - ! dimensions - integer, pointer :: & - nCellsSolve, & - nCategories, & - nIceLayers, & - nSnowLayers, & - nAerosols, & - nAlgae, & - nBioLayers, & - nzAerosols, & - maxAerosolType + real(kind=RKIND), dimension(:,:,:), pointer :: & + snowIceMass, & + snowLiquidMass, & + snowDensity, & + snowVolumeCategory, & + iceAreaCategory, & + iceVolumeCategory, & + levelIceArea, & + levelIceVolume, & + iceEnthalpy, & + snowEnthalpy, & + iceSalinity, & + surfaceTemperature, & + snowGrainRadius, & + snowEmpiricalGrowthParameterTau, & + snowEmpiricalGrowthParameterKappa, & + snowPropertyRate + + real(kind=RKIND), dimension(:,:), pointer :: & + snowMeltMassCategory - ! variables real(kind=RKIND), dimension(:), pointer :: & - latCell, & - lonCell, & - shortwaveVisibleDirectDown, & - shortwaveVisibleDiffuseDown, & - shortwaveIRDirectDown, & - shortwaveIRDiffuseDown, & - solarZenithAngleCosine, & + windSpeed, & + oceanFreshWaterFlux, & + oceanHeatFlux, & + snowLossToLeads, & snowfallRate, & - verticalShortwaveGrid, & - verticalGrid + iceAreaCell, & + iceVolumeCell, & + snowVolumeCell, & + snowDensityViaContent, & + snowDensityViaCompaction, & + snowMeltMassCell - real(kind=RKIND), dimension(:,:), pointer :: & - surfaceShortwaveFlux, & - interiorShortwaveFlux, & - penetratingShortwaveFlux, & + real(kind=RKIND), pointer :: & + config_dt, & + config_new_snow_density, & + config_max_snow_density, & + config_minimum_wind_compaction, & + config_wind_compaction_factor + + integer, pointer :: & + nCellsSolve, & + nSnowLayers, & + nIceLayers, & + nCategories, & + nGrainAgingTemperature, & + nGrainAgingTempGradient, & + nGrainAgingSnowDensity + + integer :: & + iCell, & + iSnowLayer, & + iIceLayer, & + iCategory + + logical :: & + abortFlag, & + setGetPhysicsTracers, & + setGetBGCTracers + + real(kind=RKIND), dimension(:,:), allocatable :: & + effectiveSnowDensityCategory + + character(len=strKIND) :: & + abortMessage, & + abortLocation + + character(len=strKINDWarnings), dimension(:), allocatable :: & + warnings + + block => domain % blocklist + do while (associated(block)) + + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + call MPAS_pool_get_subpool(block % structs, "tracers", tracers) + call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracers_aggregate) + call MPAS_pool_get_subpool(block % structs, "snow", snow) + call MPAS_pool_get_subpool(block % structs, "atmos_forcing", atmos_forcing) + call MPAS_pool_get_subpool(block % structs, "ocean_fluxes", ocean_fluxes) + call MPAS_pool_get_subpool(block % structs, "atmos_coupling", atmos_coupling) + + call MPAS_pool_get_config(block % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(block % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) + call MPAS_pool_get_config(block % configs, "config_dt", config_dt) + call MPAS_pool_get_config(block % configs, "config_new_snow_density", config_new_snow_density) + call MPAS_pool_get_config(block % configs, "config_max_snow_density", config_max_snow_density) + call MPAS_pool_get_config(block % configs, "config_minimum_wind_compaction", config_minimum_wind_compaction) + call MPAS_pool_get_config(block % configs, "config_wind_compaction_factor", config_wind_compaction_factor) + call MPAS_pool_get_config(block % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + + call MPAS_pool_get_dimension(block % dimensions, "nCellsSolve", nCellsSolve) + call MPAS_pool_get_dimension(block % dimensions, "nCategories", nCategories) + call MPAS_pool_get_dimension(block % dimensions, "nSnowLayers", nSnowLayers) + call MPAS_pool_get_dimension(block % dimensions, "nIceLayers", nIceLayers) + call MPAS_pool_get_dimension(block % dimensions, "nGrainAgingTemperature", nGrainAgingTemperature) + call MPAS_pool_get_dimension(block % dimensions, "nGrainAgingTempGradient", nGrainAgingTempGradient) + call MPAS_pool_get_dimension(block % dimensions, "nGrainAgingSnowDensity", nGrainAgingSnowDensity) + + call MPAS_pool_get_array(snow, "snowDensityViaContent", snowDensityViaContent) + call MPAS_pool_get_array(snow, "snowDensityViaCompaction", snowDensityViaCompaction) + call MPAS_pool_get_array(snow, "snowMeltMassCategory", snowMeltMassCategory) + call MPAS_pool_get_array(snow, "snowMeltMassCell", snowMeltMassCell) + call MPAS_pool_get_array(snow, "snowLossToLeads", snowLossToLeads) + call MPAS_pool_get_array(snow, "snowEmpiricalGrowthParameterTau", snowEmpiricalGrowthParameterTau) + call MPAS_pool_get_array(snow, "snowEmpiricalGrowthParameterKappa", snowEmpiricalGrowthParameterKappa) + call MPAS_pool_get_array(snow, "snowPropertyRate", snowPropertyRate) + + call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) + call MPAS_pool_get_array(tracers, "iceVolumeCategory", iceVolumeCategory, 1) + call MPAS_pool_get_array(tracers, "iceAreaCategory", iceAreaCategory, 1) + call MPAS_pool_get_array(tracers, "snowIceMass", snowIceMass, 1) + call MPAS_pool_get_array(tracers, "snowLiquidMass", snowLiquidMass, 1) + call MPAS_pool_get_array(tracers, "snowDensity", snowDensity, 1) + call MPAS_pool_get_array(tracers, "snowGrainRadius", snowGrainRadius, 1) + call MPAS_pool_get_array(tracers, "levelIceArea", levelIceArea, 1) + call MPAS_pool_get_array(tracers, "levelIceVolume", levelIceVolume, 1) + call MPAS_pool_get_array(tracers, "iceEnthalpy", iceEnthalpy, 1) + call MPAS_pool_get_array(tracers, "snowEnthalpy", snowEnthalpy, 1) + call MPAS_pool_get_array(tracers, "iceSalinity", iceSalinity, 1) + call MPAS_pool_get_array(tracers, "surfaceTemperature", surfaceTemperature, 1) + + call MPAS_pool_get_array(tracers_aggregate, "iceAreaCell", iceAreaCell) + call MPAS_pool_get_array(tracers_aggregate, "snowVolumeCell", snowVolumeCell) + + call MPAS_pool_get_array(atmos_coupling, "snowfallRate", snowfallRate) + + call MPAS_pool_get_array(atmos_forcing, "windSpeed", windSpeed) + + call MPAS_pool_get_array(ocean_fluxes, "oceanFreshWaterFlux", oceanFreshWaterFlux) + call MPAS_pool_get_array(ocean_fluxes, "oceanHeatFlux", oceanHeatFlux) + + setGetPhysicsTracers = .true. + setGetBGCTracers = config_use_column_biogeochemistry + + allocate(effectiveSnowDensityCategory(1:nSnowLayers,1:nCategories)) + + do iCell = 1, nCellsSolve + + effectiveSnowDensityCategory(:,:) = 0.0_RKIND + + abortFlag = .false. + abortMessage = "" + + call colpkg_clear_warnings() + call colpkg_step_snow (& + config_dt, & + windSpeed(iCell), & + nIceLayers, & + nSnowLayers, & + nCategories, & + iceAreaCell(iCell), & + iceAreaCategory(1,:,iCell), & + iceVolumeCategory(1,:,iCell), & + snowVolumeCategory(1,:,iCell), & + levelIceArea(1,:,iCell), & + levelIceVolume(1,:,iCell), & + snowIceMass(:,:,iCell), & + snowLiquidMass(:,:,iCell), & + effectiveSnowDensityCategory(:,:), & + snowDensityViaContent(iCell), & + snowDensity(:,:,iCell), & + snowDensityViaCompaction(iCell), & + snowGrainRadius(:,:,iCell), & + iceEnthalpy(1,:,iCell), & + iceSalinity(1,:,iCell), & + surfaceTemperature(1,:,iCell), & + snowEnthalpy(:,:,iCell), & + oceanFreshWaterFlux(iCell), & + oceanHeatFlux(iCell), & + snowLossToLeads(iCell), & + snowfallRate(iCell), & + config_new_snow_density, & + config_max_snow_density, & + config_minimum_wind_compaction, & + config_wind_compaction_factor, & + snowEmpiricalGrowthParameterTau(:,:,:), & + snowEmpiricalGrowthParameterKappa(:,:,:), & + snowPropertyRate(:,:,:), & + nGrainAgingTemperature, & + nGrainAgingTempGradient, & + nGrainAgingSnowDensity, & + abortFlag, & + abortMessage) + + call column_write_warnings(abortFlag) + + enddo !iCell + + deallocate(effectiveSnowDensityCategory) + + block => block % next + end do + + end subroutine column_snow + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! column_radiation +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 21th January 2015 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine column_radiation(domain, clock, lInitialization) + + use ice_colpkg, only: & + colpkg_step_radiation, & + colpkg_clear_warnings + + use seaice_constants, only: & + pii + + type(domain_type), intent(inout) :: domain + + type(MPAS_clock_type), intent(in) :: clock + + logical, intent(in) :: & + lInitialization + + type(block_type), pointer :: block + + type(MPAS_pool_type), pointer :: & + mesh, & + tracers, & + atmos_coupling, & + shortwave, & + ponds, & + aerosols, & + biogeochemistry, & + snicar, & + snow + + ! configs + real(kind=RKIND), pointer :: & + config_dt + + logical, pointer :: & + config_use_shortwave_bioabsorption, & + config_use_brine, & + config_use_modal_aerosols, & + config_use_column_biogeochemistry + + character(len=strKIND), pointer :: & + config_snow_redistribution_scheme + + ! dimensions + integer, pointer :: & + nCellsSolve, & + nCategories, & + nIceLayers, & + nSnowLayers, & + nAerosols, & + nAlgae, & + nBioLayers, & + nzAerosols, & + maxAerosolType + + ! variables + real(kind=RKIND), dimension(:), pointer :: & + latCell, & + lonCell, & + shortwaveVisibleDirectDown, & + shortwaveVisibleDiffuseDown, & + shortwaveIRDirectDown, & + shortwaveIRDiffuseDown, & + solarZenithAngleCosine, & + snowfallRate, & + verticalShortwaveGrid, & + verticalGrid + + real(kind=RKIND), dimension(:,:), pointer :: & + surfaceShortwaveFlux, & + interiorShortwaveFlux, & + penetratingShortwaveFlux, & bareIceAlbedoCategory, & snowAlbedoCategory, & pondAlbedoCategory, & @@ -2199,7 +2932,20 @@ subroutine column_radiation(domain, clock, lInitialization) albedoVisibleDiffuseCategory, & albedoIRDirectCategory, & albedoIRDiffuseCategory, & - snowFractionCategory + snowFractionCategory, & + iceAsymmetryParameterDirect, & + iceAsymmetryParameterDiffuse, & + iceSingleScatterAlbedoDirect, & + iceSingleScatterAlbedoDiffuse, & + iceMassExtinctionCrossSectionDirect, & + iceMassExtinctionCrossSectionDiffuse, & + aerosolAsymmetryParameter5band, & + aerosolMassExtinctionCrossSection5band, & + aerosolSingleScatterAlbedo5band, & + modalAsymmetryParameter5band, & + modalMassExtinctionCrossSection5band, & + modalSingleScatterAlbedo5band, & + snowRadiusInStandardRadiationSchemeCategory real(kind=RKIND), dimension(:,:,:), pointer :: & iceAreaCategory, & @@ -2219,7 +2965,9 @@ subroutine column_radiation(domain, clock, lInitialization) iceBodyAerosol, & brineFraction, & modalBCabsorptionParameter, & - bioTracerShortwave + bioTracerShortwave, & + modalBCabsorptionParameter5band, & + snowGrainRadius real(kind=RKIND), pointer :: & dayOfNextShortwaveCalculation ! needed for CESM like coupled simulations @@ -2256,9 +3004,6 @@ subroutine column_radiation(domain, clock, lInitialization) setGetPhysicsTracers, & setGetBGCTracers - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - ! day of year call get_day_of_year(clock, dayOfYear) @@ -2271,7 +3016,6 @@ subroutine column_radiation(domain, clock, lInitialization) call MPAS_pool_get_config(domain % configs, "config_use_brine", config_use_brine) call MPAS_pool_get_config(domain % configs, "config_use_shortwave_bioabsorption", config_use_shortwave_bioabsorption) call MPAS_pool_get_config(domain % configs, "config_use_modal_aerosols",config_use_modal_aerosols) - call MPAS_pool_get_config(domain % configs, "config_use_zaerosols",config_use_zaerosols) call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry",config_use_column_biogeochemistry) block => domain % blocklist @@ -2284,8 +3028,11 @@ subroutine column_radiation(domain, clock, lInitialization) call MPAS_pool_get_subpool(block % structs, "ponds", ponds) call MPAS_pool_get_subpool(block % structs, "aerosols", aerosols) call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistry) + call MPAS_pool_get_subpool(block % structs, "snicar", snicar) + call MPAS_pool_get_subpool(block % structs, "snow", snow) call MPAS_pool_get_config(block % configs, "config_dt", config_dt) + call MPAS_pool_get_config(block % configs, "config_snow_redistribution_scheme", config_snow_redistribution_scheme) call MPAS_pool_get_dimension(mesh, "nCellsSolve", nCellsSolve) call MPAS_pool_get_dimension(mesh, "nCategories", nCategories) @@ -2313,6 +3060,7 @@ subroutine column_radiation(domain, clock, lInitialization) call MPAS_pool_get_array(tracers, "iceScatteringAerosol", iceScatteringAerosol, 1) call MPAS_pool_get_array(tracers, "iceBodyAerosol", iceBodyAerosol, 1) call MPAS_pool_get_array(tracers, "brineFraction", brineFraction, 1) + call MPAS_pool_get_array(tracers, "snowGrainRadius", snowGrainRadius, 1) call MPAS_pool_get_array(atmos_coupling, "shortwaveVisibleDirectDown", shortwaveVisibleDirectDown) call MPAS_pool_get_array(atmos_coupling, "shortwaveVisibleDiffuseDown", shortwaveVisibleDiffuseDown) @@ -2353,6 +3101,23 @@ subroutine column_radiation(domain, clock, lInitialization) call MPAS_pool_get_array(biogeochemistry, "verticalShortwaveGrid", verticalShortwaveGrid) call MPAS_pool_get_array(biogeochemistry, "verticalGrid", verticalGrid) + ! snicar 5-band snow IOPs + call MPAS_pool_get_array(snicar, "iceAsymmetryParameterDirect", iceAsymmetryParameterDirect) + call MPAS_pool_get_array(snicar, "iceAsymmetryParameterDiffuse", iceAsymmetryParameterDiffuse) + call MPAS_pool_get_array(snicar, "iceSingleScatterAlbedoDirect", iceSingleScatterAlbedoDirect) + call MPAS_pool_get_array(snicar, "iceSingleScatterAlbedoDiffuse", iceSingleScatterAlbedoDiffuse) + call MPAS_pool_get_array(snicar, "iceMassExtinctionCrossSectionDirect", iceMassExtinctionCrossSectionDirect) + call MPAS_pool_get_array(snicar, "iceMassExtinctionCrossSectionDiffuse", iceMassExtinctionCrossSectionDiffuse) + call MPAS_pool_get_array(snicar, "aerosolMassExtinctionCrossSection5band", aerosolMassExtinctionCrossSection5band) + call MPAS_pool_get_array(snicar, "aerosolSingleScatterAlbedo5band", aerosolSingleScatterAlbedo5band) + call MPAS_pool_get_array(snicar, "aerosolAsymmetryParameter5band", aerosolAsymmetryParameter5band) + call MPAS_pool_get_array(snicar, "modalMassExtinctionCrossSection5band", modalMassExtinctionCrossSection5band) + call MPAS_pool_get_array(snicar, "modalSingleScatterAlbedo5band", modalSingleScatterAlbedo5band) + call MPAS_pool_get_array(snicar, "modalAsymmetryParameter5band", modalAsymmetryParameter5band) + call MPAS_pool_get_array(snicar, "modalBCabsorptionParameter5band", modalBCabsorptionParameter5band) + + call MPAS_pool_get_array(snow, "snowRadiusInStandardRadiationSchemeCategory", snowRadiusInStandardRadiationSchemeCategory) + ! calendar type call MPAS_pool_get_config(block % configs, "config_calendar_type", config_calendar_type) if (trim(config_calendar_type) == "gregorian") then @@ -2364,6 +3129,7 @@ subroutine column_radiation(domain, clock, lInitialization) ! aerosols array allocate(aerosolsArray(4*nAerosols,nCategories)) allocate(index_shortwaveAerosol(maxAerosolType)) + if (.not. config_use_column_biogeochemistry) then index_shortwaveAerosol(1:maxAerosolType) = 1 else @@ -2375,6 +3141,8 @@ subroutine column_radiation(domain, clock, lInitialization) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + !$omp parallel do default(shared) firstprivate(aerosolsArray,index_shortwaveAerosol) & + !$omp& private(iCategory,iAerosol,lonCellColumn) do iCell = 1, nCellsSolve ! set aerosols array @@ -2393,14 +3161,14 @@ subroutine column_radiation(domain, clock, lInitialization) if (lonCellColumn > pii) lonCellColumn = lonCellColumn - 2.0_RKIND * pii ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_clear_warnings() call colpkg_step_radiation(& config_dt, & nCategories, & nAlgae, & - config_use_zaerosols, & nBioLayers, & ciceTracerObject % nTracers, & ciceTracerObject % nBioTracers, & @@ -2411,9 +3179,9 @@ subroutine column_radiation(domain, clock, lInitialization) nzAerosols, & config_use_shortwave_bioabsorption, & ciceTracerObject % index_chlorophyllShortwave, & - index_shortwaveAerosol, & - verticalShortwaveGrid(:), & - verticalGrid(:), & + index_shortwaveAerosol, & ! nlt_zaero_sw, dimension(:), intent(in) + verticalShortwaveGrid(:), & ! swgrid, dimension (:), intent(in) + verticalGrid(:), & ! igrid, dimension (:), intent(in) brineFraction(1,:,iCell), & iceAreaCategory(1,:,iCell), & iceVolumeCategory(1,:,iCell), & @@ -2423,9 +3191,11 @@ subroutine column_radiation(domain, clock, lInitialization) pondArea(1,:,iCell), & pondDepth(1,:,iCell), & pondLidThickness(1,:,iCell), & + config_snow_redistribution_scheme, & + snowGrainRadius(:,:,iCell), & aerosolsArray, & bioTracerShortwave(:,:,iCell), & - ciceTracerObject % tracerArrayCategory(:,:), & + tracerArrayCategory, & ! trcrn, dimension(:,:), intent(in) latCell(iCell), & lonCellColumn, & calendarType, & @@ -2433,13 +3203,13 @@ subroutine column_radiation(domain, clock, lInitialization) dayOfNextShortwaveCalculation, & dayOfYear, & secondsIntoDay, & - aerosolMassExtinctionCrossSection(:,:), & - aerosolSingleScatterAlbedo(:,:), & - aerosolAsymmetryParameter(:,:), & - modalMassExtinctionCrossSection(:,:), & - modalSingleScatterAlbedo(:,:), & - modalAsymmetryParameter(:,:), & - modalBCabsorptionParameter(:,:,:), & + aerosolMassExtinctionCrossSection(:,:), & ! kaer_tab, dimension(:,:), intent(in) + aerosolSingleScatterAlbedo(:,:), & ! waer_tab, dimension(:,:), intent(in) + aerosolAsymmetryParameter(:,:), & ! gaer_tab, dimension(:,:), intent(in) + modalMassExtinctionCrossSection(:,:), & ! kaer_bc_tab, dimension(:,:), intent(in) + modalSingleScatterAlbedo(:,:), & ! waer_bc_tab, dimension(:,:), intent(in) + modalAsymmetryParameter(:,:), & ! gaer_bc_tab, dimension(:,:), intent(in) + modalBCabsorptionParameter(:,:,:), & ! bcenh, dimension(:,:,:), intent(in) config_use_modal_aerosols, & shortwaveVisibleDirectDown(iCell), & shortwaveVisibleDiffuseDown(iCell), & @@ -2465,12 +3235,27 @@ subroutine column_radiation(domain, clock, lInitialization) pondSnowDepthDifference(:,iCell), & pondLidMeltFluxFraction(:,iCell), & .false., & - lInitialization) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + lInitialization, & + iceAsymmetryParameterDirect(:,:), & + iceAsymmetryParameterDiffuse(:,:), & + iceSingleScatterAlbedoDirect(:,:), & + iceSingleScatterAlbedoDiffuse(:,:), & + iceMassExtinctionCrossSectionDirect(:,:), & + iceMassExtinctionCrossSectionDiffuse(:,:), & + aerosolMassExtinctionCrossSection5band(:,:), & + aerosolSingleScatterAlbedo5band(:,:), & + aerosolAsymmetryParameter5band(:,:), & + modalMassExtinctionCrossSection5band(:,:), & + modalSingleScatterAlbedo5band(:,:), & + modalAsymmetryParameter5band(:,:), & + modalBCabsorptionParameter5band(:,:,:), & + snowRadiusInStandardRadiationSchemeCategory(:,iCell)) + + call column_write_warnings(.false.) ! set the category tracer array - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) enddo ! iCell @@ -2495,17 +3280,14 @@ end subroutine column_radiation ! !----------------------------------------------------------------------- - subroutine column_ridging(domain, ierr) + subroutine column_ridging(domain) use ice_colpkg, only: & colpkg_step_ridge, & - colpkg_get_warnings, & colpkg_clear_warnings type(domain_type), intent(inout) :: domain - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -2515,6 +3297,7 @@ subroutine column_ridging(domain, ierr) tracers_aggregate, & ponds, & ocean_fluxes, & + ocean_coupling, & ridging, & aerosols, & biogeochemistry, & @@ -2546,6 +3329,7 @@ subroutine column_ridging(domain, ierr) oceanFreshWaterFlux, & oceanSaltFlux, & oceanHeatFlux, & + seaFreezingTemperature, & iceAreaCell, & ridgeConvergence, & ridgeShear, & @@ -2601,9 +3385,6 @@ subroutine column_ridging(domain, ierr) abortMessage, & abortLocation - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - block => domain % blocklist do while (associated(block)) @@ -2618,6 +3399,7 @@ subroutine column_ridging(domain, ierr) call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistry) call MPAS_pool_get_subpool(block % structs, "initial", initial) call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocity_solver) + call MPAS_pool_get_subpool(block % structs, "ocean_coupling", ocean_coupling) call MPAS_pool_get_config(block % configs, "config_dynamics_subcycle_number", config_dynamics_subcycle_number) call MPAS_pool_get_config(block % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) @@ -2641,6 +3423,8 @@ subroutine column_ridging(domain, ierr) call MPAS_pool_get_array(tracers, "iceVolumeCategory", iceVolumeCategory, 1) call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) + call MPAS_pool_get_array(ocean_coupling, "seaFreezingTemperature", seaFreezingTemperature) + call MPAS_pool_get_array(ocean_fluxes, "oceanFreshWaterFlux", oceanFreshWaterFlux) call MPAS_pool_get_array(ocean_fluxes, "oceanSaltFlux", oceanSaltFlux) call MPAS_pool_get_array(ocean_fluxes, "oceanHeatFlux", oceanHeatFlux) @@ -2677,6 +3461,10 @@ subroutine column_ridging(domain, ierr) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + ! code abort + abortFlag = .false. + abortMessage = "" + do iCell = 1, nCellsSolve ! newly formed ice @@ -2685,10 +3473,8 @@ subroutine column_ridging(domain, ierr) enddo ! iCategory ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) - - abortFlag = .false. - abortMessage = "" + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_clear_warnings() call colpkg_step_ridge(& @@ -2698,11 +3484,12 @@ subroutine column_ridging(domain, ierr) nSnowLayers, & nBioLayers, & nCategories, & - categoryThicknessLimits, & + categoryThicknessLimits, & ! hin_max, dimension(0:ncat), intent(inout) ridgeConvergence(iCell), & ridgeShear(iCell), & + seaFreezingTemperature(iCell), & iceAreaCategory(1,:,iCell), & - ciceTracerObject % tracerArrayCategory, & ! trcrn + tracerArrayCategory, & ! trcrn, dimension(:,:), intent(inout) iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & openWaterArea(iCell), & @@ -2735,8 +3522,7 @@ subroutine column_ridging(domain, ierr) oceanBioFluxes(:,iCell), & abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) ! update do iCategory = 1, nCategories @@ -2745,19 +3531,22 @@ subroutine column_ridging(domain, ierr) enddo ! iCategory ! get category tracer array - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) ! code abort - if (abortFlag) then - call mpas_log_write("column_ridging: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) - call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_RIDGING - call seaice_critical_error_write_block(domain, block) - return - endif + if (abortFlag) exit enddo ! iCell + ! code abort + if (abortFlag) then + call mpas_log_write("column_ridging: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) + call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) + endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + ! newly formed ice deallocate(newlyFormedIceLogical) @@ -2778,12 +3567,11 @@ end subroutine column_ridging ! !----------------------------------------------------------------------- - subroutine column_biogeochemistry(domain, ierr) + subroutine column_biogeochemistry(domain) use ice_colpkg, only: & colpkg_biogeochemistry, & colpkg_init_OceanConcArray, & - colpkg_get_warnings, & colpkg_clear_warnings use seaice_constants, only: & @@ -2791,8 +3579,6 @@ subroutine column_biogeochemistry(domain, ierr) type(domain_type), intent(inout) :: domain - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -2858,6 +3644,7 @@ subroutine column_biogeochemistry(domain, ierr) verticalGrid, & seaSurfaceTemperature, & seaSurfaceSalinity, & + seaFreezingTemperature, & snowfallRate, & zSalinityFlux, & zSalinityGDFlux, & @@ -2917,7 +3704,9 @@ subroutine column_biogeochemistry(domain, ierr) iceAreaCategory, & ! tracers (1,ncat,ncell) iceVolumeCategory, & ! tracers (1,ncat,ncell) snowVolumeCategory, & ! tracers (1,ncat,ncell) - skeletalAlgaeConc + skeletalAlgaeConc, & + oceanBioFluxesCategory, & + brineFraction integer, dimension(:,:), pointer :: & newlyFormedIce @@ -2939,8 +3728,21 @@ subroutine column_biogeochemistry(domain, ierr) indexj, & iBioLayers + ! test carbon conservation real(kind=RKIND), dimension(:), allocatable :: & - oceanBioConcentrationsUsed + totalCarbonCatFinal, & + totalCarbonCatInitial, & + totalCarbonCatFlux, & + brineHeightCatInitial, & + brineHeightCatFinal + + real(kind=RKIND), dimension(:), allocatable :: & + oceanBioConcentrationsUsed, & + iceCarbonInitialCategory, & + iceCarbonFinalCategory, & + iceCarbonFluxCategory, & + iceBrineInitialCategory, & + iceBrineFinalCategory logical, dimension(:), allocatable :: & newlyFormedIceLogical @@ -2949,14 +3751,22 @@ subroutine column_biogeochemistry(domain, ierr) abortFlag, & rayleighCriteria, & setGetPhysicsTracers, & - setGetBGCTracers + setGetBGCTracers, & + checkCarbon character(len=strKIND) :: & abortMessage, & abortLocation - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings + real(kind=RKIND) :: & + carbonErrorCat, & + carbonErrorColumnPackage + + real(kind=RKIND), parameter :: & + accuracy = 1.0e-14_RKIND + + ! test carbon conservation + checkCarbon = .false. block => domain % blocklist do while (associated(block)) @@ -3036,6 +3846,7 @@ subroutine column_biogeochemistry(domain, ierr) call MPAS_pool_get_array(biogeochemistry, "atmosBlackCarbonFlux", atmosBlackCarbonFlux) call MPAS_pool_get_array(biogeochemistry, "atmosDustFlux", atmosDustFlux) call MPAS_pool_get_array(biogeochemistry, "oceanBioFluxes", oceanBioFluxes) + call MPAS_pool_get_array(biogeochemistry, "oceanBioFluxesCategory", oceanBioFluxesCategory) call MPAS_pool_get_array(biogeochemistry, "verticalNitrogenLosses", verticalNitrogenLosses) call MPAS_pool_get_array(biogeochemistry, "bioTemperature", bioTemperature) call MPAS_pool_get_array(biogeochemistry, "totalSkeletalAlgae", totalSkeletalAlgae) @@ -3055,6 +3866,7 @@ subroutine column_biogeochemistry(domain, ierr) call MPAS_pool_get_array(ocean_coupling, "seaSurfaceTemperature", seaSurfaceTemperature) call MPAS_pool_get_array(ocean_coupling, "seaSurfaceSalinity", seaSurfaceSalinity) + call MPAS_pool_get_array(ocean_coupling, "seaFreezingTemperature", seaFreezingTemperature) call MPAS_pool_get_array(ocean_coupling, "oceanMixedLayerDepth", oceanMixedLayerDepth) call MPAS_pool_get_array(atmos_coupling, "snowfallRate", snowfallRate) @@ -3080,19 +3892,33 @@ subroutine column_biogeochemistry(domain, ierr) call MPAS_pool_get_array(tracers, "iceVolumeCategory", iceVolumeCategory, 1) call MPAS_pool_get_array(tracers, "snowVolumeCategory", snowVolumeCategory, 1) call MPAS_pool_get_array(tracers, "skeletalAlgaeConc", skeletalAlgaeConc, 1) + call MPAS_pool_get_array(tracers, "brineFraction", brineFraction, 1) ! newly formed ice allocate(newlyFormedIceLogical(nCategories)) allocate(oceanBioConcentrationsUsed(ciceTracerObject % nBioTracers)) + allocate(brineHeightCatInitial(nCategories)) + + if (checkCarbon) then + allocate(totalCarbonCatFinal(nCategories)) + allocate(totalCarbonCatInitial(nCategories)) + allocate(totalCarbonCatFlux(nCategories)) + allocate(brineHeightCatFinal(nCategories)) + endif setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry - do iCell = 1, nCellsSolve + ! code abort + abortFlag = .false. + abortMessage = "" + do iCell = 1, nCellsSolve ! newly formed ice do iCategory = 1, nCategories newlyFormedIceLogical(iCategory) = (newlyFormedIce(iCategory,iCell) == 1) + brineHeightCatInitial(iCategory) = brineFraction(1,iCategory,iCell) * & + iceVolumeCategoryInitial(iCategory,iCell)/(iceAreaCategoryInitial(iCategory,iCell) + seaicePuny) enddo ! iCategory rayleighCriteria = (rayleighCriteriaReal(iCell) > 0.5_RKIND) @@ -3146,7 +3972,11 @@ subroutine column_biogeochemistry(domain, ierr) abortFlag = .false. - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) + + if (checkCarbon) call seaice_total_carbon_content_category(block,& + totalCarbonCatInitial,iceAreaCategoryInitial,iceVolumeCategoryInitial,iCell) call colpkg_clear_warnings() call colpkg_biogeochemistry(& @@ -3202,6 +4032,7 @@ subroutine column_biogeochemistry(domain, ierr) snowiceFormationCategory(:,iCell), & seaSurfaceTemperature(iCell), & seaSurfaceSalinity(iCell), & + seaFreezingTemperature(iCell), & snowfallRate(iCell), & snowMeltCategory(:,iCell), & oceanMixedLayerDepth(iCell), & @@ -3215,28 +4046,47 @@ subroutine column_biogeochemistry(domain, ierr) iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & openWaterArea(iCell), & - ciceTracerObject % tracerArrayCategory(:,:), & + tracerArrayCategory, & snowVolumeCategoryInitial(:,iCell), & config_use_skeletal_biochemistry, & maxAlgaeType, & nZBGCTracers, & + oceanBioFluxesCategory(:,:,iCell), & abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) - - ! code abort - if (abortFlag) then - call mpas_log_write("column_biogeochemistry: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) - call mpas_log_write("iCell: $i", messageType=MPAS_LOG_CRIT, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_BGC - call seaice_critical_error_write_block(domain, block) - return + call column_write_warnings(abortFlag) + + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) + + if (checkCarbon) then + call seaice_total_carbon_content_category(block,totalCarbonCatFinal,iceAreaCategory(1,:,:),iceVolumeCategory(1,:,:),iCell) + call seaice_ocean_carbon_flux(block,totalCarbonCatFlux,oceanBioFluxesCategory(:,:,:),iCell) + do iCategory = 1,nCategories + brineHeightCatFinal(iCategory) = brineFraction(1,iCategory,iCell) * & + iceVolumeCategory(1,iCategory,iCell)/(iceAreaCategory(1,iCategory,iCell) + seaicePuny) + carbonErrorCat = totalCarbonCatInitial(iCategory) - totalCarbonCatFlux(iCategory)*config_dt - & + totalCarbonCatFinal(iCategory) + if (abs(carbonErrorCat) > accuracy*MAXVAL((/totalCarbonCatInitial(iCategory),totalCarbonCatFinal(iCategory)/))) then +! abortFlag = .true. +! abortMessage = "carbon conservation errror after column bgc" + call mpas_log_write("column_biogeochemistry, carbon conservation error", messageType=MPAS_LOG_ERR) + call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write("iCategory: $i", messageType=MPAS_LOG_ERR, intArgs=(/iCategory/)) + call mpas_log_write("carbonErrorCat: $r", messageType=MPAS_LOG_ERR, realArgs=(/carbonErrorCat/)) + call mpas_log_write("carbonErrorCat*iceAreaCategory: $r", messageType=MPAS_LOG_ERR, realArgs=(/carbonErrorCat*iceAreaCategory(1,iCategory,iCell)/)) + call mpas_log_write("totalCarbonCatInitial(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonCatInitial(iCategory)/)) + call mpas_log_write("totalCarbonCatFinal(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonCatFinal(iCategory)/)) + call mpas_log_write("totalCarbonCatFlux(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/totalCarbonCatFlux(iCategory)/)) + call mpas_log_write("brineHeightCatInitial(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/brineHeightCatInitial(iCategory)/)) + call mpas_log_write("brineHeightCatFinal(iCategory): $r", messageType=MPAS_LOG_ERR, realArgs=(/brineHeightCatFinal(iCategory)/)) + endif + enddo endif - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + ! code abort + if (abortFlag) exit - ! update totalSkeletalAlgae(iCell) = 0.0_RKIND bioShortwaveFluxCell(:,iCell) = 0.0_RKIND @@ -3263,6 +4113,22 @@ subroutine column_biogeochemistry(domain, ierr) enddo ! iCell + ! code abort + if (abortFlag) then + call mpas_log_write("column_biogeochemistry: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) + call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) + endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + + if (checkCarbon) then + deallocate(totalCarbonCatFinal) + deallocate(totalCarbonCatInitial) + deallocate(totalCarbonCatFlux) + deallocate(brineHeightCatFinal) + endif + + deallocate(brineHeightCatInitial) deallocate(newlyFormedIceLogical) deallocate(oceanBioConcentrationsUsed) @@ -3534,7 +4400,8 @@ subroutine seaice_column_aggregate(domain) mesh, & tracers, & tracers_aggregate, & - icestate + icestate, & + ocean_coupling logical, pointer :: & config_use_column_biogeochemistry @@ -3543,7 +4410,8 @@ subroutine seaice_column_aggregate(domain) iceAreaCell, & iceVolumeCell, & snowVolumeCell, & - openWaterArea + openWaterArea, & + seaFreezingTemperature real(kind=RKIND), dimension(:,:,:), pointer :: & iceAreaCategory, & @@ -3568,6 +4436,7 @@ subroutine seaice_column_aggregate(domain) call MPAS_pool_get_subpool(block % structs, "tracers", tracers) call MPAS_pool_get_subpool(block % structs, "icestate", icestate) call MPAS_pool_get_subpool(block % structs, "tracers_aggregate", tracers_aggregate) + call MPAS_pool_get_subpool(block % structs, "ocean_coupling", ocean_coupling) call MPAS_pool_get_config(block % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) @@ -3584,22 +4453,26 @@ subroutine seaice_column_aggregate(domain) call MPAS_pool_get_array(icestate, "openWaterArea", openWaterArea) + call MPAS_pool_get_array(ocean_coupling, "seaFreezingTemperature", seaFreezingTemperature) + setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry do iCell = 1, nCellsSolve ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_aggregate(& nCategories, & + seaFreezingTemperature(iCell), & iceAreaCategory(1,:,iCell), & - ciceTracerObject % tracerArrayCategory, & ! trcrn + tracerArrayCategory, & ! trcrn iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & iceAreaCell(iCell), & - ciceTracerObject % tracerArrayCell, & ! trcr + tracerArrayCell, & ! trcr iceVolumeCell(iCell), & snowVolumeCell(iCell), & openWaterArea(iCell), & @@ -3610,7 +4483,8 @@ subroutine seaice_column_aggregate(domain) ciceTracerObject % ancestorIndices) ! nt_strata ! set the cell tracer array - call get_cice_tracer_array_cell(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_cell(block, ciceTracerObject, & + tracerArrayCell, iCell, setGetPhysicsTracers, setGetBGCTracers) enddo ! iCell @@ -3694,7 +4568,8 @@ subroutine seaice_column_coupling_prep(domain) oceanDMSPpFlux, & oceanDMSPdFlux, & oceanHumicsFlux, & - oceanDustIronFlux + oceanDustIronFlux, & + totalOceanCarbonFlux real(kind=RKIND), dimension(:,:), pointer :: & albedoVisibleDirectCategory, & @@ -3719,7 +4594,13 @@ subroutine seaice_column_coupling_prep(domain) real(kind=RKIND), pointer :: & config_dt - integer, pointer :: & + real(kind=RKIND), pointer :: & + config_ratio_C_to_N_diatoms, & + config_ratio_C_to_N_small_plankton, & + config_ratio_C_to_N_phaeocystis, & + config_ratio_C_to_N_proteins + + integer, pointer :: & nCellsSolve, & nCategories, & nZBGCTracers, & @@ -3738,6 +4619,9 @@ subroutine seaice_column_coupling_prep(domain) iBioTracers, & iBioData + real(kind=RKIND), dimension(:), allocatable :: & + ratio_C_to_N + real(kind=RKIND), dimension(:), allocatable :: & oceanBioFluxesAll @@ -3745,6 +4629,10 @@ subroutine seaice_column_coupling_prep(domain) call MPAS_pool_get_config(domain % configs, "config_dt", config_dt) call MPAS_pool_get_config(domain % configs, "config_include_pond_freshwater_feedback", config_include_pond_freshwater_feedback) call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + call MPAS_pool_get_config(domain % configs, "config_ratio_C_to_N_diatoms", config_ratio_C_to_N_diatoms) + call MPAS_pool_get_config(domain % configs, "config_ratio_C_to_N_small_plankton", config_ratio_C_to_N_small_plankton) + call MPAS_pool_get_config(domain % configs, "config_ratio_C_to_N_phaeocystis", config_ratio_C_to_N_phaeocystis) + call MPAS_pool_get_config(domain % configs, "config_ratio_C_to_N_proteins", config_ratio_C_to_N_proteins) if (config_use_ocean_mixed_layer) & call seaice_column_ocean_mixed_layer(domain) @@ -3827,6 +4715,7 @@ subroutine seaice_column_coupling_prep(domain) call MPAS_pool_get_array(biogeochemistry, "oceanDONFlux", oceanDONFlux) call MPAS_pool_get_array(biogeochemistry, "oceanParticulateIronFlux", oceanParticulateIronFlux) call MPAS_pool_get_array(biogeochemistry, "oceanDissolvedIronFlux", oceanDissolvedIronFlux) + call MPAS_pool_get_array(biogeochemistry, "totalOceanCarbonFlux", totalOceanCarbonFlux) call MPAS_pool_get_dimension(mesh, "nZBGCTracers", nZBGCTracers) call MPAS_pool_get_dimension(mesh, "maxAlgaeType", maxAlgaeType) @@ -3840,6 +4729,12 @@ subroutine seaice_column_coupling_prep(domain) allocate(oceanBioFluxesAll(nZBGCTracers)) + allocate(ratio_C_to_N(3)) + + ratio_C_to_N(1) = config_ratio_C_to_N_diatoms + ratio_C_to_N(2) = config_ratio_C_to_N_small_plankton + ratio_C_to_N(3) = config_ratio_C_to_N_phaeocystis + do iCell = 1, nCellsSolve !------------------------------------------------------------------- @@ -3928,6 +4823,7 @@ subroutine seaice_column_coupling_prep(domain) !----------------------------------------------------------------- if (config_use_column_biogeochemistry) then + totalOceanCarbonFlux(iCell) = 0.0_RKIND oceanBioFluxesAll(:) = 0.0_RKIND oceanAlgaeFlux(:,iCell) = 0.0_RKIND oceanDOCFlux(:,iCell) = 0.0_RKIND @@ -3954,6 +4850,8 @@ subroutine seaice_column_coupling_prep(domain) do iBioTracers = 1, maxAlgaeType iBioData = iBioData+1 oceanAlgaeFlux(iBioTracers,iCell) = oceanBioFluxesAll(iBioData) + totalOceanCarbonFlux(iCell) = totalOceanCarbonFlux(iCell) + & + oceanAlgaeFlux(iBioTracers,iCell) * ratio_C_to_N(iBioTracers) enddo ! Nitrate @@ -3964,12 +4862,16 @@ subroutine seaice_column_coupling_prep(domain) do iBioTracers = 1, maxDOCType iBioData = iBioData+1 oceanDOCFlux(iBioTracers,iCell) = oceanBioFluxesAll(iBioData) + totalOceanCarbonFlux(iCell) = totalOceanCarbonFlux(iCell) + & + oceanDOCFlux(iBioTracers,iCell) enddo ! DIC do iBioTracers = 1, maxDICType iBioData = iBioData+1 oceanDICFlux(iBioTracers,iCell) = oceanBioFluxesAll(iBioData) + totalOceanCarbonFlux(iCell) = totalOceanCarbonFlux(iCell) + & + oceanDICFlux(iBioTracers,iCell) enddo ! Chlorophyll (not saved) @@ -3995,11 +4897,15 @@ subroutine seaice_column_coupling_prep(domain) iBioData = iBioData+1 oceanDMSFlux(iCell) = oceanBioFluxesAll(iBioData) - ! DON (Proteins) + ! PON iBioData = iBioData+1 + + ! DON (Proteins) do iBioTracers = 1, maxDONType iBioData = iBioData+1 oceanDONFlux(iBioTracers,iCell) = oceanBioFluxesAll(iBioData) + totalOceanCarbonFlux(iCell) = totalOceanCarbonFlux(iCell) + & + oceanDONFlux(iBioTracers,iCell) * config_ratio_C_to_N_proteins enddo ! Dissolved Iron @@ -4026,12 +4932,15 @@ subroutine seaice_column_coupling_prep(domain) ! Humics iBioData = iBioData+1 oceanHumicsFlux(iCell) = oceanBioFluxesAll(iBioData) + totalOceanCarbonFlux(iCell) = totalOceanCarbonFlux(iCell) + & + oceanHumicsFlux(iCell) endif ! config_use_column_biogeochemistry enddo ! iCell deallocate(oceanBioFluxesAll) + deallocate(ratio_C_to_N) block => block % next enddo @@ -4581,12 +5490,6 @@ subroutine init_column_tracer_object(domain, tracerObject) ! get the number of CICE tracers in trcrn call init_column_tracer_object_tracer_number(domain, tracerObject) - ! allocate the category tracer array - allocate(tracerObject % tracerArrayCategory(tracerObject % nTracers,nCategories)) - - ! allocate the cell tracer array - allocate(tracerObject % tracerArrayCell(tracerObject % nTracers)) - ! allocate other arrays allocate(tracerObject % parentIndex(tracerObject % nTracers)) allocate(tracerObject % firstAncestorMask(tracerObject % nTracers, tracerObject % nBaseTracers)) @@ -4614,8 +5517,39 @@ subroutine init_column_tracer_object(domain, tracerObject) ! set all indices for biogeochemistry including parent, ancestor and ancestor mask call init_column_tracer_object_for_biogeochemistry(domain, tracerObject) + else + allocate(tracerObject % index_algaeConc(1)) + allocate(tracerObject % index_algalCarbon(1)) + allocate(tracerObject % index_algalChlorophyll(1)) + allocate(tracerObject % index_DOCConc(1)) + allocate(tracerObject % index_DONConc(1)) + allocate(tracerObject % index_DICConc(1)) + allocate(tracerObject % index_dissolvedIronConc(1)) + allocate(tracerObject % index_particulateIronConc(1)) + allocate(tracerObject % index_verticalAerosolsConc(1)) + + allocate(tracerObject % index_algaeConcLayer(1)) + allocate(tracerObject % index_algalCarbonLayer(1)) + allocate(tracerObject % index_algalChlorophyllLayer(1)) + allocate(tracerObject % index_DOCConcLayer(1)) + allocate(tracerObject % index_DONConcLayer(1)) + allocate(tracerObject % index_DICConcLayer(1)) + allocate(tracerObject % index_dissolvedIronConcLayer(1)) + allocate(tracerObject % index_particulateIronConcLayer(1)) + allocate(tracerObject % index_verticalAerosolsConcLayer(1)) + allocate(tracerObject % index_verticalAerosolsConcShortwave(1)) + + allocate(tracerObject % index_LayerIndexToDataArray(1)) + allocate(tracerObject % index_LayerIndexToBioIndex(1)) endif + ! allocate tracer arrays + !$omp parallel + allocate(tracerArrayCategory(tracerObject % nTracers, nCategories)) + !$omp end parallel + + allocate(tracerArrayCell(tracerObject % nTracers)) + end subroutine init_column_tracer_object !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -4662,7 +5596,9 @@ subroutine init_column_tracer_object_tracer_number(domain, tracerObject) config_use_humics, & config_use_DON, & config_use_iron, & - config_use_zaerosols + config_use_zaerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius integer, pointer :: & nIceLayers, & @@ -4690,6 +5626,8 @@ subroutine init_column_tracer_object_tracer_number(domain, tracerObject) call MPAS_pool_get_config(domain % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(domain % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(domain % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(domain % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(domain % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) call MPAS_pool_get_config(domain % configs, "config_use_brine", config_use_brine) @@ -4761,6 +5699,16 @@ subroutine init_column_tracer_object_tracer_number(domain, tracerObject) config_use_topo_meltponds) & tracerObject % nTracers = tracerObject % nTracers + 1 + ! snow density (ice mass, liquid mass, density) + if (config_use_effective_snow_density) then + tracerObject % nTracers = tracerObject % nTracers + nSnowLayers*3 + endif + + ! snow grain radius + if (config_use_snow_grain_radius) then + tracerObject % nTracers = tracerObject % nTracers + nSnowLayers + endif + ! aerosols if (config_use_aerosols) & tracerObject % nTracers = tracerObject % nTracers + nAerosols*4 @@ -4922,7 +5870,10 @@ subroutine init_column_tracer_object_child_indices(domain, tracerObject) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & - config_use_aerosols + config_use_aerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius + integer :: & nTracers @@ -4939,6 +5890,8 @@ subroutine init_column_tracer_object_child_indices(domain, tracerObject) call MPAS_pool_get_config(domain % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(domain % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(domain % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(domain % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(domain % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nSnowLayers", nSnowLayers) @@ -5005,6 +5958,26 @@ subroutine init_column_tracer_object_child_indices(domain, tracerObject) tracerObject % index_pondLidThickness = nTracers endif + ! snow density + tracerObject % index_snowIceMass = indexMissingValue + tracerObject % index_snowLiquidMass = indexMissingValue + tracerObject % index_snowDensity = indexMissingValue + if (config_use_effective_snow_density) then + tracerObject % index_snowIceMass = nTracers + 1 + nTracers = nTracers + nSnowLayers + tracerObject % index_snowLiquidMass = nTracers + 1 + nTracers = nTracers + nSnowLayers + tracerObject % index_snowDensity = nTracers + 1 + nTracers = nTracers + nSnowLayers + endif + + ! snow grain radius + tracerObject % index_snowGrainRadius = indexMissingValue + if (config_use_snow_grain_radius) then + tracerObject % index_snowGrainRadius = nTracers + 1 + nTracers = nTracers + nSnowLayers + endif + ! aerosols tracerObject % index_aerosols = indexMissingValue if (config_use_aerosols) then @@ -5044,7 +6017,9 @@ subroutine init_column_tracer_object_parent_indices(domain, tracerObject) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & - config_use_aerosols + config_use_aerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius integer :: & iIceLayer, & @@ -5063,6 +6038,8 @@ subroutine init_column_tracer_object_parent_indices(domain, tracerObject) call MPAS_pool_get_config(domain % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(domain % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(domain % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(domain % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(domain % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nSnowLayers", nSnowLayers) @@ -5079,7 +6056,7 @@ subroutine init_column_tracer_object_parent_indices(domain, tracerObject) ! snow enthalpy do iSnowLayer = 1, nSnowLayers - tracerObject % parentIndex(tracerObject % index_snowEnthalpy) = 2 + tracerObject % parentIndex(tracerObject % index_snowEnthalpy + iSnowLayer - 1) = 2 enddo ! iSnowLayer ! ice age @@ -5116,6 +6093,22 @@ subroutine init_column_tracer_object_parent_indices(domain, tracerObject) tracerObject % parentIndex(tracerObject % index_pondLidThickness) = 2 + tracerObject % index_pondArea endif + ! snow density + if (config_use_effective_snow_density) then + do iSnowLayer = 1, nSnowLayers + tracerObject % parentIndex(tracerObject % index_snowIceMass + iSnowLayer - 1) = 2 + tracerObject % parentIndex(tracerObject % index_snowLiquidMass + iSnowLayer - 1) = 2 + tracerObject % parentIndex(tracerObject % index_snowDensity + iSnowLayer - 1) = 2 + enddo ! iSnowLayer + endif + + ! snow grain radius + if (config_use_snow_grain_radius) then + do iSnowLayer = 1, nSnowLayers + tracerObject % parentIndex(tracerObject % index_snowGrainRadius + iSnowLayer - 1) = 2 + enddo ! iSnowLayer + endif + ! aerosols if (config_use_aerosols) then do iAerosol = 1, nAerosols @@ -5284,14 +6277,17 @@ end subroutine init_column_tracer_object_ancestor_indices ! !----------------------------------------------------------------------- - subroutine set_cice_tracer_array_category(block, tracerObject, iCell, setPhysicsTracers, setBGCTracers) + subroutine set_cice_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell, setPhysicsTracers, setBGCTracers) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(inout) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -5301,11 +6297,11 @@ subroutine set_cice_tracer_array_category(block, tracerObject, iCell, setPhysics ! get physics tracers if (setPhysicsTracers) & - call set_cice_physics_tracer_array_category(block, tracerObject, iCell) + call set_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) ! get BGC tracers if (setBGCTracers) & - call set_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + call set_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) end subroutine set_cice_tracer_array_category @@ -5321,7 +6317,7 @@ end subroutine set_cice_tracer_array_category ! !----------------------------------------------------------------------- - subroutine get_cice_tracer_array_category(block, tracerObject, iCell, getPhysicsTracers, getBGCTracers) + subroutine get_cice_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell, getPhysicsTracers, getBGCTracers) type(block_type), intent(inout) :: & block @@ -5329,6 +6325,9 @@ subroutine get_cice_tracer_array_category(block, tracerObject, iCell, getPhysics type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(in) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -5338,11 +6337,11 @@ subroutine get_cice_tracer_array_category(block, tracerObject, iCell, getPhysics ! get physics tracers if (getPhysicsTracers) & - call get_cice_physics_tracer_array_category(block, tracerObject, iCell) + call get_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) ! get BGC tracers if (getBGCTracers) & - call get_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + call get_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) end subroutine get_cice_tracer_array_category @@ -5358,14 +6357,17 @@ end subroutine get_cice_tracer_array_category ! !----------------------------------------------------------------------- - subroutine set_cice_tracer_array_cell(block, tracerObject, iCell, setPhysicsTracers, setBGCTracers) + subroutine set_cice_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell, setPhysicsTracers, setBGCTracers) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(inout) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -5375,11 +6377,11 @@ subroutine set_cice_tracer_array_cell(block, tracerObject, iCell, setPhysicsTrac ! get physics tracers if (setPhysicsTracers) & - call set_cice_physics_tracer_array_cell(block, tracerObject, iCell) + call set_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) ! get BGC tracers if (setBGCTracers) & - call set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + call set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) end subroutine set_cice_tracer_array_cell @@ -5395,7 +6397,7 @@ end subroutine set_cice_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine get_cice_tracer_array_cell(block, tracerObject, iCell, getPhysicsTracers, getBGCTracers) + subroutine get_cice_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell, getPhysicsTracers, getBGCTracers) type(block_type), intent(inout) :: & block @@ -5403,6 +6405,9 @@ subroutine get_cice_tracer_array_cell(block, tracerObject, iCell, getPhysicsTrac type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(in) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -5412,11 +6417,11 @@ subroutine get_cice_tracer_array_cell(block, tracerObject, iCell, getPhysicsTrac ! get physics tracers if (getPhysicsTracers) & - call get_cice_physics_tracer_array_cell(block, tracerObject, iCell) + call get_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) ! get BGC tracers if (getBGCTracers) & - call get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + call get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) end subroutine get_cice_tracer_array_cell @@ -5432,13 +6437,13 @@ end subroutine get_cice_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) + subroutine set_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & - tracerObject + real(kind=RKIND), dimension(:,:), intent(inout) :: & + tracerArrayCategory integer, intent(in) :: & iCell @@ -5450,7 +6455,9 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & - config_use_aerosols + config_use_aerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius integer, pointer :: & nIceLayers, & @@ -5475,7 +6482,11 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) snowScatteringAerosol, & snowBodyAerosol, & iceScatteringAerosol, & - iceBodyAerosol + iceBodyAerosol, & + snowIceMass, & + snowLiquidMass, & + snowGrainRadius, & + snowDensity integer :: & nTracers, & @@ -5488,6 +6499,8 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) call MPAS_pool_get_config(block % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(block % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(block % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(block % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(block % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_dimension(block % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_dimension(block % dimensions, "nSnowLayers", nSnowLayers) @@ -5510,42 +6523,46 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) call MPAS_pool_get_array(tracers, "snowBodyAerosol", snowBodyAerosol, 1) call MPAS_pool_get_array(tracers, "iceScatteringAerosol", iceScatteringAerosol, 1) call MPAS_pool_get_array(tracers, "iceBodyAerosol", iceBodyAerosol, 1) + call MPAS_pool_get_array(tracers, "snowIceMass", snowIceMass, 1) + call MPAS_pool_get_array(tracers, "snowLiquidMass", snowLiquidMass, 1) + call MPAS_pool_get_array(tracers, "snowDensity", snowDensity, 1) + call MPAS_pool_get_array(tracers, "snowGrainRadius", snowGrainRadius, 1) nTracers = 1 ! surfaceTemperature - tracerObject % tracerArrayCategory(nTracers,:) = surfaceTemperature(1,:,iCell) + tracerArrayCategory(nTracers,:) = surfaceTemperature(1,:,iCell) nTracers = nTracers + 1 ! iceEnthalpy - tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceEnthalpy(:,:,iCell) + tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceEnthalpy(:,:,iCell) nTracers = nTracers + nIceLayers ! snowEnthalpy - tracerObject % tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowEnthalpy(:,:,iCell) + tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowEnthalpy(:,:,iCell) nTracers = nTracers + nSnowLayers ! ice Salinity - tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceSalinity(:,:,iCell) + tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceSalinity(:,:,iCell) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - tracerObject % tracerArrayCategory(nTracers,:) = iceAge(1,:,iCell) + tracerArrayCategory(nTracers,:) = iceAge(1,:,iCell) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - tracerObject % tracerArrayCategory(nTracers,:) = firstYearIceArea(1,:,iCell) + tracerArrayCategory(nTracers,:) = firstYearIceArea(1,:,iCell) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - tracerObject % tracerArrayCategory(nTracers,:) = levelIceArea(1,:,iCell) + tracerArrayCategory(nTracers,:) = levelIceArea(1,:,iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCategory(nTracers,:) = levelIceVolume(1,:,iCell) + tracerArrayCategory(nTracers,:) = levelIceVolume(1,:,iCell) nTracers = nTracers + 1 endif @@ -5553,27 +6570,43 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCategory(nTracers,:) = pondArea(1,:,iCell) + tracerArrayCategory(nTracers,:) = pondArea(1,:,iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCategory(nTracers,:) = pondDepth(1,:,iCell) + tracerArrayCategory(nTracers,:) = pondDepth(1,:,iCell) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCategory(nTracers,:) = pondLidThickness(1,:,iCell) + tracerArrayCategory(nTracers,:) = pondLidThickness(1,:,iCell) nTracers = nTracers + 1 end if + ! snow density (ice mass, liquid mass, density) + if (config_use_effective_snow_density) then + tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowIceMass(:,:,iCell) + nTracers = nTracers + nSnowLayers + tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowLiquidMass(:,:,iCell) + nTracers = nTracers + nSnowLayers + tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowDensity(:,:,iCell) + nTracers = nTracers + nSnowLayers + endif + + ! snow grain radius + if (config_use_snow_grain_radius) then + tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowGrainRadius(:,:,iCell) + nTracers = nTracers + nSnowLayers + endif + ! aerosols if (config_use_aerosols) then do iAerosol = 1, nAerosols - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) = snowScatteringAerosol(iAerosol,:,iCell) - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) = snowBodyAerosol(iAerosol,:,iCell) - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) = iceScatteringAerosol(iAerosol,:,iCell) - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) = iceBodyAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) = snowScatteringAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) = snowBodyAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) = iceScatteringAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) = iceBodyAerosol(iAerosol,:,iCell) enddo ! iAerosol endif @@ -5592,13 +6625,13 @@ end subroutine set_cice_physics_tracer_array_category ! !----------------------------------------------------------------------- - subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) + subroutine get_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(in) :: & - tracerObject + real(kind=RKIND), dimension(:,:), intent(in) :: & + tracerArrayCategory integer, intent(in) :: & iCell @@ -5610,7 +6643,10 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & - config_use_aerosols + config_use_aerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius + integer, pointer :: & nIceLayers, & @@ -5635,7 +6671,11 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) snowScatteringAerosol, & snowBodyAerosol, & iceScatteringAerosol, & - iceBodyAerosol + iceBodyAerosol, & + snowIceMass, & + snowLiquidMass, & + snowGrainRadius, & + snowDensity integer :: & nTracers, & @@ -5648,6 +6688,8 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) call MPAS_pool_get_config(block % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(block % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(block % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(block % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(block % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_dimension(block % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_dimension(block % dimensions, "nSnowLayers", nSnowLayers) @@ -5670,42 +6712,46 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) call MPAS_pool_get_array(tracers, "snowBodyAerosol", snowBodyAerosol, 1) call MPAS_pool_get_array(tracers, "iceScatteringAerosol", iceScatteringAerosol, 1) call MPAS_pool_get_array(tracers, "iceBodyAerosol", iceBodyAerosol, 1) + call MPAS_pool_get_array(tracers, "snowIceMass", snowIceMass, 1) + call MPAS_pool_get_array(tracers, "snowLiquidMass", snowLiquidMass, 1) + call MPAS_pool_get_array(tracers, "snowDensity", snowDensity, 1) + call MPAS_pool_get_array(tracers, "snowGrainRadius", snowGrainRadius, 1) nTracers = 1 ! surfaceTemperature - surfaceTemperature(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + surfaceTemperature(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 ! iceEnthalpy - iceEnthalpy(:,:,iCell) = tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) + iceEnthalpy(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) nTracers = nTracers + nIceLayers ! snowEnthalpy - snowEnthalpy(:,:,iCell) = tracerObject % tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) + snowEnthalpy(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) nTracers = nTracers + nSnowLayers ! ice Salinity - iceSalinity(:,:,iCell) = tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) + iceSalinity(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - iceAge(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + iceAge(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - firstYearIceArea(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + firstYearIceArea(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - levelIceArea(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + levelIceArea(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 - levelIceVolume(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + levelIceVolume(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif @@ -5713,27 +6759,43 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondArea(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + pondArea(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 - pondDepth(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + pondDepth(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondLidThickness(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + pondLidThickness(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 end if + ! snow density (ice mass, liquid mass, density) + if (config_use_effective_snow_density) then + snowIceMass(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) + nTracers = nTracers + nSnowLayers + snowLiquidMass(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) + nTracers = nTracers + nSnowLayers + snowDensity(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) + nTracers = nTracers + nSnowLayers + endif + + ! snow grain radius + if (config_use_snow_grain_radius) then + snowGrainRadius(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) + nTracers = nTracers + nSnowLayers + endif + ! aerosols if (config_use_aerosols) then do iAerosol = 1, nAerosols - snowScatteringAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) - snowBodyAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) - iceScatteringAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) - iceBodyAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) + snowScatteringAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) + snowBodyAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) + iceScatteringAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) + iceBodyAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) enddo ! iAerosol endif @@ -5752,13 +6814,13 @@ end subroutine get_cice_physics_tracer_array_category ! !----------------------------------------------------------------------- - subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) + subroutine set_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & - tracerObject + real(kind=RKIND), dimension(:), intent(inout) :: & + tracerArrayCell integer, intent(in) :: & iCell @@ -5770,7 +6832,9 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & - config_use_aerosols + config_use_aerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius integer, pointer :: & nIceLayers, & @@ -5797,7 +6861,11 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) snowScatteringAerosolCell, & snowBodyAerosolCell, & iceScatteringAerosolCell, & - iceBodyAerosolCell + iceBodyAerosolCell, & + snowIceMassCell, & + snowLiquidMassCell, & + snowGrainRadiusCell, & + snowDensityCell integer :: & nTracers, & @@ -5810,6 +6878,8 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) call MPAS_pool_get_config(block % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(block % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(block % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(block % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(block % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_dimension(block % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_dimension(block % dimensions, "nSnowLayers", nSnowLayers) @@ -5832,42 +6902,46 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) call MPAS_pool_get_array(tracers_aggregate, "snowBodyAerosolCell", snowBodyAerosolCell) call MPAS_pool_get_array(tracers_aggregate, "iceScatteringAerosolCell", iceScatteringAerosolCell) call MPAS_pool_get_array(tracers_aggregate, "iceBodyAerosolCell", iceBodyAerosolCell) + call MPAS_pool_get_array(tracers_aggregate, "snowIceMassCell", snowIceMassCell) + call MPAS_pool_get_array(tracers_aggregate, "snowLiquidMassCell", snowLiquidMassCell) + call MPAS_pool_get_array(tracers_aggregate, "snowDensityCell", snowDensityCell) + call MPAS_pool_get_array(tracers_aggregate, "snowGrainRadiusCell", snowGrainRadiusCell) nTracers = 1 ! surfaceTemperature - tracerObject % tracerArrayCell(nTracers) = surfaceTemperatureCell(iCell) + tracerArrayCell(nTracers) = surfaceTemperatureCell(iCell) nTracers = nTracers + 1 ! iceEnthalpy - tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceEnthalpyCell(:,iCell) + tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceEnthalpyCell(:,iCell) nTracers = nTracers + nIceLayers ! snowEnthalpy - tracerObject % tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowEnthalpyCell(:,iCell) + tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowEnthalpyCell(:,iCell) nTracers = nTracers + nSnowLayers ! ice Salinity - tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceSalinityCell(:,iCell) + tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceSalinityCell(:,iCell) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - tracerObject % tracerArrayCell(nTracers) = iceAgeCell(iCell) + tracerArrayCell(nTracers) = iceAgeCell(iCell) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - tracerObject % tracerArrayCell(nTracers) = firstYearIceAreaCell(iCell) + tracerArrayCell(nTracers) = firstYearIceAreaCell(iCell) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - tracerObject % tracerArrayCell(nTracers) = levelIceAreaCell(iCell) + tracerArrayCell(nTracers) = levelIceAreaCell(iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCell(nTracers) = levelIceVolumeCell(iCell) + tracerArrayCell(nTracers) = levelIceVolumeCell(iCell) nTracers = nTracers + 1 endif @@ -5875,27 +6949,43 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCell(nTracers) = pondAreaCell(iCell) + tracerArrayCell(nTracers) = pondAreaCell(iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCell(nTracers) = pondDepthCell(iCell) + tracerArrayCell(nTracers) = pondDepthCell(iCell) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCell(nTracers) = pondLidThicknessCell(iCell) + tracerArrayCell(nTracers) = pondLidThicknessCell(iCell) nTracers = nTracers + 1 end if + ! snow density (ice mass, liquid mass, density) + if (config_use_effective_snow_density) then + tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowIceMassCell(:,iCell) + nTracers = nTracers + nSnowLayers + tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowLiquidMassCell(:,iCell) + nTracers = nTracers + nSnowLayers + tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowDensityCell(:,iCell) + nTracers = nTracers + nSnowLayers + endif + + ! snow grain radius + if (config_use_snow_grain_radius) then + tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowGrainRadiusCell(:,iCell) + nTracers = nTracers + nSnowLayers + endif + ! aerosols if (config_use_aerosols) then do iAerosol = 1, nAerosols - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1) ) = snowScatteringAerosolCell(iAerosol,iCell) - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+1) = snowBodyAerosolCell(iAerosol,iCell) - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+2) = iceScatteringAerosolCell(iAerosol,iCell) - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+3) = iceBodyAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1) ) = snowScatteringAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1)+1) = snowBodyAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1)+2) = iceScatteringAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1)+3) = iceBodyAerosolCell(iAerosol,iCell) enddo ! iAerosol endif @@ -5914,13 +7004,13 @@ end subroutine set_cice_physics_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) + subroutine get_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(in) :: & - tracerObject + real(kind=RKIND), dimension(:), intent(in) :: & + tracerArrayCell integer, intent(in) :: & iCell @@ -5932,7 +7022,9 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & - config_use_aerosols + config_use_aerosols, & + config_use_effective_snow_density, & + config_use_snow_grain_radius integer, pointer :: & nIceLayers, & @@ -5959,7 +7051,11 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) snowScatteringAerosolCell, & snowBodyAerosolCell, & iceScatteringAerosolCell, & - iceBodyAerosolCell + iceBodyAerosolCell, & + snowIceMassCell, & + snowLiquidMassCell, & + snowGrainRadiusCell, & + snowDensityCell integer :: & nTracers, & @@ -5972,6 +7068,8 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) call MPAS_pool_get_config(block % configs, "config_use_level_meltponds", config_use_level_meltponds) call MPAS_pool_get_config(block % configs, "config_use_topo_meltponds", config_use_topo_meltponds) call MPAS_pool_get_config(block % configs, "config_use_aerosols", config_use_aerosols) + call MPAS_pool_get_config(block % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(block % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) call MPAS_pool_get_dimension(block % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_dimension(block % dimensions, "nSnowLayers", nSnowLayers) @@ -5994,42 +7092,46 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) call MPAS_pool_get_array(tracers_aggregate, "snowBodyAerosolCell", snowBodyAerosolCell) call MPAS_pool_get_array(tracers_aggregate, "iceScatteringAerosolCell", iceScatteringAerosolCell) call MPAS_pool_get_array(tracers_aggregate, "iceBodyAerosolCell", iceBodyAerosolCell) + call MPAS_pool_get_array(tracers_aggregate, "snowIceMassCell", snowIceMassCell) + call MPAS_pool_get_array(tracers_aggregate, "snowLiquidMassCell", snowLiquidMassCell) + call MPAS_pool_get_array(tracers_aggregate, "snowDensityCell", snowDensityCell) + call MPAS_pool_get_array(tracers_aggregate, "snowGrainRadiusCell", snowGrainRadiusCell) nTracers = 1 ! surfaceTemperature - surfaceTemperatureCell(iCell) = tracerObject % tracerArrayCell(nTracers) + surfaceTemperatureCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 ! iceEnthalpy - iceEnthalpyCell(:,iCell) = tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) + iceEnthalpyCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nIceLayers-1) nTracers = nTracers + nIceLayers ! snowEnthalpy - snowEnthalpyCell(:,iCell) = tracerObject % tracerArrayCell(nTracers:nTracers+nSnowLayers-1) + snowEnthalpyCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nSnowLayers-1) nTracers = nTracers + nSnowLayers ! ice Salinity - iceSalinityCell(:,iCell) = tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) + iceSalinityCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nIceLayers-1) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - iceAgeCell(iCell) = tracerObject % tracerArrayCell(nTracers) + iceAgeCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - firstYearIceAreaCell(iCell) = tracerObject % tracerArrayCell(nTracers) + firstYearIceAreaCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - levelIceAreaCell(iCell) = tracerObject % tracerArrayCell(nTracers) + levelIceAreaCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 - levelIceVolumeCell(iCell) = tracerObject % tracerArrayCell(nTracers) + levelIceVolumeCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif @@ -6037,27 +7139,43 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondAreaCell(iCell) = tracerObject % tracerArrayCell(nTracers) + pondAreaCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 - pondDepthCell(iCell) = tracerObject % tracerArrayCell(nTracers) + pondDepthCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondLidThicknessCell(iCell) = tracerObject % tracerArrayCell(nTracers) + pondLidThicknessCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 end if + ! snow density (ice mass, liquid mass, density) + if (config_use_effective_snow_density) then + snowIceMassCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nSnowLayers-1) + nTracers = nTracers + nSnowLayers + snowLiquidMassCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nSnowLayers-1) + nTracers = nTracers + nSnowLayers + snowDensityCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nSnowLayers-1) + nTracers = nTracers + nSnowLayers + endif + + ! snow grain radius + if (config_use_snow_grain_radius) then + snowGrainRadiusCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nSnowLayers-1) + nTracers = nTracers + nSnowLayers + endif + ! aerosols if (config_use_aerosols) then do iAerosol = 1, nAerosols - snowScatteringAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1) ) - snowBodyAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+1) - iceScatteringAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+2) - iceBodyAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+3) + snowScatteringAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1) ) + snowBodyAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1)+1) + iceScatteringAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1)+2) + iceBodyAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1)+3) enddo ! iAerosol endif @@ -6076,14 +7194,17 @@ end subroutine get_cice_physics_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(inout) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -6222,69 +7343,69 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! brine height fraction if (config_use_brine) & - tracerObject % tracerArrayCategory(tracerObject % index_brineFraction,:) = brineFraction(1,:,iCell) + tracerArrayCategory(tracerObject % index_brineFraction,:) = brineFraction(1,:,iCell) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) = & + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) = & skeletalAlgaeConc(iBioTracers,:,iCell) enddo ! nitrate if (config_use_nitrate) & - tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc,:) = skeletalNitrateConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_nitrateConc,:) = skeletalNitrateConc(1,:,iCell) ! DOC if (config_use_carbon) then do iBioTracers = 1, nDOC - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) = skeletalDOCConc(iBioTracers,:,iCell) + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) = skeletalDOCConc(iBioTracers,:,iCell) enddo ! DIC do iBioTracers = 1, nDIC - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) = skeletalDICConc(iBioTracers,:,iCell) + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) = skeletalDICConc(iBioTracers,:,iCell) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) = skeletalDONConc(iBioTracers,:,iCell) + tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) = skeletalDONConc(iBioTracers,:,iCell) enddo endif ! ammonium if (config_use_ammonium) & - tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc,:) = skeletalAmmoniumConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_ammoniumConc,:) = skeletalAmmoniumConc(1,:,iCell) ! silicate if (config_use_silicate) & - tracerObject % tracerArrayCategory(tracerObject % index_silicateConc,:) = skeletalSilicateConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_silicateConc,:) = skeletalSilicateConc(1,:,iCell) ! DMS, DMSPp, DMSPd if (config_use_DMS) then - tracerObject % tracerArrayCategory(tracerObject % index_DMSConc,:) = skeletalDMSConc(1,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc,:) = skeletalDMSPpConc(1,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc,:) = skeletalDMSPdConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_DMSConc,:) = skeletalDMSConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPpConc,:) = skeletalDMSPpConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPdConc,:) = skeletalDMSPdConc(1,:,iCell) endif ! nonreactive mobile tracer if (config_use_nonreactive) & - tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc,:) = skeletalNonreactiveConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_nonreactiveConc,:) = skeletalNonreactiveConc(1,:,iCell) ! humic material if (config_use_humics) & - tracerObject % tracerArrayCategory(tracerObject % index_humicsConc,:) = skeletalHumicsConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_humicsConc,:) = skeletalHumicsConc(1,:,iCell) ! Particulate and dissovled Iron if (config_use_iron) then do iBioTracers = 1, nParticulateIron - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) = & + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) = & skeletalParticulateIronConc(iBioTracers,:,iCell) enddo do iBioTracers = 1, nDissolvedIron - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) = & + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) = & skeletalDissolvedIronConc(iBioTracers,:,iCell) enddo endif @@ -6293,7 +7414,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! Fraction of biogeochemical tracer in the mobile phase do iLayers = 1, tracerObject % nBioTracers - tracerObject % tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) = mobileFraction(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) = mobileFraction(iLayers,:,iCell) enddo ! algal nitrogen @@ -6302,7 +7423,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nAlgae do iLayers = 1,nBioLayersP3 iBiocount = iBiocount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) = & verticalAlgaeConc(iBioCount,:,iCell) enddo enddo @@ -6311,7 +7432,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! nitrate if (config_use_nitrate) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) = & verticalNitrateConc(iLayers,:,iCell) enddo endif @@ -6322,7 +7443,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDOC do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) = & verticalDOCConc(iBioCount,:,iCell) enddo enddo @@ -6332,7 +7453,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDIC do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) = & verticalDICConc(iBioCount,:,iCell) enddo enddo @@ -6344,7 +7465,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDON do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) = & verticalDONConc(iBioCount,:,iCell) enddo enddo @@ -6353,7 +7474,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! ammonium if (config_use_ammonium) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) = & verticalAmmoniumConc(iLayers,:,iCell) enddo endif @@ -6361,7 +7482,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! silicate if (config_use_silicate) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) = & verticalSilicateConc(iLayers,:,iCell) enddo endif @@ -6369,16 +7490,16 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) = verticalDMSConc(iLayers,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) = verticalDMSPpConc(iLayers,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) = verticalDMSPdConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) = verticalDMSConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) = verticalDMSPpConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) = verticalDMSPdConc(iLayers,:,iCell) enddo endif ! nonreactive purely mobile tracers if (config_use_nonreactive) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) = & verticalNonreactiveConc(iLayers,:,iCell) enddo endif @@ -6386,7 +7507,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) = verticalHumicsConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) = verticalHumicsConc(iLayers,:,iCell) enddo endif @@ -6396,7 +7517,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nParticulateIron do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) = & verticalParticulateIronConc(iBioCount,:,iCell) enddo enddo @@ -6404,7 +7525,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDissolvedIron do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) = & verticalDissolvedIronConc(iBioCount,:,iCell) enddo enddo @@ -6416,7 +7537,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nzAerosols do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) = & verticalAerosolsConc(iBioCount,:,iCell) enddo enddo @@ -6425,7 +7546,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! salinity used with BL99 thermodynamics if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers - tracerObject % tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) = & verticalSalinity(iLayers,:,iCell) enddo endif @@ -6445,7 +7566,7 @@ end subroutine set_cice_biogeochemistry_tracer_array_category ! !----------------------------------------------------------------------- - subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) type(block_type), intent(inout) :: & block @@ -6453,6 +7574,9 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(in) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -6590,74 +7714,74 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! biogeochemistry ! brine height fraction if (config_use_brine) & - brineFraction(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_brineFraction,:) + brineFraction(1,:,iCell) = tracerArrayCategory(tracerObject % index_brineFraction,:) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae skeletalAlgaeConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) enddo ! nitrate if (config_use_nitrate) & - skeletalNitrateConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc,:) + skeletalNitrateConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_nitrateConc,:) if (config_use_carbon) then ! DOC do iBioTracers = 1, nDOC skeletalDOCConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) enddo ! DIC do iBioTracers = 1, nDIC skeletalDICConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - skeletalDONConc(iBioTracers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) + skeletalDONConc(iBioTracers,:,iCell) = tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) enddo endif ! ammonium if (config_use_ammonium) & - skeletalAmmoniumConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc,:) + skeletalAmmoniumConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_ammoniumConc,:) ! silicate if (config_use_silicate) & - skeletalSilicateConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_silicateConc,:) + skeletalSilicateConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_silicateConc,:) ! DNS, DMSPp, DMSPd if (config_use_DMS) then - skeletalDMSConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSConc,:) - skeletalDMSPpConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc,:) - skeletalDMSPdConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc,:) + skeletalDMSConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_DMSConc,:) + skeletalDMSPpConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPpConc,:) + skeletalDMSPdConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPdConc,:) endif ! nonreactive tracer if (config_use_nonreactive) & - skeletalNonreactiveConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc,:) + skeletalNonreactiveConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_nonreactiveConc,:) ! humic material if (config_use_humics) & - skeletalHumicsConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_humicsConc,:) + skeletalHumicsConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_humicsConc,:) if (config_use_iron) then ! Particulate Iron do iBioTracers = 1, nParticulateIron skeletalParticulateIronConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) enddo ! Dissolved Iron do iBioTracers = 1, nDissolvedIron skeletalDissolvedIronConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) enddo endif @@ -6665,7 +7789,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! fraction of biogeochemical tracer in the mobile phase do iLayers = 1, tracerObject % nBioTracers - mobileFraction(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) + mobileFraction(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) enddo if (config_use_vertical_biochemistry) then @@ -6676,7 +7800,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBiocount = iBiocount + 1 verticalAlgaeConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) enddo enddo endif @@ -6685,7 +7809,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_nitrate) then do iLayers = 1, nBioLayersP3 verticalNitrateConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) + tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) enddo endif @@ -6697,7 +7821,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDOCConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) enddo enddo iBioCount = 0 @@ -6707,7 +7831,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDICConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) enddo enddo endif @@ -6719,7 +7843,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDONConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) + tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) enddo enddo endif @@ -6728,7 +7852,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_ammonium) then do iLayers = 1, nBioLayersP3 verticalAmmoniumConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) + tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) enddo endif @@ -6736,16 +7860,16 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_silicate) then do iLayers = 1, nBioLayersP3 verticalSilicateConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) + tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) enddo endif ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP3 - verticalDMSConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) - verticalDMSPpConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) - verticalDMSPdConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) + verticalDMSConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) + verticalDMSPpConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) + verticalDMSPdConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) enddo endif @@ -6753,14 +7877,14 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_nonreactive) then do iLayers = 1, nBioLayersP3 verticalNonreactiveConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) + tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) enddo endif ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP3 - verticalHumicsConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) + verticalHumicsConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) enddo endif @@ -6772,7 +7896,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalParticulateIronConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) enddo enddo iBioCount = 0 @@ -6782,7 +7906,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDissolvedIronConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) enddo enddo endif @@ -6794,7 +7918,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalAerosolsConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) enddo enddo endif @@ -6803,7 +7927,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers verticalSalinity(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) + tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) enddo endif endif @@ -6822,14 +7946,17 @@ end subroutine get_cice_biogeochemistry_tracer_array_category ! !----------------------------------------------------------------------- - subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(inout) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -7002,72 +8129,72 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! biogeochemistry ! brine height fraction if (config_use_brine) & - tracerObject % tracerArrayCell(tracerObject % index_brineFraction) = brineFractionCell(iCell) + tracerArrayCell(tracerObject % index_brineFraction) = brineFractionCell(iCell) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) = skeletalAlgaeConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) = skeletalAlgaeConcCell(iBioTracers,iCell) enddo ! nitrate if (config_use_nitrate) & - tracerObject % tracerArrayCell(tracerObject % index_nitrateConc) = skeletalNitrateConcCell(1,iCell) + tracerArrayCell(tracerObject % index_nitrateConc) = skeletalNitrateConcCell(1,iCell) if (config_use_carbon) then ! DOC do iBioTracers = 1, nDOC - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) = skeletalDOCConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) = skeletalDOCConcCell(iBioTracers,iCell) enddo ! DIC do iBioTracers = 1, nDIC - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) = skeletalDICConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) = skeletalDICConcCell(iBioTracers,iCell) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) = skeletalDONConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) = skeletalDONConcCell(iBioTracers,iCell) enddo endif ! ammonium if (config_use_ammonium) & - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc) = skeletalAmmoniumConcCell(1,iCell) + tracerArrayCell(tracerObject % index_ammoniumConc) = skeletalAmmoniumConcCell(1,iCell) ! silicate if (config_use_silicate) & - tracerObject % tracerArrayCell(tracerObject % index_silicateConc) = skeletalSilicateConcCell(1,iCell) + tracerArrayCell(tracerObject % index_silicateConc) = skeletalSilicateConcCell(1,iCell) ! DMS, DMSPp, DMSPd if (config_use_DMS) then - tracerObject % tracerArrayCell(tracerObject % index_DMSConc) = skeletalDMSConcCell(1,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc) = skeletalDMSPpConcCell(1,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc) = skeletalDMSPdConcCell(1,iCell) + tracerArrayCell(tracerObject % index_DMSConc) = skeletalDMSConcCell(1,iCell) + tracerArrayCell(tracerObject % index_DMSPpConc) = skeletalDMSPpConcCell(1,iCell) + tracerArrayCell(tracerObject % index_DMSPdConc) = skeletalDMSPdConcCell(1,iCell) endif ! nonreactive tracer if (config_use_nonreactive) & - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc) = skeletalNonreactiveConcCell(1,iCell) + tracerArrayCell(tracerObject % index_nonreactiveConc) = skeletalNonreactiveConcCell(1,iCell) ! humic material if (config_use_humics) & - tracerObject % tracerArrayCell(tracerObject % index_humicsConc) = skeletalHumicsConcCell(1,iCell) + tracerArrayCell(tracerObject % index_humicsConc) = skeletalHumicsConcCell(1,iCell) if (config_use_iron) then ! particulate iron do iBioTracers = 1, nParticulateIron - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) = & + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) = & skeletalParticulateIronConcCell(iBioTracers,iCell) enddo ! dissolved iron do iBioTracers = 1, nDissolvedIron - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) = & + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) = & skeletalDissolvedIronConcCell(iBioTracers,iCell) enddo endif @@ -7083,13 +8210,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBiocount = iBiocount + 1 - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & verticalAlgaeConcCell(iBioCount,iCell) verticalAlgaeIceCell(iLayers+iIceCount,iCell) = verticalAlgaeConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBiocount = iBiocount + 1 - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & verticalAlgaeConcCell(iBioCount,iCell) enddo enddo @@ -7098,11 +8225,11 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! nitrate if (config_use_nitrate) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) verticalNitrateIceCell(iLayers,iCell) = verticalNitrateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) enddo endif @@ -7115,13 +8242,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & verticalDOCConcCell(iBioCount,iCell) verticalDOCIceCell(iLayers+iIceCount,iCell) = verticalDOCConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & verticalDOCConcCell(iBioCount,iCell) enddo enddo @@ -7133,13 +8260,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & verticalDICConcCell(iBioCount,iCell) verticalDICIceCell(iLayers+iIceCount,iCell) = verticalDICConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & verticalDICConcCell(iBioCount,iCell) enddo enddo @@ -7153,13 +8280,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & verticalDONConcCell(iBioCount,iCell) verticalDONIceCell(iLayers+iIceCount,iCell) = verticalDONConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & verticalDONConcCell(iBioCount,iCell) enddo enddo @@ -7168,12 +8295,12 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! ammonium if (config_use_ammonium) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & verticalAmmoniumConcCell(iLayers,iCell) verticalAmmoniumIceCell(iLayers,iCell) = verticalAmmoniumConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & verticalAmmoniumConcCell(iLayers,iCell) enddo endif @@ -7181,40 +8308,40 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! silicate if (config_use_silicate) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) verticalSilicateIceCell(iLayers,iCell) = verticalSilicateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) enddo endif ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) verticalDMSIceCell(iLayers,iCell) = verticalDMSConcCell(iLayers,iCell) verticalDMSPpIceCell(iLayers,iCell) = verticalDMSPpConcCell(iLayers,iCell) verticalDMSPdIceCell(iLayers,iCell) = verticalDMSPdConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) enddo endif ! nonreactive if (config_use_nonreactive) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & verticalNonreactiveConcCell(iLayers,iCell) verticalNonreactiveIceCell(iLayers,iCell) = verticalNonreactiveConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & verticalNonreactiveConcCell(iLayers,iCell) enddo endif @@ -7222,11 +8349,11 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) verticalHumicsIceCell(iLayers,iCell) = verticalHumicsConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) enddo endif @@ -7239,13 +8366,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & verticalParticulateIronConcCell(iBioCount,iCell) verticalParticulateIronIceCell(iLayers+iIceCount,iCell) = verticalParticulateIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & verticalParticulateIronConcCell(iBioCount,iCell) enddo enddo @@ -7257,13 +8384,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & verticalDissolvedIronConcCell(iBioCount,iCell) verticalDissolvedIronIceCell(iLayers+iIceCount,iCell) = verticalDissolvedIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & verticalDissolvedIronConcCell(iBioCount,iCell) enddo enddo @@ -7277,13 +8404,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & verticalAerosolsConcCell(iBioCount,iCell) verticalAerosolsIceCell(iLayers+iIceCount,iCell) = verticalAerosolsConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & verticalAerosolsConcCell(iBioCount,iCell) enddo enddo @@ -7292,7 +8419,7 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! salinity for use with BL99 thermodynamics if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers - tracerObject % tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) = verticalSalinityCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) = verticalSalinityCell(iLayers,iCell) enddo endif endif @@ -7311,7 +8438,7 @@ end subroutine set_cice_biogeochemistry_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) type(block_type), intent(inout) :: & block @@ -7319,6 +8446,9 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(in) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -7402,13 +8532,15 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell verticalHumicsIceCell, & verticalParticulateIronIceCell, & verticalDissolvedIronIceCell, & - verticalAerosolsIceCell + verticalAerosolsIceCell, & + verticalAerosolsSnowCell integer :: & iBioTracers, & iBioCount, & iLayers, & - iIceCount + iIceCount, & + iSnowCount call MPAS_pool_get_config(block % configs, "config_use_skeletal_biochemistry", config_use_skeletal_biochemistry) call MPAS_pool_get_config(block % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) @@ -7483,79 +8615,80 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell call MPAS_pool_get_array(tracers_aggregate, "verticalParticulateIronIceCell", verticalParticulateIronIceCell) call MPAS_pool_get_array(tracers_aggregate, "verticalDissolvedIronIceCell", verticalDissolvedIronIceCell) call MPAS_pool_get_array(tracers_aggregate, "verticalAerosolsIceCell", verticalAerosolsIceCell) + call MPAS_pool_get_array(tracers_aggregate, "verticalAerosolsSnowCell", verticalAerosolsSnowCell) call MPAS_pool_get_array(tracers_aggregate, "verticalSalinityCell", verticalSalinityCell) call MPAS_pool_get_array(tracers_aggregate, "brineFractionCell", brineFractionCell) ! biogeochemistry ! brine height fraction if (config_use_brine) & - brineFractionCell(iCell) = tracerObject % tracerArrayCell(tracerObject % index_brineFraction) + brineFractionCell(iCell) = tracerArrayCell(tracerObject % index_brineFraction) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae - skeletalAlgaeConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) + skeletalAlgaeConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) enddo ! nitrate if (config_use_nitrate) & - skeletalNitrateConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nitrateConc) + skeletalNitrateConcCell(1,iCell) = tracerArrayCell(tracerObject % index_nitrateConc) if (config_use_carbon) then ! DOC do iBioTracers = 1, nDOC - skeletalDOCConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) + skeletalDOCConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) enddo ! DIC do iBioTracers = 1, nDIC - skeletalDICConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) + skeletalDICConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - skeletalDONConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) + skeletalDONConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) enddo endif ! ammonium if (config_use_ammonium) & - skeletalAmmoniumConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc) + skeletalAmmoniumConcCell(1,iCell) = tracerArrayCell(tracerObject % index_ammoniumConc) ! silicate if (config_use_silicate) & - skeletalSilicateConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_silicateConc) + skeletalSilicateConcCell(1,iCell) = tracerArrayCell(tracerObject % index_silicateConc) ! DMS if (config_use_DMS) then - skeletalDMSConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSConc) - skeletalDMSPpConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc) - skeletalDMSPdConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc) + skeletalDMSConcCell(1,iCell) = tracerArrayCell(tracerObject % index_DMSConc) + skeletalDMSPpConcCell(1,iCell) = tracerArrayCell(tracerObject % index_DMSPpConc) + skeletalDMSPdConcCell(1,iCell) = tracerArrayCell(tracerObject % index_DMSPdConc) endif ! nonreactive tracer if (config_use_nonreactive) & - skeletalNonreactiveConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc) + skeletalNonreactiveConcCell(1,iCell) = tracerArrayCell(tracerObject % index_nonreactiveConc) ! humic material if (config_use_humics) & - skeletalHumicsConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_humicsConc) + skeletalHumicsConcCell(1,iCell) = tracerArrayCell(tracerObject % index_humicsConc) if (config_use_iron) then ! particulate iron do iBioTracers = 1, nParticulateIron skeletalParticulateIronConcCell(iBioTracers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) enddo ! dissolved iron do iBioTracers = 1, nDissolvedIron skeletalDissolvedIronConcCell(iBioTracers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) enddo endif @@ -7571,13 +8704,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBiocount = iBiocount + 1 verticalAlgaeConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) verticalAlgaeIceCell(iLayers+iIceCount,iCell) = verticalAlgaeConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBiocount = iBiocount + 1 verticalAlgaeConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) enddo enddo endif @@ -7585,11 +8718,11 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! nitrate if (config_use_nitrate) then do iLayers = 1, nBioLayersP1 - verticalNitrateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) + verticalNitrateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) verticalNitrateIceCell(iLayers,iCell) = verticalNitrateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalNitrateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) + verticalNitrateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) enddo endif @@ -7603,13 +8736,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDOCConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) verticalDOCIceCell(iLayers+iIceCount,iCell) = verticalDOCConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDOCConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) enddo enddo iBioCount = 0 @@ -7621,13 +8754,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDICConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) verticalDICIceCell(iLayers+iIceCount,iCell) = verticalDICConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDICConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) enddo enddo endif @@ -7641,13 +8774,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDONConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) verticalDONIceCell(iLayers+iIceCount,iCell) = verticalDONConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDONConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) enddo enddo endif @@ -7656,40 +8789,40 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell if (config_use_ammonium) then do iLayers = 1, nBioLayersP1 verticalAmmoniumConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) verticalAmmoniumIceCell(iLayers,iCell) = verticalAmmoniumConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 verticalAmmoniumConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) enddo endif ! silicate if (config_use_silicate) then do iLayers = 1, nBioLayersP1 - verticalSilicateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) + verticalSilicateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) verticalSilicateIceCell(iLayers,iCell) = verticalSilicateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalSilicateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) + verticalSilicateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) enddo endif ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP1 - verticalDMSConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) - verticalDMSPpConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) - verticalDMSPdConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) + verticalDMSConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) + verticalDMSPpConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) + verticalDMSPdConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) verticalDMSIceCell(iLayers,iCell) = verticalDMSConcCell(iLayers,iCell) verticalDMSPpIceCell(iLayers,iCell) = verticalDMSPpConcCell(iLayers,iCell) verticalDMSPdIceCell(iLayers,iCell) = verticalDMSPdConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalDMSConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) - verticalDMSPpConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) - verticalDMSPdConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) + verticalDMSConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) + verticalDMSPpConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) + verticalDMSPdConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) enddo endif @@ -7697,23 +8830,23 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell if (config_use_nonreactive) then do iLayers = 1, nBioLayersP1 verticalNonreactiveConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) verticalNonreactiveIceCell(iLayers,iCell) = verticalNonreactiveConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 verticalNonreactiveConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) enddo endif ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP1 - verticalHumicsConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) + verticalHumicsConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) verticalHumicsIceCell(iLayers,iCell) = verticalHumicsConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalHumicsConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) + verticalHumicsConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) enddo endif @@ -7727,13 +8860,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalParticulateIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) verticalDissolvedIronIceCell(iLayers+iIceCount,iCell) = verticalDissolvedIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalParticulateIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) enddo enddo iBioCount = 0 @@ -7745,13 +8878,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDissolvedIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) verticalDissolvedIronIceCell(iLayers+iIceCount,iCell) = verticalDissolvedIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDissolvedIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) enddo enddo endif @@ -7761,17 +8894,20 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell iBioCount = 0 do iBioTracers = 1, nzAerosols iIceCount = (iBioTracers-1)*nBioLayersP1 + iSnowCount = (iBioTracers-1)*2 do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalAerosolsConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) verticalAerosolsIceCell(iLayers+iIceCount,iCell) = verticalAerosolsConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalAerosolsConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) + verticalAerosolsSnowCell(iLayers-nBioLayersP1+iSnowCount,iCell) = & + verticalAerosolsConcCell(iBioCount,iCell) enddo enddo endif @@ -7779,7 +8915,7 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! salinity for use with BL99 thermodynamics if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers - verticalSalinityCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) + verticalSalinityCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) enddo endif endif @@ -7847,7 +8983,11 @@ subroutine check_column_package_configs(domain) domain integer, pointer :: & - nCategories + nCategories, & + nSnowLayers, & + nIceLayers, & + config_nSnowLayers, & + config_nIceLayers character(len=strKIND), pointer :: & config_thermodynamics_type, & @@ -7904,6 +9044,8 @@ subroutine check_column_package_configs(domain) config_snow_to_ice_transition_depth call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nCategories", nCategories) + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nSnowLayers", nSnowLayers) + call MPAS_pool_get_dimension(domain % blocklist % dimensions, "nIceLayers", nIceLayers) call MPAS_pool_get_config(domain % configs, "config_thermodynamics_type", config_thermodynamics_type) call MPAS_pool_get_config(domain % configs, "config_heat_conductivity_type", config_heat_conductivity_type) @@ -7947,6 +9089,8 @@ subroutine check_column_package_configs(domain) call MPAS_pool_get_config(domain % configs, "config_use_modal_aerosols", config_use_modal_aerosols) call MPAS_pool_get_config(domain % configs, "config_use_zaerosols", config_use_zaerosols) call MPAS_pool_get_config(domain % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + call MPAS_pool_get_config(domain % configs, "config_nSnowLayers", config_nSnowLayers) + call MPAS_pool_get_config(domain % configs, "config_nIceLayers", config_nIceLayers) !----------------------------------------------------------------------- ! Check values @@ -8025,6 +9169,18 @@ subroutine check_column_package_configs(domain) call config_error("config_pond_refreezing_type", config_pond_refreezing_type, "'cesm' or 'hlid'") endif + ! check for consistency in snow vertical dimension + if (config_nSnowLayers /= nSnowlayers) & + call mpas_log_write(& + 'Check for inconsistencies in restart file: config_nSnowLayers /= nSnowLayers', & + messageType=MPAS_LOG_CRIT) + + ! check for consistency in ice vertical dimension + if (config_nIceLayers /= nIcelayers) & + call mpas_log_write(& + 'Check for inconsistencies in restart file: config_nIceLayers /= nIceLayers', & + messageType=MPAS_LOG_CRIT) + !----------------------------------------------------------------------- ! Check combinations !----------------------------------------------------------------------- @@ -8259,7 +9415,9 @@ subroutine init_column_package_tracer_flags(domain) config_use_humics, & config_use_nonreactive, & config_use_vertical_biochemistry, & - config_use_skeletal_biochemistry + config_use_skeletal_biochemistry, & + config_use_effective_snow_density, & + config_use_snow_grain_radius logical :: & use_meltponds, & @@ -8287,6 +9445,8 @@ subroutine init_column_package_tracer_flags(domain) call MPAS_pool_get_config(domain % configs, "config_use_nonreactive", config_use_nonreactive) call MPAS_pool_get_config(domain % configs, "config_use_skeletal_biochemistry", config_use_skeletal_biochemistry) call MPAS_pool_get_config(domain % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) + call MPAS_pool_get_config(domain % configs, "config_use_effective_snow_density", config_use_effective_snow_density) + call MPAS_pool_get_config(domain % configs, "config_use_snow_grain_radius", config_use_snow_grain_radius) use_nitrogen = .false. if (config_use_skeletal_biochemistry .or. config_use_vertical_biochemistry) & @@ -8302,6 +9462,8 @@ subroutine init_column_package_tracer_flags(domain) config_use_cesm_meltponds, & config_use_level_meltponds, & config_use_topo_meltponds, & + config_use_effective_snow_density, & + config_use_snow_grain_radius, & config_use_aerosols, & config_use_brine, & config_use_vertical_zsalinity, & @@ -8325,6 +9487,8 @@ subroutine init_column_package_tracer_flags(domain) !tr_pond_cesm = config_use_cesm_meltponds !tr_pond_lvl = config_use_level_meltponds !tr_pond_topo = config_use_topo_meltponds + !tr_snow = config_use_effective_snow_density + !tr_rsnw = config_use_snow_grain_radius !tr_aero = config_use_aerosols !tr_brine = config_use_brine !tr_bgc_S = config_use_vertical_zsalinity @@ -8407,6 +9571,10 @@ subroutine init_column_package_tracer_indices(tracerObject) ! nt_hpnd, & ! melt pond depth ! nt_ipnd, & ! melt pond refrozen lid thickness ! nt_aero, & ! starting index for aerosols in ice + ! nt_smice, & ! snow ice mass + ! nt_smliq, & ! snow liquid mass + ! nt_rsnw, & ! snow grain radius + ! nt_rhos, & ! snow density tracer ! nt_fbri, & ! volume fraction of ice with dynamic salt (hinS/vicen*aicen) ! nt_bgc_Nit, & ! nutrients ! nt_bgc_Am, & ! @@ -8468,6 +9636,10 @@ subroutine init_column_package_tracer_indices(tracerObject) tracerObject % index_pondDepth, & tracerObject % index_pondLidThickness, & tracerObject % index_aerosols, & + tracerObject % index_snowIceMass, & + tracerObject % index_snowLiquidMass, & + tracerObject % index_snowGrainRadius, & + tracerObject % index_snowDensity, & tracerObject % index_verticalAerosolsConc, & tracerObject % index_algaeConc, & tracerObject % index_algalCarbon, & @@ -8531,6 +9703,10 @@ subroutine init_column_package_tracer_indices(tracerObject) !nt_hpnd = tracerObject % index_pondDepth !nt_ipnd = tracerObject % index_pondLidThickness !nt_aero = tracerObject % index_aerosols + !nt_smice = tracerObject % index_snowIceMass + !nt_rsnw = tracerObject % index_snowGrainRadius + !nt_rhos = tracerObject % index_snowDensity + !nt_smliq = tracerObject % index_snowLiquidMass !nt_fbri = tracerObject % index_brineFraction !nt_zaeros = tracerObject % index_verticalAerosolsConc !nt_bgc_N = tracerObject % index_algaeConc @@ -8776,7 +9952,7 @@ subroutine init_column_package_configs(domain) ! F_abs_chl_diatoms , & ! F_abs_chl_sp , & ! F_abs_chl_phaeo , & - ! ratio_C2N_proteins + ! ratio_C2N_proteins use ice_colpkg, only: & colpkg_init_parameters @@ -8798,7 +9974,8 @@ subroutine init_column_package_configs(domain) config_pond_refreezing_type, & config_ocean_heat_transfer_type, & config_sea_freezing_temperature_type, & - config_skeletal_bgc_flux_type + config_skeletal_bgc_flux_type, & + config_snow_redistribution_scheme logical, pointer :: & config_calc_surface_temperature, & @@ -8812,7 +9989,9 @@ subroutine init_column_package_configs(domain) config_use_shortwave_bioabsorption, & config_use_skeletal_biochemistry, & config_use_vertical_zsalinity, & - config_use_modal_aerosols + config_use_modal_aerosols, & + config_use_snicar_ad, & + config_use_snow_liquid_ponds real(kind=RKIND), pointer :: & config_min_friction_velocity, & @@ -8945,6 +10124,7 @@ subroutine init_column_package_configs(domain) config_mobility_type_humics, & config_mobility_type_saccharids, & config_mobility_type_lipids, & + config_mobility_type_inorganic_carbon, & config_mobility_type_proteins, & config_mobility_type_dissolved_iron, & config_mobility_type_particulate_iron, & @@ -8963,7 +10143,13 @@ subroutine init_column_package_configs(domain) config_scales_absorption_diatoms, & config_scales_absorption_small_plankton, & config_scales_absorption_phaeocystis, & - config_ratio_C_to_N_proteins + config_ratio_C_to_N_proteins, & + config_fallen_snow_radius, & + config_new_snow_density, & + config_max_snow_density, & + config_minimum_wind_compaction, & + config_wind_compaction_factor, & + config_max_dry_snow_radius integer, pointer :: & config_boundary_layer_iteration_number @@ -8980,6 +10166,7 @@ subroutine init_column_package_configs(domain) call MPAS_pool_get_config(domain % configs, "config_slow_mode_critical_porosity", config_slow_mode_critical_porosity) call MPAS_pool_get_config(domain % configs, "config_congelation_ice_porosity", config_congelation_ice_porosity) call MPAS_pool_get_config(domain % configs, "config_shortwave_type", config_shortwave_type) + call MPAS_pool_get_config(domain % configs, "config_use_snicar_ad", config_use_snicar_ad) call MPAS_pool_get_config(domain % configs, "config_albedo_type", config_albedo_type) call MPAS_pool_get_config(domain % configs, "config_visible_ice_albedo", config_visible_ice_albedo) call MPAS_pool_get_config(domain % configs, "config_infrared_ice_albedo", config_infrared_ice_albedo) @@ -9138,6 +10325,7 @@ subroutine init_column_package_configs(domain) call MPAS_pool_get_config(domain % configs, "config_mobility_type_humics", config_mobility_type_humics) call MPAS_pool_get_config(domain % configs, "config_mobility_type_saccharids", config_mobility_type_saccharids) call MPAS_pool_get_config(domain % configs, "config_mobility_type_lipids", config_mobility_type_lipids) + call MPAS_pool_get_config(domain % configs, "config_mobility_type_inorganic_carbon", config_mobility_type_inorganic_carbon) call MPAS_pool_get_config(domain % configs, "config_mobility_type_proteins", config_mobility_type_proteins) call MPAS_pool_get_config(domain % configs, "config_mobility_type_dissolved_iron", config_mobility_type_dissolved_iron) call MPAS_pool_get_config(domain % configs, "config_mobility_type_particulate_iron", config_mobility_type_particulate_iron) @@ -9157,6 +10345,14 @@ subroutine init_column_package_configs(domain) call MPAS_pool_get_config(domain % configs, "config_scales_absorption_small_plankton", config_scales_absorption_small_plankton) call MPAS_pool_get_config(domain % configs, "config_scales_absorption_phaeocystis", config_scales_absorption_phaeocystis) call MPAS_pool_get_config(domain % configs, "config_ratio_C_to_N_proteins", config_ratio_C_to_N_proteins) + call MPAS_pool_get_config(domain % configs, "config_snow_redistribution_scheme", config_snow_redistribution_scheme) + call MPAS_pool_get_config(domain % configs, "config_fallen_snow_radius", config_fallen_snow_radius) + call MPAS_pool_get_config(domain % configs, "config_use_snow_liquid_ponds", config_use_snow_liquid_ponds) + call MPAS_pool_get_config(domain % configs, "config_new_snow_density", config_new_snow_density) + call MPAS_pool_get_config(domain % configs, "config_max_snow_density", config_max_snow_density) + call MPAS_pool_get_config(domain % configs, "config_minimum_wind_compaction", config_minimum_wind_compaction) + call MPAS_pool_get_config(domain % configs, "config_wind_compaction_factor", config_wind_compaction_factor) + call MPAS_pool_get_config(domain % configs, "config_max_dry_snow_radius", config_max_dry_snow_radius) call colpkg_init_parameters(& config_cice_int("config_thermodynamics_type", config_thermodynamics_type), & @@ -9171,6 +10367,7 @@ subroutine init_column_package_configs(domain) config_slow_mode_critical_porosity, & config_congelation_ice_porosity, & config_shortwave_type, & + config_use_snicar_ad, & config_albedo_type, & config_visible_ice_albedo, & config_infrared_ice_albedo, & @@ -9316,6 +10513,7 @@ subroutine init_column_package_configs(domain) config_mobility_type_humics, & config_mobility_type_saccharids, & config_mobility_type_lipids, & + config_mobility_type_inorganic_carbon, & config_mobility_type_proteins, & config_mobility_type_dissolved_iron, & config_mobility_type_particulate_iron, & @@ -9334,7 +10532,15 @@ subroutine init_column_package_configs(domain) config_scales_absorption_diatoms, & config_scales_absorption_small_plankton, & config_scales_absorption_phaeocystis, & - config_ratio_C_to_N_proteins) + config_ratio_C_to_N_proteins, & + config_snow_redistribution_scheme, & + config_use_snow_liquid_ponds, & + config_fallen_snow_radius, & + config_max_dry_snow_radius, & + config_new_snow_density, & + config_max_snow_density, & + config_minimum_wind_compaction, & + config_wind_compaction_factor) !----------------------------------------------------------------------- ! Parameters for thermodynamics @@ -10012,6 +11218,10 @@ subroutine init_column_package_configs(domain) ! mobility type lipids ! doctype_l = config_mobility_type_lipids + ! dictype_1: + ! mobility type dissolved inorganic carbon + ! dictype_1 = config_mobility_type_inorganic_carbon + ! dontype_protein: ! mobility type proteins ! dontype_protein = config_mobility_type_proteins @@ -10096,6 +11306,42 @@ subroutine init_column_package_configs(domain) ! 0.02 characteristic skeletal layer thickness (m) (zsalinity) !l_skS = config_zsalinity_gravity_drainage_scale + !----------------------------------------------------------------------- + ! Parameters for snow + !----------------------------------------------------------------------- + + ! snwredist: + ! snow redistribution type + ! snwredist = config_snow_redistribution_scheme + + ! use_smliq_pnd: + ! convert excess snow liquid to ponds + ! use_smliq_pnd = config_use_snow_liquid_ponds + + ! rsnw_fall: + ! fallen snow grain radius (um) + ! rsnw_fall = config_fallen_snow_radius + + ! rsnw_tmax: + ! maximum dry metamorphism snow grain radius (um) + ! rsnw_tmax = config_max_dry_snow_radius + + ! rhosnew: + ! new snow density (kg/m^3) + ! rhosnew = config_new_snow_density + + ! rhosmax: + ! maximum snow density (kg/m^3) + ! rhosmax = config_max_snow_density + + ! windmin: + ! minimum wind speed to compact snow (m/s) + ! windmin = config_minimum_wind_compaction + + ! drhosdwind: + ! wind compaction factor (kg s/m^4) + ! drhosdwind = config_wind_compaction_factor + end subroutine init_column_package_configs !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -10273,7 +11519,9 @@ subroutine init_column_non_activated_pointers(domain) pkgTracerVerticalDONActive, & pkgTracerVerticalIronActive, & pkgTracerZAerosolsActive, & - pkgTracerZSalinityActive + pkgTracerZSalinityActive, & + pkgColumnTracerEffectiveSnowDensityActive, & + pkgColumnTracerSnowGrainRadiusActive ! mesh stand-ins @@ -10339,6 +11587,8 @@ subroutine init_column_non_activated_pointers(domain) call MPAS_pool_get_package(block % packages, "pkgTracerVerticalIronActive", pkgTracerVerticalIronActive) call MPAS_pool_get_package(block % packages, "pkgTracerZAerosolsActive", pkgTracerZAerosolsActive) call MPAS_pool_get_package(block % packages, "pkgTracerZSalinityActive", pkgTracerZSalinityActive) + call MPAS_pool_get_package(block % packages, "pkgColumnTracerEffectiveSnowDensityActive", pkgColumnTracerEffectiveSnowDensityActive) + call MPAS_pool_get_package(block % packages, "pkgColumnTracerSnowGrainRadiusActive", pkgColumnTracerSnowGrainRadiusActive) ! ice age @@ -10488,6 +11738,17 @@ subroutine init_column_non_activated_pointers(domain) call set_stand_in_tracer_array(block, "verticalSalinity") endif + ! snow density tracers + if (.not. pkgColumnTracerEffectiveSnowDensityActive) then + call set_stand_in_tracer_array(block, "snowDensity") + call set_stand_in_tracer_array(block, "snowLiquidMass") + call set_stand_in_tracer_array(block, "snowIceMass") + endif + + ! snow grain radius + if (.not. pkgColumnTracerSnowGrainRadiusActive) then + call set_stand_in_tracer_array(block, "snowGrainRadius") + endif !----------------------------------------------------------------------- ! other column packages !----------------------------------------------------------------------- @@ -10597,7 +11858,9 @@ subroutine finalize_column_non_activated_pointers(domain) pkgTracerVerticalDONActive, & pkgTracerVerticalIronActive, & pkgTracerZAerosolsActive, & - pkgTracerZSalinityActive + pkgTracerZSalinityActive, & + pkgColumnTracerEffectiveSnowDensityActive, & + pkgColumnTracerSnowGrainRadiusActive ! drag variables type(field1DReal), pointer :: & @@ -10655,6 +11918,8 @@ subroutine finalize_column_non_activated_pointers(domain) call MPAS_pool_get_package(block % packages, "pkgTracerVerticalIronActive", pkgTracerVerticalIronActive) call MPAS_pool_get_package(block % packages, "pkgTracerZAerosolsActive", pkgTracerZAerosolsActive) call MPAS_pool_get_package(block % packages, "pkgTracerZSalinityActive", pkgTracerZSalinityActive) + call MPAS_pool_get_package(block % packages, "pkgColumnTracerEffectiveSnowDensityActive", pkgColumnTracerEffectiveSnowDensityActive) + call MPAS_pool_get_package(block % packages, "pkgColumnTracerSnowGrainRadiusActive", pkgColumnTracerSnowGrainRadiusActive) ! ice age if (.not. pkgColumnTracerIceAgeActive) then @@ -10803,6 +12068,17 @@ subroutine finalize_column_non_activated_pointers(domain) call finalize_stand_in_tracer_array(block, "verticalSalinity") endif + ! snow density tracers + if (.not. pkgColumnTracerEffectiveSnowDensityActive) then + call finalize_stand_in_tracer_array(block, "snowDensity") + call finalize_stand_in_tracer_array(block, "snowLiquidMass") + call finalize_stand_in_tracer_array(block, "snowIceMass") + endif + + ! snow grain radius + if (.not. pkgColumnTracerSnowGrainRadiusActive) then + call finalize_stand_in_tracer_array(block, "snowGrainRadius") + endif !----------------------------------------------------------------------- ! other column packages !----------------------------------------------------------------------- @@ -11119,21 +12395,26 @@ subroutine seaice_column_reinitialize_oceanic_fluxes(domain) type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & - oceanFluxesPool + oceanFluxesPool, & + snowPool real(kind=RKIND), dimension(:), pointer :: & oceanFreshWaterFlux, & oceanSaltFlux, & oceanHeatFlux, & - oceanShortwaveFlux + oceanShortwaveFlux, & + snowLossToLeads, & + snowMeltMassCell logical, pointer :: & - config_use_column_package + config_use_column_package, & + config_use_column_snow_tracers block => domain % blocklist do while (associated(block)) call MPAS_pool_get_config(block % configs, "config_use_column_package", config_use_column_package) + call MPAS_pool_get_config(block % configs, "config_use_column_snow_tracers", config_use_column_snow_tracers) if (config_use_column_package) then @@ -11149,6 +12430,17 @@ subroutine seaice_column_reinitialize_oceanic_fluxes(domain) oceanHeatFlux(:) = 0.0_RKIND oceanShortwaveFlux(:) = 0.0_RKIND + if (config_use_column_snow_tracers) then + call MPAS_pool_get_subpool(block % structs, "snow", snowPool) + + call MPAS_pool_get_array(snowPool, "snowLossToLeads", snowLossToLeads) + call MPAS_pool_get_array(snowPool, "snowMeltMassCell", snowMeltMassCell) + + snowLossToLeads(:) = 0.0_RKIND + snowMeltMassCell(:) = 0.0_RKIND + + endif ! config_use_column_snow_tracers + endif ! config_use_column_package block => block % next @@ -11268,6 +12560,7 @@ subroutine init_column_tracer_object_for_biogeochemistry(domain, tracerObject) config_mobility_type_phaeocystis, & config_mobility_type_saccharids, & config_mobility_type_lipids, & + config_mobility_type_inorganic_carbon, & config_mobility_type_proteins, & config_mobility_type_dissolved_iron, & config_mobility_type_particulate_iron, & @@ -11429,6 +12722,7 @@ subroutine init_column_tracer_object_for_biogeochemistry(domain, tracerObject) call MPAS_pool_get_config(domain % configs, "config_mobility_type_humics", config_mobility_type_humics) call MPAS_pool_get_config(domain % configs, "config_mobility_type_saccharids", config_mobility_type_saccharids) call MPAS_pool_get_config(domain % configs, "config_mobility_type_lipids", config_mobility_type_lipids) + call MPAS_pool_get_config(domain % configs, "config_mobility_type_inorganic_carbon", config_mobility_type_inorganic_carbon) call MPAS_pool_get_config(domain % configs, "config_mobility_type_proteins", config_mobility_type_proteins) call MPAS_pool_get_config(domain % configs, "config_mobility_type_dissolved_iron", config_mobility_type_dissolved_iron) call MPAS_pool_get_config(domain % configs, "config_mobility_type_particulate_iron", config_mobility_type_particulate_iron) @@ -11662,6 +12956,7 @@ subroutine init_column_tracer_object_for_biogeochemistry(domain, tracerObject) config_mobility_type_phaeocystis, & config_mobility_type_saccharids, & config_mobility_type_lipids, & + config_mobility_type_inorganic_carbon, & config_mobility_type_proteins, & config_mobility_type_dissolved_iron, & config_mobility_type_particulate_iron, & @@ -11905,7 +13200,8 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) endif ! set the category tracer array - call set_cice_tracer_array_category(block, tracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, tracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) if (config_use_vertical_zsalinity) then call colpkg_init_zsalinity(& @@ -11914,7 +13210,7 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) config_do_restart_zsalinity, & rayleighCriteria, & rayleighCriteriaReal(iCell), & - tracerObject % tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & + tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & tracerObject % index_verticalSalinity, & nCategories, & seaSurfaceSalinity(iCell)) @@ -11933,7 +13229,7 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) tracerObject % nTracers, & tracerObject % nBiotracers, & iceSalinity(:,:,iCell), & - tracerObject % tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & + tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & seaSurfaceSalinity(iCell), & oceanNitrateConc(iCell), & oceanAmmoniumConc(iCell), & @@ -11967,7 +13263,8 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) endif ! biogeochemistry ! get the category tracer array - call get_cice_tracer_array_category(block, tracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, tracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) enddo ! iCell @@ -12004,7 +13301,8 @@ subroutine seaice_column_reinitialize_diagnostics_thermodynamics(domain) tracersAggregatePool, & pondsPool, & shortwavePool, & - dragPool + dragPool, & + snowPool ! atmospheric fluxes real(kind=RKIND), dimension(:), pointer :: & @@ -12028,6 +13326,18 @@ subroutine seaice_column_reinitialize_diagnostics_thermodynamics(domain) basalIceMelt, & lateralIceMelt + ! snow model + real(kind=RKIND), dimension(:), pointer :: & + snowLossToLeads, & + snowMeltMassCell, & + snowDensityViaContent, & + snowDensityViaCompaction, & + snowRadiusInStandardRadiationScheme + + real(kind=RKIND), dimension(:,:), pointer :: & + snowMeltMassCategory, & + snowRadiusInStandardRadiationSchemeCategory + ! diagnostic tendencies real(kind=RKIND), dimension(:), pointer :: & iceAreaTendencyThermodynamics, & @@ -12071,7 +13381,8 @@ subroutine seaice_column_reinitialize_diagnostics_thermodynamics(domain) logical, pointer :: & config_use_ice_age, & config_use_form_drag, & - config_use_column_package + config_use_column_package, & + config_use_column_snow_tracers call MPAS_pool_get_config(domain % blocklist % configs, "config_use_column_package", config_use_column_package) @@ -12213,6 +13524,29 @@ subroutine seaice_column_reinitialize_diagnostics_thermodynamics(domain) endif ! config_use_form_drag + ! snow + call MPAS_pool_get_config(block % configs, "config_use_column_snow_tracers", config_use_column_snow_tracers) + if (config_use_column_snow_tracers) then + + call MPAS_pool_get_subpool(block % structs, "snow", snowPool) + call MPAS_pool_get_array(snowPool, "snowLossToLeads", snowLossToLeads) + call MPAS_pool_get_array(snowPool, "snowMeltMassCell", snowMeltMassCell) + call MPAS_pool_get_array(snowPool, "snowMeltMassCategory", snowMeltMassCategory) + call MPAS_pool_get_array(snowPool, "snowDensityViaContent", snowDensityViaContent) + call MPAS_pool_get_array(snowPool, "snowDensityViaCompaction", snowDensityViaCompaction) + call MPAS_pool_get_array(snowPool, "snowRadiusInStandardRadiationScheme", snowRadiusInStandardRadiationScheme) + call MPAS_pool_get_array(snowPool, "snowRadiusInStandardRadiationSchemeCategory", snowRadiusInStandardRadiationSchemeCategory) + + snowLossToLeads = 0.0_RKIND + snowMeltMassCell = 0.0_RKIND + snowMeltMassCategory = 0.0_RKIND + snowDensityViaContent = 0.0_RKIND + snowDensityViaCompaction = 0.0_RKIND + snowRadiusInStandardRadiationScheme = 0.0_RKIND + snowRadiusInStandardRadiationSchemeCategory = 0.0_RKIND + + end if ! config_use_column_snow_tracers + block => block % next end do @@ -12416,7 +13750,8 @@ subroutine seaice_column_reinitialize_diagnostics_bgc(domain) netBrineHeight, & zSalinityFlux, & zSalinityGDFlux, & - totalChlorophyll + totalChlorophyll, & + totalCarbonContentCell real(kind=RKIND), dimension(:,:), pointer :: & oceanBioFluxes, & @@ -12431,45 +13766,61 @@ subroutine seaice_column_reinitialize_diagnostics_bgc(domain) logical, pointer :: & config_use_column_biogeochemistry, & config_use_column_shortwave, & - config_use_column_package + config_use_column_package, & + config_use_vertical_biochemistry, & + config_use_vertical_zsalinity call MPAS_pool_get_config(domain % blocklist % configs, "config_use_column_package", config_use_column_package) - if (config_use_column_package) then + if (config_use_column_package) then block => domain % blocklist do while (associated(block)) ! biogeochemistry call MPAS_pool_get_config(block % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + call MPAS_pool_get_config(block % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) + call MPAS_pool_get_config(block % configs, "config_use_vertical_zsalinity", config_use_vertical_zsalinity) if (config_use_column_biogeochemistry) then call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistryPool) - call MPAS_pool_get_array(biogeochemistryPool, "primaryProduction", primaryProduction) - call MPAS_pool_get_array(biogeochemistryPool, "totalChlorophyll", totalChlorophyll) - call MPAS_pool_get_array(biogeochemistryPool, "netSpecificAlgalGrowthRate", netSpecificAlgalGrowthRate) + if (config_use_vertical_biochemistry) then + call MPAS_pool_get_array(biogeochemistryPool, "primaryProduction", primaryProduction) + call MPAS_pool_get_array(biogeochemistryPool, "totalChlorophyll", totalChlorophyll) + call MPAS_pool_get_array(biogeochemistryPool, "netSpecificAlgalGrowthRate", netSpecificAlgalGrowthRate) + + primaryProduction = 0.0_RKIND + totalChlorophyll = 0.0_RKIND + netSpecificAlgalGrowthRate = 0.0_RKIND + + end if + + if (config_use_vertical_zsalinity) then + call MPAS_pool_get_array(biogeochemistryPool, "zSalinityFlux", zSalinityFlux) + call MPAS_pool_get_array(biogeochemistryPool, "zSalinityGDFlux", zSalinityGDFlux) + + zSalinityFlux = 0.0_RKIND + zSalinityGDFlux = 0.0_RKIND + + end if + call MPAS_pool_get_array(biogeochemistryPool, "netBrineHeight", netBrineHeight) - call MPAS_pool_get_array(biogeochemistryPool, "zSalinityFlux", zSalinityFlux) - call MPAS_pool_get_array(biogeochemistryPool, "zSalinityGDFlux", zSalinityGDFlux) call MPAS_pool_get_array(biogeochemistryPool, "oceanBioFluxes", oceanBioFluxes) call MPAS_pool_get_array(biogeochemistryPool, "atmosIceBioFluxes", atmosIceBioFluxes) call MPAS_pool_get_array(biogeochemistryPool, "snowIceBioFluxes", snowIceBioFluxes) call MPAS_pool_get_array(biogeochemistryPool, "totalVerticalBiologyIce", totalVerticalBiologyIce) call MPAS_pool_get_array(biogeochemistryPool, "totalVerticalBiologySnow", totalVerticalBiologySnow) + call MPAS_pool_get_array(biogeochemistryPool, "totalCarbonContentCell", totalCarbonContentCell) - primaryProduction = 0.0_RKIND - totalChlorophyll = 0.0_RKIND - netSpecificAlgalGrowthRate = 0.0_RKIND netBrineHeight = 0.0_RKIND - zSalinityFlux = 0.0_RKIND - zSalinityGDFlux = 0.0_RKIND oceanBioFluxes = 0.0_RKIND atmosIceBioFluxes = 0.0_RKIND snowIceBioFluxes = 0.0_RKIND totalVerticalBiologyIce = 0.0_RKIND totalVerticalBiologySnow = 0.0_RKIND + totalCarbonContentCell = 0.0_RKIND endif @@ -13265,23 +14616,642 @@ subroutine column_combine_snow_ice_tracers(domain) end subroutine column_combine_snow_ice_tracers +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_ocean_carbon_flux +! +!> \brief +!> \author Nicole Jeffery, LANL +!> \date 26 May 2020 +!> \details Calculate the ocean carbon flux +!> by summing the appropriate biogeochemical tracer fluxes in units of mmol C/m2/s +!> +!> ocean carbon flux = algal nitrogen group fluxes * (C to N ratios) +!> + dissolved carbon group fluxes +!> + dissolved organic nitrogen * (C to N ratio) +!> + dissolved inorganic carbon fluxes + humic fluxes +! +!----------------------------------------------------------------------- + + subroutine seaice_ocean_carbon_flux(block,oceanCarbonFlux,oceanBioFluxes,iCell) + + real(kind=RKIND), dimension(:), intent(out) :: & + oceanCarbonFlux + + real(kind=RKIND), dimension(:,:,:), intent(in) :: & + oceanBioFluxes + + integer, intent(in) :: & + iCell + + type(block_type), intent(in) :: & + block + + logical, pointer :: & + config_use_column_biogeochemistry, & + config_use_vertical_biochemistry, & + config_use_carbon, & + config_use_DON, & + config_use_humics + + integer, pointer :: & + nAlgae, & + nDOC, & + nDON, & + nDIC + + type(MPAS_pool_type), pointer :: & + mesh, & + biogeochemistry + + real(kind=RKIND), pointer :: & + config_ratio_C_to_N_diatoms, & + config_ratio_C_to_N_small_plankton, & + config_ratio_C_to_N_phaeocystis, & + config_ratio_C_to_N_proteins + + integer, pointer :: & + nCategories, & + nZBGCTracers, & + maxAlgaeType, & + maxDOCType, & + maxDICType, & + maxDONType, & + maxIronType, & + maxBCType, & + maxDustType, & + maxAerosolType + + real(kind=RKIND), dimension(:), allocatable :: & + ratio_C_to_N + + real(kind=RKIND), dimension(:,:), allocatable :: & + oceanBioFluxesAll + + integer :: & + iBioTracers, & + iBioData, & + iCategory + + call MPAS_pool_get_config(block % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + call MPAS_pool_get_config(block % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) + call MPAS_pool_get_config(block % configs, "config_use_carbon", config_use_carbon) + call MPAS_pool_get_config(block % configs, "config_use_DON", config_use_DON) + call MPAS_pool_get_config(block % configs, "config_use_humics",config_use_humics) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_diatoms", config_ratio_C_to_N_diatoms) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_small_plankton", config_ratio_C_to_N_small_plankton) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_phaeocystis", config_ratio_C_to_N_phaeocystis) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_proteins", config_ratio_C_to_N_proteins) + + call MPAS_pool_get_dimension(block % dimensions, "nAlgae", nAlgae) + call MPAS_pool_get_dimension(block % dimensions, "nDOC", nDOC) + call MPAS_pool_get_dimension(block % dimensions, "nDIC", nDIC) + call MPAS_pool_get_dimension(block % dimensions, "nDON", nDON) + + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistry) + + call MPAS_pool_get_dimension(mesh, "nCategories", nCategories) + call MPAS_pool_get_dimension(mesh, "nZBGCTracers", nZBGCTracers) + call MPAS_pool_get_dimension(mesh, "maxAlgaeType", maxAlgaeType) + call MPAS_pool_get_dimension(mesh, "maxDOCType", maxDOCType) + call MPAS_pool_get_dimension(mesh, "maxDICType", maxDICType) + call MPAS_pool_get_dimension(mesh, "maxDONType", maxDONType) + call MPAS_pool_get_dimension(mesh, "maxAerosolType", maxAerosolType) + call MPAS_pool_get_dimension(mesh, "maxIronType", maxIronType) + call MPAS_pool_get_dimension(mesh, "maxBCType", maxBCType) + call MPAS_pool_get_dimension(mesh, "maxDustType", maxDustType) + + + allocate(oceanBioFluxesAll(nZBGCTracers,nCategories)) + allocate(ratio_C_to_N(3)) + + ratio_C_to_N(1) = config_ratio_C_to_N_diatoms + ratio_C_to_N(2) = config_ratio_C_to_N_small_plankton + ratio_C_to_N(3) = config_ratio_C_to_N_phaeocystis + + if (config_use_column_biogeochemistry) then + + do iCategory = 1, nCategories + + oceanCarbonFlux(iCategory) = 0.0_RKIND + oceanBioFluxesAll(:,iCategory) = 0.0_RKIND + + do iBioTracers = 1, ciceTracerObject % nBioTracers + iBioData = ciceTracerObject % index_LayerIndexToDataArray(iBioTracers) + oceanBioFluxesAll(iBioData,iCategory) = oceanBioFluxes(iBioTracers,iCategory,iCell) + enddo + iBioData = 0 + + ! Algae + do iBioTracers = 1, maxAlgaeType + iBioData = iBioData+1 + oceanCarbonFlux(iCategory) = oceanCarbonFlux(iCategory) + & + oceanBioFluxesAll(iBioData,iCategory) * ratio_C_to_N(iBioTracers) + enddo + + ! Nitrate + iBioData = iBioData+1 + + ! Polysaccharids and Lipids + do iBioTracers = 1, maxDOCType + iBioData = iBioData+1 + oceanCarbonFlux(iCategory) = oceanCarbonFlux(iCategory) + & + oceanBioFluxesAll(iBioData,iCategory) + enddo + + ! DIC + do iBioTracers = 1, maxDICType + iBioData = iBioData+1 + oceanCarbonFlux(iCategory) = oceanCarbonFlux(iCategory) + & + oceanBioFluxesAll(iBioData,iCategory) + enddo + + ! + Chlorophyll (maxAlgaeType) + Ammonium (1) + Silicate (1) + DMSPp (1) + DMSPd (1) + ! + DMS (1) + PON (1) + + iBioData = iBioData+maxAlgaeType + 6 + + ! DON + do iBioTracers = 1, maxDONType + iBioData = iBioData+1 + oceanCarbonFlux(iCategory) = oceanCarbonFlux(iCategory) + & + oceanBioFluxesAll(iBioData,iCategory) * config_ratio_C_to_N_proteins + enddo + + ! + dFe (maxIronType) + pFe (maxIronType) + ! + Black Carbon (maxBCType) + Dust (maxDustType) + + iBioData = iBioData + 2*maxIronType + maxBCType + maxDustType + + ! Humics + iBioData = iBioData+1 + oceanCarbonFlux(iCategory) = oceanCarbonFlux(iCategory) + & + oceanBioFluxesAll(iBioData,iCategory) + + enddo ! nCategories + endif + + deallocate(oceanBioFluxesAll) + deallocate(ratio_C_to_N) + + end subroutine seaice_ocean_carbon_flux + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_ocean_carbon_flux_cell +! +!> \brief +!> \author Nicole Jeffery, LANL +!> \date 26 May 2020 +!> \details Calculate the ocean carbon flux +!> by summing the appropriate biogeochemical tracer fluxes in units of mmol C/m2/s +!> +!> ocean carbon flux = algal nitrogen group fluxes * (C to N ratios) +!> + dissolved carbon group fluxes +!> + dissolved organic nitrogen * (C to N ratio) +!> + dissolved inorganic carbon fluxes + humic fluxes +! +!----------------------------------------------------------------------- + + subroutine seaice_ocean_carbon_flux_cell(block,oceanCarbonFlux,oceanBioFluxes,iCell) + + real(kind=RKIND), intent(out) :: & + oceanCarbonFlux + + real(kind=RKIND), dimension(:), intent(in) :: & + oceanBioFluxes + + integer, intent(in) :: & + iCell + + type(block_type), intent(in) :: & + block + + logical, pointer :: & + config_use_column_biogeochemistry, & + config_use_vertical_biochemistry, & + config_use_carbon, & + config_use_DON, & + config_use_humics + + integer, pointer :: & + nAlgae, & + nDOC, & + nDON, & + nDIC + + type(MPAS_pool_type), pointer :: & + mesh, & + biogeochemistry + + real(kind=RKIND), pointer :: & + config_ratio_C_to_N_diatoms, & + config_ratio_C_to_N_small_plankton, & + config_ratio_C_to_N_phaeocystis, & + config_ratio_C_to_N_proteins + + integer, pointer :: & + nZBGCTracers, & + maxAlgaeType, & + maxDOCType, & + maxDICType, & + maxDONType, & + maxIronType, & + maxBCType, & + maxDustType, & + maxAerosolType + + real(kind=RKIND), dimension(:), allocatable :: & + ratio_C_to_N + + real(kind=RKIND), dimension(:), allocatable :: & + oceanBioFluxesAll + + integer :: & + iBioTracers, & + iBioData + + call MPAS_pool_get_config(block % configs, "config_use_column_biogeochemistry", config_use_column_biogeochemistry) + call MPAS_pool_get_config(block % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) + call MPAS_pool_get_config(block % configs, "config_use_carbon", config_use_carbon) + call MPAS_pool_get_config(block % configs, "config_use_DON", config_use_DON) + call MPAS_pool_get_config(block % configs, "config_use_humics",config_use_humics) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_diatoms", config_ratio_C_to_N_diatoms) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_small_plankton", config_ratio_C_to_N_small_plankton) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_phaeocystis", config_ratio_C_to_N_phaeocystis) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_proteins", config_ratio_C_to_N_proteins) + + call MPAS_pool_get_dimension(block % dimensions, "nAlgae", nAlgae) + call MPAS_pool_get_dimension(block % dimensions, "nDOC", nDOC) + call MPAS_pool_get_dimension(block % dimensions, "nDIC", nDIC) + call MPAS_pool_get_dimension(block % dimensions, "nDON", nDON) + + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistry) + + call MPAS_pool_get_dimension(mesh, "nZBGCTracers", nZBGCTracers) + call MPAS_pool_get_dimension(mesh, "maxAlgaeType", maxAlgaeType) + call MPAS_pool_get_dimension(mesh, "maxDOCType", maxDOCType) + call MPAS_pool_get_dimension(mesh, "maxDICType", maxDICType) + call MPAS_pool_get_dimension(mesh, "maxDONType", maxDONType) + call MPAS_pool_get_dimension(mesh, "maxAerosolType", maxAerosolType) + call MPAS_pool_get_dimension(mesh, "maxIronType", maxIronType) + call MPAS_pool_get_dimension(mesh, "maxBCType", maxBCType) + call MPAS_pool_get_dimension(mesh, "maxDustType", maxDustType) + + allocate(oceanBioFluxesAll(nZBGCTracers)) + allocate(ratio_C_to_N(3)) + + ratio_C_to_N(1) = config_ratio_C_to_N_diatoms + ratio_C_to_N(2) = config_ratio_C_to_N_small_plankton + ratio_C_to_N(3) = config_ratio_C_to_N_phaeocystis + + if (config_use_column_biogeochemistry) then + + oceanCarbonFlux = 0.0_RKIND + oceanBioFluxesAll(:) = 0.0_RKIND + + do iBioTracers = 1, ciceTracerObject % nBioTracers + iBioData = ciceTracerObject % index_LayerIndexToDataArray(iBioTracers) + oceanBioFluxesAll(iBioData) = oceanBioFluxes(iBioTracers) + enddo + iBioData = 0 + + ! Algae + do iBioTracers = 1, maxAlgaeType + iBioData = iBioData+1 + oceanCarbonFlux = oceanCarbonFlux + & + oceanBioFluxesAll(iBioData) * ratio_C_to_N(iBioTracers) + enddo + + ! Nitrate + iBioData = iBioData+1 + + ! Polysaccharids and Lipids + do iBioTracers = 1, maxDOCType + iBioData = iBioData+1 + oceanCarbonFlux = oceanCarbonFlux + & + oceanBioFluxesAll(iBioData) + enddo + + ! DIC + do iBioTracers = 1, maxDICType + iBioData = iBioData+1 + oceanCarbonFlux = oceanCarbonFlux + & + oceanBioFluxesAll(iBioData) + enddo + + ! + Chlorophyll (maxAlgaeType) + Ammonium (1) + Silicate (1) + DMSPp (1) + DMSPd (1) + ! + DMS (1) + PON (1) + + iBioData = iBioData+maxAlgaeType + 6 + + ! DON + do iBioTracers = 1, maxDONType + iBioData = iBioData+1 + oceanCarbonFlux = oceanCarbonFlux + & + oceanBioFluxesAll(iBioData) * config_ratio_C_to_N_proteins + enddo + + ! + dFe (maxIronType) + pFe (maxIronType) + ! + Black Carbon (maxBCType) + Dust (maxDustType) + + iBioData = iBioData + 2*maxIronType + maxBCType + maxDustType + + ! Humics + iBioData = iBioData+1 + oceanCarbonFlux = oceanCarbonFlux + & + oceanBioFluxesAll(iBioData) + + endif + + deallocate(oceanBioFluxesAll) + deallocate(ratio_C_to_N) + + end subroutine seaice_ocean_carbon_flux_cell + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_total_carbon_content_category +! +!> \brief +!> \author Nicole Jeffery, LANL +!> \date 26 May 2020 +!> \details Calculate the total carbon concentration in the sea ice category +!> by summing the appropriate biogeochemical tracers in units of mmol C +!> +!> Total carbon = algal nitrogen groups * (C to N ratios) + dissolved carbon groups +!> + dissolved inorganic carbon + humic material +!> + dissolved organic nitrogen * (C to N ratio) +! +!----------------------------------------------------------------------- + + subroutine seaice_total_carbon_content_category(block,totalCarbonContentCategory,iceAreaCategory,iceVolumeCategory,iCell) + + use seaice_constants, only: & + skeletalLayerThickness, & + seaicePuny + + real(kind=RKIND), dimension(:), intent(out) :: & + totalCarbonContentCategory + + real(kind=RKIND), dimension(:,:), intent(in) :: & + iceAreaCategory, & + iceVolumeCategory + + integer, intent(in) :: & + iCell + + type(block_type), intent(in) :: & + block + + logical, pointer :: & + config_use_skeletal_biochemistry, & + config_use_vertical_biochemistry, & + config_use_vertical_tracers, & + config_use_carbon, & + config_use_DON, & + config_use_humics + + integer, pointer :: & + nCategories, & + nBioLayersP1, & + nBioLayers, & + nAlgae, & + nDOC, & + nDIC, & + nDON + + type(MPAS_pool_type), pointer :: & + mesh, & + biogeochemistry, & + tracers + + real(kind=RKIND), dimension(:,:,:), pointer :: & + skeletalAlgaeConc, & + skeletalDOCConc, & + skeletalDICConc, & + skeletalDONConc, & + skeletalHumicsConc, & + verticalAlgaeConc, & + verticalDOCConc, & + verticalDICConc, & + verticalDONConc, & + verticalHumicsConc, & + brineFraction + + real(kind=RKIND), pointer :: & + config_ratio_C_to_N_diatoms, & + config_ratio_C_to_N_small_plankton, & + config_ratio_C_to_N_phaeocystis, & + config_ratio_C_to_N_proteins + + real(kind=RKIND), dimension(:), allocatable :: & + ratio_C_to_N, & + verticalGridSpace + + real(kind=RKIND) :: & + brineHeight + + integer :: & + iBioTracers, & + iBioCount, & + iLayers, & + iCategory + + call MPAS_pool_get_config(block % configs, "config_use_skeletal_biochemistry", config_use_skeletal_biochemistry) + call MPAS_pool_get_config(block % configs, "config_use_vertical_biochemistry", config_use_vertical_biochemistry) + call MPAS_pool_get_config(block % configs, "config_use_vertical_tracers", config_use_vertical_tracers) + call MPAS_pool_get_config(block % configs, "config_use_carbon", config_use_carbon) + call MPAS_pool_get_config(block % configs, "config_use_DON", config_use_DON) + call MPAS_pool_get_config(block % configs, "config_use_humics",config_use_humics) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_diatoms", config_ratio_C_to_N_diatoms) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_small_plankton", config_ratio_C_to_N_small_plankton) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_phaeocystis", config_ratio_C_to_N_phaeocystis) + call MPAS_pool_get_config(block % configs, "config_ratio_C_to_N_proteins", config_ratio_C_to_N_proteins) + + call MPAS_pool_get_dimension(block % dimensions, "nBioLayers", nBioLayers) + call MPAS_pool_get_dimension(block % dimensions, "nBioLayersP1", nBioLayersP1) + call MPAS_pool_get_dimension(block % dimensions, "nAlgae", nAlgae) + call MPAS_pool_get_dimension(block % dimensions, "nDOC", nDOC) + call MPAS_pool_get_dimension(block % dimensions, "nDIC", nDIC) + call MPAS_pool_get_dimension(block % dimensions, "nDON", nDON) + + call MPAS_pool_get_subpool(block % structs, "tracers", tracers) + call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistry) + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + + call MPAS_pool_get_dimension(mesh, "nCategories", nCategories) + + call MPAS_pool_get_array(tracers, "skeletalAlgaeConc", skeletalAlgaeConc, 1) + call MPAS_pool_get_array(tracers, "skeletalDOCConc", skeletalDOCConc, 1) + call MPAS_pool_get_array(tracers, "skeletalDICConc", skeletalDICConc, 1) + call MPAS_pool_get_array(tracers, "skeletalDONConc", skeletalDONConc, 1) + call MPAS_pool_get_array(tracers, "skeletalHumicsConc", skeletalHumicsConc, 1) + call MPAS_pool_get_array(tracers, "verticalAlgaeConc", verticalAlgaeConc, 1) + call MPAS_pool_get_array(tracers, "verticalDOCConc", verticalDOCConc, 1) + call MPAS_pool_get_array(tracers, "verticalDICConc", verticalDICConc, 1) + call MPAS_pool_get_array(tracers, "verticalDONConc", verticalDONConc, 1) + call MPAS_pool_get_array(tracers, "verticalHumicsConc", verticalHumicsConc, 1) + call MPAS_pool_get_array(tracers, "brineFraction", brineFraction, 1) + + allocate(ratio_C_to_N(3)) + allocate(verticalGridSpace(nBioLayersP1)) + + ratio_C_to_N(1) = config_ratio_C_to_N_diatoms + ratio_C_to_N(2) = config_ratio_C_to_N_small_plankton + ratio_C_to_N(3) = config_ratio_C_to_N_phaeocystis + + + verticalGridSpace(:) = 1.0_RKIND/real(nBioLayers,kind=RKIND) + verticalGridSpace(1) = verticalGridSpace(1)/2.0_RKIND + verticalGridSpace(nBioLayersP1) = verticalGridSpace(1) + totalCarbonContentCategory(:) = 0.0_RKIND + + + if (config_use_skeletal_biochemistry) then + + do iCategory = 1, nCategories + ! algal nitrogen + do iBioTracers = 1, nAlgae + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + skeletalAlgaeConc(iBioTracers,iCategory,iCell)* & + skeletalLayerThickness * ratio_C_to_N(iBioTracers) + enddo + + if (config_use_carbon) then + ! DOC + do iBioTracers = 1, nDOC + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + skeletalDOCConc(iBioTracers,iCategory,iCell)* & + skeletalLayerThickness + enddo + + ! DIC + do iBioTracers = 1, nDIC + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + skeletalDICConc(iBioTracers,iCategory,iCell)* & + skeletalLayerThickness + enddo + endif + + if (config_use_DON) then + ! DON + do iBioTracers = 1, nDON + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + skeletalDONConc(iBioTracers,iCategory,iCell)* & + config_ratio_C_to_N_proteins * skeletalLayerThickness + enddo + endif + + ! humic material + if (config_use_humics) & + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + skeletalHumicsConc(1,iCategory,iCell)* & + skeletalLayerThickness + enddo + elseif (config_use_vertical_tracers) then + + do iCategory = 1, nCategories + brineHeight = 0.0_RKIND + if (iceAreaCategory(iCategory,iCell) > seaicePuny) then + brineHeight = iceVolumeCategory(iCategory,iCell)/iceAreaCategory(iCategory,iCell) * brineFraction(1,iCategory,iCell) + endif + + if (config_use_vertical_biochemistry) then + iBioCount = 0 + ! algal nitrogen + do iBioTracers = 1, nAlgae + do iLayers = 1,nBioLayersP1 + iBiocount = iBiocount + 1 + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + & + verticalAlgaeConc(iBioCount,iCategory,iCell) * ratio_C_to_N(iBioTracers) * & + verticalGridSpace(iLayers) * brineHeight + enddo + iBioCount = iBioCount+2 ! snow layers + enddo + endif + + if (config_use_carbon) then + iBioCount = 0 + ! DOC + do iBioTracers = 1, nDOC + do iLayers = 1,nBioLayersP1 + iBioCount = iBioCount + 1 + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + & + verticalDOCConc(iBioCount,iCategory,iCell) * verticalGridSpace(iLayers) * brineHeight + enddo + iBioCount = iBioCount+2 ! snow layers + enddo + iBioCount = 0 + ! DIC + do iBioTracers = 1, nDIC + + do iLayers = 1,nBioLayersP1 + iBioCount = iBioCount + 1 + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + & + verticalDICConc(iBioCount,iCategory,iCell) * verticalGridSpace(iLayers) * brineHeight + enddo + iBioCount = iBioCount + 2 ! snow layers + enddo + endif + + if (config_use_DON) then + iBioCount = 0 + ! dissolved organic nitrogen + do iBioTracers = 1, nDON + do iLayers = 1,nBioLayersP1 + iBiocount = iBiocount + 1 + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + & + verticalDONConc(iBioCount,iCategory,iCell) * config_ratio_C_to_N_proteins * & + verticalGridSpace(iLayers) * brineHeight + enddo + iBioCount = iBioCount+2 ! snow layers + enddo + endif + + ! humic material + if (config_use_humics) then + do iLayers = 1, nBioLayersP1 + totalCarbonContentCategory(iCategory) = totalCarbonContentCategory(iCategory) + & + verticalHumicsConc(iLayers,iCategory,iCell) * verticalGridSpace(iLayers) * brineHeight + enddo + endif + enddo + endif + + deallocate(ratio_C_to_N) + deallocate(verticalGridSpace) + + end subroutine seaice_total_carbon_content_category + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| !----------------------------------------------------------------------- ! Warning messages !----------------------------------------------------------------------- - subroutine column_write_warnings(warnings) + subroutine column_write_warnings(logAsErrors) + + use ice_colpkg, only: colpkg_get_warnings - character(len=strKINDWarnings), dimension(:), intent(in) :: & + character(len=strKINDWarnings), dimension(:), allocatable :: & warnings + logical, intent(in) :: & + logAsErrors + integer :: & iWarning - do iWarning = 1, size(warnings) - - call mpas_log_write(trim(warnings(iWarning)), messageType=MPAS_LOG_WARN) + call colpkg_get_warnings(warnings) - enddo ! iWarning + if (logAsErrors) then + do iWarning = 1, size(warnings) + call mpas_log_write(trim(warnings(iWarning)), messageType=MPAS_LOG_ERR) + enddo ! iWarning + else + do iWarning = 1, size(warnings) + call mpas_log_write(trim(warnings(iWarning)), messageType=MPAS_LOG_WARN) + enddo ! iWarning + endif end subroutine column_write_warnings diff --git a/src/core_seaice/shared/mpas_seaice_constants.F b/src/core_seaice/shared/mpas_seaice_constants.F index e722807e7f..dcc6ec76b6 100644 --- a/src/core_seaice/shared/mpas_seaice_constants.F +++ b/src/core_seaice/shared/mpas_seaice_constants.F @@ -33,7 +33,8 @@ module seaice_constants rhofresh, & vonkar, & iceruf, & - zref + zref, & + sk_l private save @@ -50,8 +51,7 @@ module seaice_constants ! Earth constants real (kind=RKIND), parameter, public :: & seaiceGravity = gravit, & ! gravitational acceleration (m/s^2) - earthRadius = 6371229.0_RKIND, & ! radius of Earth in [m] - omega = 7.29212e-5_RKIND ! angular rotation rate of the Earth [s-1] + omega = 7.29212e-5_RKIND ! angular rotation rate of the Earth [s-1] character (len=*), public, parameter :: & coupleAlarmID = 'coupling' @@ -91,5 +91,9 @@ module seaice_constants iceThicknessMinimum = seaicePuny, & snowThicknessMinimum = seaicePuny + ! biogeochemistry constants + real(kind=RKIND), parameter, public :: & + skeletalLayerThickness = sk_l + end module seaice_constants diff --git a/src/core_seaice/shared/mpas_seaice_error.F b/src/core_seaice/shared/mpas_seaice_error.F index ea61e7ed3b..1a21bfde52 100644 --- a/src/core_seaice/shared/mpas_seaice_error.F +++ b/src/core_seaice/shared/mpas_seaice_error.F @@ -22,20 +22,6 @@ module seaice_error private save - ! column critical error codes - integer, parameter, public :: & - SEAICE_ERROR_COL_VERT_THERM = 10, & - SEAICE_ERROR_COL_ITD_THERM = 11, & - SEAICE_ERROR_COL_RIDGING = 12, & - SEAICE_ERROR_COL_BGC = 13 - - ! incremental remap critical error codes - integer, parameter, public :: & - SEAICE_ERROR_IR_NEG_AREA = 20, & - SEAICE_ERROR_IR_NEG_MASS = 21, & - SEAICE_ERROR_IR_MONO = 22, & - SEAICE_ERROR_IR_CONS = 23 - ! public routines public :: & seaice_critical_error_write_block, & @@ -55,7 +41,7 @@ module seaice_error ! !----------------------------------------------------------------------- - subroutine seaice_critical_error_write_block(domain, block) + subroutine seaice_critical_error_write_block(domain, block, abortFlag) type(domain_type), intent(in) :: & domain @@ -63,12 +49,19 @@ subroutine seaice_critical_error_write_block(domain, block) type(block_type), intent(in) :: & block - ! write out block streams - call mpas_stream_mgr_block_write(& - domain % streamManager, & - writeBlock=block, & - streamID='abort_block', & - forceWriteNow=.true.) + logical, intent(in) :: & + abortFlag + + if (abortFlag) then + + ! write out block streams + call mpas_stream_mgr_block_write(& + domain % streamManager, & + writeBlock=block, & + streamID='abort_block', & + forceWriteNow=.true.) + + endif ! abortFlag end subroutine seaice_critical_error_write_block @@ -84,27 +77,29 @@ end subroutine seaice_critical_error_write_block ! !----------------------------------------------------------------------- - subroutine seaice_check_critical_error(domain, ierr) + subroutine seaice_check_critical_error(domain, abortFlag) use mpas_dmpar, only: mpas_dmpar_max_int - type(domain_type), intent(inout) :: & + type(domain_type), intent(in) :: & domain !< Input/Output: - integer, intent(in) :: & - ierr + logical, intent(in) :: & + abortFlag logical, pointer :: & config_full_abort_write integer :: & + ierr, & ierrmax call MPAS_pool_get_config(domain % configs, "config_full_abort_write", config_full_abort_write) - if (config_full_abort_write) then ! find if anyone failed + ierr = 0 + if (abortFlag) ierr = 1 call mpas_dmpar_max_int(domain % dminfo, ierr, ierrmax) if (ierrmax > 0) then @@ -115,47 +110,10 @@ subroutine seaice_check_critical_error(domain, ierr) endif ! check if a critical error occured - if (ierr > 0) then - - ! kill the model - call mpas_log_write("Runtime error $i: "//trim(error_string(ierr)), & - messageType=MPAS_LOG_CRIT, intArgs=(/ierr/)) - - endif + if (abortFlag) call mpas_log_write("Runtime error", messageType=MPAS_LOG_CRIT) end subroutine seaice_check_critical_error !----------------------------------------------------------------------- - function error_string(ierr) result(errorStr) - - character(len=strKIND) :: errorStr - - integer, intent(in) :: ierr - - select case(ierr) - case(SEAICE_ERROR_COL_VERT_THERM) - errorStr = "Column: Vertical Thermodynamics" - case(SEAICE_ERROR_COL_ITD_THERM) - errorStr = "Column: ITD thermodynamics" - case(SEAICE_ERROR_COL_RIDGING) - errorStr = "Column: Ridging" - case(SEAICE_ERROR_COL_BGC) - errorStr = "Column: BGC" - case(SEAICE_ERROR_IR_NEG_AREA) - errorStr = "IR: Negative area" - case(SEAICE_ERROR_IR_NEG_MASS) - errorStr = "IR: Negative mass" - case(SEAICE_ERROR_IR_MONO) - errorStr = "IR: Monotonicity violation" - case(SEAICE_ERROR_IR_CONS) - errorStr = "IR: Conservation violation" - case default - errorStr = "Unknown error code" - end select - - end function error_string - - !----------------------------------------------------------------------- - end module seaice_error diff --git a/src/core_seaice/shared/mpas_seaice_forcing.F b/src/core_seaice/shared/mpas_seaice_forcing.F index 1f95cbfc1c..e6a2a243a3 100644 --- a/src/core_seaice/shared/mpas_seaice_forcing.F +++ b/src/core_seaice/shared/mpas_seaice_forcing.F @@ -13,6 +13,8 @@ module seaice_forcing + use mpas_kind_types + use mpas_timer use mpas_derived_types use mpas_pool_routines use mpas_timekeeping @@ -32,7 +34,7 @@ module seaice_forcing post_atmospheric_forcing, & post_oceanic_coupling - type (MPAS_forcing_group_type), pointer :: seaiceForcingGroups + type (MPAS_forcing_group_type), pointer, public :: seaiceForcingGroups ! forcing parameters real (kind=RKIND), parameter :: & @@ -63,14 +65,18 @@ module seaice_forcing ! !----------------------------------------------------------------------- - subroutine seaice_forcing_init(domain) + subroutine seaice_forcing_init(domain, clock) type (domain_type) :: domain + type (MPAS_Clock_type) :: clock + logical, pointer :: & - config_use_forcing + config_use_forcing, & + config_use_data_icebergs call MPAS_pool_get_config(domain % configs, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_data_icebergs", config_use_data_icebergs) if (config_use_forcing) then @@ -82,6 +88,9 @@ subroutine seaice_forcing_init(domain) endif + ! init the data iceberg forcing + if (config_use_data_icebergs) call init_data_iceberg_forcing(domain, clock) + end subroutine seaice_forcing_init !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -310,9 +319,11 @@ subroutine seaice_forcing_get(& firstTimeStep logical, pointer :: & - config_use_forcing + config_use_forcing, & + config_use_data_icebergs call MPAS_pool_get_config(domain % configs, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_data_icebergs", config_use_data_icebergs) if (config_use_forcing) then @@ -329,6 +340,15 @@ subroutine seaice_forcing_get(& endif + ! data iceberg forcing + if (config_use_data_icebergs) then + + call data_iceberg_forcing(& + streamManager, & + domain) + + endif + end subroutine seaice_forcing_get !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -1590,6 +1610,203 @@ subroutine post_oceanic_coupling(block) end subroutine post_oceanic_coupling +!----------------------------------------------------------------------- +! data iceberg forcing +!----------------------------------------------------------------------- + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! init_data_iceberg_forcing +! +!> \brief +!> \author Darin Comeau, LANL +!> \date 27th August 2018 +!> \details This initializes the data iceberg forcing group. +!> +!----------------------------------------------------------------------- + + subroutine init_data_iceberg_forcing(domain, clock) + + type (domain_type) :: domain + + type (MPAS_Clock_type) :: clock + + logical, pointer :: & + config_do_restart + + character(len=strKIND) :: & + forcingIntervalMonthly, & + forcingReferenceTimeMonthly + + type (MPAS_Time_Type) :: currTime + character(len=strKIND) :: timeStamp + integer :: ierr + + ! get configuration options + call MPAS_pool_get_config(domain % configs, "config_do_restart", config_do_restart) + + forcingIntervalMonthly = "00-01-00_00:00:00" + forcingReferenceTimeMonthly = "0001-01-15_00:00:00" + + currTime = mpas_get_clock_time(clock, MPAS_NOW, ierr) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=ierr) + timeStamp = '0000'//trim(timeStamp(5:)) + + ! create own data iceberg forcing group + call MPAS_forcing_init_group(& + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + domain, & + timeStamp, & + '0000-01-01_00:00:00', & + '0001-00-00_00:00:00', & + config_do_restart) + + ! iceberg freshwater fluxes + call MPAS_forcing_init_field(& + domain % streamManager, & + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + "bergFreshwaterFluxData", & + "dataIcebergForcing", & + "berg_forcing", & + "bergFreshwaterFluxData", & + "linear", & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field_data(& + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + domain % streamManager, & + config_do_restart, & + .false.) + + end subroutine init_data_iceberg_forcing + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! data_iceberg_forcing +! +!> \brief +!> \author Darin Comeau, LANL +!> \date 27th Aug 2018 +!> \details This routine is the timestep for getting and setting fluxes +!> from data icebergs. +! +!----------------------------------------------------------------------- + + subroutine data_iceberg_forcing(& + streamManager, & + domain) + + type (MPAS_streamManager_type), intent(inout) :: streamManager + + type (domain_type) :: domain + + ! Arguments for verbose debugging + ! type (MPAS_time_type) :: currentForcingTime + ! character(len=strKIND) :: currentForcingTimeStr + + real(kind=RKIND), pointer :: & + config_dt + + call mpas_pool_get_config(domain % configs, 'config_dt', config_dt) + + ! For verbose debugging. + ! Uncomment the lines below and the arguments above to print the forcing time + ! to the log file, to ensure the forcing times match the simulation times. + + ! call MPAS_forcing_get_forcing_time(& + ! seaiceForcingGroups, & + ! "seaice_data_iceberg_forcing_monthly", & + ! currentForcingTime) + + ! call MPAS_get_time(currentForcingTime, dateTimeString=currentForcingTimeStr) + ! call MPAS_log_write('Get Data icebergs at: '//trim(currentForcingTimeStr)) + + ! use the forcing layer to get + call MPAS_forcing_get_forcing(& + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + streamManager, & + config_dt) + + call get_data_iceberg_fluxes(domain) + + end subroutine data_iceberg_forcing + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! get_data_iceberg_fluxes +! +!> \brief Initialize with icebergs calving mass +!> \author Darin Comeau, LANL +!> \date 20 Aug 2018 +!> \details This routine is intended to initialize the set data iceberg +!> meltwater and latent heat fluxes from a forcing file of monthly +!> climatologies. +! +!----------------------------------------------------------------------- + + subroutine get_data_iceberg_fluxes(domain) + + use seaice_constants, only: & + seaiceLatentHeatMelting ! latent heat of melting of fresh ice (J/kg) + + type(domain_type), intent(inout) :: & + domain + + type(block_type), pointer :: & + block + + type(MPAS_pool_type), pointer :: & + mesh, & + berg_forcing, & + berg_fluxes + + integer, pointer :: & + nCellsSolve + + real(kind=RKIND), dimension(:), pointer :: & + bergFreshwaterFluxData, & ! iceberg freshwater flux read in from file (kg/m^2/s) + bergFreshwaterFlux, & ! iceberg freshwater flux for ocean (kg/m^2/s) + bergLatentHeatFlux ! iceberg latent heat flux for ocean (J/m^2/s) + + integer :: & + iCell + + ! dc including as parameters here so as not to create new namelist options + real(kind=RKIND), parameter :: & + specificHeatFreshIce = 2106.0_RKIND, & ! specific heat of fresh ice J * kg^-1 * K^-1 + bergTemperature = -4.0_RKIND ! iceberg temperature, assumed constant + + block => domain % blocklist + do while (associated(block)) + + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + call MPAS_pool_get_subpool(block % structs, "berg_forcing", berg_forcing) + call MPAS_pool_get_subpool(block % structs, "berg_fluxes", berg_fluxes) + + call MPAS_pool_get_dimension(mesh, "nCellsSolve", nCellsSolve) + + call MPAS_pool_get_array(berg_forcing, "bergFreshwaterFluxData", bergFreshwaterFluxData) + call MPAS_pool_get_array(berg_fluxes, "bergFreshwaterFlux", bergFreshwaterFlux) + call MPAS_pool_get_array(berg_fluxes, "bergLatentHeatFlux", bergLatentHeatFlux) + + do iCell = 1, nCellsSolve + + bergFreshwaterFlux(iCell) = bergFreshwaterFluxData(iCell) + bergLatentHeatFlux(iCell) = bergFreshwaterFluxData(iCell) * & + (seaiceLatentHeatMelting - specificHeatFreshIce*bergTemperature) + + enddo + + block => block % next + enddo + + end subroutine get_data_iceberg_fluxes + !----------------------------------------------------------------------- ! coupler fluxes initialization !----------------------------------------------------------------------- @@ -1748,6 +1965,7 @@ subroutine reset_ocean_coupler_fluxes(domain) type(MPAS_pool_type), pointer :: & oceanFluxes, & + icebergFluxes, & biogeochemistry real(kind=RKIND), dimension(:), pointer :: & @@ -1756,6 +1974,10 @@ subroutine reset_ocean_coupler_fluxes(domain) oceanHeatFlux, & oceanShortwaveFlux + real(kind=RKIND), dimension(:), pointer :: & + bergFreshwaterFlux, & + bergLatentHeatFlux + real(kind=RKIND), dimension(:), pointer :: & oceanNitrateFlux, & oceanSilicateFlux, & @@ -1776,7 +1998,8 @@ subroutine reset_ocean_coupler_fluxes(domain) oceanDissolvedIronFlux logical, pointer :: & - config_use_column_biogeochemistry + config_use_column_biogeochemistry, & + config_use_data_icebergs block => domain % blocklist do while (associated(block)) @@ -1834,6 +2057,21 @@ subroutine reset_ocean_coupler_fluxes(domain) endif + ! data icebergs + call MPAS_pool_get_config(block % configs, "config_use_data_icebergs", config_use_data_icebergs) + + if (config_use_data_icebergs) then + + call MPAS_pool_get_subpool(block % structs, "berg_fluxes", icebergFluxes) + + call MPAS_pool_get_array(icebergFluxes, "bergFreshwaterFlux", bergFreshwaterFlux) + call MPAS_pool_get_array(icebergFluxes, "bergLatentHeatFlux", bergLatentHeatFlux) + + bergFreshwaterFlux = 0.0_RKIND + bergLatentHeatFlux = 0.0_RKIND + + endif + block => block % next end do @@ -1859,11 +2097,19 @@ subroutine seaice_forcing_write_restart_times(domain) type(domain_type) :: domain logical, pointer :: & - config_use_forcing + config_use_forcing, & + config_use_prescribed_ice, & + config_use_prescribed_ice_forcing, & + config_use_data_icebergs call MPAS_pool_get_config(domain % configs, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_prescribed_ice", config_use_prescribed_ice) + call MPAS_pool_get_config(domain % configs, "config_use_prescribed_ice_forcing", config_use_prescribed_ice_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_data_icebergs", config_use_data_icebergs) - if (config_use_forcing) then + if (config_use_forcing .or. & + (config_use_prescribed_ice .and. config_use_prescribed_ice_forcing) .or. & + config_use_data_icebergs) then call MPAS_forcing_write_restart_times(seaiceForcingGroups) diff --git a/src/core_seaice/shared/mpas_seaice_initialize.F b/src/core_seaice/shared/mpas_seaice_initialize.F index ab437c38c8..2f7247ff0c 100644 --- a/src/core_seaice/shared/mpas_seaice_initialize.F +++ b/src/core_seaice/shared/mpas_seaice_initialize.F @@ -20,6 +20,7 @@ module seaice_initialize use mpas_io_units use mpas_abort use mpas_log, only: mpas_log_write + use mpas_timer, only: mpas_timer_start, mpas_timer_stop implicit none @@ -58,6 +59,13 @@ subroutine seaice_init(& use seaice_forcing, only: seaice_forcing_init, seaice_reset_coupler_fluxes use seaice_diagnostics, only: & seaice_set_testing_system_test_arrays + use seaice_mesh_pool, only: & + seaice_mesh_pool_create + use seaice_prescribed, only: & + seaice_init_prescribed_ice + use seaice_special_boundaries, only: & + seaice_init_special_boundaries, & + seaice_set_special_boundaries_zero_tracers type(domain_type), intent(inout) :: & domain !< Input/Output: @@ -79,6 +87,12 @@ subroutine seaice_init(& ! initialize junk values call init_junk_values(domain) + ! initialize special boundaries + call seaice_init_special_boundaries(domain) + + ! initialize prescribed ice + call seaice_init_prescribed_ice(domain) + ! initialize landice mask if needed for testing call init_test_ice_shelf_mask(domain) @@ -86,6 +100,9 @@ subroutine seaice_init(& call mpas_log_write(" Initialize mesh...") call seaice_init_mesh(domain) + call mpas_log_write(" Initialize mesh pool...") + call seaice_mesh_pool_create(domain) + ! init the basic column physics package call mpas_log_write(" Initialize column parameters...") call seaice_init_column_physics_package_parameters(domain) @@ -96,11 +113,13 @@ subroutine seaice_init(& ! initialize forcing call mpas_log_write(" Initialize forcing...") - call seaice_forcing_init(domain) + call seaice_forcing_init(domain, clock) ! init dynamics call mpas_log_write(" Initialize velocity solver...") + call mpas_timer_start("Velocity solver init") call seaice_init_velocity_solver(domain) + call mpas_timer_stop("Velocity solver init") ! init advection call mpas_log_write(" Initialize advection...") @@ -122,6 +141,9 @@ subroutine seaice_init(& call mpas_log_write(" Initialize coupler fluxes...") call seaice_reset_coupler_fluxes(domain) + ! special boundaries tracers + call seaice_set_special_boundaries_zero_tracers(domain) + end subroutine seaice_init!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -605,7 +627,8 @@ subroutine init_ice_cice_default(& integer :: & iCell, & iCategory, & - iIceLayer + iIceLayer, & + iSnowLayer real(kind=RKIND), parameter :: & initialCategorySnowThickness = 0.2_RKIND @@ -688,8 +711,9 @@ subroutine init_ice_cice_default(& snowVolumeCategory(1,:,iCell) = 0.0_RKIND surfaceTemperature(1,:,iCell) = seaFreezingTemperature(iCell) - - snowEnthalpy(1,:,iCell) = colpkg_enthalpy_snow(0.0_RKIND) + do iSnowLayer = 1, nSnowLayers + snowEnthalpy(iSnowLayer,:,iCell) = colpkg_enthalpy_snow(0.0_RKIND) + end do endif @@ -958,9 +982,6 @@ subroutine init_ice_state_circle_of_ice(& block, & configs)!{{{ - use seaice_constants, only: & - earthRadius - type(block_type), intent(inout) :: & block !< Input/Output: @@ -986,6 +1007,7 @@ subroutine init_ice_state_circle_of_ice(& surfaceTemperature real(kind=RKIND), pointer :: & + earthRadius, & config_initial_ice_area, & config_initial_ice_volume, & config_initial_snow_volume @@ -996,8 +1018,12 @@ subroutine init_ice_state_circle_of_ice(& integer :: & iCell - real(kind=RKIND), parameter :: & - circle_radius = 0.1_RKIND*earthRadius + real(kind=RKIND) :: & + circle_radius + + call MPAS_pool_get_config(configs, "config_earth_radius", earthRadius) + + circle_radius = 0.1_RKIND*earthRadius call MPAS_pool_get_config(configs, "config_initial_ice_area", config_initial_ice_area) call MPAS_pool_get_config(configs, "config_initial_ice_volume", config_initial_ice_volume) diff --git a/src/core_seaice/shared/mpas_seaice_mesh.F b/src/core_seaice/shared/mpas_seaice_mesh.F index 9ba4e64bfb..5ca85939ac 100644 --- a/src/core_seaice/shared/mpas_seaice_mesh.F +++ b/src/core_seaice/shared/mpas_seaice_mesh.F @@ -84,9 +84,6 @@ subroutine rescale_mesh(& use mpas_stream_manager - use seaice_constants, only: & - earthRadius - type (domain_type), intent(inout) :: & domain !< Input/Output: @@ -96,7 +93,8 @@ subroutine rescale_mesh(& real(kind=RKIND), pointer :: & oldRadius, & - sphere_radius + sphere_radius, & + earthRadius real(kind=RKIND) :: & newRadius @@ -154,6 +152,7 @@ subroutine rescale_mesh(& directionProperty call MPAS_pool_get_config(domain % blocklist % configs, "config_do_restart", config_do_restart) + call MPAS_pool_get_config(domain % blocklist % configs, "config_earth_radius", earthRadius) if (.not. config_do_restart) then @@ -631,25 +630,26 @@ end subroutine interior_edges !----------------------------------------------------------------------- subroutine seaice_cell_vertices_at_vertex(& - mesh, & - cellVerticesAtVertex)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: + cellVerticesAtVertex, & + nVertices, & + vertexDegree, & + nEdgesOnCell, & + verticesOnCell, & + cellsOnVertex)!{{{ integer, dimension(:,:), intent(out) :: & cellVerticesAtVertex !< Output: - integer, pointer :: & - nVertices, & - vertexDegree + integer, intent(in) :: & + nVertices, & !< Input: + vertexDegree !< Input: - integer, dimension(:,:), pointer :: & - cellsOnVertex, & - verticesOnCell + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: - integer, dimension(:), pointer :: & - nEdgesOnCell + integer, dimension(:,:), intent(in) :: & + cellsOnVertex, & !< Input: + verticesOnCell !< Input: integer :: & iVertex, & @@ -658,14 +658,6 @@ subroutine seaice_cell_vertices_at_vertex(& iVertexOnCell, & jVertex - ! init variables - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - call MPAS_pool_get_dimension(mesh, "vertexDegree", vertexDegree) - - call MPAS_pool_get_array(mesh, "cellsOnVertex", cellsOnVertex) - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - do iVertex = 1, nVertices do iVertexDegree = 1, vertexDegree @@ -880,31 +872,43 @@ subroutine normal_vectors_planar_polygon(& nEdgesOnCell integer, dimension(:,:), pointer :: & - edgesOnCell + edgesOnCell, & + verticesOnEdge real(kind=RKIND), dimension(:), pointer :: & - xEdge, & - yEdge, & + xVertex, & + yVertex, & xCell, & - yCell + yCell, & + xEdge, & + yEdge integer :: & iCell, & iEdgeOnCell, & - iEdge + iEdge, & + iVertex1, & + iVertex2 real(kind=RKIND) :: & - dx, dy + tx, & + ty, & + tmag, & + nx, & + ny ! init variables call MPAS_pool_get_dimension(mesh, "nCells", nCells) call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) call MPAS_pool_get_array(mesh, "edgesOnCell", edgesOnCell) - call MPAS_pool_get_array(mesh, "xEdge", xEdge) - call MPAS_pool_get_array(mesh, "yEdge", yEdge) + call MPAS_pool_get_array(mesh, "verticesOnEdge", verticesOnEdge) + call MPAS_pool_get_array(mesh, "xVertex", xVertex) + call MPAS_pool_get_array(mesh, "yVertex", yVertex) call MPAS_pool_get_array(mesh, "xCell", xCell) call MPAS_pool_get_array(mesh, "yCell", yCell) + call MPAS_pool_get_array(mesh, "xEdge", xEdge) + call MPAS_pool_get_array(mesh, "yEdge", yEdge) do iCell = 1, nCells @@ -912,11 +916,25 @@ subroutine normal_vectors_planar_polygon(& iEdge = edgesOnCell(iEdgeOnCell,iCell) - dx = xEdge(iEdge) - xCell(iCell) - dy = yEdge(iEdge) - yCell(iCell) + iVertex1 = verticesOnEdge(1,iEdge) + iVertex2 = verticesOnEdge(2,iEdge) + + tx = xVertex(iVertex2) - xVertex(iVertex1) + ty = yVertex(iVertex2) - yVertex(iVertex1) + tmag = sqrt(tx**2 + ty**2) + tx = tx / tmag + ty = ty / tmag + + nx = xEdge(iEdge) - xCell(iCell) + ny = yEdge(iEdge) - yCell(iCell) + + if ((nx * ty - ny * tx) < 0.0_RKIND) then + tx = -tx + ty = -ty + endif - normalVectorPolygon(1,iEdgeOnCell,iCell) = dx / sqrt(dx**2 + dy**2) - normalVectorPolygon(2,iEdgeOnCell,iCell) = dy / sqrt(dx**2 + dy**2) + normalVectorPolygon(1,iEdgeOnCell,iCell) = ty + normalVectorPolygon(2,iEdgeOnCell,iCell) = -tx enddo ! iEdgeOnCell diff --git a/src/core_seaice/shared/mpas_seaice_mesh_pool.F b/src/core_seaice/shared/mpas_seaice_mesh_pool.F new file mode 100644 index 0000000000..4594a89e36 --- /dev/null +++ b/src/core_seaice/shared/mpas_seaice_mesh_pool.F @@ -0,0 +1,286 @@ +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_mesh_pool +! +!> \brief +!> \date 2020 +!> \details +!> +! +!----------------------------------------------------------------------- + +module seaice_mesh_pool + +#include "gpu_macros.inc" + + use mpas_derived_types + use mpas_pool_routines + use mpas_log + + implicit none + private + + integer, public :: & + nCells, & + nVerticesSolve, & + vertexDegree + + integer, public, dimension(:), pointer :: & + nEdgesOnCell, & + solveStress, & + solveVelocity + + integer, public, dimension(:,:), pointer :: & + verticesOnCell, & + cellsOnVertex, & + cellVerticesAtVertex + + real(kind=RKIND), public, dimension(:), pointer :: & + areaTriangle, & + tanLatVertexRotatedOverRadius, & + icePressure, & + uVelocity, & + vVelocity, & + stressDivergenceU, & + stressDivergenceV + + real(kind=RKIND), public, dimension(:,:), pointer :: & + stress11, & + stress12, & + stress22 + + real(kind=RKIND), public, dimension(:,:,:), pointer :: & + basisGradientU, & + basisGradientV, & + basisIntegralsU,& + basisIntegralsV,& + basisIntegralsMetric + + public :: & + seaice_mesh_pool_create, & + seaice_mesh_pool_update, & + seaice_mesh_pool_destroy + +!----------------------------------------------------------------------- + +contains + + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_mesh_pool_create +! +!----------------------------------------------------------------------- + + subroutine seaice_mesh_pool_create(& + domain)!{{{ + + type(domain_type) :: & + domain + + integer :: & + blockCount + + type(block_type), pointer :: & + block + + type (mpas_pool_type), pointer :: & + meshPool, & + velocitySolverPool, & + velocityVariationalPool + + integer, pointer :: & + nCellsTmp, & + nVerticesSolveTmp, & + vertexDegreeTmp + + blockCount = 0 + block => domain % blocklist + do while ( associated(block) ) + + blockCount = blockCount + 1 + if (blockCount > 1) then + call mpas_log_write('seaice_mesh_pool_create: more than one block is no longer supported', MPAS_LOG_CRIT) + endif + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity_solver', velocitySolverPool) + call MPAS_pool_get_subpool(block % structs, "velocity_variational", velocityVariationalPool) + + ! convert mesh dimensions from pointers to scalars + call mpas_pool_get_dimension(meshPool, 'nCells', nCellsTmp) + call MPAS_pool_get_dimension(meshPool, "nVerticesSolve", nVerticesSolveTmp) + call MPAS_pool_get_dimension(meshPool, "vertexDegree", vertexDegreeTmp) + + nCells = nCellsTmp + nVerticesSolve = nVerticesSolveTmp + vertexDegree = vertexDegreeTmp + + ! point to existing arrays + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + call mpas_pool_get_array(meshPool, 'areaTriangle', areaTriangle) + + call MPAS_pool_get_array(velocitySolverPool, "solveStress", solveStress) + call MPAS_pool_get_array(velocitySolverPool, "solveVelocity", solveVelocity) + call MPAS_pool_get_array(velocitySolverPool, "icePressure", icePressure) + call MPAS_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) + call MPAS_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) + call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceU", stressDivergenceU) + call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceV", stressDivergenceV) + + call MPAS_pool_get_array(velocityVariationalPool, "basisGradientU", basisGradientU) + call MPAS_pool_get_array(velocityVariationalPool, "basisGradientV", basisGradientV) + call MPAS_pool_get_array(velocityVariationalPool, "basisIntegralsU", basisIntegralsU) + call MPAS_pool_get_array(velocityVariationalPool, "basisIntegralsV", basisIntegralsV) + call MPAS_pool_get_array(velocityVariationalPool, "basisIntegralsMetric", basisIntegralsMetric) + call MPAS_pool_get_array(velocityVariationalPool, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) + call MPAS_pool_get_array(velocityVariationalPool, "cellVerticesAtVertex", cellVerticesAtVertex) + call MPAS_pool_get_array(velocityVariationalPool, "stress11", stress11) + call MPAS_pool_get_array(velocityVariationalPool, "stress22", stress22) + call MPAS_pool_get_array(velocityVariationalPool, "stress12", stress12) + +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) +!$GPU ENTER_DATA COPY_IN_LP & +!$GPUC nCells, & +!$GPUC nVerticesSolve, & +!$GPUC vertexDegree, & +!$GPUC nEdgesOnCell, & +!$GPUC verticesOnCell, & +!$GPUC cellsOnVertex, & +!$GPUC areaTriangle, & +!$GPUC solveStress, & +!$GPUC solveVelocity, & +!$GPUC icePressure, & +!$GPUC uVelocity, & +!$GPUC vVelocity, & +!$GPUC stressDivergenceU, & +!$GPUC stressDivergenceV, & +!$GPUC basisGradientU, & +!$GPUC basisGradientV, & +!$GPUC basisIntegralsU, & +!$GPUC basisIntegralsV, & +!$GPUC basisIntegralsMetric, & +!$GPUC tanLatVertexRotatedOverRadius, & +!$GPUC cellVerticesAtVertex, & +!$GPUC stress11, & +!$GPUC stress12, & +!$GPUC stress22 & +!$GPUF +#endif + + block => block % next + end do + + end subroutine seaice_mesh_pool_create!}}} +!----------------------------------------------------------------------- + + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_mesh_pool_destroy +! +!----------------------------------------------------------------------- + + subroutine seaice_mesh_pool_destroy(& + err)!{{{ + + integer, intent(out) :: & + err ! returned error flag + + err = 0 + +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) + ! first delete on device +!$GPU EXIT_DATA COPY_DEL_LP & +!$GPUC nCells, & +!$GPUC nVerticesSolve, & +!$GPUC vertexDegree, & +!$GPUC nEdgesOnCell, & +!$GPUC verticesOnCell, & +!$GPUC cellsOnVertex, & +!$GPUC areaTriangle, & +!$GPUC solveStress, & +!$GPUC solveVelocity, & +!$GPUC icePressure, & +!$GPUC uVelocity, & +!$GPUC vVelocity, & +!$GPUC stressDivergenceU, & +!$GPUC stressDivergenceV, & +!$GPUC basisGradientU, & +!$GPUC basisGradientV, & +!$GPUC basisIntegralsU, & +!$GPUC basisIntegralsV, & +!$GPUC basisIntegralsMetric, & +!$GPUC tanLatVertexRotatedOverRadius, & +!$GPUC cellVerticesAtVertex, & +!$GPUC stress11, & +!$GPUC stress12, & +!$GPUC stress22 & +!$GPUF +#endif + + ! then nullify on host + nullify(nEdgesOnCell, & + verticesOnCell, & + cellsOnVertex, & + areaTriangle, & + solveStress, & + solveVelocity, & + icePressure, & + uVelocity, & + vVelocity, & + stressDivergenceU, & + stressDivergenceV, & + basisGradientU, & + basisGradientV, & + basisIntegralsU, & + basisIntegralsV, & + basisIntegralsMetric, & + tanLatVertexRotatedOverRadius, & + cellVerticesAtVertex, & + stress11, & + stress12, & + stress22 & + ) + + end subroutine seaice_mesh_pool_destroy!}}} +!----------------------------------------------------------------------- + + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_mesh_pool_update +! +!----------------------------------------------------------------------- + + subroutine seaice_mesh_pool_update(& + domain)!{{{ + + type(domain_type) :: & + domain + +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) + ! update arrays on device +!$GPU UPDATE_D_LP & +!$GPUC solveStress, & +!$GPUC solveVelocity, & +!$GPUC icePressure, & +!$GPUC uVelocity, & +!$GPUC vVelocity, & +!$GPUC stress11, & +!$GPUC stress12, & +!$GPUC stress22 & +!$GPUF +#endif + + end subroutine seaice_mesh_pool_update!}}} +!----------------------------------------------------------------------- + + + +end module seaice_mesh_pool diff --git a/src/core_seaice/shared/mpas_seaice_prescribed.F b/src/core_seaice/shared/mpas_seaice_prescribed.F new file mode 100644 index 0000000000..fe6c16dca6 --- /dev/null +++ b/src/core_seaice/shared/mpas_seaice_prescribed.F @@ -0,0 +1,374 @@ +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_prescribed +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 7th November 2018 +!> \details +!> +! +!----------------------------------------------------------------------- + +module seaice_prescribed + + use mpas_derived_types + use mpas_pool_routines + use mpas_forcing + use seaice_forcing, only: seaiceForcingGroups + + implicit none + + private + save + + public :: & + seaice_init_prescribed_ice, & + seaice_run_prescribed_ice + +contains + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_init_prescribed_ice +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 7th November 2018 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_init_prescribed_ice(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + logical, pointer :: & + config_use_prescribed_ice, & + config_use_prescribed_ice_forcing, & + config_use_dynamics, & + config_use_column_itd_thermodynamics, & + config_do_restart + + character(len=strKIND) :: & + forcingIntervalMonthly, & + forcingReferenceTimeMonthly + + call mpas_pool_get_config(domain % configs, "config_use_prescribed_ice", config_use_prescribed_ice) + if (config_use_prescribed_ice) then + + ! check for compatable options + call mpas_pool_get_config(domain % configs, "config_use_dynamics", config_use_dynamics) + if (config_use_dynamics) then + call mpas_log_write("Prescribed ice mode: Dynamics must be off (config_use_dynamics = false)", MPAS_LOG_CRIT) + endif + + call mpas_pool_get_config(domain % configs, "config_use_column_itd_thermodynamics", config_use_column_itd_thermodynamics) + if (config_use_column_itd_thermodynamics) then + call mpas_log_write("Prescribed ice mode: ITD thermodynamics must be off (config_use_column_itd_thermodynamics = false)", MPAS_LOG_CRIT) + endif + + call mpas_pool_get_config(domain % configs, "config_use_prescribed_ice_forcing", config_use_prescribed_ice_forcing) + if (config_use_prescribed_ice_forcing) then + + call MPAS_pool_get_config(domain % configs, "config_do_restart", config_do_restart) + + ! create the monthly forcing group + call MPAS_forcing_init_group(& + seaiceForcingGroups, & + "seaice_prescribed_ice_coverage", & + domain, & + '0000-01-01_00:00:00', & + '0000-01-01_00:00:00', & + '0001-00-00_00:00:00', & + config_do_restart) + + forcingIntervalMonthly = "00-01-00_00:00:00" + forcingReferenceTimeMonthly = "0001-01-15_00:00:00" + + call MPAS_forcing_init_field(& + domain % streamManager, & + seaiceForcingGroups, & + "seaice_prescribed_ice_coverage", & + "iceCoverage", & + "prescribedIceForcing", & + "prescribed_ice", & + "iceCoverage", & + "linear", & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field_data(& + seaiceForcingGroups, & + "seaice_prescribed_ice_coverage", & + domain % streamManager, & + config_do_restart, & + .false.) + + endif ! config_use_prescribed_ice_forcing + + endif ! config_use_prescribed_ice + + end subroutine seaice_init_prescribed_ice + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_run_prescribed_ice +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 7th November 2018 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_run_prescribed_ice(domain) + + use seaice_constants, only: & + seaicePuny + + use ice_colpkg, only: & + colpkg_enthalpy_snow, & + colpkg_enthalpy_ice, & + colpkg_salinity_profile + + use seaice_column, only: & + seaice_column_reinitialize_fluxes, & + seaice_column_aggregate + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + logical, pointer :: & + config_use_prescribed_ice, & + config_use_prescribed_ice_forcing + + type(block_type), pointer :: & + blockPtr + + type(mpas_pool_type), pointer :: & + prescribedIcePool, & + tracersPool, & + initialPool, & + oceanCouplingPool, & + meshPool, & + velocitySolverPool + + real(kind=RKIND), dimension(:), pointer :: & + iceCoverage, & + categoryThicknessLimits, & + seaFreezingTemperature, & + latCell, & + freezingMeltingPotential, & + uVelocity, & + vVelocity, & + oceanStressCellU, & + oceanStressCellV + + real(kind=RKIND), dimension(:,:,:), pointer :: & + iceAreaCategory, & + iceVolumeCategory, & + snowVolumeCategory, & + surfaceTemperature, & + iceEnthalpy, & + iceSalinity, & + snowEnthalpy + + real(kind=RKIND) :: & + iceThickness, & + snowThickness, & + depth, & + iceTemperature, & + temperatureGradient + + real(kind=RKIND), pointer :: & + config_dt + + integer, pointer :: & + nCells, & + nCategories, & + nIceLayers, & + nSnowLayers + + integer :: & + iCell, & + iCategory, & + iIceLayer, & + iSnowLayer + + call mpas_pool_get_config(domain % configs, "config_use_prescribed_ice", config_use_prescribed_ice) + if (config_use_prescribed_ice) then + + ! get ice coverage + call mpas_pool_get_config(domain % configs, "config_use_prescribed_ice_forcing", config_use_prescribed_ice_forcing) + if (config_use_prescribed_ice_forcing) then + + call mpas_pool_get_config(domain % configs, 'config_dt', config_dt) + + call MPAS_forcing_get_forcing(& + seaiceForcingGroups, & + "seaice_prescribed_ice_coverage", & + domain % streamManager, & + config_dt) + + endif ! config_use_prescribed_ice_forcing + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call mpas_pool_get_subpool(blockPtr % structs, "prescribed_ice", prescribedIcePool) + call mpas_pool_get_subpool(blockPtr % structs, "tracers", tracersPool) + call mpas_pool_get_subpool(blockPtr % structs, "initial", initialPool) + call mpas_pool_get_subpool(blockPtr % structs, "ocean_coupling", oceanCouplingPool) + call mpas_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + + call mpas_pool_get_array(prescribedIcePool, "iceCoverage", iceCoverage) + + call mpas_pool_get_array(tracersPool, "iceAreaCategory", iceAreaCategory, 1) + call mpas_pool_get_array(tracersPool, "iceVolumeCategory", iceVolumeCategory, 1) + call mpas_pool_get_array(tracersPool, "snowVolumeCategory", snowVolumeCategory, 1) + call mpas_pool_get_array(tracersPool, "surfaceTemperature", surfaceTemperature, 1) + call mpas_pool_get_array(tracersPool, "iceEnthalpy", iceEnthalpy, 1) + call mpas_pool_get_array(tracersPool, "iceSalinity", iceSalinity, 1) + call mpas_pool_get_array(tracersPool, "snowEnthalpy", snowEnthalpy, 1) + + call mpas_pool_get_array(initialPool, "categoryThicknessLimits", categoryThicknessLimits) + + call mpas_pool_get_array(oceanCouplingPool, "seaFreezingTemperature", seaFreezingTemperature) + + call mpas_pool_get_array(meshPool, "latCell", latCell) + + call mpas_pool_get_dimension(blockPtr % dimensions, "nCells", nCells) + call mpas_pool_get_dimension(blockPtr % dimensions, "nCategories", nCategories) + call mpas_pool_get_dimension(blockPtr % dimensions, "nIceLayers", nIceLayers) + call mpas_pool_get_dimension(blockPtr % dimensions, "nSnowLayers", nSnowLayers) + + do iCell = 1, nCells + + ! limit ice coverage + if (iceCoverage(iCell) < 1e-4_RKIND) iceCoverage(iCell) = 0.0_RKIND + if (iceCoverage(iCell) > 1.0_RKIND) iceCoverage(iCell) = 1.0_RKIND + + if (iceCoverage(iCell) >= 1e-4_RKIND) then + + ! set thickness based on hemisphere + if (latCell(iCell) >= 0.0) then + iceThickness = 2.0_RKIND + else if (latCell(iCell) < 0.0) then + iceThickness = 1.0_RKIND + endif + + ! loop over categories + do iCategory = 1, nCategories + + ! find relevant thickness category + if (iceThickness >= categoryThicknessLimits(iCategory) .and. & !!!! index limits + iceThickness < categoryThicknessLimits(iCategory+1)) then + + if (iceAreaCategory(1,iCategory,iCell) > 0.0_RKIND) then + snowThickness = snowVolumeCategory(1,iCategory,iCell) / iceAreaCategory(1,iCategory,iCell) + else + snowThickness = 0.0_RKIND + endif ! prognostic ice present + + ! reset ice state + iceAreaCategory(1,iCategory,iCell) = iceCoverage(iCell) + iceVolumeCategory(1,iCategory,iCell) = iceThickness * iceAreaCategory(1,iCategory,iCell) + snowVolumeCategory(1,iCategory,iCell) = snowThickness * iceAreaCategory(1,iCategory,iCell) + + ! set thermodynamic tracers + if (abs(iceEnthalpy(1,iCategory,iCell)) < seaicePuny) then !!!!! check indices + + if (iceAreaCategory(1,iCategory,iCell) < seaicePuny) then + surfaceTemperature(1,iCategory,iCell) = seaFreezingTemperature(iCell) + endif + + temperatureGradient = seaFreezingTemperature(iCell) - surfaceTemperature(1,iCategory,iCell) + + ! ice quantities + do iIceLayer = 1, nIceLayers + + depth = (real(iIceLayer,kind=RKIND) - 0.5_RKIND) / real(nIceLayers,kind=RKIND) + iceTemperature = surfaceTemperature(1,iCategory,iCell) + temperatureGradient * depth + iceSalinity(iIceLayer,iCategory,iCell) = colpkg_salinity_profile(depth) + iceEnthalpy(iIceLayer,iCategory,iCell) = colpkg_enthalpy_ice(iceTemperature,iceSalinity(iIceLayer,iCategory,iCell)) + + enddo ! iIceLayer + + ! snow quantities + do iSnowLayer = 1, nSnowLayers + snowEnthalpy(iSnowLayer,iCategory,iCell) = colpkg_enthalpy_snow(surfaceTemperature(1,iCategory,iCell)) + enddo ! iSnowLayer + + endif + + else + + surfaceTemperature(1,iCategory,iCell) = seaFreezingTemperature(iCell) + iceAreaCategory(1,iCategory,iCell) = 0.0_RKIND + iceVolumeCategory(1,iCategory,iCell) = 0.0_RKIND + snowVolumeCategory(1,iCategory,iCell) = 0.0_RKIND + iceSalinity(:,iCategory,iCell) = 0.0_RKIND + iceEnthalpy(:,iCategory,iCell) = 0.0_RKIND + snowEnthalpy(:,iCategory,iCell) = 0.0_RKIND + + endif ! in category + + enddo ! iCategory + + else + + ! low ice coverage + surfaceTemperature(1,:,iCell) = seaFreezingTemperature(iCell) + iceAreaCategory(1,:,iCell) = 0.0_RKIND + iceVolumeCategory(1,:,iCell) = 0.0_RKIND + snowVolumeCategory(1,:,iCell) = 0.0_RKIND + iceSalinity(:,:,iCell) = 0.0_RKIND + iceEnthalpy(:,:,iCell) = 0.0_RKIND + snowEnthalpy(:,:,iCell) = 0.0_RKIND + + endif ! ice coverage + + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + ! aggregate tracers + call seaice_column_aggregate(domain) + + ! set non-computed fluxes, ice velocities, ice-ocn stresses to zero + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call mpas_pool_get_subpool(blockPtr % structs, "velocity_solver", velocitySolverPool) + call mpas_pool_get_subpool(blockPtr % structs, "ocean_coupling", oceanCouplingPool) + + call mpas_pool_get_array(oceanCouplingPool, "freezingMeltingPotential", freezingMeltingPotential) + call mpas_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) + call mpas_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) + call mpas_pool_get_array(velocitySolverPool, "oceanStressCellU", oceanStressCellU) + call mpas_pool_get_array(velocitySolverPool, "oceanStressCellV", oceanStressCellV) + + freezingMeltingPotential(:) = 0.0_RKIND + uVelocity(:) = 0.0_RKIND + vVelocity(:) = 0.0_RKIND + oceanStressCellU(:) = 0.0_RKIND + oceanStressCellV(:) = 0.0_RKIND + + blockPtr => blockPtr % next + enddo + + ! reinitialize fluxes + call seaice_column_reinitialize_fluxes(domain) + + endif ! prescribed ice mode + + end subroutine seaice_run_prescribed_ice + + !----------------------------------------------------------------------- + +end module seaice_prescribed diff --git a/src/core_seaice/shared/mpas_seaice_special_boundaries.F b/src/core_seaice/shared/mpas_seaice_special_boundaries.F new file mode 100644 index 0000000000..8af16514df --- /dev/null +++ b/src/core_seaice/shared/mpas_seaice_special_boundaries.F @@ -0,0 +1,414 @@ +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_special_boundaries +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 23rd December 2020 +!> \details +!> +! +!----------------------------------------------------------------------- + +module seaice_special_boundaries + + use mpas_derived_types + use mpas_pool_routines + + implicit none + + private + save + + public :: & + seaice_init_special_boundaries, & + seaice_set_special_boundaries_velocity, & + seaice_set_special_boundaries_velocity_masks, & + seaice_set_special_boundaries_zero_tracers + + logical, pointer :: & + useSpecialBoundariesVelocity, & + useSpecialBoundariesVelocityMasks, & + useSpecialBoundariesZeroTracers + + ! vertex flags + integer, parameter :: & + VELOCITY_BOUNDARY_NONE = 0, & + VELOCITY_BOUNDARY_PERIODIC = 1, & + VELOCITY_BOUNDARY_REVERSE = 2, & + VELOCITY_BOUNDARY_ZERO = 3 + + ! cell flags + integer, parameter :: & + TRACER_BOUNDARY_NONE = 0, & + TRACER_BOUNDARY_ZERO = 1 + +contains + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_init_special_boundaries +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 23rd December 2020 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_init_special_boundaries(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + call init_special_boundaries_velocity(domain) + + call init_special_boundaries_tracers(domain) + + end subroutine seaice_init_special_boundaries + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! init_special_boundaries_velocity +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 23rd December 2020 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine init_special_boundaries_velocity(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + type(block_type), pointer :: & + blockPtr + + type(mpas_pool_type), pointer :: & + specialBoundariesPool, & + meshPool + + real(kind=RKIND), dimension(:), pointer :: & + xVertex, & + yVertex + + integer, dimension(:), pointer :: & + vertexBoundaryType, & + vertexBoundarySource, & + vertexBoundarySourceLocal, & + indexToVertexID + + integer, dimension(:), allocatable :: & + globalToLocalID + + integer, pointer :: & + nVertices + + integer :: & + iVertex + + call MPAS_pool_get_config(domain % configs, "config_use_special_boundaries_velocity", useSpecialBoundariesVelocity) + call MPAS_pool_get_config(domain % configs, "config_use_special_boundaries_velocity_masks", useSpecialBoundariesVelocityMasks) + + if (useSpecialBoundariesVelocity) then + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nVertices", nVertices) + + call MPAS_pool_get_subpool(blockPtr % structs, "special_boundaries", specialBoundariesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + + call MPAS_pool_get_array(specialBoundariesPool, "vertexBoundaryType", vertexBoundaryType) + call MPAS_pool_get_array(specialBoundariesPool, "vertexBoundarySource", vertexBoundarySource) + call MPAS_pool_get_array(specialBoundariesPool, "vertexBoundarySourceLocal", vertexBoundarySourceLocal) + + call MPAS_pool_get_array(meshPool, "xVertex", xVertex) + call MPAS_pool_get_array(meshPool, "yVertex", yVertex) + call MPAS_pool_get_array(meshPool, "indexToVertexID", indexToVertexID) + + allocate(globalToLocalID(nVertices)) + + do iVertex = 1, nVertices + + globalToLocalID(indexToVertexID(iVertex)) = iVertex + + enddo ! iVertex + + do iVertex = 1, nVertices + + vertexBoundarySourceLocal(iVertex) = globalToLocalID(vertexBoundarySource(iVertex)) + + enddo ! iVertex + + deallocate(globalToLocalID) + + blockPtr => blockPtr % next + enddo + + endif + + end subroutine init_special_boundaries_velocity + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! init_special_boundaries_tracers +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 16th January 2021 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine init_special_boundaries_tracers(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + call MPAS_pool_get_config(domain % configs, "config_use_special_boundaries_zero_tracers", useSpecialBoundariesZeroTracers) + + end subroutine init_special_boundaries_tracers + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_set_special_boundaries_velocity +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 23rd December 2020 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_set_special_boundaries_velocity(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + type(block_type), pointer :: & + blockPtr + + type(mpas_pool_type), pointer :: & + velocitySolverPool, & + specialBoundariesPool, & + meshPool + + real(kind=RKIND), dimension(:), pointer :: & + uVelocity, & + vVelocity, & + xVertex, & + yVertex + + integer, dimension(:), pointer :: & + vertexBoundaryType, & + vertexBoundarySource, & + indexToVertexID + + integer, pointer :: & + nVertices + + integer :: & + iVertex, & + iVertexSource + + if (useSpecialBoundariesVelocity) then + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nVertices", nVertices) + + call MPAS_pool_get_subpool(blockPtr % structs, "special_boundaries", specialBoundariesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "velocity_solver", velocitySolverPool) + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + + call MPAS_pool_get_array(specialBoundariesPool, "vertexBoundaryType", vertexBoundaryType) + call MPAS_pool_get_array(specialBoundariesPool, "vertexBoundarySourceLocal", vertexBoundarySource) + + call MPAS_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) + call MPAS_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) + + do iVertex = 1, nVertices + + if (vertexBoundaryType(iVertex) == VELOCITY_BOUNDARY_PERIODIC) then + + iVertexSource = vertexBoundarySource(iVertex) + + uVelocity(iVertex) = uVelocity(iVertexSource) + vVelocity(iVertex) = vVelocity(iVertexSource) + + else if (vertexBoundaryType(iVertex) == VELOCITY_BOUNDARY_REVERSE) then + + iVertexSource = vertexBoundarySource(iVertex) + + uVelocity(iVertex) = -uVelocity(iVertexSource) + vVelocity(iVertex) = -vVelocity(iVertexSource) + + else if (vertexBoundaryType(iVertex) == VELOCITY_BOUNDARY_ZERO) then + + uVelocity(iVertex) = 0.0 + vVelocity(iVertex) = 0.0 + + endif + + enddo ! iVertex + + blockPtr => blockPtr % next + enddo + + endif + + end subroutine seaice_set_special_boundaries_velocity + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_set_special_boundaries_velocity_masks +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 23rd December 2020 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_set_special_boundaries_velocity_masks(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + type(block_type), pointer :: & + blockPtr + + type(mpas_pool_type), pointer :: & + velocitySolverPool, & + specialBoundariesPool + + integer, dimension(:), pointer :: & + solveVelocity, & + solveStress, & + solveVelocitySpecialBoundaries, & + solveStressSpecialBoundaries + + integer, pointer :: & + nCells, & + nVertices + + integer :: & + iCell, & + iVertex + + if (useSpecialBoundariesVelocityMasks) then + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCells", nCells) + call MPAS_pool_get_dimension(blockPtr % dimensions, "nVertices", nVertices) + + call MPAS_pool_get_subpool(blockPtr % structs, "special_boundaries", specialBoundariesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "velocity_solver", velocitySolverPool) + + call MPAS_pool_get_array(specialBoundariesPool, "solveVelocitySpecialBoundaries", solveVelocitySpecialBoundaries) + call MPAS_pool_get_array(specialBoundariesPool, "solveStressSpecialBoundaries", solveStressSpecialBoundaries) + + call MPAS_pool_get_array(velocitySolverPool, "solveVelocity", solveVelocity) + call MPAS_pool_get_array(velocitySolverPool, "solveStress", solveStress) + + do iVertex = 1, nVertices + solveVelocity(iVertex) = solveVelocitySpecialBoundaries(iVertex) + enddo ! iVertex + + do iCell = 1, nCells + solveStress(iCell) = solveStressSpecialBoundaries(iCell) + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + endif + + end subroutine seaice_set_special_boundaries_velocity_masks + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_set_special_boundaries_zero_tracers +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 16th January 2021 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_set_special_boundaries_zero_tracers(domain) + + type (domain_type), intent(inout) :: & + domain !< Input/Output: + + type(block_type), pointer :: & + blockPtr + + type(mpas_pool_type), pointer :: & + specialBoundariesPool, & + tracersPool + + integer, dimension(:), pointer :: & + tracerBoundaryType + + real(kind=RKIND), dimension(:,:,:), pointer :: & + iceAreaCategory, & + iceVolumeCategory, & + snowVolumeCategory + + integer, pointer :: & + nCells + + integer :: & + iCell + + if (useSpecialBoundariesZeroTracers) then + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_dimension(blockPtr % dimensions, "nCells", nCells) + + call MPAS_pool_get_subpool(blockPtr % structs, "special_boundaries", specialBoundariesPool) + call MPAS_pool_get_subpool(blockPtr % structs, "tracers", tracersPool) + + call MPAS_pool_get_array(specialBoundariesPool, "tracerBoundaryType", tracerBoundaryType) + + call MPAS_pool_get_array(tracersPool, "iceAreaCategory", iceAreaCategory, 1) + call MPAS_pool_get_array(tracersPool, "iceVolumeCategory", iceVolumeCategory, 1) + call MPAS_pool_get_array(tracersPool, "snowVolumeCategory", snowVolumeCategory, 1) + + do iCell = 1, nCells + + if (tracerBoundaryType(iCell) == TRACER_BOUNDARY_ZERO) then + iceAreaCategory(:,:,iCell) = 0.0_RKIND + iceVolumeCategory(:,:,iCell) = 0.0_RKIND + snowVolumeCategory(:,:,iCell) = 0.0_RKIND + endif + + enddo ! iCell + + blockPtr => blockPtr % next + enddo + + endif + + end subroutine seaice_set_special_boundaries_zero_tracers + + !----------------------------------------------------------------------- + +end module seaice_special_boundaries diff --git a/src/core_seaice/shared/mpas_seaice_time_integration.F b/src/core_seaice/shared/mpas_seaice_time_integration.F index 331c8c6be9..348bcf2190 100644 --- a/src/core_seaice/shared/mpas_seaice_time_integration.F +++ b/src/core_seaice/shared/mpas_seaice_time_integration.F @@ -42,8 +42,7 @@ module seaice_time_integration subroutine seaice_timestep(& domain, & clock, & - itimestep, & - ierr)!{{{ + itimestep)!{{{ use mpas_timer @@ -52,9 +51,6 @@ subroutine seaice_timestep(& seaice_check_state, & seaice_load_balance_timers - use seaice_unit_test, only: & - seaice_perform_unit_test - use seaice_velocity_solver, only: & seaice_run_velocity_solver @@ -70,6 +66,12 @@ subroutine seaice_timestep(& seaice_column_reinitialize_diagnostics_bgc, & seaice_column_reinitialize_diagnostics_dynamics + use seaice_prescribed, only: & + seaice_run_prescribed_ice + + use seaice_special_boundaries, only: & + seaice_set_special_boundaries_zero_tracers + type(domain_type), intent(inout) :: & domain !< Input/Output: @@ -79,9 +81,6 @@ subroutine seaice_timestep(& integer, intent(in) :: & itimestep !< Input: - integer, intent(inout) :: & - ierr !< Input - type(block_type), pointer :: & block @@ -89,7 +88,7 @@ subroutine seaice_timestep(& configs logical, pointer :: & - config_perform_unit_test, & + config_use_dynamics, & config_use_advection integer, pointer :: & @@ -121,47 +120,46 @@ subroutine seaice_timestep(& configs => domain % configs call MPAS_pool_get_config(configs, "config_use_advection", config_use_advection) - call MPAS_pool_get_config(configs, "config_perform_unit_test", config_perform_unit_test) - if (config_perform_unit_test) then - call seaice_perform_unit_test(domain) - return - endif + ! prescribed ice mode + call seaice_run_prescribed_ice(domain) ! pre dynamics column physics call mpas_timer_start("Column pre-dynamics") - call seaice_column_predynamics_time_integration(domain, clock, ierr) - if (ierr > 0) return + call seaice_column_predynamics_time_integration(domain, clock) call mpas_timer_stop("Column pre-dynamics") - ! loop of dynamcis subcycle - call MPAS_pool_get_config(configs, "config_dynamics_subcycle_number", config_dynamics_subcycle_number) - do iDynamicsSubcycle = 1, config_dynamics_subcycle_number + ! dynamics + call MPAS_pool_get_config(configs, "config_use_dynamics", config_use_dynamics) + if (config_use_dynamics) then + + ! loop of dynamcis subcycle + call MPAS_pool_get_config(configs, "config_dynamics_subcycle_number", config_dynamics_subcycle_number) + do iDynamicsSubcycle = 1, config_dynamics_subcycle_number - ! reinitialize dynamics diagnostics - call mpas_timer_start("Reinitialize diagnostics dynamics") - call seaice_column_reinitialize_diagnostics_dynamics(domain) - call mpas_timer_stop("Reinitialize diagnostics dynamics") + ! reinitialize dynamics diagnostics + call mpas_timer_start("Reinitialize diagnostics dynamics") + call seaice_column_reinitialize_diagnostics_dynamics(domain) + call mpas_timer_stop("Reinitialize diagnostics dynamics") - ! velocity solve - call mpas_timer_start("Velocity solver") - call seaice_run_velocity_solver(domain, clock) - call mpas_timer_stop("Velocity solver") + ! velocity solve + call mpas_timer_start("Velocity solver") + call seaice_run_velocity_solver(domain, clock) + call mpas_timer_stop("Velocity solver") - ! advection - call mpas_timer_start("Advection") - if (config_use_advection) & - call seaice_run_advection(domain, clock, ierr) - call mpas_timer_stop("Advection") - if (ierr > 0) return + ! advection + call mpas_timer_start("Advection") + if (config_use_advection) & + call seaice_run_advection(domain, clock) + call mpas_timer_stop("Advection") - ! ridging - call mpas_timer_start("Column") - call seaice_column_dynamics_time_integration(domain, clock, ierr) - call mpas_timer_stop("Column") - if (ierr > 0) return + ! ridging + call mpas_timer_start("Column") + call seaice_column_dynamics_time_integration(domain, clock) + call mpas_timer_stop("Column") - enddo ! iDynamicsSubcycle + enddo ! iDynamicsSubcycle + endif ! config_use_dynamics ! shortwave call mpas_timer_start("Column post-dynamics") @@ -171,6 +169,9 @@ subroutine seaice_timestep(& ! check the physical state of the model call seaice_check_state(domain) + ! tracer special boundaries + call seaice_set_special_boundaries_zero_tracers(domain) + end subroutine seaice_timestep!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_seaice/shared/mpas_seaice_unit_test.F b/src/core_seaice/shared/mpas_seaice_unit_test.F deleted file mode 100644 index db9ff7a3ac..0000000000 --- a/src/core_seaice/shared/mpas_seaice_unit_test.F +++ /dev/null @@ -1,74 +0,0 @@ -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_unit_test -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - -module seaice_unit_test - - use mpas_derived_types - use mpas_pool_routines - use mpas_log, only: mpas_log_write - - private - save - - public :: & - seaice_perform_unit_test - -contains - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_perform_unit_test -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_perform_unit_test(& - domain)!{{{ - - use seaice_velocity_solver_unit_tests, only: & - seaice_strain_rate_operator_unit_test, & - seaice_stress_divergence_operator_unit_test, & - seaice_constitutive_relationship_unit_test - - type(domain_type), intent(inout) :: & - domain !< Input/Output: - - character(len=strKIND), pointer :: & - config_unit_test_type, & - config_unit_test_subtype - - call MPAS_pool_get_config(domain % configs, "config_unit_test_type", config_unit_test_type) - call MPAS_pool_get_config(domain % configs, "config_unit_test_subtype", config_unit_test_subtype) - - call mpas_log_write("Unit test: "//trim(config_unit_test_type)//" "//trim(config_unit_test_subtype)) - - select case (trim(config_unit_test_type)) - case ("strain rate operator") - call seaice_strain_rate_operator_unit_test(domain, trim(config_unit_test_subtype)) - case ("stress divergence operator") - call seaice_stress_divergence_operator_unit_test(domain, trim(config_unit_test_subtype)) - case ("constitutive relationship") - call seaice_constitutive_relationship_unit_test(domain) - case default - call mpas_log_write("seaice_perform_unit_test: config_unit_test_type unknown: "//trim(config_unit_test_type)) - end select - - end subroutine seaice_perform_unit_test!}}} - -!----------------------------------------------------------------------- - -end module seaice_unit_test diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver.F b/src/core_seaice/shared/mpas_seaice_velocity_solver.F index e960bc82e7..2f4fdae5c2 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver.F @@ -12,12 +12,14 @@ module seaice_velocity_solver +#include "gpu_macros.inc" + use mpas_derived_types use mpas_pool_routines use mpas_timekeeping use mpas_dmpar use mpas_timer - use mpas_log, only: mpas_log_write + use mpas_log, only: mpas_log_write, mpas_log_info implicit none @@ -28,6 +30,33 @@ module seaice_velocity_solver seaice_init_velocity_solver, & seaice_run_velocity_solver + ! strain scheme type + integer :: & + strainSchemeType + + integer, parameter :: & + WEAK_STRAIN_SCHEME = 1, & + VARIATIONAL_STRAIN_SCHEME = 2 + + logical :: & + averageVariationalStrains + + ! stress divergence scheme type + integer :: & + stressDivergenceSchemeType + + integer, parameter :: & + WEAK_STRESS_DIVERGENCE_SCHEME = 1, & + VARIATIONAL_STRESS_DIVERGENCE_SCHEME = 2 + + ! ocean stress type + integer :: & + oceanStressType + + integer, parameter :: & + QUADRATIC_OCEAN_STRESS = 1, & + LINEAR_OCEAN_STRESS = 2 + ! velocity solver constants real(kind=RKIND), parameter, private :: & sinOceanTurningAngle = 0.0_RKIND, & ! northern hemisphere @@ -72,12 +101,16 @@ subroutine seaice_init_velocity_solver(& block character(len=strKIND), pointer :: & + config_strain_scheme, & config_stress_divergence_scheme, & config_variational_basis, & - config_wachspress_integration_type + config_variational_denominator_type, & + config_wachspress_integration_type, & + config_ocean_stress_type logical, pointer :: & config_use_velocity_solver, & + config_average_variational_strain, & config_rotate_cartesian_grid, & config_include_metric_terms, & config_aggregate_halo_exch, & @@ -131,6 +164,42 @@ subroutine seaice_init_velocity_solver(& if (config_use_velocity_solver) then + ! options + call MPAS_pool_get_config(domain % configs, "config_strain_scheme", config_strain_scheme) + if (trim(config_strain_scheme) == "weak") then + strainSchemeType = WEAK_STRAIN_SCHEME + else if (trim(config_strain_scheme) == "variational") then + strainSchemeType = VARIATIONAL_STRAIN_SCHEME + else + call MPAS_log_write("config_strain_scheme unknown: "//trim(config_strain_scheme), MPAS_LOG_CRIT) + endif + + call MPAS_pool_get_config(domain % configs, "config_stress_divergence_scheme", config_stress_divergence_scheme) + if (trim(config_stress_divergence_scheme) == "weak") then + stressDivergenceSchemeType = WEAK_STRESS_DIVERGENCE_SCHEME + else if (trim(config_stress_divergence_scheme) == "variational") then + stressDivergenceSchemeType = VARIATIONAL_STRESS_DIVERGENCE_SCHEME + else + call MPAS_log_write("config_stress_divergence_scheme unknown: "//trim(config_stress_divergence_scheme), MPAS_LOG_CRIT) + endif + + call MPAS_pool_get_config(domain % configs, "config_ocean_stress_type", config_ocean_stress_type) + if (trim(config_ocean_stress_type) == "quadratic") then + oceanStressType = QUADRATIC_OCEAN_STRESS + else if (trim(config_ocean_stress_type) == "linear") then + oceanStressType = LINEAR_OCEAN_STRESS + else + call MPAS_log_write("config_ocean_stress_type unknown: "//trim(config_ocean_stress_type), MPAS_LOG_CRIT) + endif + + if (strainSchemeType == VARIATIONAL_STRAIN_SCHEME .and. & + stressDivergenceSchemeType == WEAK_STRESS_DIVERGENCE_SCHEME) then + call MPAS_log_write("Cannot have variational strain scheme with variational stress divergence scheme", MPAS_LOG_CRIT) + endif + + call MPAS_pool_get_config(domain % configs, "config_average_variational_strain", config_average_variational_strain) + averageVariationalStrains = config_average_variational_strain + ! initialize the evp solver call seaice_init_evp(domain) @@ -142,14 +211,16 @@ subroutine seaice_init_velocity_solver(& call MPAS_pool_get_subpool(block % structs, "velocity_weak", velocity_weak) call MPAS_pool_get_subpool(block % structs, "velocity_variational", velocity_variational) - call MPAS_pool_get_config(block % configs, "config_stress_divergence_scheme", config_stress_divergence_scheme) call MPAS_pool_get_config(block % configs, "config_variational_basis", config_variational_basis) + call MPAS_pool_get_config(block % configs, "config_variational_denominator_type", config_variational_denominator_type) call MPAS_pool_get_config(block % configs, "config_rotate_cartesian_grid", config_rotate_cartesian_grid) call MPAS_pool_get_config(block % configs, "config_include_metric_terms", config_include_metric_terms) call MPAS_pool_get_config(block % configs, "config_wachspress_integration_type", config_wachspress_integration_type) call MPAS_pool_get_config(block % configs, "config_wachspress_integration_order", config_wachspress_integration_order) - if (trim(config_stress_divergence_scheme) == "weak") then + ! init solvers + if (strainSchemeType == WEAK_STRAIN_SCHEME .or. & + stressDivergenceSchemeType == WEAK_STRESS_DIVERGENCE_SCHEME) then call seaice_init_velocity_solver_weak(& mesh, & @@ -157,7 +228,10 @@ subroutine seaice_init_velocity_solver(& velocity_weak, & config_rotate_cartesian_grid) - else if (trim(config_stress_divergence_scheme) == "variational") then + endif + + if (strainSchemeType == VARIATIONAL_STRAIN_SCHEME .or. & + stressDivergenceSchemeType == VARIATIONAL_STRESS_DIVERGENCE_SCHEME) then call seaice_init_velocity_solver_variational(& mesh, & @@ -166,17 +240,10 @@ subroutine seaice_init_velocity_solver(& config_rotate_cartesian_grid, & config_include_metric_terms, & config_variational_basis, & + config_variational_denominator_type, & config_wachspress_integration_type, & config_wachspress_integration_order) - else if (trim(config_stress_divergence_scheme) == "none") then - - continue - - else - - call MPAS_log_write("config_stress_divergence_scheme unknown: "//trim(config_stress_divergence_scheme), MPAS_LOG_CRIT) - endif block => block % next @@ -545,6 +612,9 @@ end subroutine seaice_run_velocity_solver!}}} subroutine velocity_solver_pre_subcycle(domain) + use seaice_mesh_pool, only: & + seaice_mesh_pool_update + type(domain_type), intent(inout) :: & domain @@ -593,6 +663,11 @@ subroutine velocity_solver_pre_subcycle(domain) call init_subcycle_variables(domain) call mpas_timer_stop("init subcycle var") + ! update mesh pool variables + call mpas_timer_start("update mesh pool") + call seaice_mesh_pool_update(domain) + call mpas_timer_stop("update mesh pool") + end subroutine velocity_solver_pre_subcycle !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -719,7 +794,8 @@ subroutine calculation_masks(domain) config_use_column_vertical_thermodynamics, & config_use_halo_exch, & config_aggregate_halo_exch, & - config_reuse_halo_exch + config_reuse_halo_exch, & + config_calc_velocity_masks integer :: & ierr @@ -818,9 +894,11 @@ subroutine calculation_masks(domain) end do ! calculate computational masks - call stress_calculation_mask(domain) - - call velocity_calculation_mask(domain) + call MPAS_pool_get_config(domain % configs, "config_calc_velocity_masks", config_calc_velocity_masks) + if (config_calc_velocity_masks) then + call stress_calculation_mask(domain) + call velocity_calculation_mask(domain) + endif ! halo exchange velocity mask call seaice_load_balance_timers(domain, "vel prep before") @@ -2327,6 +2405,10 @@ subroutine subcycle_velocity_solver(& domain, & clock)!{{{ + use seaice_special_boundaries, only: & + seaice_set_special_boundaries_velocity, & + seaice_set_special_boundaries_velocity_masks + type(domain_type), intent(inout) :: & domain !< Input/Output: @@ -2339,6 +2421,27 @@ subroutine subcycle_velocity_solver(& integer :: & iElasticSubcycle +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) + type (MPAS_pool_type), pointer :: & + velocityVariationalPool + + real(kind=RKIND), dimension(:,:), pointer :: & + replacementPressure + + call MPAS_pool_get_subpool(domain % blocklist % structs, "velocity_variational", velocityVariationalPool) + call MPAS_pool_get_array(velocityVariationalPool, "replacementPressure", replacementPressure) + + call mpas_timer_start("Velocity solver memcpy HtoD") +!$GPU DATA COPY_OUT_LP & +!$GPUC replacementPressure & +!$GPUF + call mpas_timer_stop("Velocity solver memcpy HtoD") +#endif + + ! special boundaries + call seaice_set_special_boundaries_velocity(domain) + call seaice_set_special_boundaries_velocity_masks(domain) + call MPAS_pool_get_config(domain % configs, "config_elastic_subcycle_number", config_elastic_subcycle_number) do iElasticSubcycle = 1, config_elastic_subcycle_number @@ -2348,8 +2451,16 @@ subroutine subcycle_velocity_solver(& clock, & iElasticSubcycle) + ! special boundaries + call seaice_set_special_boundaries_velocity(domain) + call seaice_set_special_boundaries_velocity_masks(domain) + enddo +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) +!$GPU DATA_END +#endif + end subroutine subcycle_velocity_solver!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -2369,15 +2480,20 @@ subroutine single_subcycle_velocity_solver(& clock, & iElasticSubcycle)!{{{ - use seaice_velocity_solver_weak, only: & - seaice_internal_stress_weak - - use seaice_velocity_solver_variational, only: & - seaice_internal_stress_variational + use seaice_velocity_solver_constitutive_relation, only: & + constitutiveRelationType, & + EVP_CONSTITUTIVE_RELATION, & + REVISED_EVP_CONSTITUTIVE_RELATION use seaice_diagnostics, only: & seaice_load_balance_timers + use seaice_mesh_pool, only: & + stressDivergenceU, & + stressDivergenceV, & + uVelocity, & + vVelocity + type(domain_type), intent(inout) :: & domain !< Input/Output: @@ -2387,11 +2503,7 @@ subroutine single_subcycle_velocity_solver(& integer, intent(in) :: & iElasticSubcycle !< Input: !! testing - character(len=strKIND), pointer :: & - config_stress_divergence_scheme - logical, pointer :: & - config_revised_evp, & config_use_halo_exch, & config_aggregate_halo_exch, & config_reuse_halo_exch @@ -2399,23 +2511,14 @@ subroutine single_subcycle_velocity_solver(& integer :: & ierr - call MPAS_pool_get_config(domain % configs, "config_stress_divergence_scheme", config_stress_divergence_scheme) - call MPAS_pool_get_config(domain % configs, "config_revised_evp", config_revised_evp) - ! calculate internal stresses - if (trim(config_stress_divergence_scheme) == "weak") then - - call mpas_timer_start("Velocity solver internal stress") - call seaice_internal_stress_weak(domain) - call mpas_timer_stop("Velocity solver internal stress") + call mpas_timer_start("Velocity solver internal stress") + call seaice_internal_stress(domain) + call mpas_timer_stop("Velocity solver internal stress") - else if (trim(config_stress_divergence_scheme) == "variational") then - - call mpas_timer_start("Velocity solver internal stress") - call seaice_internal_stress_variational(domain) - call mpas_timer_stop("Velocity solver internal stress") - - endif +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) +!$GPU UPDATE_H((stressDivergenceU, stressDivergenceV)) +#endif ! ocean stress coefficient call mpas_timer_start("ocn stress coef") @@ -2423,13 +2526,13 @@ subroutine single_subcycle_velocity_solver(& call mpas_timer_stop("ocn stress coef") ! solve for velocity - if (.not. config_revised_evp) then + if (constitutiveRelationType == EVP_CONSTITUTIVE_RELATION) then call mpas_timer_start("Velocity solver compute") call solve_velocity(domain) call mpas_timer_stop("Velocity solver compute") - else + else if (constitutiveRelationType == REVISED_EVP_CONSTITUTIVE_RELATION) then call mpas_timer_start("Velocity solver compute") call solve_velocity_revised(domain) @@ -2482,8 +2585,392 @@ subroutine single_subcycle_velocity_solver(& call seaice_load_balance_timers(domain, "vel after") +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) +!$GPU UPDATE_D((uVelocity, vVelocity)) +#endif + end subroutine single_subcycle_velocity_solver!}}} +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_internal_stress +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 28th January 2021 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_internal_stress(domain) + + use seaice_mesh_pool, only: & + basisGradientU, & + basisGradientV, & + basisIntegralsMetric, & + basisIntegralsU, & + basisIntegralsV, & + cellVerticesAtVertex, & + icePressure, & + solveStress, & + solveVelocity, & + stress11var => stress11, & + stress22var => stress22, & + stress12var => stress12, & + tanLatVertexRotatedOverRadius, & + uVelocity, & + vVelocity + + use seaice_velocity_solver_weak, only: & + seaice_strain_tensor_weak, & + seaice_stress_tensor_weak, & + seaice_stress_divergence_weak + + use seaice_velocity_solver_variational, only: & + seaice_strain_tensor_variational, & + seaice_stress_tensor_variational, & + seaice_stress_divergence_variational, & + seaice_average_strains_on_vertex + + type(domain_type), intent(inout) :: & + domain + + type(block_type), pointer :: & + blockPtr + + type (MPAS_pool_type), pointer :: & + meshPool, & + velocityWeakPool, & + velocityVariationalPool, & + velocityWeakVariationalPool, & + velocitySolverPool + + real(kind=RKIND), pointer :: & + elasticTimeStep + + real(kind=RKIND), dimension(:), pointer :: & + stressDivergenceU, & + stressDivergenceV + + real(kind=RKIND), dimension(:), pointer :: & + replacementPressureWeak, & + strain11weak, & + strain22weak, & + strain12weak, & + stress11weak, & + stress22weak, & + stress12weak, & + latCellRotated, & + latVertexRotated, & + areaCell + + real(kind=RKIND), dimension(:,:,:), pointer :: & + normalVectorPolygon, & + normalVectorTriangle + + real(kind=RKIND), dimension(:,:), pointer :: & + replacementPressureVar, & + strain11var, & + strain22var, & + strain12var + + real(kind=RKIND), dimension(:), pointer :: & + variationalDenominator + + blockPtr => domain % blocklist + do while (associated(blockPtr)) + + call MPAS_pool_get_subpool(blockPtr % structs, "mesh", meshPool) + call MPAS_pool_get_subpool(blockPtr % structs, "velocity_weak", velocityWeakPool) + call MPAS_pool_get_subpool(blockPtr % structs, "velocity_variational", velocityVariationalPool) + call MPAS_pool_get_subpool(blockPtr % structs, "velocity_weak_variational", velocityWeakVariationalPool) + call MPAS_pool_get_subpool(blockPtr % structs, "velocity_solver", velocitySolverPool) + + call MPAS_pool_get_array(velocitySolverPool, "elasticTimeStep", elasticTimeStep) + call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceU", stressDivergenceU) + call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceV", stressDivergenceV) + + if (strainSchemeType == WEAK_STRAIN_SCHEME .or. & + stressDivergenceSchemeType == WEAK_STRESS_DIVERGENCE_SCHEME) then + + call MPAS_pool_get_array(velocityWeakPool, "normalVectorPolygon", normalVectorPolygon) + call MPAS_pool_get_array(velocityWeakPool, "normalVectorTriangle", normalVectorTriangle) + call MPAS_pool_get_array(velocityWeakPool, "latCellRotated", latCellRotated) + call MPAS_pool_get_array(velocityWeakPool, "latVertexRotated", latVertexRotated) + call MPAS_pool_get_array(velocityWeakPool, "strain11", strain11weak) + call MPAS_pool_get_array(velocityWeakPool, "strain22", strain22weak) + call MPAS_pool_get_array(velocityWeakPool, "strain12", strain12weak) + call MPAS_pool_get_array(velocityWeakPool, "stress11", stress11weak) + call MPAS_pool_get_array(velocityWeakPool, "stress22", stress22weak) + call MPAS_pool_get_array(velocityWeakPool, "stress12", stress12weak) + call MPAS_pool_get_array(velocityWeakPool, "replacementPressure", replacementPressureWeak) + + endif + + if (strainSchemeType == VARIATIONAL_STRAIN_SCHEME .or. & + stressDivergenceSchemeType == VARIATIONAL_STRESS_DIVERGENCE_SCHEME) then + + call MPAS_pool_get_array(velocityVariationalPool, "strain11", strain11var) + call MPAS_pool_get_array(velocityVariationalPool, "strain22", strain22var) + call MPAS_pool_get_array(velocityVariationalPool, "strain12", strain12var) + call MPAS_pool_get_array(velocityVariationalPool, "replacementPressure", replacementPressureVar) + call MPAS_pool_get_array(velocityVariationalPool, "variationalDenominator", variationalDenominator) + + endif + + ! strain + if (strainSchemeType == WEAK_STRAIN_SCHEME) then + + call mpas_timer_start("Velocity solver strain tensor") + call seaice_strain_tensor_weak(& + meshPool, & + strain11weak, & + strain22weak, & + strain12weak, & + uVelocity, & + vVelocity, & + normalVectorPolygon, & + latCellRotated, & + solveStress) + call mpas_timer_stop("Velocity solver strain tensor") + + else if (strainSchemeType == VARIATIONAL_STRAIN_SCHEME) then + + call mpas_timer_start("Velocity solver strain tensor") + call seaice_strain_tensor_variational(& + meshPool, & + strain11var, & + strain22var, & + strain12var, & + uVelocity, & + vVelocity, & + basisGradientU, & + basisGradientV, & + tanLatVertexRotatedOverRadius, & + solveStress) + call mpas_timer_stop("Velocity solver strain tensor") + + endif + + ! average variational strains around vertex + if (strainSchemeType == VARIATIONAL_STRAIN_SCHEME .and. & + averageVariationalStrains) then + + call MPAS_pool_get_array(meshPool, "areaCell", areaCell) + call seaice_average_strains_on_vertex(& + areaCell, & + strain11var, & + strain22var, & + strain12var) + endif + + ! weak strain / variational stress divergence + if (strainSchemeType == WEAK_STRAIN_SCHEME .and. & + stressDivergenceSchemeType == VARIATIONAL_STRESS_DIVERGENCE_SCHEME) then + + call mpas_timer_start("Velocity solver interpolate strain") + call interpolate_strains_weak_to_variational(& + meshPool, & + velocityWeakVariationalPool, & + strain11weak, & + strain22weak, & + strain12weak, & + strain11var, & + strain22var, & + strain12var) + call mpas_timer_stop("Velocity solver interpolate strain") + + endif + + ! consitutive relation + if (stressDivergenceSchemeType == WEAK_STRESS_DIVERGENCE_SCHEME) then + + call mpas_timer_start("Velocity solver stress tensor") + call seaice_stress_tensor_weak(& + meshPool, & + stress11weak, & + stress22weak, & + stress12weak, & + strain11weak, & + strain22weak, & + strain12weak, & + icePressure, & + replacementPressureWeak, & + solveStress, & + elasticTimeStep) + call mpas_timer_stop("Velocity solver stress tensor") + + else if (stressDivergenceSchemeType == VARIATIONAL_STRESS_DIVERGENCE_SCHEME) then + + call mpas_timer_start("Velocity solver stress tensor") + call seaice_stress_tensor_variational(& + meshPool, & + stress11var, & + stress22var, & + stress12var, & + strain11var, & + strain22var, & + strain12var, & + icePressure, & + replacementPressureVar, & + solveStress, & + elasticTimeStep) + call mpas_timer_stop("Velocity solver stress tensor") + + endif + + ! stress divergence + if (stressDivergenceSchemeType == WEAK_STRESS_DIVERGENCE_SCHEME) then + + call mpas_timer_start("Velocity solver stress divergence") + call seaice_stress_divergence_weak(& + meshPool, & + stressDivergenceU, & + stressDivergenceV, & + stress11weak, & + stress22weak, & + stress12weak, & + normalVectorTriangle, & + latVertexRotated, & + solveVelocity) + call mpas_timer_stop("Velocity solver stress divergence") + + else if (stressDivergenceSchemeType == VARIATIONAL_STRESS_DIVERGENCE_SCHEME) then + + call mpas_timer_start("Velocity solver stress divergence") + call seaice_stress_divergence_variational(& + meshPool, & + stressDivergenceU, & + stressDivergenceV, & + stress11var, & + stress22var, & + stress12var, & + basisIntegralsU, & + basisIntegralsV, & + basisIntegralsMetric, & + variationalDenominator, & + tanLatVertexRotatedOverRadius, & + cellVerticesAtVertex, & + solveVelocity) + call mpas_timer_stop("Velocity solver stress divergence") + + endif + + blockPtr => blockPtr % next + end do + + end subroutine seaice_internal_stress + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! interpolate_strains_weak_to_variational +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 29th January 2021 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine interpolate_strains_weak_to_variational(& + meshPool, & + velocityWeakVariationalPool, & + strain11weak, & + strain22weak, & + strain12weak, & + strain11var, & + strain22var, & + strain12var) + + use seaice_mesh_pool, only: & + nCells, & + nVerticesSolve, & + vertexDegree, & + cellsOnVertex, & + nEdgesOnCell, & + verticesOnCell + + type (MPAS_pool_type), pointer :: & + meshPool, & + velocityWeakVariationalPool + + real(kind=RKIND), dimension(:), intent(in) :: & + strain11weak, & !< Input/Output: + strain22weak, & !< Input/Output: + strain12weak !< Input/Output: + + real(kind=RKIND), dimension(:,:), intent(out) :: & + strain11var, & !< Input/Output: + strain22var, & !< Input/Output: + strain12var !< Input/Output: + + real(kind=RKIND), dimension(:), pointer :: & + strain11Vertex, & + strain22Vertex, & + strain12Vertex + + real(kind=RKIND), dimension(:), pointer :: & + areaCell + + real(kind=RKIND) :: & + denom + + integer :: & + iVertex, & + iCellOnVertex, & + iCell, & + iVertexOnCell + + call MPAS_pool_get_array(meshPool, "areaCell", areaCell) + + call MPAS_pool_get_array(velocityWeakVariationalPool, "strain11Vertex", strain11Vertex) + call MPAS_pool_get_array(velocityWeakVariationalPool, "strain22Vertex", strain22Vertex) + call MPAS_pool_get_array(velocityWeakVariationalPool, "strain12Vertex", strain12Vertex) + + do iVertex = 1, nVerticesSolve + + strain11Vertex(iVertex) = 0.0_RKIND + strain22Vertex(iVertex) = 0.0_RKIND + strain12Vertex(iVertex) = 0.0_RKIND + denom = 0.0_RKIND + + do iCellOnVertex = 1, vertexDegree + + iCell = cellsOnVertex(iCellOnVertex,iVertex) + + if (iCell >= 1 .and. iCell <= nCells) then + strain11Vertex(iVertex) = strain11Vertex(iVertex) + areaCell(iCell) * strain11weak(iCell) + strain22Vertex(iVertex) = strain22Vertex(iVertex) + areaCell(iCell) * strain22weak(iCell) + strain12Vertex(iVertex) = strain12Vertex(iVertex) + areaCell(iCell) * strain12weak(iCell) + denom = denom + areaCell(iCell) + endif + + enddo ! iCellOnVertex + + strain11Vertex(iVertex) = strain11Vertex(iVertex) / denom + strain22Vertex(iVertex) = strain22Vertex(iVertex) / denom + strain12Vertex(iVertex) = strain12Vertex(iVertex) / denom + + enddo ! iVertex + + do iCell = 1, nCells + + do iVertexOnCell = 1, nEdgesOnCell(iCell) + + iVertex = verticesOnCell(iVertexOnCell,iCell) + + strain11var(iVertexOnCell,iCell) = strain11Vertex(iVertex) + strain22var(iVertexOnCell,iCell) = strain22Vertex(iVertex) + strain12var(iVertexOnCell,iCell) = strain12Vertex(iVertex) + + enddo ! iVertexOnCell + + enddo ! iCell + + end subroutine interpolate_strains_weak_to_variational + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! ocean_stress_coefficient @@ -2554,24 +3041,40 @@ subroutine ocean_stress_coefficient(domain) call MPAS_pool_get_array(icestatePool, "iceAreaVertex", iceAreaVertex) - do iVertex = 1, nVerticesSolve + if (oceanStressType == QUADRATIC_OCEAN_STRESS) then - if (solveVelocity(iVertex) == 1) then + do iVertex = 1, nVerticesSolve - oceanStressCoeff(iVertex) = seaiceIceOceanDragCoefficient * seaiceDensitySeaWater * iceAreaVertex(iVertex) * & - sqrt((uOceanVelocityVertex(iVertex) - uVelocity(iVertex))**2 + & - (vOceanVelocityVertex(iVertex) - vVelocity(iVertex))**2) + if (solveVelocity(iVertex) == 1) then - endif + oceanStressCoeff(iVertex) = seaiceIceOceanDragCoefficient * seaiceDensitySeaWater * iceAreaVertex(iVertex) * & + sqrt((uOceanVelocityVertex(iVertex) - uVelocity(iVertex))**2 + & + (vOceanVelocityVertex(iVertex) - vVelocity(iVertex))**2) - enddo ! iVertex + endif - else + enddo ! iVertex + + else if (oceanStressType == LINEAR_OCEAN_STRESS) then + + do iVertex = 1, nVerticesSolve + + if (solveVelocity(iVertex) == 1) then + + oceanStressCoeff(iVertex) = seaiceIceOceanDragCoefficient * seaiceDensitySeaWater * iceAreaVertex(iVertex) + + endif + + enddo ! iVertex + + end if ! oceanStressType + + else ! configUseOceanStress ! no ocean stress oceanStressCoeff = 0.0_RKIND - endif + endif ! configUseOceanStress block => block % next end do @@ -2608,8 +3111,6 @@ subroutine solve_velocity(domain) real(kind=RKIND), dimension(:), pointer :: & uVelocity, & vVelocity, & - uVelocityInitial, & - vVelocityInitial, & totalMassVertex, & totalMassVertexfVertex, & stressDivergenceU, & @@ -2654,8 +3155,6 @@ subroutine solve_velocity(domain) call MPAS_pool_get_array(velocitySolverPool, "solveVelocity", solveVelocity) call MPAS_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) call MPAS_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) - call MPAS_pool_get_array(velocitySolverPool, "uVelocityInitial", uVelocityInitial) - call MPAS_pool_get_array(velocitySolverPool, "vVelocityInitial", vVelocityInitial) call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceU", stressDivergenceU) call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceV", stressDivergenceV) call MPAS_pool_get_array(velocitySolverPool, "airStressVertexU", airStressVertexU) @@ -2667,7 +3166,9 @@ subroutine solve_velocity(domain) call MPAS_pool_get_array(velocitySolverPool, "oceanStressV", oceanStressV) call MPAS_pool_get_array(velocitySolverPool, "oceanStressCoeff", oceanStressCoeff) - !$omp parallel do default(shared) private(iVertex, leftMatrix, rightHandSide, solutionDenominator) +#ifdef MPAS_OPENMP +!$omp parallel do default(shared) private(iVertex, leftMatrix, rightHandSide, solutionDenominator) +#endif do iVertex = 1, nVerticesSolve if (solveVelocity(iVertex) == 1) then diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F index 27c04b5506..324187c1f4 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F @@ -24,19 +24,29 @@ module seaice_velocity_solver_constitutive_relation public :: & seaice_init_evp, & seaice_evp_constitutive_relation, & - seaice_evp_constitutive_relation_revised + seaice_evp_constitutive_relation_revised, & + seaice_linear_constitutive_relation + + ! constitutive relation options + integer, public :: & + constitutiveRelationType + + integer, public :: & + EVP_CONSTITUTIVE_RELATION = 1, & + REVISED_EVP_CONSTITUTIVE_RELATION = 2, & + LINEAR_CONSTITUTIVE_RELATION = 3, & + NONE_CONSTITUTIVE_RELATION = 4 ! general EVP parameters real(kind=RKIND), parameter, private :: & eccentricity = 2.0_RKIND, & - dampingTimescaleParameter = 0.36_RKIND, & - puny = 1.0e-11_RKIND + dampingTimescaleParameter = 0.36_RKIND real(kind=RKIND), parameter, public :: & - eccentricitySquared = eccentricity**2 + eccentricitySquared = eccentricity**2, & + puny = 1.0e-11_RKIND real(kind=RKIND), private :: & - dampingTimescale, & evpDampingCriterion ! Bouillon et al. 2013 parameters @@ -45,6 +55,7 @@ module seaice_velocity_solver_constitutive_relation dampingRatio = 5.5e-3_RKIND ! xi = Sv/Sc < 1 real(kind=RKIND), public :: & + dampingTimescale, & numericalInertiaCoefficient ! brlx contains @@ -67,6 +78,9 @@ subroutine seaice_init_evp(domain) type(block_type), pointer :: block + character(len=strKIND), pointer :: & + config_constitutive_relation_type + type(MPAS_pool_type), pointer :: & velocitySolver, & mesh @@ -86,6 +100,20 @@ subroutine seaice_init_evp(domain) dvEdgeMin, & dvEdgeMinGlobal + ! constitutive relation type + call MPAS_pool_get_config(domain % configs, "config_constitutive_relation_type", config_constitutive_relation_type) + if (trim(config_constitutive_relation_type) == 'evp') then + constitutiveRelationType = EVP_CONSTITUTIVE_RELATION + else if (trim(config_constitutive_relation_type) == 'evp_revised') then + constitutiveRelationType = REVISED_EVP_CONSTITUTIVE_RELATION + else if (trim(config_constitutive_relation_type) == 'linear') then + constitutiveRelationType = LINEAR_CONSTITUTIVE_RELATION + else if (trim(config_constitutive_relation_type) == 'none') then + constitutiveRelationType = NONE_CONSTITUTIVE_RELATION + else + call MPAS_log_write("config_constitutive_relation_type unknown: "//trim(config_constitutive_relation_type), MPAS_LOG_CRIT) + endif + ! general EVP block => domain % blocklist do while (associated(block)) @@ -158,6 +186,10 @@ subroutine seaice_evp_constitutive_relation(& replacementPressure, & areaCell, & dtElastic) +#ifdef CPRINTEL +!$omp declare simd(seaice_evp_constitutive_relation) uniform(dtElastic), notinbranch & +!$omp& linear(ref(stress11,stress22,stress12,strain11,strain22,strain12,replacementPressure,icePressure,areaCell)) +#endif real(kind=RKIND), intent(inout) :: & stress11, & !< Input/Output: @@ -237,6 +269,10 @@ subroutine seaice_evp_constitutive_relation_revised(& icePressure, & replacementPressure, & areaCell) +#ifdef CPRINTEL +!$omp declare simd(seaice_evp_constitutive_relation_revised) uniform(icePressure,areaCell), notinbranch & +!$omp& linear(ref(stress11,stress22,stress12,strain11,strain22,strain12,replacementPressure)) +#endif real(kind=RKIND), intent(inout) :: & stress11, & !< Input/Output: @@ -312,6 +348,10 @@ subroutine seaice_linear_constitutive_relation(& strain11, & strain22, & strain12) +#ifdef CPRINTEL +!$omp declare simd(seaice_linear_constitutive_relation) notinbranch & +!$omp& linear(ref(stress11,stress22,stress12,strain11,strain22,strain12)) +#endif real(kind=RKIND), intent(out) :: & stress11, & !< Output: diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F index a9c3b48a0e..09da732bcd 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F @@ -42,119 +42,20 @@ module seaice_velocity_solver_pwl !----------------------------------------------------------------------- subroutine seaice_init_velocity_solver_pwl(& - mesh, & - velocity_variational, & - boundary, & - rotateCartesianGrid, & - includeMetricTerms)!{{{ - - use seaice_mesh, only: & - seaice_cell_vertices_at_vertex - - use seaice_velocity_solver_variational_shared, only: & - seaice_calc_local_coords, & - seaice_calc_variational_metric_terms - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - type(MPAS_pool_type), pointer :: & - velocity_variational, & !< Input/Output: - boundary !< Input/Output: - - logical, intent(in) :: & - rotateCartesianGrid, & !< Input: - includeMetricTerms !< Input: - - integer, dimension(:,:), pointer :: & - cellVerticesAtVertex - - integer, pointer :: & - nCells, & - maxEdges - - real(kind=RKIND), dimension(:), pointer :: & - tanLatVertexRotatedOverRadius - - real(kind=RKIND), dimension(:,:), allocatable :: & - xLocal, & - yLocal - - real(kind=RKIND), dimension(:,:,:), pointer :: & - basisGradientU, & - basisGradientV, & - basisIntegralsMetric, & - basisIntegralsU, & - basisIntegralsV - - integer :: iCell, i1, i2 - - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) - - call MPAS_pool_get_array(velocity_variational, "cellVerticesAtVertex", cellVerticesAtVertex) - call MPAS_pool_get_array(velocity_variational, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) - call MPAS_pool_get_array(velocity_variational, "basisGradientU", basisGradientU) - call MPAS_pool_get_array(velocity_variational, "basisGradientV", basisGradientV) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsMetric", basisIntegralsMetric) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsU", basisIntegralsU) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsV", basisIntegralsV) - - allocate(xLocal(maxEdges,nCells)) - allocate(yLocal(maxEdges,nCells)) - - call seaice_calc_local_coords(& - mesh, & - xLocal, & - yLocal, & - rotateCartesianGrid) - - call seaice_calc_variational_metric_terms(& - mesh, & - tanLatVertexRotatedOverRadius, & - rotateCartesianGrid, & - includeMetricTerms) - - call seaice_cell_vertices_at_vertex(& - mesh, & - cellVerticesAtVertex) - - call init_velocity_solver_pwl_basis(& - mesh, & - basisGradientU, & - basisGradientV, & - basisIntegralsMetric, & - basisIntegralsU, & - basisIntegralsV, & - xLocal, & - yLocal) - - deallocate(xLocal) - deallocate(yLocal) - - end subroutine seaice_init_velocity_solver_pwl!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! init_velocity_solver_pwl_basis -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine init_velocity_solver_pwl_basis(& - mesh, & + nCells, & + maxEdges, & + nEdgesOnCell, & + verticesOnCell, & + edgesOnCell, & + dvEdge, & + areaCell, & + xLocal, & + yLocal, & basisGradientU, & basisGradientV, & basisIntegralsMetric, & basisIntegralsU, & - basisIntegralsV, & - xLocal, & - yLocal)!{{{ + basisIntegralsV)!{{{ use seaice_numerics, only: & seaice_solve_linear_basis_system @@ -162,8 +63,24 @@ subroutine init_velocity_solver_pwl_basis(& use seaice_velocity_solver_variational_shared, only: & seaice_wrapped_index - type(MPAS_pool_type), pointer :: & - mesh !< Input: + real(kind=RKIND), dimension(:,:), intent(in) :: & + xLocal, & !< Input: + yLocal !< Input: + + integer, intent(in) :: & + nCells, & !< Input: + maxEdges !< Input: + + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: + + integer, dimension(:,:), intent(in) :: & + verticesOnCell, & !< Input: + edgesOnCell !< Input: + + real(kind=RKIND), dimension(:), intent(in) :: & + dvEdge, & !< Input: + areaCell !< Input: real(kind=RKIND), dimension(:,:,:), intent(out) :: & basisGradientU, & !< Output: @@ -172,10 +89,6 @@ subroutine init_velocity_solver_pwl_basis(& basisIntegralsU, & !< Output: basisIntegralsV !< Output: - real(kind=RKIND), dimension(:,:), intent(in) :: & - xLocal, & !< Input: - yLocal !< Input: - real(kind=RKIND) :: & xPWLCentre, & yPWLCentre, & @@ -209,21 +122,6 @@ subroutine init_velocity_solver_pwl_basis(& rightHandSide, & solutionVector - integer, pointer :: & - nCells, & - maxEdges - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - verticesOnCell, & - edgesOnCell - - real(kind=RKIND), dimension(:), pointer :: & - dvEdge, & - areaCell - real(kind=RKIND), dimension(:,:), allocatable :: & subBasisGradientU, & subBasisGradientV, & @@ -234,16 +132,6 @@ subroutine init_velocity_solver_pwl_basis(& real(kind=RKIND), dimension(:), allocatable :: & basisSubArea - ! init variables - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "edgesOnCell", edgesOnCell) - call MPAS_pool_get_array(mesh, "dvEdge", dvEdge) - call MPAS_pool_get_array(mesh, "areaCell", areaCell) - allocate(subBasisGradientU(maxEdges,3)) allocate(subBasisGradientV(maxEdges,3)) allocate(subBasisConstant(maxEdges,3)) @@ -482,7 +370,7 @@ subroutine init_velocity_solver_pwl_basis(& deallocate(subCellgradientV) deallocate(basisSubArea) - end subroutine init_velocity_solver_pwl_basis!}}} + end subroutine seaice_init_velocity_solver_pwl!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_unit_tests.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_unit_tests.F deleted file mode 100644 index 7b273f5fbc..0000000000 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_unit_tests.F +++ /dev/null @@ -1,2414 +0,0 @@ -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_velocity_solver_unit_tests -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - -module seaice_velocity_solver_unit_tests - - use mpas_derived_types - use mpas_pool_routines - use mpas_log, only: mpas_log_write - - implicit none - - private - save - - public :: & - seaice_strain_rate_operator_unit_test, & - seaice_stress_divergence_operator_unit_test, & - seaice_constitutive_relationship_unit_test - - integer, private :: & - iObject = 2 - -contains - -!----------------------------------------------------------------------- -! Spherical strain rate unit test -!----------------------------------------------------------------------- - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_strain_rate_operator_unit_test -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_strain_rate_operator_unit_test(& - domain, & - unit_test_subtype)!{{{ - - type(domain_type), intent(inout) :: & - domain !< Input/Output: - - character(len=*), intent(in) :: & - unit_test_subtype !< Input: - - integer, parameter :: ntests = 6 - character(len=200), dimension(ntests), parameter :: & - test_names = (/"zero ", & - "zonal ", & - "meridonal ", & - "solid_body ", & - "sinusoidal1", & - "sinusoidal2"/) - - integer :: & - itest - - if (trim(unit_test_subtype) == "all") then - - do itest = 1, ntests - - call seaice_strain_rate_operator_unit_test_individual(& - domain, & - trim(test_names(itest))) - - enddo ! itest - - else - - call seaice_strain_rate_operator_unit_test_individual(& - domain, & - unit_test_subtype) - - endif - - end subroutine seaice_strain_rate_operator_unit_test - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_strain_rate_operator_unit_test -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_strain_rate_operator_unit_test_individual(& - domain, & - unit_test_subtype)!{{{ - - use seaice_velocity_solver_weak, only: & - seaice_strain_tensor_weak - - use seaice_velocity_solver_variational, only: & - seaice_strain_tensor_variational - - use seaice_mesh, only: & - seaice_latlon_from_xyz, & - seaice_grid_rotation_forward - - use seaice_constants, only: & - seaiceRadiansToDegrees, & - pii - - type(domain_type) :: & - domain !< Input/Output: - - character(len=*), intent(in) :: & - unit_test_subtype !< Input: - - type(block_type), pointer :: & - block - - type (MPAS_pool_type), pointer :: & - mesh, & - boundary, & - velocity_solver, & - velocity_weak, & - velocity_variational - - real(kind=RKIND), dimension(:), allocatable :: & - strain11, & - strain22, & - strain12, & - strain11_test, & - strain22_test, & - strain12_test, & - strain11_diff, & - strain22_diff, & - strain12_diff, & - latVertexRotated0, & - lonVertexRotated0, & - latCellRotated0, & - lonCellRotated0 - - integer :: & - iCell, & - iCellOnCell, & - iVertex - - real(kind=RKIND) :: & - up, vp, ut, vt, & - xp, yp, zp, & - rms_strain11, & - rms_strain22, & - rms_strain12 - - integer, pointer :: & - nCells, & - nVertices - - integer, dimension(:), pointer :: & - solveStress, & - interiorCell - - real(kind=RKIND), pointer :: & - sphere_radius - - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - zVertex, & - xCell, & - yCell, & - zCell, & - latCell, & - lonCell, & - uVelocity, & - vVelocity, & - latCellRotated, & - strain11_weak, & - strain22_weak, & - strain12_weak, & - tanLatVertexRotatedOverRadius - - real(kind=RKIND), dimension(:,:), pointer :: & - strain11_variational, & - strain22_variational, & - strain12_variational - - real(kind=RKIND), dimension(:,:,:), pointer :: & - basisGradientU, & - basisGradientV - - real(kind=RKIND), dimension(:,:,:), pointer :: & - normalVectorPolygon - - logical, pointer :: & - config_rotate_cartesian_grid - - character(len=strKIND), pointer :: & - config_stress_divergence_scheme - - real(kind=RKIND), parameter :: & - polarAngleLimit = 0.95_RKIND * 0.5_RKIND * pii - - call MPAS_pool_get_config(domain % configs, "config_rotate_cartesian_grid", config_rotate_cartesian_grid) - call MPAS_pool_get_config(domain % configs, "config_stress_divergence_scheme", config_stress_divergence_scheme) - - block => domain % blocklist - do while (associated(block)) - - call MPAS_pool_get_subpool(block % structs, "mesh", mesh) - call MPAS_pool_get_subpool(block % structs, "boundary", boundary) - call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocity_solver) - call MPAS_pool_get_subpool(block % structs, "velocity_weak", velocity_weak) - call MPAS_pool_get_subpool(block % structs, "velocity_variational", velocity_variational) - - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) - sphere_radius = 6371220.0_RKIND - - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "zVertex", zVertex) - call MPAS_pool_get_array(mesh, "xCell", xCell) - call MPAS_pool_get_array(mesh, "yCell", yCell) - call MPAS_pool_get_array(mesh, "zCell", zCell) - call MPAS_pool_get_array(mesh, "latCell", latCell) - call MPAS_pool_get_array(mesh, "lonCell", lonCell) - - call MPAS_pool_get_array(velocity_solver, "solveStress", solveStress) - call MPAS_pool_get_array(velocity_solver, "uVelocity", uVelocity) - call MPAS_pool_get_array(velocity_solver, "vVelocity", vVelocity) - - call MPAS_pool_get_array(boundary, "interiorCell", interiorCell) - - if (trim(config_stress_divergence_scheme) == "weak") then - call MPAS_pool_get_array(velocity_weak, "latCellRotated", latCellRotated) - call MPAS_pool_get_array(velocity_weak, "normalVectorPolygon", normalVectorPolygon) - call MPAS_pool_get_array(velocity_weak, "strain11", strain11_weak) - call MPAS_pool_get_array(velocity_weak, "strain22", strain22_weak) - call MPAS_pool_get_array(velocity_weak, "strain12", strain12_weak) - else if (trim(config_stress_divergence_scheme) == "variational") then - call MPAS_pool_get_array(velocity_variational, "strain11", strain11_variational) - call MPAS_pool_get_array(velocity_variational, "strain22", strain22_variational) - call MPAS_pool_get_array(velocity_variational, "strain12", strain12_variational) - call MPAS_pool_get_array(velocity_variational, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) - call MPAS_pool_get_array(velocity_variational, "basisGradientU", basisGradientU) - call MPAS_pool_get_array(velocity_variational, "basisGradientV", basisGradientV) - endif - - open(54,file="strain_rate_test_"//trim(unit_test_subtype)//".txt",position="append") - - write(54,*) "-----------------------------------------------------------" - write(54,*) - write(54,*) "Test type: ", trim(unit_test_subtype) - write(54,*) - - allocate(strain11(nCells)) - allocate(strain22(nCells)) - allocate(strain12(nCells)) - allocate(strain11_test(nCells)) - allocate(strain22_test(nCells)) - allocate(strain12_test(nCells)) - allocate(strain11_diff(nCells)) - allocate(strain22_diff(nCells)) - allocate(strain12_diff(nCells)) - - allocate(lonVertexRotated0(nVertices)) - allocate(latVertexRotated0(nVertices)) - allocate(lonCellRotated0(nCells)) - allocate(latCellRotated0(nCells)) - - do iVertex = 1, nVertices - - call seaice_grid_rotation_forward(& - xp, yp, zp, & - xVertex(iVertex), yVertex(iVertex), zVertex(iVertex), & - config_rotate_cartesian_grid) - - call seaice_latlon_from_xyz(& - latVertexRotated0(iVertex), lonVertexRotated0(iVertex), & - xp, yp, zp, sphere_radius) - - enddo ! iVertex - - do iCell = 1, nCells - - call seaice_grid_rotation_forward(& - xp, yp, zp, & - xCell(iCell), yCell(iCell), zCell(iCell), & - config_rotate_cartesian_grid) - - call seaice_latlon_from_xyz(& - latCellRotated0(iCell), lonCellRotated0(iCell), & - xp, yp, zp, sphere_radius) - - enddo ! iCell - - ! set solveStrain away from rotated pole - do iCell = 1, nCells - - solveStress(iCell) = interiorCell(iCell) - - if (abs(latCellRotated0(iCell)) > 0.8_RKIND * pii * 0.5_RKIND) then - solveStress(iCell) = 0 - endif - - enddo ! iCell - - ! set test velocities and expected outputs - call spherical_test_strain(& - mesh, & - uVelocity, & - vVelocity, & - lonVertexRotated0, & - latVertexRotated0, & - strain11_test, & - strain22_test, & - strain12_test, & - lonCellRotated0, & - latCellRotated0, & - nVertices, & - nCells, & - trim(unit_test_subtype)) - - write(54,*) - write(54,*) "Analytic solution:" - write(54,*) "strain11 (min/max):", minval(strain11_test,mask=(solveStress(1:nCells)==1)), & - maxval(strain11_test,mask=(solveStress(1:nCells)==1)) - write(54,*) "strain22 (min/max):", minval(strain22_test,mask=(solveStress(1:nCells)==1)), & - maxval(strain22_test,mask=(solveStress(1:nCells)==1)) - write(54,*) "strain12 (min/max):", minval(strain12_test,mask=(solveStress(1:nCells)==1)), & - maxval(strain12_test,mask=(solveStress(1:nCells)==1)) - - ! calculate strain - if (trim(config_stress_divergence_scheme) == "weak") then - - call seaice_strain_tensor_weak(& - mesh, & - strain11_weak, & - strain22_weak, & - strain12_weak, & - uVelocity, & - vVelocity, & - normalVectorPolygon, & - latCellRotated, & - solveStress) - - strain11(1:nCells) = strain11_weak(1:nCells) - strain22(1:nCells) = strain22_weak(1:nCells) - strain12(1:nCells) = strain12_weak(1:nCells) - - else if (trim(config_stress_divergence_scheme) == "variational") then - - call seaice_strain_tensor_variational(& - mesh, & - strain11_variational, & - strain22_variational, & - strain12_variational, & - uVelocity, & - vVelocity, & - basisGradientU, & - basisGradientV, & - tanLatVertexRotatedOverRadius, & - solveStress) - - call average_variational_stress(& - mesh, & - strain11_variational, & - strain22_variational, & - strain12_variational, & - strain11, & - strain22, & - strain12) - - endif - - write(54,*) - write(54,*) "Numerical solution:" - write(54,*) "strain11 (min/max):", minval(strain11(1:nCells),mask=(solveStress(1:nCells)==1)), & - maxval(strain11(1:nCells),mask=(solveStress(1:nCells)==1)) - write(54,*) "strain22 (min/max):", minval(strain22(1:nCells),mask=(solveStress(1:nCells)==1)), & - maxval(strain22(1:nCells),mask=(solveStress(1:nCells)==1)) - write(54,*) "strain12 (min/max):", minval(strain12(1:nCells),mask=(solveStress(1:nCells)==1)), & - maxval(strain12(1:nCells),mask=(solveStress(1:nCells)==1)) - - ! calculate difference - strain11_diff = strain11(1:nCells) - strain11_test - strain22_diff = strain22(1:nCells) - strain22_test - strain12_diff = strain12(1:nCells) - strain12_test - - write(54,*) - write(54,*) "Difference:" - write(54,*) "strain11 (min/max):", minval(strain11_diff,mask=(solveStress(1:nCells)==1)), & - maxval(strain11_diff,mask=(solveStress(1:nCells)==1)) - write(54,*) "strain22 (min/max):", minval(strain22_diff,mask=(solveStress(1:nCells)==1)), & - maxval(strain22_diff,mask=(solveStress(1:nCells)==1)) - write(54,*) "strain12 (min/max):", minval(strain12_diff,mask=(solveStress(1:nCells)==1)), & - maxval(strain12_diff,mask=(solveStress(1:nCells)==1)) - - open(55,file="strains_"//trim(unit_test_subtype)//".txt") - do iCell = 1, nCells - - write(55,*) iCell, solveStress(iCell), & - strain11(iCell), strain11_test(iCell), & - strain22(iCell), strain22_test(iCell), & - strain12(iCell), strain12_test(iCell), & - strain12(iCell) - strain12_test(iCell) - - enddo ! iCell - close(55) - - open(55,file="strain11_"//trim(unit_test_subtype)//".txt") - open(56,file="strain22_"//trim(unit_test_subtype)//".txt") - open(57,file="strain12_"//trim(unit_test_subtype)//".txt") - do iCell = 1, nCells - write(55,*) iCell, lonCell(iCell) * seaiceRadiansToDegrees, latCell(iCell) * seaiceRadiansToDegrees, & - strain11(iCell), strain11_test(iCell), strain11_diff(iCell), & - strain11_diff(iCell) * real(solveStress(iCell),RKIND) - write(56,*) iCell, lonCell(iCell) * seaiceRadiansToDegrees, latCell(iCell) * seaiceRadiansToDegrees, & - strain22(iCell), strain22_test(iCell), strain22_diff(iCell), & - strain22_diff(iCell) * real(solveStress(iCell),RKIND) - write(57,*) iCell, lonCell(iCell) * seaiceRadiansToDegrees, latCell(iCell) * seaiceRadiansToDegrees, & - strain12(iCell), strain12_test(iCell), strain12_diff(iCell), & - strain12_diff(iCell) * real(solveStress(iCell),RKIND) - enddo ! iCell - close(55) - close(56) - close(57) - - ! rms difference - call rms_difference( & - strain11(1:nCells), & - strain11_test, & - solveStress(1:nCells), & - nCells, & - rms_strain11) - - call rms_difference( & - strain22(1:nCells), & - strain22_test, & - solveStress(1:nCells), & - nCells, & - rms_strain22) - - call rms_difference( & - strain12(1:nCells), & - strain12_test, & - solveStress(1:nCells), & - nCells, & - rms_strain12) - - write(54,*) - write(54,*) "RMS:" - write(54,*) "strain11:", rms_strain11 - write(54,*) "strain22:", rms_strain22 - write(54,*) "strain12:", rms_strain12 - - do iCell = 1, nCells - - if (solveStress(iCell) == 0) then - strain11_test(iCell) = 0.0_RKIND - strain22_test(iCell) = 0.0_RKIND - strain12_test(iCell) = 0.0_RKIND - strain11_diff(iCell) = 0.0_RKIND - strain22_diff(iCell) = 0.0_RKIND - strain12_diff(iCell) = 0.0_RKIND - endif - - enddo ! iCell - - ! plot spatial distribution - call plot_spherical_latlon(mesh, real(interiorCell,RKIND), "interiorCell_"//trim(unit_test_subtype)//".txt") - call plot_spherical_latlon(mesh, real(solveStress,RKIND), "solveStress_"//trim(unit_test_subtype)//".txt") - call plot_spherical_latlon(mesh, latCellRotated0, "latCellRotated0_"//trim(unit_test_subtype)//".txt") - - call plot_spherical_latlon(mesh, strain11, "strain11_plot_"//trim(unit_test_subtype)//"_model.txt") - call plot_spherical_latlon(mesh, strain11_test, "strain11_plot_"//trim(unit_test_subtype)//"_analytical.txt") - call plot_spherical_latlon(mesh, strain11_diff, "strain11_plot_"//trim(unit_test_subtype)//"_diff.txt") - - call plot_spherical_latlon(mesh, strain22, "strain22_plot_"//trim(unit_test_subtype)//"_model.txt") - call plot_spherical_latlon(mesh, strain22_test, "strain22_plot_"//trim(unit_test_subtype)//"_analytical.txt") - call plot_spherical_latlon(mesh, strain22_diff, "strain22_plot_"//trim(unit_test_subtype)//"_diff.txt") - - call plot_spherical_latlon(mesh, strain12, "strain12_plot_"//trim(unit_test_subtype)//"_model.txt") - call plot_spherical_latlon(mesh, strain12_test, "strain12_plot_"//trim(unit_test_subtype)//"_analytical.txt") - call plot_spherical_latlon(mesh, strain12_diff, "strain12_plot_"//trim(unit_test_subtype)//"_diff.txt") - - deallocate(strain11) - deallocate(strain22) - deallocate(strain12) - deallocate(strain11_test) - deallocate(strain22_test) - deallocate(strain12_test) - deallocate(strain11_diff) - deallocate(strain22_diff) - deallocate(strain12_diff) - - deallocate(lonVertexRotated0) - deallocate(latVertexRotated0) - deallocate(lonCellRotated0) - deallocate(latCellRotated0) - - write(54,*) - - close(54) - - block => block % next - enddo - - end subroutine seaice_strain_rate_operator_unit_test_individual!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! average_variational_stress -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date December 12th 2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine average_variational_stress(& - mesh, & - strain11_variational, & - strain22_variational, & - strain12_variational, & - strain11, & - strain22, & - strain12) - - type(mpas_pool_type), intent(in) :: & - mesh - - real(kind=RKIND), dimension(:,:), intent(in) :: & - strain11_variational, & - strain22_variational, & - strain12_variational - - real(kind=RKIND), dimension(:), intent(out) :: & - strain11, & - strain22, & - strain12 - - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer :: & - iCell, & - iVertexOnCell - - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - - do iCell = 1, nCells - - strain11(iCell) = 0.0_RKIND - strain22(iCell) = 0.0_RKIND - strain12(iCell) = 0.0_RKIND - - do iVertexOnCell = 1, nEdgesOnCell(iCell) - - strain11(iCell) = strain11(iCell) + strain11_variational(iVertexOnCell,iCell) - strain22(iCell) = strain22(iCell) + strain22_variational(iVertexOnCell,iCell) - strain12(iCell) = strain12(iCell) + strain12_variational(iVertexOnCell,iCell) - - enddo ! iVertexOnCell - - strain11(iCell) = strain11(iCell) / real(nEdgesOnCell(iCell), RKIND) - strain22(iCell) = strain22(iCell) / real(nEdgesOnCell(iCell), RKIND) - strain12(iCell) = strain12(iCell) / real(nEdgesOnCell(iCell), RKIND) - - enddo ! iCell - - end subroutine average_variational_stress - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! spherical_test_strain -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine spherical_test_strain(mesh, & - uVelocity, & - vVelocity, & - longitudeVelocity, & - latitudeVelocity, & - strain11, & - strain22, & - strain12, & - longitudeStrain, & - latitudeStrain, & - nPointsVelocity, & - nPointsStrain, & - test_type)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - real(kind=RKIND), dimension(:), intent(out) :: & - uVelocity, & !< Output: - vVelocity, & !< Output: - strain11, & !< Output: - strain22, & !< Output: - strain12 !< Output: - - real(kind=RKIND), dimension(:), intent(in) :: & - longitudeVelocity, & !< Input: - latitudeVelocity, & !< Input: - longitudeStrain, & !< Input: - latitudeStrain !< Input: - - integer, intent(in) :: & - nPointsVelocity, & !< Input: - nPointsStrain !< Input: - - character(len=*), intent(in) :: & - test_type !< Input: - - real(kind=RKIND) :: & - uVelocity_test, & - vVelocity_test, & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat - - integer :: & - iPoint - - real(kind=RKIND), pointer :: & - sphere_radius - - call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) - - ! set velocity points - do iPoint = 1, nPointsVelocity - - call spherical_test_strain_velocities( & - uVelocity(iPoint), & - vVelocity(iPoint), & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat, & - longitudeVelocity(iPoint), & - latitudeVelocity(iPoint), & - test_type) - - enddo ! iPoint - - ! set strain points - do iPoint = 1, nPointsStrain - - call spherical_test_strain_velocities( & - uVelocity_test, & - vVelocity_test, & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat, & - longitudeStrain(iPoint), & - latitudeStrain(iPoint), & - test_type) - - strain11(iPoint) = strain11_component(uVelocity_test, vVelocity_test, & - du_dlon, du_dlat, dv_dlon, dv_dlat, & - sphere_radius, longitudeStrain(iPoint), latitudeStrain(iPoint)) - strain22(iPoint) = strain22_component(uVelocity_test, vVelocity_test, & - du_dlon, du_dlat, dv_dlon, dv_dlat, & - sphere_radius, longitudeStrain(iPoint), latitudeStrain(iPoint)) - strain12(iPoint) = strain12_component(uVelocity_test, vVelocity_test, & - du_dlon, du_dlat, dv_dlon, dv_dlat, & - sphere_radius, longitudeStrain(iPoint), latitudeStrain(iPoint)) - - enddo ! iPoint - - end subroutine spherical_test_strain!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! spherical_test_strain_velocities -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine spherical_test_strain_velocities( & - u, & - v, & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat, & - lon, & - lat, & - test_type)!{{{ - - real(kind=RKIND), intent(out) :: & - u, & !< Output: - v, & !< Output: - du_dlon, & !< Output: - du_dlat, & !< Output: - dv_dlon, & !< Output: - dv_dlat !< Output: - - real(kind=RKIND), intent(in) :: & - lon, & !< Input: - lat !< Input: - - character(len=*), intent(in) :: & - test_type !< Input: - - if (trim(test_type) == "zero") then - - u = 0.0_RKIND - v = 0.0_RKIND - - du_dlon = 0.0_RKIND - du_dlat = 0.0_RKIND - dv_dlon = 0.0_RKIND - dv_dlat = 0.0_RKIND - - else if (trim(test_type) == "zonal") then - - u = 1.0_RKIND - v = 0.0_RKIND - - du_dlon = 0.0_RKIND - du_dlat = 0.0_RKIND - dv_dlon = 0.0_RKIND - dv_dlat = 0.0_RKIND - - else if (trim(test_type) == "meridonal") then - - u = 0.0_RKIND - v = 1.0_RKIND - - du_dlon = 0.0_RKIND - du_dlat = 0.0_RKIND - dv_dlon = 0.0_RKIND - dv_dlat = 0.0_RKIND - - else if (trim(test_type) == "solid_body") then - - u = cos(lat) - v = 0.0_RKIND - - du_dlon = 0.0_RKIND - du_dlat = -sin(lat) - dv_dlon = 0.0_RKIND - dv_dlat = 0.0_RKIND - - else if (trim(test_type) == "sinusoidal1") then - - u = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - v = 0.0_RKIND - - du_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - du_dlat = -2.0_RKIND * cos(lon) * sin(2.0_RKIND * lat) - dv_dlon = 0.0_RKIND - dv_dlat = 0.0_RKIND - - else if (trim(test_type) == "sinusoidal2") then - - u = 0.0_RKIND - v = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - - du_dlon = 0.0_RKIND - du_dlat = 0.0_RKIND - dv_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dv_dlat = -2.0_RKIND * cos(lon) * sin(2.0_RKIND * lat) - - else - - call mpas_log_write("spherical_test_strain_velocities: Unknown test case: "//trim(test_type), MPAS_LOG_CRIT) - - endif - - end subroutine spherical_test_strain_velocities!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! strain11_component -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - function strain11_component(& - u, & - v, & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat, & - r, & - lon, & - lat) & - result(e11)!{{{ - - real(kind=RKIND), intent(in) :: & - u, & !< Input: - v, & !< Input: - du_dlon, & !< Input: - du_dlat, & !< Input: - dv_dlon, & !< Input: - dv_dlat, & !< Input: - r, & !< Input: - lon, & !< Input: - lat !< Input: - - real(kind=RKIND) :: e11 - - e11 = (1.0_RKIND / (r * cos(lat))) * (du_dlon - v * sin(lat)) - - end function strain11_component!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! strain22_component -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - function strain22_component(& - u, & - v, & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat, & - r, & - lon, & - lat) & - result(e22)!{{{ - - real(kind=RKIND), intent(in) :: & - u, & !< Input: - v, & !< Input: - du_dlon, & !< Input: - du_dlat, & !< Input: - dv_dlon, & !< Input: - dv_dlat, & !< Input: - r, & !< Input: - lon, & !< Input: - lat !< Input: - - real(kind=RKIND) :: e22 - - e22 = dv_dlat / r - - end function strain22_component!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! strain12_component -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - function strain12_component(& - u, & - v, & - du_dlon, & - du_dlat, & - dv_dlon, & - dv_dlat, & - r, & - lon, & - lat) & - result(e12)!{{{ - - real(kind=RKIND), intent(in) :: & - u, & !< Input: - v, & !< Input: - du_dlon, & !< Input: - du_dlat, & !< Input: - dv_dlon, & !< Input: - dv_dlat, & !< Input: - r, & !< Input: - lon, & !< Input: - lat !< Input: - - real(kind=RKIND) :: e12 - - e12 = (0.5_RKIND / r) * (du_dlat + u * tan(lat) + dv_dlon / cos(lat)) - - end function strain12_component!}}} - -!----------------------------------------------------------------------- -! Spherical stress divergence unit test -!----------------------------------------------------------------------- - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_stress_divergence_operator_unit_test -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_stress_divergence_operator_unit_test(& - domain, & - unit_test_subtype)!{{{ - - type(domain_type) :: & - domain !< Input/Output: - - character(len=*), intent(in) :: & - unit_test_subtype !< Input: - - integer, parameter :: ntests = 8 - character(len=200), dimension(ntests), parameter :: & - test_names = (/"zero ", & - "const11", & - "const22", & - "const12", & - "test1 ", & - "test2 ", & - "test3 ", & - "test4 "/) - - integer :: & - itest - - if (trim(unit_test_subtype) == "all") then - - do itest = 1, ntests - - call seaice_stress_divergence_operator_unit_test_individual(& - domain, & - trim(test_names(itest))) - - enddo ! itest - - else - - call seaice_stress_divergence_operator_unit_test_individual(& - domain, & - unit_test_subtype) - - endif - - end subroutine seaice_stress_divergence_operator_unit_test - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_stress_divergence_operator_unit_test_individual -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_stress_divergence_operator_unit_test_individual(& - domain, & - unit_test_subtype)!{{{ - - use seaice_velocity_solver_weak, only: & - seaice_stress_divergence_weak - - use seaice_velocity_solver_variational, only: & - seaice_stress_divergence_variational - - use seaice_mesh, only: & - seaice_grid_rotation_forward, & - seaice_latlon_from_xyz - - use seaice_constants, only: & - seaiceRadiansToDegrees, & - pii - - type(domain_type), intent(inout) :: & - domain !< Input/Output: - - character(len=*), intent(in) :: & - unit_test_subtype !< Input: - - type(block_type), pointer :: & - block - - type (MPAS_pool_type), pointer :: & - mesh, & - boundary, & - velocity_solver, & - velocity_weak, & - velocity_variational - - real(kind=RKIND), dimension(:), allocatable :: & - stressDivergenceU_test, & - stressDivergenceV_test, & - stressDivergenceU_diff, & - stressDivergenceV_diff, & - latVertexRotated0, & - lonVertexRotated0, & - latCellRotated0, & - lonCellRotated0 - - real(kind=RKIND) :: & - xp, yp, zp, & - rms_divergenceU, & - rms_divergenceV - - integer :: & - iCell, & - iVertex - - integer, pointer :: & - nCells, & - nVertices - - integer, dimension(:), pointer :: & - solveVelocity, & - interiorVertex - - real(kind=RKIND), pointer :: & - sphere_radius - - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - zVertex, & - xCell, & - yCell, & - zCell, & - latVertex, & - lonVertex, & - latVertexRotated, & - stress11_weak, & - stress22_weak, & - stress12_weak, & - stressDivergenceU, & - stressDivergenceV - - integer, dimension(:,:), pointer :: & - cellVerticesAtVertex - - real(kind=RKIND), dimension(:), pointer :: & - tanLatVertexRotatedOverRadius - - real(kind=RKIND), dimension(:,:), pointer :: & - stress11_variational, & - stress22_variational, & - stress12_variational - - real(kind=RKIND), dimension(:,:,:), pointer :: & - basisGradientU, & - basisGradientV, & - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric - - real(kind=RKIND), dimension(:,:,:), pointer :: & - normalVectorTriangle - - logical, pointer :: & - config_rotate_cartesian_grid - - character(len=strKIND), pointer :: & - config_stress_divergence_scheme - - call MPAS_pool_get_config(domain % configs, "config_rotate_cartesian_grid", config_rotate_cartesian_grid) - call MPAS_pool_get_config(domain % configs, "config_stress_divergence_scheme", config_stress_divergence_scheme) - - block => domain % blocklist - do while (associated(block)) - - call MPAS_pool_get_subpool(block % structs, "mesh", mesh) - call MPAS_pool_get_subpool(block % structs, "boundary", boundary) - call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocity_solver) - call MPAS_pool_get_subpool(block % structs, "velocity_weak", velocity_weak) - call MPAS_pool_get_subpool(block % structs, "velocity_variational", velocity_variational) - - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) - - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "zVertex", zVertex) - call MPAS_pool_get_array(mesh, "xCell", xCell) - call MPAS_pool_get_array(mesh, "yCell", yCell) - call MPAS_pool_get_array(mesh, "zCell", zCell) - call MPAS_pool_get_array(mesh, "latVertex", latVertex) - call MPAS_pool_get_array(mesh, "lonVertex", lonVertex) - - call MPAS_pool_get_array(boundary, "interiorVertex", interiorVertex) - - call MPAS_pool_get_array(velocity_solver, "solveVelocity", solveVelocity) - call MPAS_pool_get_array(velocity_solver, "stressDivergenceU", stressDivergenceU) - call MPAS_pool_get_array(velocity_solver, "stressDivergenceV", stressDivergenceV) - - if (trim(config_stress_divergence_scheme) == "weak") then - call MPAS_pool_get_array(velocity_weak, "normalVectorTriangle", normalVectorTriangle) - call MPAS_pool_get_array(velocity_weak, "latVertexRotated", latVertexRotated) - call MPAS_pool_get_array(velocity_weak, "stress11", stress11_weak) - call MPAS_pool_get_array(velocity_weak, "stress22", stress22_weak) - call MPAS_pool_get_array(velocity_weak, "stress12", stress12_weak) - else if (trim(config_stress_divergence_scheme) == "variational") then - call MPAS_pool_get_array(velocity_variational, "stress11", stress11_variational) - call MPAS_pool_get_array(velocity_variational, "stress22", stress22_variational) - call MPAS_pool_get_array(velocity_variational, "stress12", stress12_variational) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsU", basisIntegralsU) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsV", basisIntegralsV) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsMetric", basisIntegralsMetric) - call MPAS_pool_get_array(velocity_variational, "cellVerticesAtVertex", cellVerticesAtVertex) - call MPAS_pool_get_array(velocity_variational, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) - endif - - open(54,file="stress_divergence_test_"//trim(unit_test_subtype)//".txt") - - write(54,*) "-----------------------------------------------------------" - write(54,*) - write(54,*) "Test type: ", trim(unit_test_subtype) - write(54,*) - - allocate(stressDivergenceU_test(nVertices)) - allocate(stressDivergenceV_test(nVertices)) - allocate(stressDivergenceU_diff(nVertices)) - allocate(stressDivergenceV_diff(nVertices)) - - allocate(lonVertexRotated0(nVertices)) - allocate(latVertexRotated0(nVertices)) - allocate(lonCellRotated0(nCells)) - allocate(latCellRotated0(nCells)) - - do iVertex = 1, nVertices - - call seaice_grid_rotation_forward(& - xp, yp, zp, & - xVertex(iVertex), yVertex(iVertex), zVertex(iVertex), & - config_rotate_cartesian_grid) - - call seaice_latlon_from_xyz(& - latVertexRotated0(iVertex), lonVertexRotated0(iVertex), & - xp, yp, zp, sphere_radius) - - enddo ! iVertex - - do iCell = 1, nCells - - call seaice_grid_rotation_forward(& - xp, yp, zp, & - xCell(iCell), yCell(iCell), zCell(iCell), & - config_rotate_cartesian_grid) - - call seaice_latlon_from_xyz(& - latCellRotated0(iCell), lonCellRotated0(iCell), & - xp, yp, zp, sphere_radius) - - enddo ! iCell - - ! set solveStrain away from rotated pole - do iVertex = 1, nVertices - - solveVelocity(iVertex) = interiorVertex(iVertex) - - if (abs(latVertexRotated0(iVertex)) > 0.8_RKIND * pii * 0.5_RKIND) then - solveVelocity(iVertex) = 0 - endif - - enddo ! iCell - - ! set test velocities and expected outputs - if (trim(config_stress_divergence_scheme) == "weak") then - call spherical_test_divergence_stress_weak(mesh, & - stress11_weak, & - stress22_weak, & - stress12_weak, & - lonCellRotated0, & - latCellRotated0, & - stressDivergenceU_test, & - stressDivergenceV_test, & - lonVertexRotated0, & - latVertexRotated0, & - trim(unit_test_subtype)) - else if (trim(config_stress_divergence_scheme) == "variational") then - call spherical_test_divergence_stress_variational(mesh, & - stress11_variational, & - stress22_variational, & - stress12_variational, & - lonVertexRotated0, & - latVertexRotated0, & - stressDivergenceU_test, & - stressDivergenceV_test, & - lonVertexRotated0, & - latVertexRotated0, & - trim(unit_test_subtype)) - endif - - write(54,*) - write(54,*) "Analytic solution:" - write(54,*) "stressDivergenceU:", minval(stressDivergenceU_test,mask=(solveVelocity(1:nVertices)==1)), & - maxval(stressDivergenceU_test,mask=(solveVelocity(1:nVertices)==1)) - write(54,*) "stressDivergenceV:", minval(stressDivergenceV_test,mask=(solveVelocity(1:nVertices)==1)), & - maxval(stressDivergenceV_test,mask=(solveVelocity(1:nVertices)==1)) - - ! calculate divergence of stress - if (trim(config_stress_divergence_scheme) == "weak") then - - call seaice_stress_divergence_weak(& - mesh, & - stressDivergenceU, & - stressDivergenceV, & - stress11_weak, & - stress22_weak, & - stress12_weak, & - normalVectorTriangle, & - latVertexRotated, & - solveVelocity) - - else if (trim(config_stress_divergence_scheme) == "variational") then - - call seaice_stress_divergence_variational(& - mesh, & - stressDivergenceU, & - stressDivergenceV, & - stress11_variational, & - stress22_variational, & - stress12_variational, & - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric, & - tanLatVertexRotatedOverRadius, & - cellVerticesAtVertex, & - solveVelocity) - - endif - - write(54,*) - write(54,*) "Numerical solution:" - write(54,*) "stressDivergenceU:", minval(stressDivergenceU(1:nVertices),mask=(solveVelocity(1:nVertices)==1)), & - maxval(stressDivergenceU(1:nVertices),mask=(solveVelocity(1:nVertices)==1)) - write(54,*) "stressDivergenceV:", minval(stressDivergenceV(1:nVertices),mask=(solveVelocity(1:nVertices)==1)), & - maxval(stressDivergenceV(1:nVertices),mask=(solveVelocity(1:nVertices)==1)) - - ! calculate difference - stressDivergenceU_diff = stressDivergenceU(1:nVertices) - & - stressDivergenceU_test - stressDivergenceV_diff = stressDivergenceV(1:nVertices) - & - stressDivergenceV_test - - write(54,*) - write(54,*) "Difference:" - write(54,*) "stressDivergenceU:", minval(stressDivergenceU_diff,mask=(solveVelocity(1:nVertices)==1)), & - maxval(stressDivergenceU_diff,mask=(solveVelocity(1:nVertices)==1)) - write(54,*) "stressDivergenceV:", minval(stressDivergenceV_diff,mask=(solveVelocity(1:nVertices)==1)), & - maxval(stressDivergenceV_diff,mask=(solveVelocity(1:nVertices)==1)) - - open(55, file="divergences_"//trim(unit_test_subtype)//".txt") - do iVertex = 1, nVertices - - write(55,*) iVertex, solveVelocity(iVertex), & - stressDivergenceU(iVertex), stressDivergenceU_test(iVertex), & - stressDivergenceV(iVertex), stressDivergenceV_test(iVertex), & - stressDivergenceU(iVertex) - stressDivergenceU_test(iVertex), & - stressDivergenceV(iVertex) - stressDivergenceV_test(iVertex) - - enddo ! iVertex - close(55) - - open(55,file="divergenceu_"//trim(unit_test_subtype)//".txt") - open(56,file="divergencev_"//trim(unit_test_subtype)//".txt") - do iVertex = 1, nVertices - write(55,*) iVertex, lonVertex(iVertex) * seaiceRadiansToDegrees, latVertex(iVertex) * seaiceRadiansToDegrees, & - stressDivergenceU(iVertex), stressDivergenceU_test(iVertex), stressDivergenceU_diff(iVertex), & - stressDivergenceU_diff(iVertex) * real(solveVelocity(iVertex),RKIND) - write(56,*) iVertex, lonVertex(iVertex) * seaiceRadiansToDegrees, latVertex(iVertex) * seaiceRadiansToDegrees, & - stressDivergenceV(iVertex), stressDivergenceV_test(iVertex), stressDivergenceV_diff(iVertex), & - stressDivergenceV_diff(iVertex) * real(solveVelocity(iVertex),RKIND) - enddo ! iVertex - close(55) - close(56) - - ! rms difference - call rms_difference( & - stressDivergenceU(1:nVertices), & - stressDivergenceU_test, & - solveVelocity(1:nVertices), & - nVertices, & - rms_divergenceU) - - call rms_difference( & - stressDivergenceV(1:nVertices), & - stressDivergenceV_test, & - solveVelocity(1:nVertices), & - nVertices, & - rms_divergenceV) - - write(54,*) - write(54,*) "RMS:" - write(54,*) "rms_divergenceU:", rms_divergenceU - write(54,*) "rms_divergenceV:", rms_divergenceV - - do iVertex = 1, nVertices - - if (solveVelocity(iVertex) == 0) then - stressDivergenceU_test(iVertex) = 0.0_RKIND - stressDivergenceU_test(iVertex) = 0.0_RKIND - stressDivergenceU_diff(iVertex) = 0.0_RKIND - stressDivergenceU_diff(iVertex) = 0.0_RKIND - endif - - enddo ! iCell - - ! plot spatial distribution - call plot_spherical_latlon(mesh, real(interiorVertex,RKIND), "interiorVertex_"//trim(unit_test_subtype)//".txt") - call plot_spherical_latlon(mesh, real(solveVelocity,RKIND), "solveVelocity_"//trim(unit_test_subtype)//".txt") - call plot_spherical_latlon(mesh, latVertexRotated0, "latVertexRotated0_"//trim(unit_test_subtype)//".txt") - - call plot_spherical_latlon(& - mesh, stressDivergenceU, "stressDivergenceU_plot_"//trim(unit_test_subtype)//"_model.txt") - call plot_spherical_latlon(& - mesh, stressDivergenceU_test, "stressDivergenceU_plot_"//trim(unit_test_subtype)//"_analytical.txt") - call plot_spherical_latlon(& - mesh, stressDivergenceU_diff, "stressDivergenceU_plot_"//trim(unit_test_subtype)//"_diff.txt") - - call plot_spherical_latlon(& - mesh, stressDivergenceV, "stressDivergenceV_plot_"//trim(unit_test_subtype)//"_model.txt") - call plot_spherical_latlon(& - mesh, stressDivergenceV_test, "stressDivergenceV_plot_"//trim(unit_test_subtype)//"_analytical.txt") - call plot_spherical_latlon(& - mesh, stressDivergenceV_diff, "stressDivergenceV_plot_"//trim(unit_test_subtype)//"_diff.txt") - - ! cleanup - deallocate(stressDivergenceU_test) - deallocate(stressDivergenceV_test) - deallocate(stressDivergenceU_diff) - deallocate(stressDivergenceV_diff) - - deallocate(lonVertexRotated0) - deallocate(latVertexRotated0) - deallocate(lonCellRotated0) - deallocate(latCellRotated0) - - close(54) - - block => block % next - enddo - - end subroutine seaice_stress_divergence_operator_unit_test_individual!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! spherical_test_divergence_stress_weak -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine spherical_test_divergence_stress_weak(mesh, & - stress11, & - stress22, & - stress12, & - longitudeStress, & - latitudeStress, & - stressDivergenceU, & - stressDivergenceV, & - longitudeDivergence, & - latitudeDivergence, & - test_type)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - real(kind=RKIND), dimension(:), intent(out) :: & - stressDivergenceU, & !< Output: - stressDivergenceV, & !< Output: - stress11, & !< Output: - stress22, & !< Output: - stress12 !< Output: - - real(kind=RKIND), dimension(:), intent(in) :: & - longitudeStress, & !< Input: - latitudeStress, & !< Input: - longitudeDivergence, & !< Input: - latitudeDivergence !< Input: - - character(len=*), intent(in) :: & - test_type !< Input: - - real(kind=RKIND) :: & - stress11_test, & - stress22_test, & - stress12_test, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat - - integer :: & - nPointsStress, & - nPointsDivergence, & - iPoint - - real(kind=RKIND), pointer :: & - sphere_radius - - call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) - - nPointsStress = size(stress11)-1 - nPointsDivergence = size(stressDivergenceU)-1 - - ! set stress points - do iPoint = 1, nPointsStress - - call spherical_test_divergence_stress_stresses( & - stress11(iPoint), & - stress22(iPoint), & - stress12(iPoint), & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - longitudeStress(iPoint), & - latitudeStress(iPoint), & - test_type) - - enddo ! iPoint - - ! set divergence poinys - do iPoint = 1, nPointsDivergence - - call spherical_test_divergence_stress_stresses( & - stress11_test, & - stress22_test, & - stress12_test, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - longitudeDivergence(iPoint), & - latitudeDivergence(iPoint), & - test_type) - - stressDivergenceU(iPoint) = & - divergenceStressU(stress11_test, stress22_test, stress12_test, & - dstress11_dlon, dstress11_dlat, dstress22_dlon, dstress22_dlat, dstress12_dlon, dstress12_dlat, & - sphere_radius, longitudeDivergence(iPoint), latitudeDivergence(iPoint)) - stressDivergenceV(iPoint) = & - divergenceStressV(stress11_test, stress22_test, stress12_test, & - dstress11_dlon, dstress11_dlat, dstress22_dlon, dstress22_dlat, dstress12_dlon, dstress12_dlat, & - sphere_radius, longitudeDivergence(iPoint), latitudeDivergence(iPoint)) - - enddo ! iPoint - - end subroutine spherical_test_divergence_stress_weak!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! spherical_test_divergence_stress_variational -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine spherical_test_divergence_stress_variational(mesh, & - stress11, & - stress22, & - stress12, & - longitudeStress, & - latitudeStress, & - stressDivergenceU, & - stressDivergenceV, & - longitudeDivergence, & - latitudeDivergence, & - test_type)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - real(kind=RKIND), dimension(:), intent(out) :: & - stressDivergenceU, & !< Output: - stressDivergenceV !< Output: - - real(kind=RKIND), dimension(:,:), intent(out) :: & - stress11, & !< Output: - stress22, & !< Output: - stress12 !< Output: - - real(kind=RKIND), dimension(:), intent(in) :: & - longitudeStress, & !< Input: - latitudeStress, & !< Input: - longitudeDivergence, & !< Input: - latitudeDivergence !< Input: - - character(len=*), intent(in) :: & - test_type !< Input: - - real(kind=RKIND) :: & - stress11_test, & - stress22_test, & - stress12_test, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat - - integer, pointer :: & - nCells, & - nVertices - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - verticesOnCell - - integer :: & - iVertex, & - iCell, & - iVertexOnCell - - real(kind=RKIND), pointer :: & - sphere_radius - - call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) - - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - - ! set stress points - do iCell = 1, nCells - - do iVertexOnCell = 1, nEdgesOnCell(iCell) - - iVertex = verticesOnCell(iVertexOnCell,iCell) - - call spherical_test_divergence_stress_stresses( & - stress11(iVertexOnCell,iCell), & - stress22(iVertexOnCell,iCell), & - stress12(iVertexOnCell,iCell), & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - longitudeStress(iVertex), & - latitudeStress(iVertex), & - test_type) - - enddo ! iVertexOnCell - - enddo ! iCell - - ! set divergence points - do iVertex = 1, nVertices - - call spherical_test_divergence_stress_stresses( & - stress11_test, & - stress22_test, & - stress12_test, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - longitudeDivergence(iVertex), & - latitudeDivergence(iVertex), & - test_type) - - stressDivergenceU(iVertex) = & - divergenceStressU(stress11_test, stress22_test, stress12_test, & - dstress11_dlon, dstress11_dlat, dstress22_dlon, dstress22_dlat, dstress12_dlon, dstress12_dlat, & - sphere_radius, longitudeDivergence(iVertex), latitudeDivergence(iVertex)) - stressDivergenceV(iVertex) = & - divergenceStressV(stress11_test, stress22_test, stress12_test, & - dstress11_dlon, dstress11_dlat, dstress22_dlon, dstress22_dlat, dstress12_dlon, dstress12_dlat, & - sphere_radius, longitudeDivergence(iVertex), latitudeDivergence(iVertex)) - - enddo ! iVertex - - end subroutine spherical_test_divergence_stress_variational!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! spherical_test_divergence_stress_stresses -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine spherical_test_divergence_stress_stresses( & - stress11, & - stress22, & - stress12, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - lon, & - lat, & - test_type)!{{{ - - real(kind=RKIND), intent(out) :: & - stress11, & - stress22, & - stress12, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat - - real(kind=RKIND), intent(in) :: & - lon, & !< Input: - lat !< Input: - - character(len=*), intent(in) :: & - test_type !< Input: - - if (trim(test_type) == "zero") then - - stress11 = 0.0_RKIND - stress22 = 0.0_RKIND - stress12 = 0.0_RKIND - - dstress11_dlon = 0.0_RKIND - dstress11_dlat = 0.0_RKIND - dstress22_dlon = 0.0_RKIND - dstress22_dlat = 0.0_RKIND - dstress12_dlon = 0.0_RKIND - dstress12_dlat = 0.0_RKIND - - else if (trim(test_type) == "const11") then - - stress11 = 1.0_RKIND - stress22 = 0.0_RKIND - stress12 = 0.0_RKIND - - dstress11_dlon = 0.0_RKIND - dstress11_dlat = 0.0_RKIND - dstress22_dlon = 0.0_RKIND - dstress22_dlat = 0.0_RKIND - dstress12_dlon = 0.0_RKIND - dstress12_dlat = 0.0_RKIND - - else if (trim(test_type) == "const22") then - - stress11 = 0.0_RKIND - stress22 = 1.0_RKIND - stress12 = 0.0_RKIND - - dstress11_dlon = 0.0_RKIND - dstress11_dlat = 0.0_RKIND - dstress22_dlon = 0.0_RKIND - dstress22_dlat = 0.0_RKIND - dstress12_dlon = 0.0_RKIND - dstress12_dlat = 0.0_RKIND - - else if (trim(test_type) == "const12") then - - stress11 = 0.0_RKIND - stress22 = 0.0_RKIND - stress12 = 1.0_RKIND - - dstress11_dlon = 0.0_RKIND - dstress11_dlat = 0.0_RKIND - dstress22_dlon = 0.0_RKIND - dstress22_dlat = 0.0_RKIND - dstress12_dlon = 0.0_RKIND - dstress12_dlat = 0.0_RKIND - - else if (trim(test_type) == "test1") then - - stress11 = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - stress22 = 0.0_RKIND - stress12 = 0.0_RKIND - - dstress11_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dstress11_dlat = -cos(lon) * 2.0_RKIND * sin(2.0_RKIND * lat) - dstress22_dlon = 0.0_RKIND - dstress22_dlat = 0.0_RKIND - dstress12_dlon = 0.0_RKIND - dstress12_dlat = 0.0_RKIND - - else if (trim(test_type) == "test2") then - - stress11 = 0.0_RKIND - stress22 = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - stress12 = 0.0_RKIND - - dstress11_dlon = 0.0_RKIND - dstress11_dlat = 0.0_RKIND - dstress22_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dstress22_dlat = -cos(lon) * 2.0_RKIND * sin(2.0_RKIND * lat) - dstress12_dlon = 0.0_RKIND - dstress12_dlat = 0.0_RKIND - - else if (trim(test_type) == "test3") then - - stress11 = 0.0_RKIND - stress22 = 0.0_RKIND - stress12 = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - - dstress11_dlon = 0.0_RKIND - dstress11_dlat = 0.0_RKIND - dstress22_dlon = 0.0_RKIND - dstress22_dlat = 0.0_RKIND - dstress12_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dstress12_dlat = -cos(lon) * 2.0_RKIND * sin(2.0_RKIND * lat) - - else if (trim(test_type) == "test4") then - - stress11 = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - stress22 = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - stress12 = cos(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - - dstress11_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dstress11_dlat = -cos(lon) * 2.0_RKIND * sin(2.0_RKIND * lat) - dstress22_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dstress22_dlat = -cos(lon) * 2.0_RKIND * sin(2.0_RKIND * lat) - dstress12_dlon = -sin(lon) * (1.0_RKIND + cos(2.0_RKIND * lat)) - dstress12_dlat = -cos(lon) * 2.0_RKIND * sin(2.0_RKIND * lat) - - else - - call mpas_log_write("spherical_test_divergence_stress_stresses: Unknown test case: "//trim(test_type), & - MPAS_LOG_CRIT) - - endif - - end subroutine spherical_test_divergence_stress_stresses!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! divergenceStressU -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - function divergenceStressU(& - stress11, & - stress22, & - stress12, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - r, & - lon, & - lat) & - result(divu)!{{{ - - real(kind=RKIND), intent(in) :: & - stress11, & !< Input: - stress22, & !< Input: - stress12, & !< Input: - dstress11_dlon, & !< Input: - dstress11_dlat, & !< Input: - dstress22_dlon, & !< Input: - dstress22_dlat, & !< Input: - dstress12_dlon, & !< Input: - dstress12_dlat, & !< Input: - r, & !< Input: - lon, & !< Input: - lat !< Input: - - real(kind=RKIND) :: divu - - divu = (1.0_RKIND / (r * cos(lat))) * dstress11_dlon + & - (1.0_RKIND / r) * dstress12_dlat - & - (2.0_RKIND / r) * tan(lat) * stress12 - - end function divergenceStressU!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! divergenceStressV -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - function divergenceStressV(& - stress11, & - stress22, & - stress12, & - dstress11_dlon, & - dstress11_dlat, & - dstress22_dlon, & - dstress22_dlat, & - dstress12_dlon, & - dstress12_dlat, & - r, & - lon, & - lat) & - result(divv)!{{{ - - real(kind=RKIND), intent(in) :: & - stress11, & !< Input: - stress22, & !< Input: - stress12, & !< Input: - dstress11_dlon, & !< Input: - dstress11_dlat, & !< Input: - dstress22_dlon, & !< Input: - dstress22_dlat, & !< Input: - dstress12_dlon, & !< Input: - dstress12_dlat, & !< Input: - r, & !< Input: - lon, & !< Input: - lat !< Input: - - real(kind=RKIND) :: divv - - divv = (1.0_RKIND / (r * cos(lat))) * dstress12_dlon + & - (1.0_RKIND / r) * dstress22_dlat + & - (1.0_RKIND / r) * tan(lat) * stress11 - & - (1.0_RKIND / r) * tan(lat) * stress22 - - end function divergenceStressV!}}} - -!----------------------------------------------------------------------- -! EVP Constitutive relationship unit test -!----------------------------------------------------------------------- - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_constitutive_relationship_unit_test -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_constitutive_relationship_unit_test(& - domain)!{{{ - - use seaice_velocity_solver_constitutive_relation, only: & - seaice_evp_constitutive_relation, & - seaice_evp_constitutive_relation_revised, & - seaice_init_evp - - type(domain_type), intent(inout) :: & - domain !< Input/Output: - - type(block_type), pointer :: & - block - - integer, parameter :: & - nTests = 2 - - real(kind=RKIND), dimension(nTests) :: & - icePressure, & - stress11, & - stress22, & - stress12, & - strain11, & - strain22, & - strain12 - - real(kind=RKIND) :: & - replacementPressure, & - areaCell - - logical, pointer :: & - config_revised_evp - - type(MPAS_pool_type), pointer :: & - velocitySolver - - real(kind=RKIND), pointer :: & - elasticTimeStep - - integer :: & - iTest - - call MPAS_pool_get_config(domain % configs, "config_revised_evp", config_revised_evp) - - call seaice_init_evp(domain) - - block => domain % blocklist - do while (associated(block)) - - call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocitySolver) - call MPAS_pool_get_array(velocitySolver, "elasticTimeStep", elasticTimeStep) - - call constitutive_relationship_set_strains(& - stress11, & - stress22, & - stress12, & - icePressure, & - strain11, & - strain22, & - strain12) - - areaCell = 1.0_RKIND - - do iTest = 1, nTests - - if (.not. config_revised_evp) then - - call seaice_evp_constitutive_relation(& - stress11(iTest), & - stress22(iTest), & - stress12(iTest), & - strain11(iTest), & - strain22(iTest), & - strain12(iTest), & - icePressure(iTest), & - replacementPressure, & - areaCell, & - elasticTimeStep) - - else - - call seaice_evp_constitutive_relation_revised(& - stress11(iTest), & - stress22(iTest), & - stress12(iTest), & - strain11(iTest), & - strain22(iTest), & - strain12(iTest), & - icePressure(iTest), & - replacementPressure, & - areaCell) - - endif - - enddo ! iTest - - call constitutive_relationship_writeout(& - icePressure, & - strain11, & - strain22, & - strain12, & - stress11, & - stress22, & - stress12) - - block => block % next - enddo - - end subroutine seaice_constitutive_relationship_unit_test!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! constitutive_relationship_set_strains -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine constitutive_relationship_set_strains(& - stress11, & - stress22, & - stress12, & - icePressure, & - strain11, & - strain22, & - strain12)!{{{ - - real(kind=RKIND), dimension(:), intent(out) :: & - stress11, & !< Output: - stress22, & !< Output: - stress12, & !< Output: - icePressure, & !< Output: - strain11, & !< Output: - strain22, & !< Output: - strain12 !< Output: - - integer :: & - iTest - - ! test 1 - iTest = 1 - stress11(iTest) = 0.0_RKIND - stress22(iTest) = 0.0_RKIND - stress12(iTest) = 0.0_RKIND - icePressure(iTest) = 0.0_RKIND - strain11(iTest) = 0.0_RKIND - strain22(iTest) = 0.0_RKIND - strain12(iTest) = 0.0_RKIND - - ! test 2 - iTest = 2 - stress11(iTest) = 0.0_RKIND - stress22(iTest) = 0.0_RKIND - stress12(iTest) = 0.0_RKIND - icePressure(iTest) = 1000000000.0_RKIND - strain11(iTest) = 1.0_RKIND - strain22(iTest) = 1.0_RKIND - strain12(iTest) = 1.0_RKIND - - end subroutine constitutive_relationship_set_strains!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! constitutive_relationship_writeout -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine constitutive_relationship_writeout(& - icePressure, & - strain11, & - strain22, & - strain12, & - stress11, & - stress22, & - stress12)!{{{ - - real(kind=RKIND), dimension(:), intent(in) :: & - icePressure, & !< Input: - strain11, & !< Input: - strain22, & !< Input: - strain12, & !< Input: - stress11, & !< Input: - stress22, & !< Input: - stress12 !< Input: - - integer :: & - iTest - - write(*,*) "Constitutive tests" - write(*,*) - - do iTest = 1, size(icePressure) - - write(*,*) "Constitutive test ", iTest - - write(*,*) "strength: ", icePressure(iTest) - write(*,*) "strain11: ", strain11(iTest) - write(*,*) "strain22: ", strain22(iTest) - write(*,*) "strain12: ", strain12(iTest) - write(*,*) "stress11: ", stress11(iTest) - write(*,*) "stress22: ", stress22(iTest) - write(*,*) "stress12: ", stress12(iTest) - - enddo ! iTest - - end subroutine constitutive_relationship_writeout!}}} - -!----------------------------------------------------------------------- -! Utils -!----------------------------------------------------------------------- - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! rms_difference -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine rms_difference(array1, array2, mask, n, rms)!{{{ - - real(kind=RKIND), dimension(:), intent(in) :: & - array1, & !< Input: - array2 !< Input: - - integer, dimension(:), intent(in) :: & - mask !< Input: - - integer, intent(in) :: & - n !< Input: - - real(kind=RKIND), intent(out) :: & - rms !< Output: - - integer :: & - i, & - num - - rms = 0.0_RKIND - num = 0 - - do i = 1, n - - if (mask(i) == 1) then - - rms = rms + (array1(i) - array2(i))**2 - num = num + 1 - - endif - - enddo ! i - - rms = sqrt(rms / real(num, RKIND)) - - end subroutine rms_difference!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! plot_spherical_latlon -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_spherical_latlon(mesh, array, filename, tag)!{{{ - - use seaice_constants, only: & - pii - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - real(kind=RKIND), dimension(:), intent(in) :: & - array !< Input: - - character(len=*), intent(in) :: & - filename !< Input: - - integer, optional, intent(in) :: & - tag !< Input: - - integer, pointer :: & - nCells, & - nVertices, & - vertexDegree - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - pointVertexOnPoint - - real(kind=RKIND), dimension(:), pointer :: & - lonPointVertex, & - latPointVertex, & - lonPoint, & - latPoint - - integer, dimension(:), allocatable :: & - nPointVerticesOnPoint, & - plotPoint - - integer :: & - iPoint, & - iPointVertexOnPoint, & - iPointVertex, & - nPoints, & - array_size - - real(kind=RKIND) :: & - plotLonPointVertex, & - plotLatPointVertex, & - plotLonPointVertex0, & - plotLatPointVertex0 - - character(len=2000) :: & - strout, & - stroutvertex - - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - - if (size(array) == nCells+1 .or. size(array) == nCells) then - - array_size = nCells - - nPoints = nCells - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - allocate(nPointVerticesOnPoint(nCells)) - nPointVerticesOnPoint(:) = nEdgesOnCell(1:nCells) - call MPAS_pool_get_array(mesh, "verticesOnCell", pointVertexOnPoint) - call MPAS_pool_get_array(mesh, "lonVertex", lonPointVertex) - call MPAS_pool_get_array(mesh, "latVertex", latPointVertex) - call MPAS_pool_get_array(mesh, "lonCell", lonPoint) - call MPAS_pool_get_array(mesh, "latCell", latPoint) - allocate(plotPoint(nCells)) - plotPoint = 1 - - else if (size(array) == nVertices+1 .or. size(array) == nVertices) then - - array_size = nVertices - - nPoints = nVertices - call MPAS_pool_get_dimension(mesh, "vertexDegree", vertexDegree) - allocate(nPointVerticesOnPoint(nVertices)) - nPointVerticesOnPoint(:) = vertexDegree - call MPAS_pool_get_array(mesh, "cellsOnVertex", pointVertexOnPoint) - call MPAS_pool_get_array(mesh, "lonCell", lonPointVertex) - call MPAS_pool_get_array(mesh, "latCell", latPointVertex) - call MPAS_pool_get_array(mesh, "lonVertex", lonPoint) - call MPAS_pool_get_array(mesh, "latVertex", latPoint) - allocate(plotPoint(nVertices)) - do iPoint = 1, nVertices - plotPoint(iPoint) = 1 - do iPointVertexOnPoint = 1, vertexDegree - if (pointVertexOnPoint(iPointVertexOnPoint,iPoint) > nCells) then - plotPoint(iPoint) = 0 - endif - enddo ! iPointVertexOnPoint - enddo ! iPoint - - else - write(*,*) "plot_spherical_latlon: size not supported: ", size(array) - endif - - call open_filename_tag(55, filename, tag) - - write(55,*) "set xrange [0:6.283]" - write(55,*) "set yrange [-1.571:1.571]" - write(55,*) "set cbrange [",minval(array(1:array_size)),":",maxval(array(1:array_size)),"]" - - write(55,*) "set pm3d" - write(55,*) "set size square" - write(55,*) "unset key" - write(55,*) "set palette defined (0 0.0 0.0 0.5, 1 0.0 0.0 1.0, 2 0.0 0.5 1.0, 3 0.0 1.0 1.0, "//& - "4 0.5 1.0 0.5, 5 1.0 1.0 0.0, 6 1.0 0.5 0.0, 7 1.0 0.0 0.0, 8 0.5 0.0 0.0 )" - - iObject = 1 - - do iPoint = 1, nPoints - - if (plotPoint(iPoint) == 1) then - - write(strout,fmt='(a,i5,a)') "set object ",iObject," polygon from " - - do iPointVertexOnPoint = 1, nPointVerticesOnPoint(iPoint) - - iPointVertex = pointVertexOnPoint(iPointVertexOnPoint,iPoint) - - plotLonPointVertex = lonPointVertex(iPointVertex) - plotLatPointVertex = latPointVertex(iPointVertex) - - if (lonPoint(iPoint) <= pii) then - - if (plotLonPointVertex >= 0.9_RKIND * 2.0_RKIND * pii) then - plotLonPointVertex = plotLonPointVertex - 2.0_RKIND * pii - endif - - endif - - if (lonPoint(iPoint) > pii) then - - if (plotLonPointVertex <= 0.1_RKIND * 2.0_RKIND * pii) then - plotLonPointVertex = plotLonPointVertex + 2.0_RKIND * pii - endif - - endif - - if (iPointVertexOnPoint == 1) then - - plotLonPointVertex0 = plotLonPointVertex - plotLatPointVertex0 = plotLatPointVertex - - endif - - write(stroutvertex,fmt='(f14.2,a,f14.2,a)') plotLonPointVertex, ",", plotLatPointVertex, " to " - strout = trim(strout)//trim(stroutvertex) - - enddo ! iPointVertexOnPoint - - write(stroutvertex,fmt='(f14.2,a,f14.2)') plotLonPointVertex0, ",", plotLatPointVertex0 - strout = trim(strout)//trim(stroutvertex) - write(55,*) trim(strout) - - write(strout,fmt='(a,i5,a,e14.6,a)') "set object ",iObject,' fc palette cb ', array(iPoint), ' fillstyle solid' - write(55,*) trim(strout) - - iObject = iObject + 1 - - endif ! plotPoint - - enddo ! iPoint - - deallocate(nPointVerticesOnPoint) - deallocate(plotPoint) - - write(55,*) "plot -10" - - close(55) - - end subroutine plot_spherical_latlon!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! open_filename_tag -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine open_filename_tag(unit, filename, tag)!{{{ - - integer, intent(in) :: & - unit !< Input: - - character(len=*), intent(in) :: & - filename !< Input: - - integer, optional, intent(in) :: & - tag !< Input: - - character(len=2000) :: & - strtag, & - filename_use - - if (present(tag)) then - write(strtag,fmt='(i5.5)') tag - filename_use = filename(1:len(trim(filename))-4)//"_"//trim(strtag)//filename(len(trim(filename))-3:) - open(unit,file=trim(filename_use),action='write') - else - open(unit,file=trim(filename),action='write') - endif - - end subroutine open_filename_tag!}}} - - !-------------------------------------------------------------------------- - -end module seaice_velocity_solver_unit_tests diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F index f090cb73b5..92eed849db 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F @@ -12,9 +12,12 @@ module seaice_velocity_solver_variational +#include "gpu_macros.inc" + use mpas_derived_types use mpas_pool_routines use mpas_timer + use mpas_log, only: mpas_log_write implicit none @@ -24,8 +27,9 @@ module seaice_velocity_solver_variational public :: & seaice_init_velocity_solver_variational, & seaice_strain_tensor_variational, & + seaice_average_strains_on_vertex, & + seaice_stress_tensor_variational, & seaice_stress_divergence_variational, & - seaice_internal_stress_variational, & seaice_final_divergence_shear_variational contains @@ -53,9 +57,72 @@ subroutine seaice_init_velocity_solver_variational(& rotateCartesianGrid, & includeMetricTerms, & variationalBasisType, & + variationalDenominatorType, & + integrationType, & + integrationOrder)!{{{ + + type(MPAS_pool_type), pointer, intent(in) :: & + mesh !< Input: + + type(MPAS_pool_type), pointer :: & + velocity_variational, & !< Input/Output: + boundary !< Input/Output: + + logical, intent(in) :: & + rotateCartesianGrid, & !< Input: + includeMetricTerms !< Input: + + character(len=*), intent(in) :: & + variationalBasisType, & !< Input: + variationalDenominatorType, & !< Input: + integrationType !< Input: + + integer, intent(in) :: & + integrationOrder !< Input: + + call init_velocity_solver_variational_primary_mesh(& + mesh, & + velocity_variational, & + boundary, & + rotateCartesianGrid, & + includeMetricTerms, & + variationalBasisType, & + variationalDenominatorType, & + integrationType, & + integrationOrder) + + end subroutine seaice_init_velocity_solver_variational + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! init_velocity_solver_variational_primary_mesh +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 24 October 2014 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine init_velocity_solver_variational_primary_mesh(& + mesh, & + velocity_variational, & + boundary, & + rotateCartesianGrid, & + includeMetricTerms, & + variationalBasisType, & + variationalDenominatorType, & integrationType, & integrationOrder)!{{{ + use seaice_mesh, only: & + seaice_cell_vertices_at_vertex + + use seaice_velocity_solver_variational_shared, only: & + seaice_calc_local_coords, & + seaice_calc_variational_metric_terms + use seaice_velocity_solver_wachspress, only: & seaice_init_velocity_solver_wachspress @@ -74,187 +141,424 @@ subroutine seaice_init_velocity_solver_variational(& includeMetricTerms !< Input: character(len=*), intent(in) :: & - variationalBasisType, & !< Input: - integrationType !< Input: + variationalBasisType, & !< Input: + variationalDenominatorType, & !< Input: + integrationType !< Input: integer, intent(in) :: & integrationOrder !< Input: + integer, dimension(:,:), pointer :: & + cellVerticesAtVertex + + real(kind=RKIND), dimension(:), pointer :: & + tanLatVertexRotatedOverRadius, & + variationalDenominator + + real(kind=RKIND), dimension(:,:,:), pointer :: & + basisGradientU, & + basisGradientV, & + basisIntegralsMetric, & + basisIntegralsU, & + basisIntegralsV + + integer, pointer :: & + nCells, & + nVertices, & + vertexDegree, & + maxEdges + + integer, dimension(:), pointer :: & + nEdgesOnCell, & + interiorVertex + + integer, dimension(:,:), pointer :: & + verticesOnCell, & + cellsOnVertex, & + edgesOnCell + + real(kind=RKIND), dimension(:), pointer :: & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell, & + areaCell, & + areaTriangle, & + dvEdge + + logical, pointer :: & + on_a_sphere + + real(kind=RKIND), pointer :: & + sphere_radius + + real(kind=RKIND), dimension(:,:), allocatable :: & + xLocal, & + yLocal + + call MPAS_pool_get_config(mesh, "on_a_sphere", on_a_sphere) + call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) + + call MPAS_pool_get_dimension(mesh, "nCells", nCells) + call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) + call MPAS_pool_get_dimension(mesh, "vertexDegree", vertexDegree) + call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) + + call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) + call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) + call MPAS_pool_get_array(mesh, "cellsOnVertex", cellsOnVertex) + call MPAS_pool_get_array(mesh, "edgesOnCell", edgesOnCell) + call MPAS_pool_get_array(mesh, "xVertex", xVertex) + call MPAS_pool_get_array(mesh, "yVertex", yVertex) + call MPAS_pool_get_array(mesh, "zVertex", zVertex) + call MPAS_pool_get_array(mesh, "xCell", xCell) + call MPAS_pool_get_array(mesh, "yCell", yCell) + call MPAS_pool_get_array(mesh, "zCell", zCell) + call MPAS_pool_get_array(mesh, "areaCell", areaCell) + call MPAS_pool_get_array(mesh, "areaTriangle", areaTriangle) + call MPAS_pool_get_array(mesh, "dvEdge", dvEdge) + + call MPAS_pool_get_array(boundary, "interiorVertex", interiorVertex) + + call MPAS_pool_get_array(velocity_variational, "cellVerticesAtVertex", cellVerticesAtVertex) + call MPAS_pool_get_array(velocity_variational, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) + call MPAS_pool_get_array(velocity_variational, "basisGradientU", basisGradientU) + call MPAS_pool_get_array(velocity_variational, "basisGradientV", basisGradientV) + call MPAS_pool_get_array(velocity_variational, "basisIntegralsU", basisIntegralsU) + call MPAS_pool_get_array(velocity_variational, "basisIntegralsV", basisIntegralsV) + call MPAS_pool_get_array(velocity_variational, "basisIntegralsMetric", basisIntegralsMetric) + call MPAS_pool_get_array(velocity_variational, "variationalDenominator", variationalDenominator) + + call mpas_timer_start("variational calc_metric_terms") + call seaice_calc_variational_metric_terms(& + tanLatVertexRotatedOverRadius, & + nVertices, & + xVertex, & + yVertex, & + zVertex, & + sphere_radius, & + rotateCartesianGrid, & + includeMetricTerms) + call mpas_timer_stop("variational calc_metric_terms") + + call mpas_timer_start("variational vertices_at_vertex") + call seaice_cell_vertices_at_vertex(& + cellVerticesAtVertex, & + nVertices, & + vertexDegree, & + nEdgesOnCell, & + verticesOnCell, & + cellsOnVertex) + call mpas_timer_stop("variational vertices_at_vertex") + + call mpas_timer_start("variational calc_local_coords") + allocate(xLocal(maxEdges,nCells)) + allocate(yLocal(maxEdges,nCells)) + + call seaice_calc_local_coords(& + xLocal, & + yLocal, & + nCells, & + nEdgesOnCell, & + verticesOnCell, & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell, & + rotateCartesianGrid, & + on_a_sphere) + call mpas_timer_stop("variational calc_local_coords") + if (trim(variationalBasisType) == "wachspress") then call seaice_init_velocity_solver_wachspress(& - mesh, & - velocity_variational, & - boundary, & + nCells, & + maxEdges, & + nEdgesOnCell, & + xLocal, & + yLocal, & rotateCartesianGrid, & includeMetricTerms, & + on_a_sphere, & integrationType, & - integrationOrder) + integrationOrder, & + sphere_radius, & + basisGradientU, & + basisGradientV, & + basisIntegralsU, & + basisIntegralsV, & + basisIntegralsMetric) else if (trim(variationalBasisType) == "pwl") then call seaice_init_velocity_solver_pwl(& - mesh, & - velocity_variational, & - boundary, & - rotateCartesianGrid, & - includeMetricTerms) + nCells, & + maxEdges, & + nEdgesOnCell, & + verticesOnCell, & + edgesOnCell, & + dvEdge, & + areaCell, & + xLocal, & + yLocal, & + basisGradientU, & + basisGradientV, & + basisIntegralsMetric, & + basisIntegralsU, & + basisIntegralsV) + + else if (trim(variationalBasisType) == "none") then + + continue + + else + + call MPAS_log_write("Unknown variational basis type: "//trim(variationalBasisType), MPAS_LOG_CRIT) endif - end subroutine seaice_init_velocity_solver_variational + call mpas_timer_start("variational denominator") + call variational_denominator(& + nVertices, & + vertexDegree, & + nEdgesOnCell, & + interiorVertex, & + areaTriangle, & + cellsOnVertex, & + cellVerticesAtVertex, & + basisIntegralsMetric, & + variationalDenominatorType, & + variationalDenominator) + call mpas_timer_stop("variational denominator") -!----------------------------------------------------------------------- -! time step -!----------------------------------------------------------------------- + ! clean up + deallocate(xLocal) + deallocate(yLocal) + + !call homogenize_variational_basis_field() + + end subroutine init_velocity_solver_variational_primary_mesh !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! seaice_internal_stress_variational +! variational_denominator ! !> \brief !> \author Adrian K. Turner, LANL -!> \date 2013-2014 +!> \date 30th January 2021 !> \details !> ! !----------------------------------------------------------------------- - subroutine seaice_internal_stress_variational(domain)!{{{ + subroutine variational_denominator(& + nVertices, & + vertexDegree, & + nEdgesOnCell, & + interiorVertex, & + areaTriangle, & + cellsOnVertex, & + cellVerticesAtVertex, & + basisIntegralsMetric, & + variationalDenominatorType, & + variationalDenominator) + + integer, intent(in) :: & + nVertices, & !< Input: + vertexDegree !< Input: - type(domain_type), intent(inout) :: & - domain + integer, dimension(:), intent(in) :: & + nEdgesOnCell, & !< Input: + interiorVertex !< Input: - type(block_type), pointer :: & - block + real(kind=RKIND), dimension(:), intent(in) :: & + areaTriangle !< Input: - type (MPAS_pool_type), pointer :: & - meshPool, & - velocityVariationalPool, & - velocitySolverPool + integer, dimension(:,:), intent(in) :: & + cellsOnVertex !< Input: - real(kind=RKIND), dimension(:), pointer :: & - uVelocity, & - vVelocity, & - icePressure, & - stressDivergenceU, & - stressDivergenceV + integer, dimension(:,:), intent(in) :: & + cellVerticesAtVertex !< Input: - real(kind=RKIND), pointer :: & - elasticTimeStep + real(kind=RKIND), dimension(:,:,:), intent(in) :: & + basisIntegralsMetric !< Input: - logical, pointer :: & - revisedEVP + character(len=*), intent(in) :: & + variationalDenominatorType !< Input: - integer, dimension(:), pointer :: & - solveStress, & - solveVelocity + real(kind=RKIND), dimension(:), intent(out) :: & + variationalDenominator - integer, dimension(:,:), pointer :: & - cellVerticesAtVertex + integer :: & + iVertex, & + iSurroundingCell, & + iStressVertex, & + iCell, & + iVelocityVertex - real(kind=RKIND), dimension(:), pointer :: & - tanLatVertexRotatedOverRadius + if (trim(variationalDenominatorType) == "alternate") then - real(kind=RKIND), dimension(:,:), pointer :: & - replacementPressure, & - strain11, & - strain22, & - strain12, & - stress11, & - stress22, & - stress12 + do iVertex = 1, nVertices - real(kind=RKIND), dimension(:,:,:), pointer :: & + variationalDenominator(iVertex) = 0.0_RKIND + + ! loop over surrounding cells + do iSurroundingCell = 1, vertexDegree + + ! get the cell number of this cell + iCell = cellsOnVertex(iSurroundingCell, iVertex) + + ! get the vertexOnCell number of the iVertex velocity point from cell iCell + iVelocityVertex = cellVerticesAtVertex(iSurroundingCell,iVertex) + + ! loop over the vertices of the surrounding cell + do iStressVertex = 1, nEdgesOnCell(iCell) + + variationalDenominator(iVertex) = variationalDenominator(iVertex) + & + basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) + + enddo ! iStressVertex + + enddo ! iSurroundingCell + + ! inverse + variationalDenominator(iVertex) = variationalDenominator(iVertex) + + enddo ! iVertex + + else if (trim(variationalDenominatorType) == "original") then + + do iVertex = 1, nVertices + variationalDenominator(iVertex) = areaTriangle(iVertex) + enddo ! iVertex + + else + + call MPAS_log_write("Unknown variational denominator type: "//trim(variationalDenominatorType), MPAS_LOG_CRIT) + + endif + + end subroutine variational_denominator + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! homogenize_variational_basis_field +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 9th January 2021 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine homogenize_variational_basis_field() + + use seaice_mesh_pool, only: & basisGradientU, & basisGradientV, & + basisIntegralsMetric, & basisIntegralsU, & basisIntegralsV, & - basisIntegralsMetric - - block => domain % blocklist - do while (associated(block)) - - call MPAS_pool_get_config(block % configs, "config_revised_evp", revisedEVP) - - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "velocity_variational", velocityVariationalPool) - call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocitySolverPool) - - call MPAS_pool_get_array(velocitySolverPool, "solveStress", solveStress) - call MPAS_pool_get_array(velocitySolverPool, "solveVelocity", solveVelocity) - call MPAS_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) - call MPAS_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) - call MPAS_pool_get_array(velocitySolverPool, "icePressure", icePressure) - call MPAS_pool_get_array(velocitySolverPool, "elasticTimeStep", elasticTimeStep) - call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceU", stressDivergenceU) - call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceV", stressDivergenceV) - - call MPAS_pool_get_array(velocityVariationalPool, "strain11", strain11) - call MPAS_pool_get_array(velocityVariationalPool, "strain22", strain22) - call MPAS_pool_get_array(velocityVariationalPool, "strain12", strain12) - call MPAS_pool_get_array(velocityVariationalPool, "stress11", stress11) - call MPAS_pool_get_array(velocityVariationalPool, "stress22", stress22) - call MPAS_pool_get_array(velocityVariationalPool, "stress12", stress12) - call MPAS_pool_get_array(velocityVariationalPool, "cellVerticesAtVertex", cellVerticesAtVertex) - call MPAS_pool_get_array(velocityVariationalPool, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) - call MPAS_pool_get_array(velocityVariationalPool, "basisGradientU", basisGradientU) - call MPAS_pool_get_array(velocityVariationalPool, "basisGradientV", basisGradientV) - call MPAS_pool_get_array(velocityVariationalPool, "basisIntegralsU", basisIntegralsU) - call MPAS_pool_get_array(velocityVariationalPool, "basisIntegralsV", basisIntegralsV) - call MPAS_pool_get_array(velocityVariationalPool, "basisIntegralsMetric", basisIntegralsMetric) - call MPAS_pool_get_array(velocityVariationalPool, "replacementPressure", replacementPressure) - - call mpas_timer_start("Velocity solver strain tensor") - call seaice_strain_tensor_variational(& - meshPool, & - strain11, & - strain22, & - strain12, & - uVelocity, & - vVelocity, & - basisGradientU, & - basisGradientV, & - tanLatVertexRotatedOverRadius, & - solveStress) - call mpas_timer_stop("Velocity solver strain tensor") - - call mpas_timer_start("Velocity solver stress tensor") - call seaice_stress_tensor_variational(& - meshPool, & - stress11, & - stress22, & - stress12, & - strain11, & - strain22, & - strain12, & - icePressure, & - replacementPressure, & - solveStress, & - elasticTimeStep, & - revisedEVP) - call mpas_timer_stop("Velocity solver stress tensor") - - call mpas_timer_start("Velocity solver stress divergence") - call seaice_stress_divergence_variational(& - meshPool, & - stressDivergenceU, & - stressDivergenceV, & - stress11, & - stress22, & - stress12, & - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric, & - tanLatVertexRotatedOverRadius, & - cellVerticesAtVertex, & - solveVelocity) - call mpas_timer_stop("Velocity solver stress divergence") + nCells + + integer :: & + iCell + + integer, parameter :: & + iCellHomogenize = 1111 + + !call homogenize_cell(basisGradientU, iCellHomogenize) + !call homogenize_cell(basisGradientV, iCellHomogenize) + !call homogenize_cell(basisIntegralsMetric, iCellHomogenize) + !call homogenize_cell(basisIntegralsU, iCellHomogenize) + !call homogenize_cell(basisIntegralsV, iCellHomogenize) - block => block % next - end do + do iCell = 1, nCells + + basisGradientU(:,:,iCell) = basisGradientU(:,:,iCellHomogenize) + basisGradientV(:,:,iCell) = basisGradientV(:,:,iCellHomogenize) + basisIntegralsMetric(:,:,iCell) = basisIntegralsMetric(:,:,iCellHomogenize) + basisIntegralsU(:,:,iCell) = basisIntegralsU(:,:,iCellHomogenize) + basisIntegralsV(:,:,iCell) = basisIntegralsV(:,:,iCellHomogenize) + + enddo ! iCell - end subroutine seaice_internal_stress_variational!}}} + end subroutine homogenize_variational_basis_field + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! homogenize_variational_basis_field +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 9th January 2021 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine homogenize_cell(& + field, & + iCell) + + real(kind=RKIND), dimension(:,:,:), intent(inout) :: & + field + + integer, intent(in) :: & + iCell + + integer :: & + iVertexOnCell1, & + iVertexOnCell2, & + nCanonicalValues, & + iCV + + real(kind=RKIND), dimension(100) :: & + canonicalValues + + logical :: & + lFound + + nCanonicalValues = 0 + + do iVertexOnCell1 = 1, 6 + do iVertexOnCell2 = 1, 6 + + lFound = .false. + do iCV = 1, nCanonicalValues + + if (abs(abs(canonicalValues(iCV) - abs(field(iVertexOnCell1,iVertexOnCell2,iCell)))) < 1e-12) then + lFound = .true. + field(iVertexOnCell1,iVertexOnCell2,iCell) = canonicalValues(iCV) * sign(1.0_RKIND, field(iVertexOnCell1,iVertexOnCell2,iCell)) + exit + endif + + enddo ! iCV + + if (.not. lFound) then + nCanonicalValues = nCanonicalValues + 1 + canonicalValues(nCanonicalValues) = abs(field(iVertexOnCell1,iVertexOnCell2,iCell)) + endif + + enddo ! iVertexOnCell2 + enddo ! iVertexOnCell1 + + !do iVertexOnCell1 = 1, 6 + ! do iVertexOnCell2 = 1, 6 + ! write(*,*) iVertexOnCell1, iVertexOnCell2, field(iVertexOnCell1,iVertexOnCell2,iCell) + ! enddo ! iVertexOnCell2 + !enddo ! iVertexOnCell1 + + end subroutine homogenize_cell + +!----------------------------------------------------------------------- +! time step +!----------------------------------------------------------------------- !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! @@ -280,6 +584,11 @@ subroutine seaice_strain_tensor_variational(& tanLatVertexRotatedOverRadius, & solveStress)!{{{ + use seaice_mesh_pool, only: & + nCells, & + verticesOnCell, & + nEdgesOnCell + type(MPAS_pool_type), pointer, intent(in) :: & mesh !< Input: @@ -293,7 +602,7 @@ subroutine seaice_strain_tensor_variational(& vVelocity, & !< Input: tanLatVertexRotatedOverRadius !< Input: - real(kind=RKIND), dimension(:,:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in) :: & basisGradientU, & !< Input: basisGradientV !< Input: @@ -307,59 +616,50 @@ subroutine seaice_strain_tensor_variational(& iVertex, & jVertex - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - verticesOnCell - - ! init variables - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) + real(kind=RKIND) :: & + strain11Tmp, & + strain22Tmp, & + strain12Tmp ! loop over cells - !$omp parallel do default(shared) private(iCell, iGradientVertex, iBasisVertex, iVertex, jVertex) +#ifdef MPAS_OPENMP_OFFLOAD +!$omp target teams distribute parallel do +#elif MPAS_OPENACC +!$acc parallel loop gang worker +#else +!$omp parallel do default(shared) private(iGradientVertex, iBasisVertex, iVertex, jVertex, & +!$omp& strain11Tmp, strain22Tmp, strain12Tmp) +#endif do iCell = 1, nCells if (solveStress(iCell) == 1) then - strain11(:,iCell) = 0.0_RKIND - strain22(:,iCell) = 0.0_RKIND - strain12(:,iCell) = 0.0_RKIND - ! loop over velocity points surrounding cell - location of stress and derivative do iGradientVertex = 1, nEdgesOnCell(iCell) + strain11Tmp = 0.0_RKIND + strain22Tmp = 0.0_RKIND + strain12Tmp = 0.0_RKIND + ! loop over basis functions do iBasisVertex = 1, nEdgesOnCell(iCell) iVertex = verticesOnCell(iBasisVertex,iCell) - strain11(iGradientVertex,iCell) = strain11(iGradientVertex,iCell) + & - uVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell) - - strain22(iGradientVertex,iCell) = strain22(iGradientVertex,iCell) + & - vVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) - - strain12(iGradientVertex,iCell) = strain12(iGradientVertex,iCell) + 0.5_RKIND * (& - uVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) + & - vVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell)) + strain11Tmp = strain11Tmp + uVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell) + strain22Tmp = strain22Tmp + vVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) + strain12Tmp = strain12Tmp + 0.5_RKIND * (& + uVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) + & + vVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell)) enddo ! iVertexOnCell ! metric terms jVertex = verticesOnCell(iGradientVertex,iCell) - strain11(iGradientVertex,iCell) = strain11(iGradientVertex,iCell) - & - vVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) - - strain12(iGradientVertex,iCell) = strain12(iGradientVertex,iCell) + & - uVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) * 0.5_RKIND + strain11(iGradientVertex,iCell) = strain11Tmp - vVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) + strain12(iGradientVertex,iCell) = strain12Tmp + uVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) * 0.5_RKIND + strain22(iGradientVertex,iCell) = strain22Tmp enddo ! jVertexOnCell @@ -369,6 +669,99 @@ subroutine seaice_strain_tensor_variational(& end subroutine seaice_strain_tensor_variational!}}} +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_average_strains_on_vertex +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 2013-2014 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_average_strains_on_vertex(& + areaCell, & + strain11, & + strain22, & + strain12) + + use seaice_mesh_pool, only: & + nCells, & + nVerticesSolve, & + cellVerticesAtVertex, & + cellsOnVertex, & + vertexDegree + + real(kind=RKIND), dimension(:), intent(in) :: & + areaCell !< Input + + real(kind=RKIND), dimension(:,:), intent(inout) :: & + strain11, & !< Input/Output: + strain22, & !< Input/Output: + strain12 !< Input/Output: + + real(kind=RKIND) :: & + strain11avg, & + strain22avg, & + strain12avg, & + denominator + + integer :: & + iVertex, & + iVertexDegree, & + iCell, & + iVertexOnCell + + do iVertex = 1, nVerticesSolve + + strain11avg = 0.0_RKIND + strain22avg = 0.0_RKIND + strain12avg = 0.0_RKIND + denominator = 0.0_RKIND + + do iVertexDegree = 1, vertexDegree + + iCell = cellsOnVertex(iVertexDegree,iVertex) + + if (iCell <= nCells) then + + iVertexOnCell = cellVerticesAtVertex(iVertexDegree,iVertex) + + strain11avg = strain11avg + strain11(iVertexOnCell,iCell) * areaCell(iCell) + strain22avg = strain22avg + strain22(iVertexOnCell,iCell) * areaCell(iCell) + strain12avg = strain12avg + strain12(iVertexOnCell,iCell) * areaCell(iCell) + denominator = denominator + areaCell(iCell) + + endif + + enddo ! iVertexDegree + + strain11avg = strain11avg / denominator + strain22avg = strain22avg / denominator + strain12avg = strain12avg / denominator + + do iVertexDegree = 1, vertexDegree + + iCell = cellsOnVertex(iVertexDegree,iVertex) + + if (iCell <= nCells) then + + iVertexOnCell = cellVerticesAtVertex(iVertexDegree,iVertex) + + strain11(iVertexOnCell,iCell) = strain11avg + strain22(iVertexOnCell,iCell) = strain22avg + strain12(iVertexOnCell,iCell) = strain12avg + + endif + + enddo ! iCellOnVertex + + enddo ! iVertex + + end subroutine seaice_average_strains_on_vertex + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_stress_tensor_variational @@ -392,25 +785,34 @@ subroutine seaice_stress_tensor_variational(& icePressure, & replacementPressure, & solveStress, & - dtElastic, & - revisedEVP)!{{{ + dtElastic)!{{{ use seaice_velocity_solver_constitutive_relation, only: & + constitutiveRelationType, & + EVP_CONSTITUTIVE_RELATION, & + REVISED_EVP_CONSTITUTIVE_RELATION, & + LINEAR_CONSTITUTIVE_RELATION, & seaice_evp_constitutive_relation, & - seaice_evp_constitutive_relation_revised + seaice_evp_constitutive_relation_revised, & + seaice_linear_constitutive_relation, & + eccentricitySquared, puny, dampingTimescale + + use seaice_mesh_pool, only: & + nCells, & + nEdgesOnCell type(MPAS_pool_type), pointer, intent(in) :: & mesh !< Input: - real(kind=RKIND), dimension(:,:), intent(inout) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(inout) :: & stress11, & !< Input/Output: stress22, & !< Input/Output: stress12 !< Input/Output: - real(kind=RKIND), dimension(:,:), intent(out) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(out) :: & replacementPressure !< Output: - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & strain11, & !< Input: strain22, & !< Input: strain12 !< Input: @@ -424,39 +826,75 @@ subroutine seaice_stress_tensor_variational(& real(kind=RKIND), intent(in) :: & dtElastic !< Input: - logical, intent(in) :: & - revisedEVP !< Input: - integer :: & iCell, & iVertexOnCell - integer, pointer :: & - nCells, & - maxEdges - - integer, dimension(:), pointer :: & - nEdgesOnCell - real(kind=RKIND), dimension(:), pointer :: & areaCell - ! init variables - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) + real(kind=RKIND) :: & + strainDivergence, & + strainTension, & + strainShearing, & + stress1, & + stress2, & + Delta, & + pressureCoefficient, & + denominator - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) + ! init variables call MPAS_pool_get_array(mesh, "areaCell", areaCell) - if (.not. revisedEVP) then + if (constitutiveRelationType == EVP_CONSTITUTIVE_RELATION) then + + denominator = 1.0_RKIND + (0.5_RKIND * dtElastic) / dampingTimescale - !$omp parallel do default(shared) private(iCell, iVertexOnCell) +#ifdef MPAS_OPENMP_OFFLOAD +!$omp target teams distribute parallel do +#elif MPAS_OPENACC +!$acc parallel loop gang worker +#else +!$omp parallel do default(shared) private(iVertexOnCell) +#endif do iCell = 1, nCells replacementPressure(:,iCell) = 0.0_RKIND if (solveStress(iCell) == 1) then +#if defined(MPAS_OPENMP_OFFLOAD) || defined(MPAS_OPENACC) + ! inline call to seaice_evp_constitutive_relation for GPUs + do iVertexOnCell = 1, nEdgesOnCell(iCell) + + ! convert from stress11 to stress1 etc + strainDivergence = strain11(iVertexOnCell,iCell) + strain22(iVertexOnCell,iCell) + strainTension = strain11(iVertexOnCell,iCell) - strain22(iVertexOnCell,iCell) + strainShearing = strain12(iVertexOnCell,iCell) * 2.0_RKIND + + stress1 = stress11(iVertexOnCell,iCell) + stress22(iVertexOnCell,iCell) + stress2 = stress11(iVertexOnCell,iCell) - stress22(iVertexOnCell,iCell) + + ! perform the constituitive relation + Delta = sqrt(strainDivergence*strainDivergence + & + (strainTension*strainTension + strainShearing*strainShearing) / eccentricitySquared) + + pressureCoefficient = icePressure(iCell) / max(Delta,puny) + replacementPressure(iVertexOnCell,iCell) = pressureCoefficient * Delta + + pressureCoefficient = (pressureCoefficient * dtElastic) / (2.0_RKIND * dampingTimescale) + + stress1 = (stress1 + pressureCoefficient * (strainDivergence - Delta)) / denominator + stress2 = (stress2 + (pressureCoefficient / eccentricitySquared) * strainTension ) / denominator + stress12(iVertexOnCell,iCell) = (stress12(iVertexOnCell,iCell) & + + (pressureCoefficient / eccentricitysquared) * strainShearing * 0.5_RKIND) / denominator + + ! convert back + stress11(iVertexOnCell,iCell) = 0.5_RKIND * (stress1 + stress2) + stress22(iVertexOnCell,iCell) = 0.5_RKIND * (stress1 - stress2) + +#else + !$omp simd do iVertexOnCell = 1, nEdgesOnCell(iCell) call seaice_evp_constitutive_relation(& @@ -470,19 +908,23 @@ subroutine seaice_stress_tensor_variational(& replacementPressure(iVertexOnCell,iCell), & areaCell(iCell), & dtElastic) - +#endif enddo ! iVertexOnCell endif ! solveStress enddo ! iCell - else + else if (constitutiveRelationType == REVISED_EVP_CONSTITUTIVE_RELATION) then +#ifdef MPAS_OPENMP +!$omp parallel do default(shared) private(iVertexOnCell) +#endif do iCell = 1, nCells if (solveStress(iCell) == 1) then + !$omp simd do iVertexOnCell = 1, nEdgesOnCell(iCell) call seaice_evp_constitutive_relation_revised(& @@ -502,10 +944,111 @@ subroutine seaice_stress_tensor_variational(& enddo ! iCell - endif + else if (constitutiveRelationType == LINEAR_CONSTITUTIVE_RELATION) then + +#ifdef MPAS_OPENMP +!$omp parallel do default(shared) private(iCell, iVertexOnCell) +#endif + do iCell = 1, nCells + + if (solveStress(iCell) == 1) then + + !$omp simd + do iVertexOnCell = 1, nEdgesOnCell(iCell) + + call seaice_linear_constitutive_relation(& + stress11(iVertexOnCell,iCell), & + stress22(iVertexOnCell,iCell), & + stress12(iVertexOnCell,iCell), & + strain11(iVertexOnCell,iCell), & + strain22(iVertexOnCell,iCell), & + strain12(iVertexOnCell,iCell)) + + enddo ! iVertexOnCell + + endif ! solveStress + + enddo ! iCell + + endif ! constitutiveRelationType end subroutine seaice_stress_tensor_variational!}}} +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_stress_tensor_variational_linear +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 3rd October 2019 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_stress_tensor_variational_linear(& + mesh, & + stress11, & + stress22, & + stress12, & + strain11, & + strain22, & + strain12, & + solveStress)!{{{ + + use seaice_velocity_solver_constitutive_relation, only: & + seaice_linear_constitutive_relation + + use seaice_mesh_pool, only: & + nCells, & + nEdgesOnCell + + type(MPAS_pool_type), pointer, intent(in) :: & + mesh !< Input: + + real(kind=RKIND), dimension(:,:), contiguous, intent(out) :: & + stress11, & !< Input/Output: + stress22, & !< Input/Output: + stress12 !< Input/Output: + + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & + strain11, & !< Input: + strain22, & !< Input: + strain12 !< Input: + + integer, dimension(:), intent(in) :: & + solveStress !< Input: + + integer :: & + iCell, & + iVertexOnCell + +#ifdef MPAS_OPENMP +!$omp parallel do default(shared) private(iCell, iVertexOnCell) +#endif + do iCell = 1, nCells + + if (solveStress(iCell) == 1) then + + !$omp simd + do iVertexOnCell = 1, nEdgesOnCell(iCell) + + call seaice_linear_constitutive_relation(& + stress11(iVertexOnCell,iCell), & + stress22(iVertexOnCell,iCell), & + stress12(iVertexOnCell,iCell), & + strain11(iVertexOnCell,iCell), & + strain22(iVertexOnCell,iCell), & + strain12(iVertexOnCell,iCell)) + + enddo ! iVertexOnCell + + endif ! solveStress + + enddo ! iCell + + end subroutine seaice_stress_tensor_variational_linear!}}} + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_stress_divergence_variational @@ -528,10 +1071,18 @@ subroutine seaice_stress_divergence_variational(& basisIntegralsU, & basisIntegralsV, & basisIntegralsMetric, & + variationalDenominator, & tanLatVertexRotatedOverRadius, & cellVerticesAtVertex, & solveVelocity)!{{{ + use seaice_mesh_pool, only: & + nVerticesSolve, & + cellsOnVertex, & + nEdgesOnCell, & + areaTriangle, & + vertexDegree + type(MPAS_pool_type), pointer, intent(in) :: & mesh !< Input: @@ -539,18 +1090,19 @@ subroutine seaice_stress_divergence_variational(& stressDivergenceU, & !< Output: stressDivergenceV !< Output: - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & stress11, & !< Input: stress22, & !< Input: stress12 !< Input: - real(kind=RKIND), dimension(:,:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in) :: & basisIntegralsU, & !< Input: basisIntegralsV, & !< Input: basisIntegralsMetric !< Input: real(kind=RKIND), dimension(:), intent(in) :: & - tanLatVertexRotatedOverRadius !< Input: + tanLatVertexRotatedOverRadius, & !< Input: + variationalDenominator !< Input: integer, dimension(:,:), intent(in) :: & cellVerticesAtVertex !< Input: @@ -559,6 +1111,8 @@ subroutine seaice_stress_divergence_variational(& solveVelocity !< Input: real(kind=RKIND) :: & + stressDivergenceUVertex, & + stressDivergenceVVertex, & stressDivergenceUCell, & stressDivergenceVCell @@ -569,37 +1123,21 @@ subroutine seaice_stress_divergence_variational(& iStressVertex, & iVelocityVertex - integer, pointer :: & - nVertices, & - vertexDegree - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - cellsOnVertex, & - verticesOnCell - - real(kind=RKIND), dimension(:), pointer :: & - areaTriangle - - ! init variables - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - call MPAS_pool_get_dimension(mesh, "vertexDegree", vertexDegree) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "cellsOnVertex", cellsOnVertex) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "areaTriangle", areaTriangle) - ! loop over velocity positions - !$omp parallel do default(shared) private(iVertex, iSurroundingCell, iCell, iVelocityVertex, stressDivergenceUCell, stressDivergenceVCell, iStressVertex) - do iVertex = 1, nVertices +#ifdef MPAS_OPENMP_OFFLOAD +!$omp target teams distribute parallel do +#elif MPAS_OPENACC +!$acc parallel loop gang worker +#else +!$omp parallel do default(shared) private(stressDivergenceUVertex, stressDivergenceVVertex, & +!$omp& iSurroundingCell, iCell, iVelocityVertex, stressDivergenceUCell, stressDivergenceVCell, iStressVertex) +#endif + do iVertex = 1, nVerticesSolve if (solveVelocity(iVertex) == 1) then - stressDivergenceU(iVertex) = 0.0_RKIND - stressDivergenceV(iVertex) = 0.0_RKIND + stressDivergenceUVertex = 0.0_RKIND + stressDivergenceVVertex = 0.0_RKIND ! loop over surrounding cells do iSurroundingCell = 1, vertexDegree @@ -616,33 +1154,28 @@ subroutine seaice_stress_divergence_variational(& ! loop over the vertices of the surrounding cell do iStressVertex = 1, nEdgesOnCell(iCell) - ! normal terms + ! normal & metric terms stressDivergenceUCell = stressDivergenceUCell - & stress11(iStressVertex,iCell) * basisIntegralsU(iStressVertex,iVelocityVertex,iCell) - & - stress12(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - - stressDivergenceVCell = stressDivergenceVCell - & - stress22(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - & - stress12(iStressVertex,iCell) * basisIntegralsU(iStressVertex,iVelocityVertex,iCell) - - ! metric terms - stressDivergenceUCell = stressDivergenceUCell - & + stress12(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - & stress12(iStressVertex,iCell) * basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) * & tanLatVertexRotatedOverRadius(iVertex) - stressDivergenceVCell = stressDivergenceVCell + & + stressDivergenceVCell = stressDivergenceVCell - & + stress22(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - & + stress12(iStressVertex,iCell) * basisIntegralsU(iStressVertex,iVelocityVertex,iCell) + & stress11(iStressVertex,iCell) * basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) * & tanLatVertexRotatedOverRadius(iVertex) enddo ! iStressVertex - stressDivergenceU(iVertex) = stressDivergenceU(iVertex) + stressDivergenceUCell - stressDivergenceV(iVertex) = stressDivergenceV(iVertex) + stressDivergenceVCell + stressDivergenceUVertex = stressDivergenceUVertex + stressDivergenceUCell + stressDivergenceVVertex = stressDivergenceVVertex + stressDivergenceVCell enddo ! iSurroundingCell - stressDivergenceU(iVertex) = stressDivergenceU(iVertex) / areaTriangle(iVertex) - stressDivergenceV(iVertex) = stressDivergenceV(iVertex) / areaTriangle(iVertex) + stressDivergenceU(iVertex) = stressDivergenceUVertex / variationalDenominator(iVertex) + stressDivergenceV(iVertex) = stressDivergenceVVertex / variationalDenominator(iVertex) endif ! solveVelocity @@ -667,22 +1200,19 @@ subroutine seaice_final_divergence_shear_variational(block) use seaice_velocity_solver_constitutive_relation, only: & eccentricitySquared + use seaice_mesh_pool, only: & + nCells, & + solveStress, & + nEdgesOnCell + type(block_type), intent(inout) :: & block type(MPAS_pool_type), pointer :: & - meshPool, & velocityVariationalPool, & velocitySolverPool, & ridgingPool - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell, & - solveStress - real(kind=RKIND), dimension(:,:), pointer :: & strain11, & strain22, & @@ -713,21 +1243,15 @@ subroutine seaice_final_divergence_shear_variational(block) iCell, & iVertexOnCell - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) call MPAS_pool_get_subpool(block % structs, "velocity_variational", velocityVariationalPool) call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocitySolverPool) - call MPAS_pool_get_dimension(meshPool, "nCells", nCells) - - call MPAS_pool_get_array(meshPool, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(velocityVariationalPool, "strain11", strain11) call MPAS_pool_get_array(velocityVariationalPool, "strain22", strain22) call MPAS_pool_get_array(velocityVariationalPool, "strain12", strain12) call MPAS_pool_get_array(velocitySolverPool, "divergence", divergence) call MPAS_pool_get_array(velocitySolverPool, "shear", shear) - call MPAS_pool_get_array(velocitySolverPool, "solveStress", solveStress) allocate(DeltaAverage(nCells)) diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_variational_shared.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_variational_shared.F index 3a0e79a107..92165961ca 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_variational_shared.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_variational_shared.F @@ -40,37 +40,72 @@ module seaice_velocity_solver_variational_shared !----------------------------------------------------------------------- subroutine seaice_calc_local_coords(& - mesh, & xLocal, & yLocal, & - rotateCartesianGrid)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: + nCells, & + nEdgesOnCell, & + verticesOnCell, & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell, & + rotateCartesianGrid, & + onASphere)!{{{ real(kind=RKIND), dimension(:,:), intent(out) :: & xLocal, & !< Output: yLocal !< Output: - logical, intent(in) :: & - rotateCartesianGrid !< Input: + integer, intent(in) :: & + nCells !< Input: - logical, pointer :: & - on_a_sphere + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: - call MPAS_pool_get_config(mesh, "on_a_sphere", on_a_sphere) + integer, dimension(:,:), intent(in) :: & + verticesOnCell !< Input: - if (on_a_sphere) then + real(kind=RKIND), dimension(:), intent(in) :: & + xVertex, & !< Input: + yVertex, & !< Input: + zVertex, & !< Input: + xCell, & !< Input: + yCell, & !< Input: + zCell !< Input: + + logical, intent(in) :: & + rotateCartesianGrid, & !< Input: + onASphere !< Input: + + if (onASphere) then call calc_local_coords_spherical(& - mesh, & xLocal, & yLocal, & + nCells, & + nEdgesOnCell, & + verticesOnCell, & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell, & rotateCartesianGrid) else call calc_local_coords_planar(& - mesh, & xLocal, & - yLocal) + yLocal, & + nCells, & + nEdgesOnCell, & + verticesOnCell, & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell) endif end subroutine seaice_calc_local_coords!}}} @@ -88,45 +123,43 @@ end subroutine seaice_calc_local_coords!}}} !----------------------------------------------------------------------- subroutine calc_local_coords_planar(& - mesh, & xLocal, & - yLocal)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: + yLocal, & + nCells, & + nEdgesOnCell, & + verticesOnCell, & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell)!{{{ real(kind=RKIND), dimension(:,:), intent(out) :: & xLocal, & !< Output: yLocal !< Output: - integer :: & - iCell, & - iVertex, & - iVertexOnCell - - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell + integer, intent(in) :: & + nCells !< Input: - integer, dimension(:,:), pointer :: & - verticesOnCell + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - xCell, & - yCell + integer, dimension(:,:), intent(in) :: & + verticesOnCell !< Input: - call MPAS_pool_get_dimension(mesh, "nCells", nCells) + real(kind=RKIND), dimension(:), intent(in) :: & + xVertex, & !< Input: + yVertex, & !< Input: + zVertex, & !< Input: + xCell, & !< Input: + yCell, & !< Input: + zCell !< Input: - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "xCell", xCell) - call MPAS_pool_get_array(mesh, "yCell", yCell) + integer :: & + iCell, & + iVertex, & + iVertexOnCell do iCell = 1, nCells @@ -156,22 +189,44 @@ end subroutine calc_local_coords_planar!}}} !----------------------------------------------------------------------- subroutine calc_local_coords_spherical(& - mesh, & xLocal, & yLocal, & + nCells, & + nEdgesOnCell, & + verticesOnCell, & + xVertex, & + yVertex, & + zVertex, & + xCell, & + yCell, & + zCell, & rotateCartesianGrid)!{{{ use seaice_mesh, only: & seaice_project_3D_vector_onto_local_2D, & seaice_grid_rotation_forward - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - real(kind=RKIND), dimension(:,:), intent(out) :: & xLocal, & !< Output: yLocal !< Output: + integer, intent(in) :: & + nCells !< Input: + + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: + + integer, dimension(:,:), intent(in) :: & + verticesOnCell !< Input: + + real(kind=RKIND), dimension(:), intent(in) :: & + xVertex, & !< Input: + yVertex, & !< Input: + zVertex, & !< Input: + xCell, & !< Input: + yCell, & !< Input: + zCell !< Input: + logical, intent(in) :: & rotateCartesianGrid !< Input: @@ -186,41 +241,18 @@ subroutine calc_local_coords_spherical(& iVertex, & iVertexOnCell - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - verticesOnCell - - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - zVertex, & - xCell, & - yCell, & - zCell - real(kind=RKIND) :: & xCellRotated, & yCellRotated, & zCellRotated - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "zVertex", zVertex) - call MPAS_pool_get_array(mesh, "xCell", xCell) - call MPAS_pool_get_array(mesh, "yCell", yCell) - call MPAS_pool_get_array(mesh, "zCell", zCell) - do iCell = 1, nCells + call seaice_grid_rotation_forward(& + xCellRotated, yCellRotated, zCellRotated, & + xCell(iCell), yCell(iCell), zCell(iCell), & + rotateCartesianGrid) + do iVertexOnCell = 1, nEdgesOnCell(iCell) iVertex = verticesOnCell(iVertexOnCell, iCell) @@ -230,11 +262,6 @@ subroutine calc_local_coords_spherical(& xVertex(iVertex), yVertex(iVertex), zVertex(iVertex), & rotateCartesianGrid) - call seaice_grid_rotation_forward(& - xCellRotated, yCellRotated, zCellRotated, & - xCell(iCell), yCell(iCell), zCell(iCell), & - rotateCartesianGrid) - call seaice_project_3D_vector_onto_local_2D(& normalVector2D, & normalVector3D, & @@ -264,51 +291,45 @@ end subroutine calc_local_coords_spherical!}}} !----------------------------------------------------------------------- subroutine seaice_calc_variational_metric_terms(& - mesh, & tanLatVertexRotatedOverRadius, & + nVertices, & + xVertex, & + yVertex, & + zVertex, & + sphereRadius, & rotateCartesianGrid, & includeMetricTerms) use seaice_mesh, only: & seaice_grid_rotation_forward - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - real(kind=RKIND), dimension(:), intent(out) :: & tanLatVertexRotatedOverRadius !< Output: + integer, intent(in) :: & + nVertices !< Input: + + real(kind=RKIND), dimension(:), pointer :: & + xVertex, & !< Input: + yVertex, & !< Input: + zVertex !< Input: + + real(kind=RKIND), pointer :: & + sphereRadius !< Input: + logical, intent(in) :: & rotateCartesianGrid, & !< Input: includeMetricTerms !< Input: - integer, pointer :: & - nVertices - integer :: & iVertex - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - zVertex - - real(kind=RKIND), pointer :: & - sphere_radius - real(kind=RKIND) :: & xVertexRotated, & yVertexRotated, & zVertexRotated, & latVertexRotated - call MPAS_pool_get_dimension(mesh, "nVertices", nVertices) - call MPAS_pool_get_config(mesh, "sphere_radius", sphere_radius) - - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "zVertex", zVertex) - if (includeMetricTerms) then do iVertex = 1, nVertices @@ -318,9 +339,9 @@ subroutine seaice_calc_variational_metric_terms(& xVertex(iVertex), yVertex(iVertex), zVertex(iVertex), & rotateCartesianGrid) - latVertexRotated = asin(zVertexRotated / sphere_radius) + latVertexRotated = asin(zVertexRotated / sphereRadius) - tanLatVertexRotatedOverRadius(iVertex) = tan(latVertexRotated) / sphere_radius + tanLatVertexRotatedOverRadius(iVertex) = tan(latVertexRotated) / sphereRadius enddo ! iVertex diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F index 0e63e9c991..1e46eea0bd 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F @@ -44,33 +44,43 @@ module seaice_velocity_solver_wachspress !----------------------------------------------------------------------- subroutine seaice_init_velocity_solver_wachspress(& - mesh, & - velocity_variational, & - boundary, & + nCells, & + maxEdges, & + nEdgesOnCell, & + xLocal, & + yLocal, & rotateCartesianGrid, & includeMetricTerms, & + onASphere, & integrationType, & - integrationOrder)!{{{ + integrationOrder, & + sphereRadius, & + basisGradientU, & + basisGradientV, & + basisIntegralsU, & + basisIntegralsV, & + basisIntegralsMetric)!{{{ use mpas_timer - use seaice_mesh, only: & - seaice_cell_vertices_at_vertex - use seaice_velocity_solver_variational_shared, only: & - seaice_calc_local_coords, & - seaice_calc_variational_metric_terms + seaice_calc_local_coords + + integer, intent(in) :: & + nCells, & !< Input: + maxEdges !< Input: - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: - type(MPAS_pool_type), pointer :: & - velocity_variational, & !< Input/Output: - boundary !< Input/Output: + real(kind=RKIND), dimension(:,:), intent(in) :: & + xLocal, & !< Input: + yLocal !< Input: logical, intent(in) :: & rotateCartesianGrid, & !< Input: - includeMetricTerms !< Input: + includeMetricTerms, & !< Input: + onASphere !< Input: character(len=strKIND), intent(in) :: & integrationType !< Input: @@ -78,26 +88,15 @@ subroutine seaice_init_velocity_solver_wachspress(& integer, intent(in) :: & integrationOrder !< Input: - integer :: & - iCell, & - iVertex - - integer, pointer :: & - nCells, & - maxEdges, i1, i2 - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - cellVerticesAtVertex - - real(kind=RKIND), dimension(:), pointer :: & - tanLatVertexRotatedOverRadius + real(kind=RKIND), intent(in) :: & + sphereRadius !< Input: - real(kind=RKIND), dimension(:,:), allocatable :: & - xLocal, & - yLocal + real(kind=RKIND), dimension(:,:,:), intent(out) :: & + basisGradientU, & !< Output: + basisGradientV, & !< Output: + basisIntegralsU, & !< Output: + basisIntegralsV, & !< Output: + basisIntegralsMetric !< Output: real(kind=RKIND), dimension(:,:), allocatable :: & wachspressA, & @@ -106,69 +105,44 @@ subroutine seaice_init_velocity_solver_wachspress(& real(kind=RKIND), dimension(:,:,:), allocatable :: & wachspressKappa - real(kind=RKIND), dimension(:,:,:), pointer :: & - basisGradientU, & - basisGradientV, & - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric - call mpas_timer_start("Velocity solver Wachpress init") - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - - call MPAS_pool_get_array(velocity_variational, "cellVerticesAtVertex", cellVerticesAtVertex) - call MPAS_pool_get_array(velocity_variational, "tanLatVertexRotatedOverRadius", tanLatVertexRotatedOverRadius) - call MPAS_pool_get_array(velocity_variational, "basisGradientU", basisGradientU) - call MPAS_pool_get_array(velocity_variational, "basisGradientV", basisGradientV) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsU", basisIntegralsU) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsV", basisIntegralsV) - call MPAS_pool_get_array(velocity_variational, "basisIntegralsMetric", basisIntegralsMetric) - - allocate(xLocal(maxEdges,nCells)) - allocate(yLocal(maxEdges,nCells)) allocate(wachspressKappa(maxEdges,maxEdges,nCells)) allocate(wachspressA(maxEdges,nCells)) allocate(wachspressB(maxEdges,nCells)) - call seaice_calc_local_coords(& - mesh, & - xLocal, & - yLocal, & - rotateCartesianGrid) - - call seaice_calc_variational_metric_terms(& - mesh, & - tanLatVertexRotatedOverRadius, & - rotateCartesianGrid, & - includeMetricTerms) - + call mpas_timer_start("wachpress calc_coefficients") call calc_wachspress_coefficients(& - mesh, & wachspressKappa, & wachspressA, & wachspressB, & + nCells, & + nEdgesOnCell, & xLocal, & yLocal) + call mpas_timer_stop("wachpress calc_coefficients") + call mpas_timer_start("wachpress calc_derivatives") call calculate_wachspress_derivatives(& - mesh, & basisGradientU, & basisGradientV, & + nCells, & + maxEdges, & + nEdgesOnCell, & xLocal, & yLocal, & wachspressA, & wachspressB, & wachspressKappa) + call mpas_timer_stop("wachpress calc_derivatives") + call mpas_timer_start("wachpress integrate") call integrate_wachspress(& - mesh, & basisIntegralsU, & basisIntegralsV, & basisIntegralsMetric, & + nCells, & + nEdgesOnCell, & xLocal, & yLocal, & wachspressA, & @@ -176,20 +150,375 @@ subroutine seaice_init_velocity_solver_wachspress(& wachspressKappa, & integrationType, & integrationOrder) + call mpas_timer_stop("wachpress integrate") + + deallocate(wachspressKappa) + deallocate(wachspressA) + deallocate(wachspressB) + + call mpas_timer_stop("Velocity solver Wachpress init") + + end subroutine seaice_init_velocity_solver_wachspress!}}} + +!----------------------------------------------------------------------- +! Integration +!----------------------------------------------------------------------- + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! integrate_wachspress +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 2013-2014 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine integrate_wachspress(& + basisIntegralsU, & + basisIntegralsV, & + basisIntegralsMetric, & + nCells, & + nEdgesOnCell, & + xLocal, & + yLocal, & + wachspressA, & + wachspressB, & + wachspressKappa, & + integrationType, & + integrationOrder)!{{{ + + ! basisIntegralsUV (iStressVertex,iVelocityVertex,iCell) + ! iCell : cell integrals are performed on + ! iStressVertex : vertex number of Wachspress function + ! iVelocityVertex : vertex number of Wachspress derivative function + ! Sij + + real(kind=RKIND), dimension(:,:,:), intent(out) :: & + basisIntegralsU, & !< Output: + basisIntegralsV, & !< Output: + basisIntegralsMetric !< Output: + + integer, intent(in) :: & + nCells !< Input: + + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: + + real(kind=RKIND), dimension(:,:), intent(in) :: & + xLocal, & !< Input: + yLocal, & !< Input: + wachspressA, & !< Input: + wachspressB !< Input: + + real(kind=RKIND), dimension(:,:,:), intent(in) :: & + wachspressKappa !< Input: + + character(len=strKIND), intent(in) :: & + integrationType !< Input: + + integer, intent(in) :: & + integrationOrder !< Input: + + integer :: & + iCell, & + iStressVertex, & + iVelocityVertex + + integer :: & + nIntegrationPoints + + real(kind=RKIND), dimension(:), allocatable :: & + integrationU, & + integrationV, & + integrationWeights + + real(kind=RKIND) :: & + normalizationFactor + + ! Quadrature rules + call get_integration_factors(& + integrationType, & + integrationOrder, & + nIntegrationPoints, & + integrationU, & + integrationV, & + integrationWeights, & + normalizationFactor) + + !$omp parallel do default(shared) private(iStressVertex, iVelocityVertex) + do iCell = 1, nCells + + do iVelocityVertex = 1, nEdgesOnCell(iCell) + + do iStressVertex = 1, nEdgesOnCell(iCell) + + basisIntegralsU(iStressVertex,iVelocityVertex,iCell) = 0.0_RKIND + basisIntegralsV(iStressVertex,iVelocityVertex,iCell) = 0.0_RKIND + basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) = 0.0_RKIND + + call integrate_wachspress_polygon(& + basisIntegralsU(iStressVertex,iVelocityVertex,iCell), & + basisIntegralsV(iStressVertex,iVelocityVertex,iCell), & + basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell), & + nEdgesOnCell(iCell), & + iStressVertex, & + iVelocityVertex, & + xLocal(:,iCell), & + yLocal(:,iCell), & + wachspressA(:,iCell), & + wachspressB(:,iCell), & + wachspressKappa(:,:,iCell), & + nIntegrationPoints, & + integrationU, & + integrationV, & + integrationWeights, & + normalizationFactor) + + enddo ! jVertex + + enddo ! iVertex + + enddo ! iCell + + deallocate(integrationU) + deallocate(integrationV) + deallocate(integrationWeights) + + end subroutine integrate_wachspress!}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! integrate_wachspress_polygon +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 2013-2014 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine integrate_wachspress_polygon(& + basisIntegralsU, & + basisIntegralsV, & + basisIntegralsMetric, & + nEdgesOnCell, & + iStressVertex, & + iVelocityVertex, & + xLocal, & + yLocal, & + wachspressA, & + wachspressB, & + wachspressKappa, & + nIntegrationPoints, & + integrationU, & + integrationV, & + integrationWeights, & + normalizationFactor)!{{{ + + use seaice_velocity_solver_variational_shared, only: & + seaice_wrapped_index + + real(kind=RKIND), intent(inout) :: & + basisIntegralsU, & !< Input/Output: + basisIntegralsV, & !< Input/Output: + basisIntegralsMetric !< Input/Output: + + integer, intent(in) :: & + nEdgesOnCell, & !< Input: + iStressVertex, & !< Input: + iVelocityVertex !< Input: + + real(kind=RKIND), dimension(:), intent(in) :: & + xLocal, & !< Input: + yLocal !< Input: + + real(kind=RKIND), dimension(:), intent(in) :: & + wachspressA, & !< Input: + wachspressB !< Input: + + real(kind=RKIND), dimension(:,:), intent(in) :: & + wachspressKappa !< Input: + + integer, intent(in) :: & + nIntegrationPoints !< Input: + + real(kind=RKIND), dimension(:), intent(in) :: & + integrationU, & !< Input: + integrationV, & !< Input: + integrationWeights !< Input: + + real(kind=RKIND), intent(in) :: & + normalizationFactor !< Input: + + integer, dimension(nEdgesOnCell) :: & + nEdgesOnCellSubset + + integer, dimension(nEdgesOnCell,nEdgesOnCell) :: & + vertexIndexSubset + + real(kind=RKIND) :: & + basisIntegralsSubTriangleTmp, & + basisIntegralsUSubTriangle, & + basisIntegralsVSubTriangle, & + basisIntegralsMetricSubTriangle + + real(kind=RKIND), dimension(nIntegrationPoints) :: & + x, & + y, & + stressBasisFunction, & + velocityBasisFunction, & + velocityBasisDerivativeU, & + velocityBasisDerivativeV + + real(kind=RKIND), dimension(2,2) :: & + mapping + + real(kind=RKIND), dimension(nEdgesOnCell) :: & + jacobian + + integer :: & + iIntegrationPoint, & + iSubTriangle, & + i1, & + i2 + + call wachspress_indexes(& + nEdgesOnCell, & + nEdgesOnCellSubset, & + vertexIndexSubset) + + do iSubTriangle = 1, nEdgesOnCell + + i1 = iSubTriangle + i2 = seaice_wrapped_index(iSubTriangle + 1, nEdgesOnCell) + + call get_triangle_mapping(& + mapping, & + jacobian(iSubTriangle), & + 1.0_RKIND, 0.0_RKIND, & + 0.0_RKIND, 1.0_RKIND, & + xLocal(i1), yLocal(i1), & + xLocal(i2), yLocal(i2)) + + !in-lined use_triangle_mapping + do iIntegrationPoint = 1, nIntegrationPoints + + x(iIntegrationPoint) = mapping(1,1) * integrationU(iIntegrationPoint) + & + mapping(1,2) * integrationV(iIntegrationPoint) + y(iIntegrationPoint) = mapping(2,1) * integrationU(iIntegrationPoint) + & + mapping(2,2) * integrationV(iIntegrationPoint) + + enddo ! iIntegrationPoint + + call wachspress_basis_function(& + nEdgesOnCell, iStressVertex, x, y, & + wachspressKappa, wachspressA, wachspressB, & + nEdgesOnCellSubset, vertexIndexSubset, & + stressBasisFunction) + + call wachspress_basis_function(& + nEdgesOnCell, iVelocityVertex, x, y, & + wachspressKappa, wachspressA, wachspressB, & + nEdgesOnCellSubset, vertexIndexSubset, & + velocityBasisFunction) + + call wachspress_basis_derivative(& + nEdgesOnCell, iVelocityVertex, x, y, & + wachspressKappa, wachspressA, wachspressB, & + nEdgesOnCellSubset, vertexIndexSubset, & + velocityBasisDerivativeU, & + velocityBasisDerivativeV) + + basisIntegralsUSubTriangle = 0.0_RKIND + basisIntegralsVSubTriangle = 0.0_RKIND + basisIntegralsMetricSubTriangle = 0.0_RKIND + + do iIntegrationPoint = 1, nIntegrationPoints + + basisIntegralsSubTriangleTmp = & + jacobian(iSubTriangle) * & + integrationWeights(iIntegrationPoint) * & + stressBasisFunction(iIntegrationPoint) + + basisIntegralsUSubTriangle = basisIntegralsUSubTriangle + & + basisIntegralsSubTriangleTmp * & + velocityBasisDerivativeU(iIntegrationPoint) + + basisIntegralsVSubTriangle = basisIntegralsVSubTriangle + & + basisIntegralsSubTriangleTmp * & + velocityBasisDerivativeV(iIntegrationPoint) + + basisIntegralsMetricSubTriangle = basisIntegralsMetricSubTriangle + & + basisIntegralsSubTriangleTmp * & + velocityBasisFunction(iIntegrationPoint) + + enddo ! iIntegrationPoint + + basisIntegralsU = basisIntegralsU + basisIntegralsUSubTriangle / normalizationFactor + basisIntegralsV = basisIntegralsV + basisIntegralsVSubTriangle / normalizationFactor + basisIntegralsMetric = basisIntegralsMetric + basisIntegralsMetricSubTriangle / normalizationFactor + + enddo ! iSubTriangle + + end subroutine integrate_wachspress_polygon!}}} + +!----------------------------------------------------------------------- +! Remapping +!----------------------------------------------------------------------- + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! get_triangle_mapping +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 2013-2014 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine get_triangle_mapping(& + mapping, & + jacobian, & + x1, y1, & + x2, y2, & + u1, v1, & + u2, v2)!{{{ + + real(kind=RKIND), dimension(2,2), intent(out) :: & + mapping !< Output: + + real(kind=RKIND), intent(out) :: & + jacobian !< Output: + + real(kind=RKIND), intent(in) :: & + x1, & !< Input: + y1, & !< Input: + x2, & !< Input: + y2, & !< Input: + u1, & !< Input: + v1, & !< Input: + u2, & !< Input: + v2 !< Input: + + mapping(1,1) = (u2*y1 - u1*y2) / (x2*y1 - x1*y2) + mapping(1,2) = (u1*x2 - u2*x1) / (y1*x2 - y2*x1) - call seaice_cell_vertices_at_vertex(& - mesh, & - cellVerticesAtVertex) + mapping(2,1) = (v2*y1 - v1*y2) / (x2*y1 - x1*y2) + mapping(2,2) = (v1*x2 - v2*x1) / (y1*x2 - y2*x1) - deallocate(xLocal) - deallocate(yLocal) - deallocate(wachspressKappa) - deallocate(wachspressA) - deallocate(wachspressB) + jacobian = mapping(1,1) * mapping(2,2) - mapping(1,2) * mapping(2,1) - call mpas_timer_stop("Velocity solver Wachpress init") + end subroutine get_triangle_mapping!}}} - end subroutine seaice_init_velocity_solver_wachspress!}}} +!----------------------------------------------------------------------- +! Wachspress function +!----------------------------------------------------------------------- !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! @@ -204,16 +533,14 @@ end subroutine seaice_init_velocity_solver_wachspress!}}} !----------------------------------------------------------------------- subroutine calc_wachspress_coefficients(& - mesh, & wachspressKappa, & wachspressA, & wachspressB, & + nCells, & + nEdgesOnCell, & xLocal, & yLocal)!{{{ - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - real(kind=RKIND), dimension(:,:,:), intent(out) :: & wachspressKappa !< Output: @@ -221,6 +548,12 @@ subroutine calc_wachspress_coefficients(& wachspressA, & !< Output: wachspressB !< Output: + integer, intent(in) :: & + nCells !< Input: + + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: + real(kind=RKIND), dimension(:,:), intent(in) :: & xLocal, & !< Input: yLocal !< Input: @@ -233,17 +566,6 @@ subroutine calc_wachspress_coefficients(& i2, & jVertex - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell - - ! init variables - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - ! loop over cells do iCell = 1, nCells @@ -312,13 +634,13 @@ subroutine wachspress_indexes(& seaice_wrapped_index integer, intent(in) :: & - nEdgesOnCell + nEdgesOnCell !< Input: integer, dimension(:), intent(out) :: & - nEdgesOnCellSubset + nEdgesOnCellSubset !< Output: integer, dimension(:,:), intent(out) :: & - vertexIndexSubset + vertexIndexSubset !< Output: integer :: & jVertex, & @@ -388,28 +710,24 @@ subroutine wachspress_basis_function(& wachspressB !< Input: integer, dimension(:), intent(in) :: & - nEdgesOnCellSubset + nEdgesOnCellSubset !< Input: integer, dimension(:,:), intent(in) :: & - vertexIndexSubset + vertexIndexSubset !< Input: real(kind=RKIND), dimension(:), intent(out) :: & wachpress !< Output: - real(kind=RKIND), dimension(:,:), allocatable :: & + real(kind=RKIND), dimension(size(x),nEdgesOnCell) :: & numerator - real(kind=RKIND), dimension(:), allocatable :: & + real(kind=RKIND), dimension(size(x)) :: & denominator, & edgeEquation integer :: & jVertex - allocate(denominator(size(x))) - allocate(numerator(size(x),nEdgesOnCell)) - allocate(edgeEquation(size(x))) - ! sum over numerators to get denominator denominator(:) = 0.0_RKIND @@ -428,10 +746,6 @@ subroutine wachspress_basis_function(& wachpress(:) = numerator(:,iVertex) / denominator(:) - deallocate(denominator) - deallocate(numerator) - deallocate(edgeEquation) - end subroutine wachspress_basis_function!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -475,39 +789,33 @@ subroutine wachspress_basis_derivative(& wachspressB !< Input: integer, dimension(:), intent(in) :: & - nEdgesOnCellSubset + nEdgesOnCellSubset !< Input: integer, dimension(:,:), intent(in) :: & - vertexIndexSubset + vertexIndexSubset !< Input: real(kind=RKIND), dimension(:), intent(out) :: & wachspressU, & !< Output: wachspressV !< Output: - real(kind=RKIND), dimension(:,:,:), allocatable :: & + real(kind=RKIND), dimension(size(x),2,nEdgesOnCell) :: & derivative - real(kind=RKIND), dimension(:,:), allocatable :: & - numerator, & + real(kind=RKIND), dimension(size(x),nEdgesOnCell) :: & + numerator + + real(kind=RKIND), dimension(size(x),2) :: & sum_of_derivatives, & sum_of_products, & product - real(kind=RKIND), dimension(:), allocatable :: & + real(kind=RKIND), dimension(size(x)) :: & denominator, & edgeEquation integer :: & jVertex - allocate(denominator(size(x))) - allocate(sum_of_derivatives(size(x),2)) - allocate(numerator(size(x),nEdgesOnCell)) - allocate(derivative(size(x),2,nEdgesOnCell)) - allocate(sum_of_products(size(x),2)) - allocate(product(size(x),2)) - allocate(edgeEquation(size(x))) - ! sum over numerators to get denominator denominator(:) = 0.0_RKIND sum_of_derivatives(:,:) = 0.0_RKIND @@ -539,14 +847,6 @@ subroutine wachspress_basis_derivative(& wachspressV(:) = derivative(:,2,iVertex) / denominator(:) - & (numerator(:,iVertex) / denominator(:)**2) * sum_of_derivatives(:,2) - deallocate(denominator) - deallocate(sum_of_derivatives) - deallocate(numerator) - deallocate(derivative) - deallocate(sum_of_products) - deallocate(product) - deallocate(edgeEquation) - end subroutine wachspress_basis_derivative!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -592,10 +892,10 @@ subroutine wachspress_numerator(& wachspressB !< Input: integer, dimension(:), intent(in) :: & - nEdgesOnCellSubset + nEdgesOnCellSubset !< Input: integer, dimension(:,:), intent(in) :: & - vertexIndexSubset + vertexIndexSubset !< Input: real(kind=RKIND), dimension(:), intent(inout) :: & edgeEquation @@ -669,20 +969,20 @@ subroutine wachspress_numerator_derivative(& wachspressB !< Input: integer, dimension(:), intent(in) :: & - nEdgesOnCellSubset + nEdgesOnCellSubset !< Input: integer, dimension(:,:), intent(in) :: & - vertexIndexSubset + vertexIndexSubset !< Input: real(kind=RKIND), dimension(:,:), intent(out) :: & derivative !< Output: real(kind=RKIND), dimension(:,:), intent(inout) :: & - sum_of_products, & - product + sum_of_products, & !< Input/Output: + product !< Input/Output: real(kind=RKIND), dimension(:), intent(inout) :: & - edgeEquation + edgeEquation !< Input/Output: integer :: & kVertex, & @@ -781,17 +1081,17 @@ end subroutine wachspress_edge_equation!}}} !----------------------------------------------------------------------- subroutine calculate_wachspress_derivatives(& - mesh, & basisGradientU, & basisGradientV, & + nCells, & + maxEdges, & + nEdgesOnCell, & xLocal, & yLocal, & wachspressA, & wachspressB, & wachspressKappa)!{{{ - use mpas_timer - use seaice_velocity_solver_variational_shared, only: & seaice_wrapped_index @@ -800,13 +1100,17 @@ subroutine calculate_wachspress_derivatives(& ! iVertexOnCell : The vertex basis function the gradient is calculated from ! jVertexOnCell : The vertex location the gradients are calculated at - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - real(kind=RKIND), dimension(:,:,:), intent(out) :: & basisGradientU, & !< Output: basisGradientV !< Output: + integer, intent(in) :: & + nCells, & !< Input: + maxEdges !< Input: + + integer, dimension(:), intent(in) :: & + nEdgesOnCell !< Input: + real(kind=RKIND), dimension(:,:), intent(in) :: & wachspressA, & !< Input: wachspressB, & !< Input: @@ -821,16 +1125,6 @@ subroutine calculate_wachspress_derivatives(& iBasisVertex, & iGradientVertex - integer, pointer :: & - nCells, & - maxEdges - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - verticesOnCell - integer, dimension(:), allocatable :: & nEdgesOnCellSubset @@ -840,13 +1134,6 @@ subroutine calculate_wachspress_derivatives(& real(kind=RKIND), dimension(:), allocatable :: & x, y, derivativeU, derivativeV - ! init variables - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) - - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - allocate(x(maxEdges)) allocate(y(maxEdges)) @@ -870,264 +1157,57 @@ subroutine calculate_wachspress_derivatives(& x(iGradientVertex) = xLocal(iGradientVertex,iCell) y(iGradientVertex) = yLocal(iGradientVertex,iCell) - enddo ! iGradientVertex - - ! loop over vertices - basis function - do iBasisVertex = 1, nEdgesOnCell(iCell) - - call wachspress_basis_derivative(& - nEdgesOnCell(iCell), & - iBasisVertex, & - x(1:nEdgesOnCell(iCell)), & - y(1:nEdgesOnCell(iCell)), & - wachspressKappa(:,:,iCell), & - wachspressA(:,iCell), & - wachspressB(:,iCell), & - nEdgesOnCellSubset(1:nEdgesOnCell(iCell)), & - vertexIndexSubset(1:nEdgesOnCell(iCell),1:nEdgesOnCell(iCell)), & - derivativeU(1:nEdgesOnCell(iCell)), & - derivativeV(1:nEdgesOnCell(iCell))) - - basisGradientU(iBasisVertex,:,iCell) = 0.0_RKIND - basisGradientV(iBasisVertex,:,iCell) = 0.0_RKIND - - iGradientVertex = iBasisVertex - basisGradientU(iBasisVertex,iGradientVertex,iCell) = derivativeU(iGradientVertex) - basisGradientV(iBasisVertex,iGradientVertex,iCell) = derivativeV(iGradientVertex) - - iGradientVertex = seaice_wrapped_index(iBasisVertex - 1, nEdgesOnCell(iCell)) - basisGradientU(iBasisVertex,iGradientVertex,iCell) = derivativeU(iGradientVertex) - basisGradientV(iBasisVertex,iGradientVertex,iCell) = derivativeV(iGradientVertex) - - iGradientVertex = seaice_wrapped_index(iBasisVertex + 1, nEdgesOnCell(iCell)) - basisGradientU(iBasisVertex,iGradientVertex,iCell) = derivativeU(iGradientVertex) - basisGradientV(iBasisVertex,iGradientVertex,iCell) = derivativeV(iGradientVertex) - - enddo ! iBasisVertex - - enddo ! iCell - - deallocate(nEdgesOnCellSubset) - deallocate(vertexIndexSubset) - - deallocate(x) - deallocate(y) - - deallocate(derivativeU) - deallocate(derivativeV) - - end subroutine calculate_wachspress_derivatives!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! get_triangle_mapping -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine get_triangle_mapping(& - mapping, & - jacobian, & - x1, y1, & - x2, y2, & - u1, v1, & - u2, v2)!{{{ - - real(kind=RKIND), dimension(2,2), intent(out) :: & - mapping !< Output: - - real(kind=RKIND), intent(out) :: & - jacobian !< Output: - - real(kind=RKIND), intent(in) :: & - x1, & !< Input: - y1, & !< Input: - x2, & !< Input: - y2, & !< Input: - u1, & !< Input: - v1, & !< Input: - u2, & !< Input: - v2 !< Input: - - mapping(1,1) = (u2*y1 - u1*y2) / (x2*y1 - x1*y2) - mapping(1,2) = (u1*x2 - u2*x1) / (y1*x2 - y2*x1) - - mapping(2,1) = (v2*y1 - v1*y2) / (x2*y1 - x1*y2) - mapping(2,2) = (v1*x2 - v2*x1) / (y1*x2 - y2*x1) - - jacobian = mapping(1,1) * mapping(2,2) - mapping(1,2) * mapping(2,1) - - end subroutine get_triangle_mapping!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! use_triangle_mapping -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine use_triangle_mapping(& - u, v, & - x, y, & - mapping)!{{{ - - real(kind=RKIND), intent(out) :: & - u, v !< Output: - - real(kind=RKIND), intent(in) :: & - x, y !< Input: - - real(kind=RKIND), dimension(2,2), intent(in) :: & - mapping !< Input: - - u = mapping(1,1) * x + mapping(1,2) * y - v = mapping(2,1) * x + mapping(2,2) * y - - end subroutine use_triangle_mapping!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! integrate_wachspress -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine integrate_wachspress(& - mesh, & - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric, & - xLocal, & - yLocal, & - wachspressA, & - wachspressB, & - wachspressKappa, & - integrationType, & - integrationOrder)!{{{ - - use mpas_timer - - ! basisIntegralsUV (iStressVertex,iVelocityVertex,iCell) - ! iCell : cell integrals are performed on - ! iStressVertex : vertex number of Wachspress function - ! iVelocityVertex : vertex number of Wachspress derivative function - ! Sij - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - real(kind=RKIND), dimension(:,:,:), intent(out) :: & - basisIntegralsU, & !< Output: - basisIntegralsV, & !< Output: - basisIntegralsMetric !< Output: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - xLocal, & !< Input: - yLocal, & !< Input: - wachspressA, & !< Input: - wachspressB !< Input: - - real(kind=RKIND), dimension(:,:,:), intent(in) :: & - wachspressKappa !< Input: - - character(len=strKIND), intent(in) :: & - integrationType !< Input: - - integer, intent(in) :: & - integrationOrder !< Input: - - real(kind=RKIND) :: & - integration - - integer :: & - iCell, & - iStressVertex, & - iVelocityVertex - - integer, pointer :: & - nCells - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer :: & - nIntegrationPoints - - real(kind=RKIND), dimension(:), allocatable :: & - integrationU, & - integrationV, & - integrationWeights - - real(kind=RKIND) :: & - normalizationFactor - - ! init variables - call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) - - call get_integration_factors(& - integrationType, & - integrationOrder, & - nIntegrationPoints, & - integrationU, & - integrationV, & - integrationWeights, & - normalizationFactor) + enddo ! iGradientVertex - do iCell = 1, nCells + ! loop over vertices - basis function + do iBasisVertex = 1, nEdgesOnCell(iCell) - do iStressVertex = 1, nEdgesOnCell(iCell) + call wachspress_basis_derivative(& + nEdgesOnCell(iCell), & + iBasisVertex, & + x(1:nEdgesOnCell(iCell)), & + y(1:nEdgesOnCell(iCell)), & + wachspressKappa(:,:,iCell), & + wachspressA(:,iCell), & + wachspressB(:,iCell), & + nEdgesOnCellSubset(1:nEdgesOnCell(iCell)), & + vertexIndexSubset(1:nEdgesOnCell(iCell),1:nEdgesOnCell(iCell)), & + derivativeU(1:nEdgesOnCell(iCell)), & + derivativeV(1:nEdgesOnCell(iCell))) - do iVelocityVertex = 1, nEdgesOnCell(iCell) + basisGradientU(iBasisVertex,:,iCell) = 0.0_RKIND + basisGradientV(iBasisVertex,:,iCell) = 0.0_RKIND - basisIntegralsU(iStressVertex,iVelocityVertex,iCell) = 0.0_RKIND - basisIntegralsV(iStressVertex,iVelocityVertex,iCell) = 0.0_RKIND - basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) = 0.0_RKIND + iGradientVertex = iBasisVertex + basisGradientU(iBasisVertex,iGradientVertex,iCell) = derivativeU(iGradientVertex) + basisGradientV(iBasisVertex,iGradientVertex,iCell) = derivativeV(iGradientVertex) - call integrate_wachspress_polygon(& - basisIntegralsU(iStressVertex,iVelocityVertex,iCell), & - basisIntegralsV(iStressVertex,iVelocityVertex,iCell), & - basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell), & - nEdgesOnCell(iCell), & - iStressVertex, & - iVelocityVertex, & - xLocal(:,iCell), & - yLocal(:,iCell), & - wachspressA(:,iCell), & - wachspressB(:,iCell), & - wachspressKappa(:,:,iCell), & - nIntegrationPoints, & - integrationU, & - integrationV, & - integrationWeights, & - normalizationFactor) + iGradientVertex = seaice_wrapped_index(iBasisVertex - 1, nEdgesOnCell(iCell)) + basisGradientU(iBasisVertex,iGradientVertex,iCell) = derivativeU(iGradientVertex) + basisGradientV(iBasisVertex,iGradientVertex,iCell) = derivativeV(iGradientVertex) - enddo ! jVertex + iGradientVertex = seaice_wrapped_index(iBasisVertex + 1, nEdgesOnCell(iCell)) + basisGradientU(iBasisVertex,iGradientVertex,iCell) = derivativeU(iGradientVertex) + basisGradientV(iBasisVertex,iGradientVertex,iCell) = derivativeV(iGradientVertex) - enddo ! iVertex + enddo ! iBasisVertex enddo ! iCell - deallocate(integrationU) - deallocate(integrationV) - deallocate(integrationWeights) + deallocate(nEdgesOnCellSubset) + deallocate(vertexIndexSubset) - end subroutine integrate_wachspress!}}} + deallocate(x) + deallocate(y) + + deallocate(derivativeU) + deallocate(derivativeV) + + end subroutine calculate_wachspress_derivatives!}}} + +!----------------------------------------------------------------------- +! Integration factors +!----------------------------------------------------------------------- !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! @@ -1141,7 +1221,14 @@ end subroutine integrate_wachspress!}}} ! !----------------------------------------------------------------------- - subroutine get_integration_factors(integrationType, integrationOrder, nIntegrationPoints, u, v, weights, normalizationFactor) + subroutine get_integration_factors(& + integrationType, & + integrationOrder, & + nIntegrationPoints, & + u, & + v, & + weights, & + normalizationFactor) character(len=strKIND), intent(in) :: & integrationType @@ -1853,218 +1940,6 @@ subroutine get_integration_factors_fekete(& end subroutine get_integration_factors_fekete -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! integrate_wachspress_polygon -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine integrate_wachspress_polygon(& - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric, & - nEdgesOnCell, & - iStressVertex, & - iVelocityVertex, & - xLocal, & - yLocal, & - wachspressA, & - wachspressB, & - wachspressKappa, & - nIntegrationPoints, & - integrationU, & - integrationV, & - integrationWeights, & - normalizationFactor)!{{{ - - use seaice_constants, only: pii - - use seaice_velocity_solver_variational_shared, only: & - seaice_wrapped_index - - real(kind=RKIND), intent(inout) :: & - basisIntegralsU, & - basisIntegralsV, & - basisIntegralsMetric - - integer, intent(in) :: & - nEdgesOnCell, & !< Input: - iStressVertex, & !< Input: - iVelocityVertex !< Input: - - real(kind=RKIND), dimension(:), intent(in) :: & - xLocal, & !< Input: - yLocal !< Input: - - real(kind=RKIND), dimension(:), intent(in) :: & - wachspressA, & !< Input: - wachspressB !< Input: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - wachspressKappa !< Input: - - integer, intent(in) :: & !< Input: - nIntegrationPoints - - real(kind=RKIND), dimension(:), intent(in) :: & - integrationU, & !< Input: - integrationV, & !< Input: - integrationWeights !< Input: - - real(kind=RKIND), intent(in) :: & - normalizationFactor !< Input: - - integer, dimension(:), allocatable :: & - nEdgesOnCellSubset - - integer, dimension(:,:), allocatable :: & - vertexIndexSubset - - real(kind=RKIND) :: & - basisIntegralsUSubTriangle, & - basisIntegralsVSubTriangle, & - basisIntegralsMetricSubTriangle - - real(kind=RKIND), dimension(:), allocatable :: & - x, & - y, & - stressBasisFunction, & - velocityBasisFunction, & - velocityBasisDerivativeU, & - velocityBasisDerivativeV - - real(kind=RKIND) :: & - u, & - v - - integer :: & - iIntegrationPoint - - real(kind=RKIND), dimension(2,2) :: & - mapping - - real(kind=RKIND), dimension(:), allocatable :: & - jacobian - - integer :: & - iSubTriangle, & - i1, & - i2 - - character(len=strKIND) :: filename - - allocate(nEdgesOnCellSubset(nEdgesOnCell)) - allocate(vertexIndexSubset(nEdgesOnCell,nEdgesOnCell)) - - call wachspress_indexes(& - nEdgesOnCell, & - nEdgesOnCellSubset, & - vertexIndexSubset) - - allocate(jacobian(nEdgesOnCell)) - - do iSubTriangle = 1, nEdgesOnCell - - i1 = iSubTriangle - i2 = seaice_wrapped_index(iSubTriangle + 1, nEdgesOnCell) - - call get_triangle_mapping(& - mapping, & - jacobian(iSubTriangle), & - 1.0_RKIND, 0.0_RKIND, & - 0.0_RKIND, 1.0_RKIND, & - xLocal(i1), yLocal(i1), & - xLocal(i2), yLocal(i2)) - - allocate(x(nIntegrationPoints)) - allocate(y(nIntegrationPoints)) - - do iIntegrationPoint = 1, nIntegrationPoints - - call use_triangle_mapping(& - x(iIntegrationPoint), y(iIntegrationPoint), & - integrationU(iIntegrationPoint), integrationV(iIntegrationPoint), & - mapping) - - enddo ! iIntegrationPoint - - allocate(stressBasisFunction(nIntegrationPoints)) - allocate(velocityBasisFunction(nIntegrationPoints)) - allocate(velocityBasisDerivativeU(nIntegrationPoints)) - allocate(velocityBasisDerivativeV(nIntegrationPoints)) - - call wachspress_basis_function(& - nEdgesOnCell, iStressVertex, x, y, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, & - stressBasisFunction) - - call wachspress_basis_function(& - nEdgesOnCell, iVelocityVertex, x, y, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, & - velocityBasisFunction) - - call wachspress_basis_derivative(& - nEdgesOnCell, iVelocityVertex, x, y, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, & - velocityBasisDerivativeU, & - velocityBasisDerivativeV) - - basisIntegralsUSubTriangle = 0.0_RKIND - basisIntegralsVSubTriangle = 0.0_RKIND - basisIntegralsMetricSubTriangle = 0.0_RKIND - - do iIntegrationPoint = 1, nIntegrationPoints - - basisIntegralsUSubTriangle = basisIntegralsUSubTriangle + & - jacobian(iSubTriangle) * & - integrationWeights(iIntegrationPoint) * & - stressBasisFunction(iIntegrationPoint) * & - velocityBasisDerivativeU(iIntegrationPoint) - - basisIntegralsVSubTriangle = basisIntegralsVSubTriangle + & - jacobian(iSubTriangle) * & - integrationWeights(iIntegrationPoint) * & - stressBasisFunction(iIntegrationPoint) * & - velocityBasisDerivativeV(iIntegrationPoint) - - basisIntegralsMetricSubTriangle = basisIntegralsMetricSubTriangle + & - jacobian(iSubTriangle) * & - integrationWeights(iIntegrationPoint) * & - stressBasisFunction(iIntegrationPoint) * & - velocityBasisFunction(iIntegrationPoint) - - enddo ! iIntegrationPoint - - basisIntegralsU = basisIntegralsU + basisIntegralsUSubTriangle / normalizationFactor - basisIntegralsV = basisIntegralsV + basisIntegralsVSubTriangle / normalizationFactor - basisIntegralsMetric = basisIntegralsMetric + basisIntegralsMetricSubTriangle / normalizationFactor - - deallocate(stressBasisFunction) - deallocate(velocityBasisFunction) - deallocate(velocityBasisDerivativeU) - deallocate(velocityBasisDerivativeV) - - deallocate(x) - deallocate(y) - - enddo ! iSubTriangle - - deallocate(nEdgesOnCellSubset) - deallocate(vertexIndexSubset) - - deallocate(jacobian) - - end subroutine integrate_wachspress_polygon!}}} - !----------------------------------------------------------------------- end module seaice_velocity_solver_wachspress diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_weak.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_weak.F index db39daf7ba..1e79492f89 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_weak.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_weak.F @@ -22,7 +22,6 @@ module seaice_velocity_solver_weak public :: & seaice_init_velocity_solver_weak, & - seaice_internal_stress_weak, & seaice_strain_tensor_weak, & seaice_stress_tensor_weak, & seaice_stress_divergence_weak, & @@ -98,132 +97,6 @@ end subroutine seaice_init_velocity_solver_weak!}}} ! Time step !----------------------------------------------------------------------- -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! seaice_internal_stress_weak -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine seaice_internal_stress_weak(domain)!{{{ - - type(domain_type), intent(inout) :: & - domain - - type(block_type), pointer :: & - block - - type(MPAS_pool_type), pointer :: & - meshPool, & - velocityWeakPool, & - velocitySolverPool - - real(kind=RKIND), dimension(:), pointer :: & - uVelocity, & - vVelocity, & - icePressure, & - replacementPressure, & - stressDivergenceU, & - stressDivergenceV, & - strain11, & - strain22, & - strain12, & - stress11, & - stress22, & - stress12, & - latCellRotated, & - latVertexRotated - - real(kind=RKIND), pointer :: & - elasticTimeStep - - logical, pointer :: & - revisedEVP - - integer, dimension(:), pointer :: & - solveStress, & - solveVelocity - - real(kind=RKIND), dimension(:,:,:), pointer :: & - normalVectorPolygon, & - normalVectorTriangle - - block => domain % blocklist - do while (associated(block)) - - call MPAS_pool_get_config(block % configs, "config_revised_evp", revisedEVP) - - call MPAS_pool_get_subpool(block % structs, "mesh", meshPool) - call MPAS_pool_get_subpool(block % structs, "velocity_weak", velocityWeakPool) - call MPAS_pool_get_subpool(block % structs, "velocity_solver", velocitySolverPool) - - call MPAS_pool_get_array(velocitySolverPool, "solveStress", solveStress) - call MPAS_pool_get_array(velocitySolverPool, "solveVelocity", solveVelocity) - call MPAS_pool_get_array(velocitySolverPool, "uVelocity", uVelocity) - call MPAS_pool_get_array(velocitySolverPool, "vVelocity", vVelocity) - call MPAS_pool_get_array(velocitySolverPool, "icePressure", icePressure) - call MPAS_pool_get_array(velocitySolverPool, "elasticTimeStep", elasticTimeStep) - call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceU", stressDivergenceU) - call MPAS_pool_get_array(velocitySolverPool, "stressDivergenceV", stressDivergenceV) - - call MPAS_pool_get_array(velocityWeakPool, "normalVectorPolygon", normalVectorPolygon) - call MPAS_pool_get_array(velocityWeakPool, "normalVectorTriangle", normalVectorTriangle) - call MPAS_pool_get_array(velocityWeakPool, "latCellRotated", latCellRotated) - call MPAS_pool_get_array(velocityWeakPool, "latVertexRotated", latVertexRotated) - call MPAS_pool_get_array(velocityWeakPool, "strain11", strain11) - call MPAS_pool_get_array(velocityWeakPool, "strain22", strain22) - call MPAS_pool_get_array(velocityWeakPool, "strain12", strain12) - call MPAS_pool_get_array(velocityWeakPool, "stress11", stress11) - call MPAS_pool_get_array(velocityWeakPool, "stress22", stress22) - call MPAS_pool_get_array(velocityWeakPool, "stress12", stress12) - call MPAS_pool_get_array(velocityWeakPool, "replacementPressure", replacementPressure) - - call seaice_strain_tensor_weak(& - meshPool, & - strain11, & - strain22, & - strain12, & - uVelocity, & - vVelocity, & - normalVectorPolygon, & - latCellRotated, & - solveStress) - - call seaice_stress_tensor_weak(& - meshPool, & - stress11, & - stress22, & - stress12, & - strain11, & - strain22, & - strain12, & - icePressure, & - replacementPressure, & - solveStress, & - elasticTimeStep, & - revisedEVP) - - call seaice_stress_divergence_weak(& - meshPool, & - stressDivergenceU, & - stressDivergenceV, & - stress11, & - stress22, & - stress12, & - normalVectorTriangle, & - latVertexRotated, & - solveVelocity) - - block => block % next - end do - - end subroutine seaice_internal_stress_weak!}}} - !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_strain_tensor_weak @@ -402,12 +275,16 @@ subroutine seaice_stress_tensor_weak(& icePressure, & replacementPressure, & solveStress, & - dtElastic, & - revisedEVP)!{{{ + dtElastic)!{{{ use seaice_velocity_solver_constitutive_relation, only: & + constitutiveRelationType, & + EVP_CONSTITUTIVE_RELATION, & + REVISED_EVP_CONSTITUTIVE_RELATION, & + LINEAR_CONSTITUTIVE_RELATION, & seaice_evp_constitutive_relation, & - seaice_evp_constitutive_relation_revised + seaice_evp_constitutive_relation_revised, & + seaice_linear_constitutive_relation type(MPAS_pool_type), pointer, intent(in) :: & mesh !< Input: @@ -430,9 +307,6 @@ subroutine seaice_stress_tensor_weak(& real(kind=RKIND), intent(in) :: & dtElastic !< Input: - logical, intent(in) :: & - revisedEVP !< Input: - integer :: & iCell @@ -447,7 +321,7 @@ subroutine seaice_stress_tensor_weak(& call MPAS_pool_get_array(mesh, "areaCell", areaCell) - if (.not. revisedEVP) then + if (constitutiveRelationType == EVP_CONSTITUTIVE_RELATION) then do iCell = 1, nCells @@ -475,7 +349,7 @@ subroutine seaice_stress_tensor_weak(& end do ! iCell - else + else if (constitutiveRelationType == REVISED_EVP_CONSTITUTIVE_RELATION) then do iCell = 1, nCells @@ -502,10 +376,108 @@ subroutine seaice_stress_tensor_weak(& end do ! iCell - endif + else if (constitutiveRelationType == LINEAR_CONSTITUTIVE_RELATION) then + + do iCell = 1, nCells + + if (solveStress(iCell) == 1) then + + call seaice_linear_constitutive_relation(& + stress11(iCell), & + stress22(iCell), & + stress12(iCell), & + strain11(iCell), & + strain22(iCell), & + strain12(iCell)) + + else + + stress11(iCell) = 0.0_RKIND + stress22(iCell) = 0.0_RKIND + stress12(iCell) = 0.0_RKIND + + endif ! solveStress + + enddo ! iCell + + endif ! constitutiveRelationType end subroutine seaice_stress_tensor_weak!}}} +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! seaice_stress_tensor_weak_linear +! +!> \brief +!> \author Adrian K. Turner, LANL +!> \date 2013-2014 +!> \details +!> +! +!----------------------------------------------------------------------- + + subroutine seaice_stress_tensor_weak_linear(& + mesh, & + stress11, & + stress22, & + stress12, & + strain11, & + strain22, & + strain12, & + solveStress)!{{{ + + use seaice_velocity_solver_constitutive_relation, only: & + seaice_linear_constitutive_relation + + type(MPAS_pool_type), pointer, intent(in) :: & + mesh !< Input: + + real(kind=RKIND), dimension(:), intent(out) :: & + stress11, & !< Output: + stress22, & !< Output: + stress12 !< Output: + + real(kind=RKIND), dimension(:), intent(in) :: & + strain11, & !< Input: + strain22, & !< Input: + strain12 !< Input: + + integer, dimension(:), intent(in) :: & + solveStress !< Input: + + integer :: & + iCell + + integer, pointer :: & + nCells + + ! init variables + call MPAS_pool_get_dimension(mesh, "nCells", nCells) + + do iCell = 1, nCells + + if (solveStress(iCell) == 1) then + + call seaice_linear_constitutive_relation(& + stress11(iCell), & + stress22(iCell), & + stress12(iCell), & + strain11(iCell), & + strain22(iCell), & + strain12(iCell)) + + else + + stress11(iCell) = 0.0_RKIND + stress22(iCell) = 0.0_RKIND + stress12(iCell) = 0.0_RKIND + + endif ! solveStress + + enddo ! iCell + + end subroutine seaice_stress_tensor_weak_linear!}}} + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_stress_divergence_weak diff --git a/testing_and_setup/compass/README_landice.md b/testing_and_setup/compass/README_landice.md new file mode 100644 index 0000000000..191e53a07c --- /dev/null +++ b/testing_and_setup/compass/README_landice.md @@ -0,0 +1,53 @@ +# Setting up COMPASS for landice test cases + +## COMPASS conda environment + +To set up and run landice test cases from COMPASS, you will need a conda +environment. First, install Miniconda3 (if miniconda is not already +installed), then create a new conda environment as follows: +``` bash +conda create -n compass_py3.7 -c conda-forge -c xylar python=3.7 geometric_features mpas_tools jigsaw jigsawpy metis pyflann scikit-image basemap pyamg ffmpeg pyqt +``` +Each time you want to work with COMPASS, you will need to run: +``` +conda activate compass_py3.7 +``` + +## Setting config options + +The file `general.config.landice` is a template containing a set of config +options that the COMPASS user must set in order to set up landice test cases. +Make a copy of this file (e.g. `general.config.landice.myEdits`) and set the options as follows. +* Set the appropriate paths in the `[namelists]` and `[streams]` sections +* Set the appropriate paths in the `[executables]` section. The `model` path should be to the compiled `landice_model` file. The other executables can currently be found in the `MPAS-Tools` repository at https://github.com/MPAS-Dev/MPAS-Tools. Eventually they will be moved into the conda package, at which point they will no longer be required here. +* The [paths] section is currently not required. + +## Setting up and running a specific test + +* To see a list of available tests: `./list_testcases.py -o landice` +* To set up a single test case, e.g.: `./setup_testcase.py -n 102 --work_dir ~/Documents/mpas-git/TESTS/ -f general.config.landice.myEdits` + In this example test number 102 will be set up within the `~/Documents/mpas-git/TESTS/` directory using the information you have set up in the `general.config.landice.myEdits` file. Note that within the `--work_dir` directory, a directory tree corresponding to the test will be created (e.g. `dome/2000m/halfar_analytic_test/`), even if a only a single test was requested. See `/setup_testcase.py -h` for detailed usage options. +* Change directory to where the test was created. Run the test with python driver script located there. Note that individual steps can be run manually by cd'ing into the step directory and running the auto-generated python script for that step. + +## Running a regression suite +Regression suites are defined that run a set of tests. Regression suite are defined in the directory `landice/regression_suites/`. +* See `./manage_regression_suite.py -h` for detailed usage options +* A common usage is like: `./manage_regression_suite.py -t landice/regression_suites/combined_integration_test_suite.xml -f general.config.landice.myEdits -c --work_dir ~/Documents/mpas-git/TESTS/TRUSTED` + This will run the regression suite without comparison to an older version of the code. +* To run the regression suite and compare to a previous run of the regression suite, the command can be called with the `-b` baseline option like: `./manage_regression_suite.py -t landice/regression_suites/combined_integration_test_suite.xml -f general.config.landice.myEdits -c -b ~/Documents/mpas-git/TESTS/TRUSTED --work_dir ~/Documents/mpas-git/TESTS/TESTING` + +## Adding a new test +See `testing_and_setup/compass/README` and `testing_and_setup/compass/docs/*` for detailed information about the COMPASS system. +In general, COMPASS is organized like: `testing_and_setup/compass////` +* `` is `landice`. +* `` is the parent directory for a test case configuration, such as the Halfar dome or Antarctica. This directory level holds general purpose files for this configuration, such as initial condition setup scripts, pre/post-processing analysis scripts, namelist/streams template files, `albany_input.yaml` files, etc. +* `` is the grid resolution for a given configuration. Note that in some cases this directory level is used for other purposes than resolution. +* `` is the specific type of test for a given configuration and resolution, e.g., a smoke test, a restart test, an analytic-solution test. Note that in some cases this directory level is used for other purposes than a specific type of test. + +To add a new test: +1. Create a new test `` directory and corresponding `` and `` levels in the `compass/landice` directory. +2. Add a namelist/streams template file, `albany_input.yaml` file, and any required pre/post-processing scripts (e.g. initial conditions, comparison to analytic solution) to the `` directory. +3. Add `config_*_step.xml` files in the `` for each step of the test required. Each step is run in a separate directory. A common layout is to create the mesh in one direcory with a `config_setup_mesh_step.xml` file and for the test to be run in a second directory with a `config_run_model_step.xml` file. +4. Add a `config_driver.xml` file to the ``. This xml file references the individual step files. +5. Try out and debug your new test using the `setup_testcase.py` command described above. +6. A good example to use for setting up a new test is `testing_and_setup/compass/landice/dome/2000m/halfar_analytic_test`. diff --git a/testing_and_setup/compass/general.config.landice b/testing_and_setup/compass/general.config.landice index 9c53cdca7e..a07da3100e 100644 --- a/testing_and_setup/compass/general.config.landice +++ b/testing_and_setup/compass/general.config.landice @@ -25,11 +25,9 @@ forward = FULL_PATH_TO_FORWARD_TEMPLATE_STREAMS # anywhere on the machine. [executables] model = FULL_PATH_TO_MODEL -mesh_converter = FULL_PATH_TO_MESH_CONVERTER -cell_culler = FULL_PATH_TO_CELL_CULLER -metis = FULL_PATH_TO_METIS -grid_to_li_grid = FULL_PATH_TO_create_landice_grid_from_generic_MPAS_grid.py -periodic_hex = FULL_PATH_TO_PERIODIC_HEX +grid_to_li_grid = FULL_PATH_TO/MPAS-Tools/landice/mesh_tools_li/create_landice_grid_from_generic_MPAS_grid.py +define_cull_mask = FULL_PATH_TO/MPAS-Tools/landice/mesh_tools_li/define_cullMask.py +interpolate_to_mpasli_grid = FULL_PATH_TO/MPAS-Tools/landice/mesh_tools_li/interpolate_to_mpasli_grid.py # The paths section describes paths that are used within the landice core test diff --git a/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml index eb4d2c0773..a1504c8953 100644 --- a/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml +++ b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml @@ -3,9 +3,8 @@ - + - @@ -21,7 +20,7 @@ - + 4 diff --git a/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_setup_mesh_step.xml b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_setup_mesh_step.xml index cf6f4554cd..15aaec9de3 100644 --- a/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_setup_mesh_step.xml +++ b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_setup_mesh_step.xml @@ -2,39 +2,35 @@ - - - - - - - + + 32 + 38 + 50000.0 + + - - + grid.nc - mpas_grid.nc - - - - mpas_grid.nc + culled.nc - - mpas_grid.nc + + culled.nc + mpas_grid.nc + - - culled.nc + + mpas_grid.nc landice_grid.nc 10 diff --git a/testing_and_setup/compass/landice/EISMINT1/check_output_eismint-mm1.py b/testing_and_setup/compass/landice/EISMINT1/check_output_eismint-mm1.py index 131b5b187c..5bc995b1c1 100755 --- a/testing_and_setup/compass/landice/EISMINT1/check_output_eismint-mm1.py +++ b/testing_and_setup/compass/landice/EISMINT1/check_output_eismint-mm1.py @@ -1,14 +1,14 @@ #!/usr/bin/env python -# A script to compare MPAS model output to the EISMINT Moving Margin 1 test case. -# Matt Hoffman, LANL, September 2013 +""" +A script to compare MPAS model output to the EISMINT Moving Margin 1 test case. +Matt Hoffman, LANL, September 2013 +""" + +from __future__ import absolute_import, division, print_function, unicode_literals import sys import datetime -try: - import netCDF4 -except ImportError: - print 'Unable to import netCDF4 python modules:' - sys.exit +import netCDF4 from optparse import OptionParser import numpy as np import matplotlib.pyplot as plt @@ -22,12 +22,12 @@ options, args = parser.parse_args() if not options.filename: options.filename = 'output.nc' - print 'No file specified. Attempting to use output.nc' + print('No file specified. Attempting to use output.nc') if options.t: timelev = int(options.t) else: timelev = -1 - print 'No time level specified. Attempting to use final time.' + print('No time level specified. Attempting to use final time.') ################### DEFINE FUNCTIONS ###################### @@ -63,36 +63,36 @@ def xtime2numtime(xtime): #numtime = xtime2numtime(xtime) # Find out what the ice density and flowA values for this run were. -print '\nCollecting parameter values from the output file.' +print('\nCollecting parameter values from the output file.') flowA = filein.config_default_flowParamA -print 'Using a flowParamA value of: ' + str(flowA) +print('Using a flowParamA value of: ' + str(flowA)) flow_n = filein.config_flowLawExponent -print 'Using a flowLawExponent value of: ' + str(flow_n) +print('Using a flowLawExponent value of: ' + str(flow_n)) rhoi = filein.config_ice_density -print 'Using an ice density value of: ' + str(rhoi) +print('Using an ice density value of: ' + str(rhoi)) dynamicThickness = filein.config_dynamic_thickness -print 'Dynamic thickness for this run = ' + str(dynamicThickness) +print('Dynamic thickness for this run = ' + str(dynamicThickness)) -print 'Using model time of ' + xtime[timelev,:].tostring().strip() + '\n' +print('Using model time of ' + xtime[timelev,:].tostring().strip() + '\n') # find divide ind = np.where(thk == thk.max()) # Print some stats about the error -print '====================================' -print 'Max modeled thickness (m) = ' + str( thk.max() ) -print 'EISMINT models ice thickness at divide (m):' -print ' 3d models (10 of them): 2978.0 +/- 19.3' -print ' 2d models (3 of them): 2982.2 +/- 26.4' -print '====================================' -print '' -print '====================================' -print 'Basal homologous temperature at divide (deg C) = {}'.format(basalTemp[ind][0]-basalPmpTemp[ind][0]) -print 'EISMINT models basal temperature at divide (m):' -print ' 3d models (6 of them): -13.34 +/- 0.56' -print '====================================' -print '' +print('====================================') +print('Max modeled thickness (m) = ' + str( thk.max() )) +print('EISMINT models ice thickness at divide (m):') +print(' 3d models (10 of them): 2978.0 +/- 19.3') +print(' 2d models (3 of them): 2982.2 +/- 26.4') +print('====================================') +print('') +print('====================================') +print('Basal homologous temperature at divide (deg C) = {}'.format(basalTemp[ind][0]-basalPmpTemp[ind][0])) +print('EISMINT models basal temperature at divide (m):') +print(' 3d models (6 of them): -13.34 +/- 0.56') +print('====================================') +print('') # Plot the results @@ -103,7 +103,7 @@ def xtime2numtime(xtime): plt.scatter(xCell,yCell,markersize,thk[:], marker='h', edgecolors='none') plt.colorbar() plt.axis('equal') -plt.title('Modeled thickness (m) \n at time ' + netCDF4.chartostring(xtime)[timelev].strip() ) +plt.title('Modeled thickness (m) \n at time ' + netCDF4.chartostring(xtime)[timelev].strip() ) plt.draw() @@ -111,12 +111,12 @@ def xtime2numtime(xtime): if options.saveimage: plotname = 'halfar-results.png' plt.savefig(plotname, dpi=150) - print 'Saved plot as ' + plotname + print('Saved plot as ' + plotname) if options.hidefigs: - print "Plot display disabled with -n argument." + print("Plot display disabled with -n argument.") else: - print 'Showing plot... Close plot window to exit.' + print('Showing plot... Close plot window to exit.') plt.show() diff --git a/testing_and_setup/compass/landice/EISMINT1/setup_initial_conditions_EISMINT1-MovingMargin-1.py b/testing_and_setup/compass/landice/EISMINT1/setup_initial_conditions_EISMINT1-MovingMargin-1.py index 46ec12f35a..1d1147cd72 100755 --- a/testing_and_setup/compass/landice/EISMINT1/setup_initial_conditions_EISMINT1-MovingMargin-1.py +++ b/testing_and_setup/compass/landice/EISMINT1/setup_initial_conditions_EISMINT1-MovingMargin-1.py @@ -1,5 +1,9 @@ #!/usr/bin/env python -# Generate initial conditions for EISMINT-1 moving margin land ice test case +""" +Generate initial conditions for EISMINT-1 moving margin land ice test case +""" + +from __future__ import absolute_import, division, print_function, unicode_literals import sys, numpy from netCDF4 import Dataset as NetCDFFile @@ -14,7 +18,7 @@ if not options.filename: options.filename = 'landice_grid.nc' - print 'No file specified. Attempting to use landice_grid.nc' + print('No file specified. Attempting to use landice_grid.nc') # Open the file, get needed dimensions @@ -90,5 +94,5 @@ gridfile.close() -print 'Successfully added initial conditions for EISMINT1-Moving Margin, experiment 1 to the file: ', options.filename +print('Successfully added initial conditions for EISMINT1-Moving Margin, experiment 1 to the file: '+options.filename) diff --git a/testing_and_setup/compass/landice/EISMINT2/25000m/EISMINT2_25000m_template.xml b/testing_and_setup/compass/landice/EISMINT2/25000m/EISMINT2_25000m_template.xml index f47c9c5ba9..3dd8d21a9e 100644 --- a/testing_and_setup/compass/landice/EISMINT2/25000m/EISMINT2_25000m_template.xml +++ b/testing_and_setup/compass/landice/EISMINT2/25000m/EISMINT2_25000m_template.xml @@ -12,12 +12,18 @@ + + + + + + @@ -53,6 +59,23 @@ + + output + globalStats.nc + 0000-00-01_00:00:00 + 0000-01-01_00:00:00 + overwrite + + + + + + + + + + + input;output restart.$Y.nc diff --git a/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_1p.xml b/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_1p.xml index c3766d6825..3c392081b7 100644 --- a/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_1p.xml +++ b/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_1p.xml @@ -3,7 +3,7 @@ - + diff --git a/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_4p.xml b/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_4p.xml index 9569b87e6c..0a2fdbaff3 100644 --- a/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_4p.xml +++ b/testing_and_setup/compass/landice/EISMINT2/25000m/decomposition_test/config_experiment_F_4p.xml @@ -3,7 +3,7 @@ - + diff --git a/testing_and_setup/compass/landice/EISMINT2/25000m/enthalpy_decomposition_test/config_driver.xml b/testing_and_setup/compass/landice/EISMINT2/25000m/enthalpy_decomposition_test/config_driver.xml new file mode 100644 index 0000000000..571b0e4223 --- /dev/null +++ b/testing_and_setup/compass/landice/EISMINT2/25000m/enthalpy_decomposition_test/config_driver.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + +