From 5f7fbe49d5414013fd36d3f0a3ce0b653d4102a6 Mon Sep 17 00:00:00 2001 From: Peter Thornton Date: Thu, 23 May 2024 19:16:56 -0400 Subject: [PATCH 1/8] Adds a lower limit to stem allocation to avoid negative states The stem:leaf allocation variable can go negative without this limiter, which can cause negative stem carbon pools. --- components/elm/src/biogeochem/AllocationMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/elm/src/biogeochem/AllocationMod.F90 b/components/elm/src/biogeochem/AllocationMod.F90 index 2795fd42c8ed..001ad75d42cc 100644 --- a/components/elm/src/biogeochem/AllocationMod.F90 +++ b/components/elm/src/biogeochem/AllocationMod.F90 @@ -645,7 +645,7 @@ subroutine Allocation1_PlantNPDemand (bounds, num_soilc, filter_soilc, num_soilp if (stem_leaf(ivt(p)) < 0._r8) then if (stem_leaf(ivt(p)) == -1._r8) then - f3 = (2.7/(1.0+exp(-0.004*(annsum_npp(p) - 300.0)))) - 0.4 + f3 = max((2.7/(1.0+exp(-0.004*(annsum_npp(p) - 300.0)))) - 0.4_r8, 0.2_r8) else f3 = max((-1.0_r8*stem_leaf(ivt(p))*2.7_r8)/(1.0_r8+exp(-0.004_r8*(annsum_npp(p) - & 300.0_r8))) - 0.4_r8, 0.2_r8) @@ -2118,7 +2118,7 @@ subroutine Allocation3_PlantCNPAlloc (bounds , & if (stem_leaf(ivt(p)) < 0._r8) then if (stem_leaf(ivt(p)) == -1._r8) then - f3 = (2.7/(1.0+exp(-0.004*(annsum_npp(p) - 300.0)))) - 0.4 + f3 = max((2.7/(1.0+exp(-0.004*(annsum_npp(p) - 300.0)))) - 0.4_r8, 0.2_r8) else f3 = max((-1.0_r8*stem_leaf(ivt(p))*2.7_r8)/(1.0_r8+exp(-0.004_r8*(annsum_npp(p) - & 300.0_r8))) - 0.4_r8, 0.2_r8) From 4bc46ebbfe35f0cd39b687afba1e5f82990035e6 Mon Sep 17 00:00:00 2001 From: Matthew Hoffman Date: Thu, 31 Oct 2024 09:09:03 -0700 Subject: [PATCH 2/8] Reset alarm for MALI adaptive timestepper force interval in GLC driver This is necessary for the MALI adaptive timestepper to work without error in E3SM. --- components/mpas-albany-landice/driver/glc_comp_mct.F | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/mpas-albany-landice/driver/glc_comp_mct.F b/components/mpas-albany-landice/driver/glc_comp_mct.F index 6a9df4f406fe..a3eb5d179242 100644 --- a/components/mpas-albany-landice/driver/glc_comp_mct.F +++ b/components/mpas-albany-landice/driver/glc_comp_mct.F @@ -557,6 +557,13 @@ end subroutine xml_stream_get_attributes call check_clocks_sync(domain % clock, Eclock, err_tmp) ierr = ior(ierr,err_tmp) + ! Reset the alarm for checking for force setting of the adaptive timestep interval + if (mpas_is_alarm_ringing(domain % clock, 'adaptiveTimestepForceInterval', ierr=err_tmp)) then + ierr = ior(ierr, err_tmp) + call mpas_reset_clock_alarm(domain % clock, 'adaptiveTimestepForceInterval', ierr=err_tmp) + ierr = ior(ierr, err_tmp) + endif + ierr = ior(ierr, err_tmp) !----------------------------------------------------------------------- ! From 80c91e7ef3c03e43ed617116f8bcb48a4e96f4b0 Mon Sep 17 00:00:00 2001 From: Matthew Hoffman Date: Thu, 31 Oct 2024 09:55:53 -0700 Subject: [PATCH 3/8] Enable MALI adaptive timestepper for mali-gis20km test --- .../testdefs/testmods_dirs/mali/gis20km/user_nl_mali | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/mpas-albany-landice/cime_config/testdefs/testmods_dirs/mali/gis20km/user_nl_mali b/components/mpas-albany-landice/cime_config/testdefs/testmods_dirs/mali/gis20km/user_nl_mali index 8b88e2add6c6..4ef8179b7cf3 100644 --- a/components/mpas-albany-landice/cime_config/testdefs/testmods_dirs/mali/gis20km/user_nl_mali +++ b/components/mpas-albany-landice/cime_config/testdefs/testmods_dirs/mali/gis20km/user_nl_mali @@ -1 +1,3 @@ config_am_globalstats_enable = .false. +config_adaptive_timestep = .true. +config_adaptive_timestep_force_interval = '0000-00-01_00:00:00' From 40d5114cc7ebae278cf299f852d2580a8889b4c7 Mon Sep 17 00:00:00 2001 From: Xylar Asay-Davis Date: Tue, 29 Oct 2024 20:20:46 -0500 Subject: [PATCH 4/8] Fix total land-ice freshwater flux in data mode Previously, the total was only being computed when thermodynamics below ice shelves are actively computed, whereas we need to compute the total of the interface flux and the frazil flux when the interface flux comes from a data file as well. While we expect the frazil flux to be zero, these code modifications do not assume or require this to be true. --- .../shared/mpas_ocn_surface_land_ice_fluxes.F | 287 +++++++++--------- 1 file changed, 145 insertions(+), 142 deletions(-) diff --git a/components/mpas-ocean/src/shared/mpas_ocn_surface_land_ice_fluxes.F b/components/mpas-ocean/src/shared/mpas_ocn_surface_land_ice_fluxes.F index c249f202757d..fe489029a139 100644 --- a/components/mpas-ocean/src/shared/mpas_ocn_surface_land_ice_fluxes.F +++ b/components/mpas-ocean/src/shared/mpas_ocn_surface_land_ice_fluxes.F @@ -492,168 +492,179 @@ subroutine ocn_surface_land_ice_fluxes_build_arrays(meshPool, & err = 0 - if (.not.landIceStandaloneOn) return + if (.not. (landIceStandaloneOn .or. landIceDataOn)) return call mpas_timer_start("land_ice_build_arrays") call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) - call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) - - call mpas_pool_get_array(forcingPool, 'landIceFloatingFraction', landIceFloatingFraction) - call mpas_pool_get_array(forcingPool, 'landIceFloatingMask', landIceFloatingMask) - call mpas_pool_get_array(forcingPool, 'landIceFreshwaterFlux', landIceFreshwaterFlux) - call mpas_pool_get_array(forcingPool, 'landIceHeatFlux', landIceHeatFlux) - call mpas_pool_get_array(forcingPool, 'heatFluxToLandIce', heatFluxToLandIce) - call mpas_pool_get_array(forcingPool, 'frazilIceFreshwaterFlux', frazilIceFreshwaterFlux) call mpas_pool_get_array(forcingPool, 'landIceFreshwaterFluxTotal', landIceFreshwaterFluxTotal) - - call mpas_pool_get_array(forcingPool, 'landIceInterfaceTracers', landIceInterfaceTracers) - call mpas_pool_get_dimension(forcingPool, & - 'index_landIceInterfaceTemperature', & - indexITPtr) - call mpas_pool_get_dimension(forcingPool, & - 'index_landIceInterfaceSalinity', & - indexISPtr) - indexIT = indexITPtr - indexIS = indexISPtr - - if (useHollandJenkinsAdvDiff) then - call mpas_pool_get_array(forcingPool, 'landIceSurfaceTemperature', landIceSurfaceTemperature) - - allocate(freezeInterfaceSalinity(nCells), & - freezeInterfaceTemperature(nCells), & - freezeFreshwaterFlux(nCells), & - freezeHeatFlux(nCells), & - freezeIceHeatFlux(nCells)) - end if + call mpas_pool_get_array(forcingPool, 'landIceFloatingMask', landIceFloatingMask) nCells = nCellsArray( size(nCellsArray) ) - if (isomipOn) then !*** ISOMIP formulation + if (landIceStandaloneOn) then - !$omp parallel - !$omp do schedule(runtime) private(freshwaterFlux, heatFlux) - do iCell = 1, nCells - if (landIceFloatingMask(iCell) == 0) cycle + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + call mpas_pool_get_array(forcingPool, 'landIceFloatingFraction', landIceFloatingFraction) + call mpas_pool_get_array(forcingPool, 'landIceHeatFlux', landIceHeatFlux) + call mpas_pool_get_array(forcingPool, 'heatFluxToLandIce', heatFluxToLandIce) + + + call mpas_pool_get_array(forcingPool, 'landIceInterfaceTracers', landIceInterfaceTracers) + call mpas_pool_get_dimension(forcingPool, & + 'index_landIceInterfaceTemperature', & + indexITPtr) + call mpas_pool_get_dimension(forcingPool, & + 'index_landIceInterfaceSalinity', & + indexISPtr) + indexIT = indexITPtr + indexIS = indexISPtr + + if (useHollandJenkinsAdvDiff) then + call mpas_pool_get_array(forcingPool, 'landIceSurfaceTemperature', landIceSurfaceTemperature) + + allocate(freezeInterfaceSalinity(nCells), & + freezeInterfaceTemperature(nCells), & + freezeFreshwaterFlux(nCells), & + freezeHeatFlux(nCells), & + freezeIceHeatFlux(nCells)) + end if - ! linearized equaiton for the S and p dependent potential freezing temperature - landIceInterfaceTracers(indexIT,iCell) = ocn_freezing_temperature( & - salinity=landIceBoundaryLayerTracers(indexBLT,iCell), & - pressure=landIcePressure(iCell), & - inLandIceCavity=.true.) + if (isomipOn) then !*** ISOMIP formulation - ! using (3) and (4) from Hunter (2006) - ! or (7) from Jenkins et al. (2001) if gamma constant - ! and no heat flux into ice - ! freshwater flux = density * melt rate is in kg/m^2/s - freshwaterFlux = -rho_sw * ISOMIPgammaT * (cp_sw/latent_heat_fusion_mks) & - * (landIceInterfaceTracers(indexIT,iCell)-landIceBoundaryLayerTracers(indexBLT,iCell)) + !$omp parallel + !$omp do schedule(runtime) private(freshwaterFlux, heatFlux) + do iCell = 1, nCells + if (landIceFloatingMask(iCell) == 0) cycle - landIceFreshwaterFlux(iCell) = landIceFloatingFraction(iCell)*freshwaterFlux + ! linearized equaiton for the S and p dependent potential freezing temperature + landIceInterfaceTracers(indexIT,iCell) = ocn_freezing_temperature( & + salinity=landIceBoundaryLayerTracers(indexBLT,iCell), & + pressure=landIcePressure(iCell), & + inLandIceCavity=.true.) - ! Using (13) from Jenkins et al. (2001) - ! heat flux is in W/s - heatFlux = cp_sw*(freshwaterFlux*landIceInterfaceTracers(indexIT,iCell) & - + rho_sw*ISOMIPgammaT & - * (landIceInterfaceTracers(indexIT,iCell)-landIceBoundaryLayerTracers(indexBLT,iCell))) - landIceHeatFlux(iCell) = landIceFloatingFraction(iCell)*heatFlux + ! using (3) and (4) from Hunter (2006) + ! or (7) from Jenkins et al. (2001) if gamma constant + ! and no heat flux into ice + ! freshwater flux = density * melt rate is in kg/m^2/s + freshwaterFlux = -rho_sw * ISOMIPgammaT * (cp_sw/latent_heat_fusion_mks) & + * (landIceInterfaceTracers(indexIT,iCell)-landIceBoundaryLayerTracers(indexBLT,iCell)) - heatFluxToLandIce(iCell) = 0.0_RKIND + landIceFreshwaterFlux(iCell) = landIceFloatingFraction(iCell)*freshwaterFlux - end do - !$omp end do - !$omp end parallel - endif ! isomipOn + ! Using (13) from Jenkins et al. (2001) + ! heat flux is in W/s + heatFlux = cp_sw*(freshwaterFlux*landIceInterfaceTracers(indexIT,iCell) & + + rho_sw*ISOMIPgammaT & + * (landIceInterfaceTracers(indexIT,iCell)-landIceBoundaryLayerTracers(indexBLT,iCell))) + landIceHeatFlux(iCell) = landIceFloatingFraction(iCell)*heatFlux - if (jenkinsOn .or. hollandJenkinsOn) then - if(useHollandJenkinsAdvDiff) then - ! melting solution - call compute_HJ99_melt_fluxes( & - landIceFloatingMask, & - landIceBoundaryLayerTracers(indexBLT,:), & - landIceBoundaryLayerTracers(indexBLS,:), & - landIceTracerTransferVelocities(indexHeatTrans,:), & - landIceTracerTransferVelocities(indexSaltTrans,:), & - landIceSurfaceTemperature, & - landIcePressure, & - landIceInterfaceTracers(indexIT,:), & - landIceInterfaceTracers(indexIS,:), & - landIceFreshwaterFlux, & - landIceHeatFlux, & - heatFluxToLandIce, & - nCells, & - err) - if(err .ne. 0) then - call mpas_log_write( & - 'compute_HJ99_melt_fluxes failed.', & - MPAS_LOG_CRIT) - end if + heatFluxToLandIce(iCell) = 0.0_RKIND - ! freezing solution - call compute_melt_fluxes( & - landIceFloatingMask, & - landIceBoundaryLayerTracers(indexBLT,:), & - landIceBoundaryLayerTracers(indexBLS,:), & - landIceTracerTransferVelocities(indexHeatTrans,:), & - landIceTracerTransferVelocities(indexSaltTrans,:), & - landIcePressure, & - freezeInterfaceTemperature, & - freezeInterfaceSalinity, & - freezeFreshwaterFlux, & - freezeHeatFlux, & - freezeIceHeatFlux, & - nCells, & - err) - if(err .ne. 0) then - call mpas_log_write( & - 'compute_melt_fluxes failed.', & - MPAS_LOG_CRIT) + end do + !$omp end do + !$omp end parallel + end if ! isomipOn + + if (jenkinsOn .or. hollandJenkinsOn) then + if(useHollandJenkinsAdvDiff) then + ! melting solution + call compute_HJ99_melt_fluxes( & + landIceFloatingMask, & + landIceBoundaryLayerTracers(indexBLT,:), & + landIceBoundaryLayerTracers(indexBLS,:), & + landIceTracerTransferVelocities(indexHeatTrans,:), & + landIceTracerTransferVelocities(indexSaltTrans,:), & + landIceSurfaceTemperature, & + landIcePressure, & + landIceInterfaceTracers(indexIT,:), & + landIceInterfaceTracers(indexIS,:), & + landIceFreshwaterFlux, & + landIceHeatFlux, & + heatFluxToLandIce, & + nCells, & + err) + if(err .ne. 0) then + call mpas_log_write( & + 'compute_HJ99_melt_fluxes failed.', & + MPAS_LOG_CRIT) + end if + + ! freezing solution + call compute_melt_fluxes( & + landIceFloatingMask, & + landIceBoundaryLayerTracers(indexBLT,:), & + landIceBoundaryLayerTracers(indexBLS,:), & + landIceTracerTransferVelocities(indexHeatTrans,:), & + landIceTracerTransferVelocities(indexSaltTrans,:), & + landIcePressure, & + freezeInterfaceTemperature, & + freezeInterfaceSalinity, & + freezeFreshwaterFlux, & + freezeHeatFlux, & + freezeIceHeatFlux, & + nCells, & + err) + if(err .ne. 0) then + call mpas_log_write( & + 'compute_melt_fluxes failed.', & + MPAS_LOG_CRIT) + end if + + do iCell = 1, nCells + if ((landIceFloatingMask(iCell) == 0) .or. (landIceFreshwaterFlux(iCell) >= 0.0_RKIND)) cycle + + landIceInterfaceTracers(indexIS,iCell) = freezeInterfaceSalinity(iCell) + landIceInterfaceTracers(indexIT,iCell) = freezeInterfaceTemperature(iCell) + landIceFreshwaterFlux(iCell) = freezeFreshwaterFlux(iCell) + landIceHeatFlux(iCell) = freezeHeatFlux(iCell) + heatFluxToLandIce(iCell) = freezeIceHeatFlux(iCell) + end do + else ! not using Holland and Jenkins advection/diffusion + call compute_melt_fluxes( & + landIceFloatingMask, & + landIceBoundaryLayerTracers(indexBLT,:), & + landIceBoundaryLayerTracers(indexBLS,:), & + landIceTracerTransferVelocities(indexHeatTrans,:), & + landIceTracerTransferVelocities(indexSaltTrans,:), & + landIcePressure, & + landIceInterfaceTracers(indexIT,:), & + landIceInterfaceTracers(indexIS,:), & + landIceFreshwaterFlux, & + landIceHeatFlux, & + heatFluxToLandIce, & + nCells, & + err) + if(err .ne. 0) then + call mpas_log_write( & + 'compute_melt_fluxes failed.', & + MPAS_LOG_CRIT) + end if end if + ! modulate the fluxes by the landIceFloatingFraction do iCell = 1, nCells - if ((landIceFloatingMask(iCell) == 0) .or. (landIceFreshwaterFlux(iCell) >= 0.0_RKIND)) cycle + if (landIceFloatingMask(iCell) == 0) cycle - landIceInterfaceTracers(indexIS,iCell) = freezeInterfaceSalinity(iCell) - landIceInterfaceTracers(indexIT,iCell) = freezeInterfaceTemperature(iCell) - landIceFreshwaterFlux(iCell) = freezeFreshwaterFlux(iCell) - landIceHeatFlux(iCell) = freezeHeatFlux(iCell) - heatFluxToLandIce(iCell) = freezeIceHeatFlux(iCell) + landIceFreshwaterFlux(iCell) = landIceFloatingFraction(iCell)*landIceFreshwaterFlux(iCell) + landIceHeatFlux(iCell) = landIceFloatingFraction(iCell)*landIceHeatFlux(iCell) + heatFluxToLandIce(iCell) = landIceFloatingFraction(iCell)*heatFluxToLandIce(iCell) end do - else ! not using Holland and Jenkins advection/diffusion - call compute_melt_fluxes( & - landIceFloatingMask, & - landIceBoundaryLayerTracers(indexBLT,:), & - landIceBoundaryLayerTracers(indexBLS,:), & - landIceTracerTransferVelocities(indexHeatTrans,:), & - landIceTracerTransferVelocities(indexSaltTrans,:), & - landIcePressure, & - landIceInterfaceTracers(indexIT,:), & - landIceInterfaceTracers(indexIS,:), & - landIceFreshwaterFlux, & - landIceHeatFlux, & - heatFluxToLandIce, & - nCells, & - err) - if(err .ne. 0) then - call mpas_log_write( & - 'compute_melt_fluxes failed.', & - MPAS_LOG_CRIT) - end if - end if - ! modulate the fluxes by the landIceFloatingFraction - do iCell = 1, nCells - if (landIceFloatingMask(iCell) == 0) cycle + end if ! jenkinsOn or hollandJenkinsOn - landIceFreshwaterFlux(iCell) = landIceFloatingFraction(iCell)*landIceFreshwaterFlux(iCell) - landIceHeatFlux(iCell) = landIceFloatingFraction(iCell)*landIceHeatFlux(iCell) - heatFluxToLandIce(iCell) = landIceFloatingFraction(iCell)*heatFluxToLandIce(iCell) - end do + if(useHollandJenkinsAdvDiff) then + deallocate(freezeInterfaceSalinity, & + freezeInterfaceTemperature, & + freezeFreshwaterFlux, & + freezeHeatFlux, & + freezeIceHeatFlux) + end if - endif ! jenkinsOn or hollandJenkinsOn + end if ! landIceStandaloneOn ! Add frazil and interface melt/freeze to get total fresh water flux if ( associated(frazilIceFreshwaterFlux) ) then @@ -666,14 +677,6 @@ subroutine ocn_surface_land_ice_fluxes_build_arrays(meshPool, & end do end if - if(useHollandJenkinsAdvDiff) then - deallocate(freezeInterfaceSalinity, & - freezeInterfaceTemperature, & - freezeFreshwaterFlux, & - freezeHeatFlux, & - freezeIceHeatFlux) - end if - call mpas_timer_stop("land_ice_build_arrays") !-------------------------------------------------------------------- From 8c66ca6aec80d8a4da4ac397d587274fd66123fb Mon Sep 17 00:00:00 2001 From: Luke Van Roekel Date: Sun, 10 Nov 2024 23:14:53 -0600 Subject: [PATCH 5/8] Adds brine rejection parameterization v1 code builds --- components/mpas-ocean/src/shared/Makefile | 2 +- .../shared/mpas_ocn_surface_bulk_forcing.F | 32 +++--- .../mpas-ocean/src/shared/mpas_ocn_tendency.F | 14 ++- .../src/shared/mpas_ocn_tracer_nonlocalflux.F | 98 ++++++++++++++++--- 4 files changed, 114 insertions(+), 32 deletions(-) diff --git a/components/mpas-ocean/src/shared/Makefile b/components/mpas-ocean/src/shared/Makefile index d378b68624bb..e0897b17822e 100644 --- a/components/mpas-ocean/src/shared/Makefile +++ b/components/mpas-ocean/src/shared/Makefile @@ -149,7 +149,7 @@ mpas_ocn_tracer_hmix_redi.o: mpas_ocn_constants.o mpas_ocn_config.o mpas_ocn_high_freq_thickness_hmix_del2.o: mpas_ocn_constants.o mpas_ocn_config.o mpas_ocn_mesh.o -mpas_ocn_tracer_nonlocalflux.o: mpas_ocn_constants.o mpas_ocn_config.o +mpas_ocn_tracer_nonlocalflux.o: mpas_ocn_constants.o mpas_ocn_config.o mpas_ocn_diagnostics_variables.o mpas_ocn_mesh.o mpas_ocn_tracer_short_wave_absorption.o: mpas_ocn_tracer_short_wave_absorption_jerlov.o mpas_ocn_tracer_short_wave_absorption_variable.o mpas_ocn_constants.o mpas_ocn_config.o diff --git a/components/mpas-ocean/src/shared/mpas_ocn_surface_bulk_forcing.F b/components/mpas-ocean/src/shared/mpas_ocn_surface_bulk_forcing.F index 11d70ae9c40d..5dedb9b497d5 100644 --- a/components/mpas-ocean/src/shared/mpas_ocn_surface_bulk_forcing.F +++ b/components/mpas-ocean/src/shared/mpas_ocn_surface_bulk_forcing.F @@ -668,22 +668,24 @@ subroutine ocn_surface_bulk_forcing_active_tracers(meshPool, forcingPool, tracer - (snowFlux(iCell) + iceRunoffFlux(iCell)) & * latent_heat_fusion_mks) * hflux_factor - ! Negative seaIceSalinityFlux is an extraction of salt from the ocean - ! So, we negate seaIceSalinityFlux when determining how much salt this flux needs. - requiredSalt = - seaIceSalinityFlux(iCell) * sflux_factor * dt / layerThickness(minLevelCell(iCell), iCell) - allowedSalt = min( 4.0_RKIND, tracerGroup(index_salinity_flux, minLevelCell(iCell), iCell) ) - - if ( allowedSalt < requiredSalt ) then - tracersSurfaceFluxRemoved(index_salinity_flux, iCell) = tracersSurfaceFluxRemoved(index_salinity_flux, iCell) & - + ( 1 - allowedSalt / requiredSalt ) * seaIceSalinityFlux(iCell) & - * sflux_factor - - tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & - + ( allowedSalt / requiredSalt ) * seaIceSalinityFlux(iCell) & - * sflux_factor - else - tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & + if (.not. config_use_brine_rejection_parameterization) then + ! Negative seaIceSalinityFlux is an extraction of salt from the ocean + ! So, we negate seaIceSalinityFlux when determining how much salt this flux needs. + requiredSalt = - seaIceSalinityFlux(iCell) * sflux_factor * dt / layerThickness(minLevelCell(iCell), iCell) + allowedSalt = min( 4.0_RKIND, tracerGroup(index_salinity_flux, minLevelCell(iCell), iCell) ) + + if ( allowedSalt < requiredSalt ) then + tracersSurfaceFluxRemoved(index_salinity_flux, iCell) = tracersSurfaceFluxRemoved(index_salinity_flux, iCell) & + + ( 1 - allowedSalt / requiredSalt ) * seaIceSalinityFlux(iCell) & + * sflux_factor + + tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & + + ( allowedSalt / requiredSalt ) * seaIceSalinityFlux(iCell) & + * sflux_factor + else + tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & + seaIceSalinityFlux(iCell) * sflux_factor + end if end if end do !$omp end do diff --git a/components/mpas-ocean/src/shared/mpas_ocn_tendency.F b/components/mpas-ocean/src/shared/mpas_ocn_tendency.F index 182fca7dbb96..5bc6e8cfaeee 100644 --- a/components/mpas-ocean/src/shared/mpas_ocn_tendency.F +++ b/components/mpas-ocean/src/shared/mpas_ocn_tendency.F @@ -128,6 +128,7 @@ subroutine ocn_tend_thick(tendPool, forcingPool)!{{{ ! pointers for the actual output variables within the pools above real (kind=RKIND), dimension(:), pointer, contiguous :: & surfaceThicknessFlux, &! surface thickness flux + seaIceSalinityFlux, & surfaceThicknessFluxRunoff, &! surface thickness flux from runoff surfaceThicknessFluxSubglacialRunoff ! surface thickness flux from runoff @@ -627,6 +628,7 @@ subroutine ocn_tend_tracer(tendPool, statePool, forcingPool, & real (kind=RKIND), dimension(:), pointer, contiguous :: & penetrativeTemperatureFlux, &! heat flux penetrating below sfc + seaIceSalinityFlux, & tracerGroupExponentialDecayRate ! exp decay rate for forcing real (kind=RKIND), dimension(:,:), pointer, contiguous :: & @@ -702,6 +704,8 @@ subroutine ocn_tend_tracer(tendPool, statePool, forcingPool, & penetrativeTemperatureFlux) call mpas_pool_get_array(forcingPool, 'fractionAbsorbed', & fractionAbsorbed) + call mpas_pool_get_array(forcingPool, 'seaIceSalinityFlux', & + seaIceSalinityFlux) call mpas_pool_get_array(forcingPool, 'fractionAbsorbedRunoff', & fractionAbsorbedRunoff) call mpas_pool_get_array(forcingPool, 'fractionAbsorbedSubglacialRunoff', & @@ -1339,15 +1343,21 @@ subroutine ocn_tend_tracer(tendPool, statePool, forcingPool, & endif ! compute budgets if (isActiveTracer) then - call ocn_tracer_nonlocalflux_tend(meshPool, & + call ocn_tracer_nonlocalflux_tend( & vertNonLocalFlux, & nonLocalSurfaceTracerFlux, & tracerGroupTend, err) else ! not active - call ocn_tracer_nonlocalflux_tend(meshPool, & + call ocn_tracer_nonlocalflux_tend( & vertNonLocalFlux, & tracerGroupSurfaceFlux, & tracerGroupTend, err) + if (config_use_brine_rejection_parameterization) then + call ocn_brine_rejection_tendency(tracerGroupTend, & + layerThickness, & + seaIceSalinityFlux, & + indexSalinity, dt, err) + end if endif ! active tracer if (computeBudgets) then diff --git a/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F b/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F index 9de12d274019..4c358750660e 100644 --- a/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F +++ b/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F @@ -26,6 +26,8 @@ module ocn_tracer_nonlocalflux use mpas_pool_routines use ocn_constants use ocn_config + use ocn_diagnostics_variables + use ocn_mesh implicit none private @@ -44,7 +46,8 @@ module ocn_tracer_nonlocalflux !-------------------------------------------------------------------- public :: ocn_tracer_nonlocalflux_tend, & - ocn_tracer_nonlocalflux_init + ocn_tracer_nonlocalflux_init, & + ocn_brine_rejection_tendency !-------------------------------------------------------------------- ! @@ -58,6 +61,84 @@ module ocn_tracer_nonlocalflux contains +!*********************************************************************** +! +! routine ocn_brine_rejction_tend +! +!> \brief Computes tendency term due to brine rejection +!> \author Luke Van Roekel +!> \date 11/08/24 +!> \details +!> This routine computes the tendency for salinity from brine rejection, based on +!> Nguyen et al 2009 +! +!----------------------------------------------------------------------- + + subroutine ocn_brine_rejection_tendency(tend, layerThickness, seaIceSalinityFlux, index_salinity, dt, err) + !---------------------------------------------------------------- + ! + ! variables + ! + !---------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tend !< tracer tendency, only will modify the salinity tend here + + real (kind=RKIND), dimension(:), intent(in) :: & + seaIceSalinityFlux + + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness + + real, dimension(:), allocatable :: sis_flux + + real (kind=RKIND) :: dt + + integer, intent(in) :: index_salinity + + integer :: err + + !---------------------------------------------------------------- + ! + ! local variables + ! + !---------------------------------------------------------------- + + integer :: iCell, k, nCells + + real (kind=RKIND) :: remainder, z, integral, D, A, rejectedSalt + + nCells = nCellsHalo(1) + + allocate(sis_flux(nVertLevels+1)) + + do iCell = 1, nCells + if (seaIceSalinityFlux(iCell) > 0) then !only compute for salinity into ocean + sis_flux(:) = 0.0_RKIND + D = boundaryLayerDepth(iCell) + z = 0.0_RKIND + A = (config_brine_param_n + 1) / D**(config_brine_param_n+1) + k = minLevelCell(iCell) + ! need a salinity flux top and bottom and the flux into a cell is the divergence?? + do while (z < D) + sis_flux(k) = A*z**config_brine_param_n*seaIceSalinityFlux(iCell) + z = z + layerThickness(k,iCell) + k = k + 1 + end do + ! need to pick up the last bit that goes over + sis_flux(k) = 1.0_RKIND ! this should finish + + do k=minLevelCell(iCell),maxLevelCell(iCell) + ! need to flip over so we take k+1 - k + tend(index_salinity, k, iCell) = tend(index_salinity, k, iCell) + & + sis_flux(k+1) - sis_flux(k) + end do + end if + end do + + deallocate(sis_flux) + end subroutine ocn_brine_rejection_tendency + !*********************************************************************** ! ! routine ocn_tracer_nonlocalflux_tend @@ -70,16 +151,13 @@ module ocn_tracer_nonlocalflux ! !----------------------------------------------------------------------- - subroutine ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTracerFlux, tend, err)!{{{ + subroutine ocn_tracer_nonlocalflux_tend(vertNonLocalFlux, surfaceTracerFlux, tend, err)!{{{ !----------------------------------------------------------------- ! ! input variables ! !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - real (kind=RKIND), dimension(:,:), intent(in) :: & surfaceTracerFlux !< Input: surface tracer fluxes @@ -110,9 +188,6 @@ subroutine ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTrace !----------------------------------------------------------------- integer :: iCell, k, iTracer, nTracers, nCells - integer, pointer :: nVertLevels - integer, dimension(:), pointer :: nCellsArray - integer, dimension(:), pointer :: minLevelCell, maxLevelCell real (kind=RKIND) :: fluxTopOfCell, fluxBottomOfCell err = 0 @@ -121,14 +196,9 @@ subroutine ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTrace call mpas_timer_start('non-local flux') - call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) nTracers = size(tend, dim=1) - call mpas_pool_get_array(meshPool, 'minLevelCell', minLevelCell) - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - - nCells = nCellsArray( 1 ) + nCells = nCellsHalo( 1 ) !$omp parallel !$omp do schedule(runtime) private(k, iTracer, fluxTopOfCell, fluxBottomOfCell) From 5f1558609e1a05e8257b7afa84030314b62845de Mon Sep 17 00:00:00 2001 From: Luke Van Roekel Date: Fri, 22 Nov 2024 16:20:21 -0600 Subject: [PATCH 6/8] updates registry value --- components/mpas-ocean/src/Registry.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/mpas-ocean/src/Registry.xml b/components/mpas-ocean/src/Registry.xml index 2a21e22f40d2..59252664cae8 100644 --- a/components/mpas-ocean/src/Registry.xml +++ b/components/mpas-ocean/src/Registry.xml @@ -463,6 +463,16 @@ possible_values=".true. or .false." /> + + + + Date: Fri, 22 Nov 2024 22:28:29 -0600 Subject: [PATCH 7/8] Fixes flux at BLD base --- .../mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F b/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F index 4c358750660e..55194be6ea9e 100644 --- a/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F +++ b/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F @@ -114,7 +114,8 @@ subroutine ocn_brine_rejection_tendency(tend, layerThickness, seaIceSalinityFlux do iCell = 1, nCells if (seaIceSalinityFlux(iCell) > 0) then !only compute for salinity into ocean - sis_flux(:) = 0.0_RKIND + !this is set to 1 such that across the BLD we don't have a huge salt flux divergence. + sis_flux(:) = 1.0_RKIND D = boundaryLayerDepth(iCell) z = 0.0_RKIND A = (config_brine_param_n + 1) / D**(config_brine_param_n+1) @@ -127,7 +128,7 @@ subroutine ocn_brine_rejection_tendency(tend, layerThickness, seaIceSalinityFlux end do ! need to pick up the last bit that goes over sis_flux(k) = 1.0_RKIND ! this should finish - + do k=minLevelCell(iCell),maxLevelCell(iCell) ! need to flip over so we take k+1 - k tend(index_salinity, k, iCell) = tend(index_salinity, k, iCell) + & From a0ee5d8250c2c2f0c343fde2e6db91289f7055b7 Mon Sep 17 00:00:00 2001 From: Luke Van Roekel Date: Sat, 23 Nov 2024 23:27:53 -0600 Subject: [PATCH 8/8] Fixes bug in nonlocal tend for brine --- .../mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F b/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F index 55194be6ea9e..43988fbfdc92 100644 --- a/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F +++ b/components/mpas-ocean/src/shared/mpas_ocn_tracer_nonlocalflux.F @@ -115,19 +115,19 @@ subroutine ocn_brine_rejection_tendency(tend, layerThickness, seaIceSalinityFlux do iCell = 1, nCells if (seaIceSalinityFlux(iCell) > 0) then !only compute for salinity into ocean !this is set to 1 such that across the BLD we don't have a huge salt flux divergence. - sis_flux(:) = 1.0_RKIND + sis_flux(:) = 1.0_RKIND*seaIceSalinityFlux(iCell) D = boundaryLayerDepth(iCell) z = 0.0_RKIND A = (config_brine_param_n + 1) / D**(config_brine_param_n+1) k = minLevelCell(iCell) ! need a salinity flux top and bottom and the flux into a cell is the divergence?? do while (z < D) - sis_flux(k) = A*z**config_brine_param_n*seaIceSalinityFlux(iCell) + sis_flux(k) = min(1.0_RKIND,A*z**config_brine_param_n)*seaIceSalinityFlux(iCell) z = z + layerThickness(k,iCell) k = k + 1 end do ! need to pick up the last bit that goes over - sis_flux(k) = 1.0_RKIND ! this should finish + sis_flux(k) = 1.0_RKIND*seaIceSalinityFlux(iCell) ! this should finish do k=minLevelCell(iCell),maxLevelCell(iCell) ! need to flip over so we take k+1 - k