diff --git a/components/mpas-ocean/src/Registry.xml b/components/mpas-ocean/src/Registry.xml index 2091c51429e2..a21d1de90a15 100644 --- a/components/mpas-ocean/src/Registry.xml +++ b/components/mpas-ocean/src/Registry.xml @@ -326,6 +326,14 @@ description="If true, the N2 limiting is applied to the horizontal diffusion term" possible_values=".true. or .false." /> + + maximumBnd(iTr, k, iCell))) then - do i = 1, nEdgesOnCell(iCell) - iEdge = edgesOnCell(i, iCell) - redi_term2_edge(iTr, k, iEdge) = 0.0_RKIND - end do + ! scale cross-fluxes down relative to size of bnds violation + over = max(tempTracer - maximumBnd(iTr, k, iCell), & + minimumBnd(iTr, k, iCell) - tempTracer) + + diff = abs(tempTracer - tracers(iTr, k, iCell)) + + scal = min(1.0_RKIND, max(0.0_RKIND, & + limiter_safety_factor * ( & + 1.0_RKIND - over / (diff + 1.0E-16_RKIND)))) + + ! scale to zero if not using monotone limiter + if (.not. use_quasi_monotone) scal = 0.0_RKIND + + ! cell flux scalings, to be interp. onto edge/levs next pass + redi_flux_scale(iTr, k, iCell) = scal - redi_term3_topOfCell(iTr, k, iCell) = 0.0_RKIND - redi_term3_topOfCell(iTr, k + 1, iCell) = 0.0_RKIND if (isActiveTracer) then rediLimiterCount(k, iCell) = rediLimiterCount(k, iCell) + 1 - endif - endif + end if + end if end do end do end do ! iCell @@ -422,7 +499,8 @@ subroutine ocn_tracer_hmix_Redi_tend(meshPool, layerThickness, layerThickEdgeMea !now go back and reapply all tendencies !$omp parallel - !$omp do schedule(runtime) private(invAreaCell, k, iTr, i, iEdge) + !$omp do schedule(runtime) & + !$omp private(invAreaCell, k, kUp, kDn, iTr, i, iEdge, jCell, scal, scalUp, scalDn) do iCell = 1, nCells invAreaCell = 1.0_RKIND/areaCell(iCell) do k = minLevelCell(iCell), maxLevelCell(iCell) @@ -432,21 +510,48 @@ subroutine ocn_tracer_hmix_Redi_tend(meshPool, layerThickness, layerThickEdgeMea end do if (maxLevelCell(iCell) .ge. config_Redi_min_layers_diag_terms) then + ! term 3: d/dz div( S grad psi ) do k = minLevelCell(iCell), maxLevelCell(iCell) + kUp = max(minLevelCell(iCell), k - 1) + kDn = min(maxLevelCell(iCell), k + 1) do iTr = 1, ntracers - tend(iTr, k, iCell) = tend(iTr, k, iCell) + rediKappaCell(iCell)*2.0_RKIND* & - (RediKappaScaling(k, iCell)* & - redi_term3_topOfCell(iTr, k, iCell) - & - RediKappaScaling(k + 1, iCell)*redi_term3_topOfCell(iTr, k + 1, iCell)) + ! harmonic-mean; not strictly monotone, but smooth + scalUp = 2.0_RKIND * & + redi_flux_scale(iTr, kUp, iCell) * & + redi_flux_scale(iTr, k, iCell) / ( & + redi_flux_scale(iTr, kUp, iCell) + & + redi_flux_scale(iTr, k, iCell) + 1.0E-16_RKIND) + + scalDn = 2.0_RKIND * & + redi_flux_scale(iTr, kDn, iCell) * & + redi_flux_scale(iTr, k, iCell) / ( & + redi_flux_scale(iTr, kDn, iCell) + & + redi_flux_scale(iTr, k, iCell) + 1.0E-16_RKIND) + + tend(iTr, k, iCell) = tend(iTr, k, iCell) + & + rediKappaCell(iCell) * ( & + RediKappaScaling(k, iCell)* & + scalUp*redi_term3_topOfCell(iTr, k, iCell) - & + RediKappaScaling(k + 1, iCell)* & + scalDn*redi_term3_topOfCell(iTr, k + 1, iCell)) end do end do + ! term 2: div( h S d/dz psi ) do i = 1, nEdgesOnCell(iCell) if (cellsOnCell(i, iCell) .eq. nCellsP1) cycle iEdge = edgesOnCell(i, iCell) + jCell = cellsOnCell(i, iCell) do k = minLevelEdgeBot(iEdge), maxLevelEdgeTop(iEdge) do iTr = 1, ntracers - tend(iTr, k, iCell) = tend(iTr, k, iCell) + edgeSignOnCell(i, iCell)*invAreaCell*redi_term2_edge(iTr, k, iEdge) + scal = 2.0_RKIND * & + redi_flux_scale(iTr, k, iCell) * & + redi_flux_scale(iTr, k, jCell) / ( & + redi_flux_scale(iTr, k, iCell) + & + redi_flux_scale(iTr, k, jCell) + 1.0E-16_RKIND) + + tend(iTr, k, iCell) = tend(iTr, k, iCell) + & + scal*edgeSignOnCell(i, iCell)*invAreaCell*redi_term2_edge(iTr, k, iEdge) end do end do end do @@ -463,6 +568,9 @@ subroutine ocn_tracer_hmix_Redi_tend(meshPool, layerThickness, layerThickEdgeMea deallocate (redi_term3) deallocate (redi_term2_edge) deallocate (redi_term3_topOfCell) + deallocate (minimumBnd) + deallocate (maximumBnd) + deallocate (redi_flux_scale) call mpas_timer_stop("tracer redi") @@ -545,6 +653,12 @@ subroutine ocn_tracer_hmix_Redi_init(domain, err)!{{{ call mpas_dmpar_finalize(domain%dminfo) end if + ! quasi bounds-preserving limiter + use_quasi_monotone = config_Redi_use_quasi_monotone_limiter + + ! safety factor for limiter: 0 <= fac <= 1 + limiter_safety_factor = config_Redi_quasi_monotone_safety_factor + ! Initialize horizontal taper if (config_Redi_horizontal_taper == 'none' .or. & config_Redi_horizontal_taper == 'RossbyRadius') then