configure_ocean_vmix Subroutine

public subroutine configure_ocean_vmix(cfg, ocean_state, compute_rank, ierr)

Thermodynamics on/off, velocity-truncation clamp (MAXVEL), DIRECT_STRESS surface-stress distribution, KV_ML_INVZ2 surface-band viscosity, HARMONIC_VISC face-thickness mean, and the DT_THERM thermo/tracer cadence — with their rank-0 log lines.

Arguments

Type IntentOptional Attributes Name
type(config_t), intent(in) :: cfg
type(ocean_state_t), intent(inout) :: ocean_state
integer, intent(in) :: compute_rank
integer, intent(out), optional :: ierr

Non-zero on a vertical-mixing configuration conflict when present; absent behaves as today (error stop).


Calls

proc~~configure_ocean_vmix~~CallsGraph proc~configure_ocean_vmix configure_ocean_vmix info info proc~configure_ocean_vmix->info proc~bkgnd_henyey_conflicts_profile bkgnd_henyey_conflicts_profile proc~configure_ocean_vmix->proc~bkgnd_henyey_conflicts_profile proc~fail fail proc~configure_ocean_vmix->proc~fail proc~ocean_dt_tracer_advect_ratios_ok ocean_dt_tracer_advect_ratios_ok proc~configure_ocean_vmix->proc~ocean_dt_tracer_advect_ratios_ok proc~parse_tracer_recon parse_tracer_recon proc~configure_ocean_vmix->proc~parse_tracer_recon proc~vdiff_bbl_configure vdiff_bbl_configure proc~configure_ocean_vmix->proc~vdiff_bbl_configure proc~vmix_resolve_kd_min vmix_resolve_kd_min proc~configure_ocean_vmix->proc~vmix_resolve_kd_min to_string to_string proc~configure_ocean_vmix->to_string error error proc~fail->error proc~error_ring_push error_ring_push proc~fail->proc~error_ring_push

Called by

proc~~configure_ocean_vmix~~CalledByGraph proc~configure_ocean_vmix configure_ocean_vmix proc~engine_setup engine_setup proc~engine_setup->proc~configure_ocean_vmix proc~complete_ocean_create complete_ocean_create proc~complete_ocean_create->proc~engine_setup proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_setup proc~driver_validate driver_validate proc~driver_validate->proc~engine_setup proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean proc~rdb_ocean_create_finalize rdb_ocean_create_finalize proc~rdb_ocean_create_finalize->proc~complete_ocean_create proc~rdb_ocean_create_from_string rdb_ocean_create_from_string proc~rdb_ocean_create_from_string->proc~complete_ocean_create

Source Code

   subroutine configure_ocean_vmix(cfg, ocean_state, compute_rank, ierr)
      !! Thermodynamics on/off, velocity-truncation clamp (MAXVEL), DIRECT_STRESS
      !! surface-stress distribution, KV_ML_INVZ2 surface-band viscosity,
      !! HARMONIC_VISC face-thickness mean, and the DT_THERM thermo/tracer
      !! cadence — with their rank-0 log lines.
      type(config_t), intent(in) :: cfg
      type(ocean_state_t), intent(inout) :: ocean_state
      integer, intent(in) :: compute_rank
      integer, intent(out), optional :: ierr
         !! Non-zero on a vertical-mixing configuration conflict when
         !! present; absent behaves as today (`error stop`).

      ocean_state%dyn%enable_thermodynamics = cfg%ocean%thermo%enable_thermodynamics

      ! Velocity-truncation clamp (MOM6 MAXVEL); 0 = off.
      ocean_state%dyn%maxvel = cfg%ocean%pgf%maxvel
      if (compute_rank == 0) then
         if (cfg%ocean%pgf%maxvel > 0.0_wp) then
            call logger%info("Velocity clamp:   "//to_string(cfg%ocean%pgf%maxvel)//" m/s")
         end if
      end if

      ! Advective-CFL velocity truncation (E7, MOM6 CFL_trunc); 0 = off.
      ocean_state%dyn%cfl_trunc = cfg%ocean%pgf%cfl_trunc
      if (compute_rank == 0) then
         if (cfg%ocean%pgf%cfl_trunc > 0.0_wp) then
            call logger%info("CFL truncation:   threshold "//to_string(cfg%ocean%pgf%cfl_trunc))
         end if
      end if

      ! DIRECT_STRESS — wind stress spread over the top hmix_stress metres.
      ocean_state%surface_stress%direct_stress = cfg%ocean%vmix%direct_stress
      ocean_state%surface_stress%hmix_stress = cfg%ocean%vmix%hmix_stress
      if (compute_rank == 0) then
         if (cfg%ocean%vmix%direct_stress) then
            call logger%info("Surface stress:   DIRECT (over top "// &
                             to_string(cfg%ocean%vmix%hmix_stress)//" m)")
         end if
      end if

      ! KV_ML_INVZ2 — extra surface-band vertical viscosity (MOM6).
      ocean_state%vmix%kv_ml_invz2 = cfg%ocean%vmix%kv_ml_invz2
      ocean_state%vmix%hmix_fixed = cfg%ocean%vmix%hmix_fixed
      if (compute_rank == 0) then
         if (cfg%ocean%vmix%kv_ml_invz2 > 0.0_wp) then
            call logger%info("KV_ML_INVZ2:      "//to_string(cfg%ocean%vmix%kv_ml_invz2)// &
                             " m²/s in top "//to_string(cfg%ocean%vmix%hmix_fixed)//" m")
         end if
      end if

      ! C11 assembly gate — ceilings / smoothing / guard.  Backgrounds
      ! stay on the slot defaults (= pp81_*_bg, set in ocean_vmix_init)
      ! so the floor is a no-op for the closure path; only the ceilings /
      ! smoothing / guard are config-exposed.
      ocean_state%vmix%kv_max = cfg%ocean%vmix%kv_max
      ocean_state%vmix%kd_max = cfg%ocean%vmix%kd_max
      ocean_state%vmix%kd_smooth_iterations = cfg%ocean%vmix%kd_smooth_iterations
      ocean_state%vmix%vmix_guard = cfg%ocean%vmix%vmix_guard

      ! C7 Bryan-Lewis depth-varying background (default off = bit-identical).
      ocean_state%vmix%bkgnd_profile = cfg%ocean%vmix%bkgnd_profile
      ocean_state%vmix%bkgnd_kd_sfc = cfg%ocean%vmix%bkgnd_kd_sfc
      ocean_state%vmix%bkgnd_kd_deep = cfg%ocean%vmix%bkgnd_kd_deep
      ocean_state%vmix%bkgnd_z0 = cfg%ocean%vmix%bkgnd_z0
      ocean_state%vmix%bkgnd_delta = cfg%ocean%vmix%bkgnd_delta
      ocean_state%vmix%bkgnd_prandtl = cfg%ocean%vmix%bkgnd_prandtl
      ocean_state%vmix%bkgnd_henyey = cfg%ocean%vmix%bkgnd_henyey
      ocean_state%vmix%bkgnd_kd_min = cfg%ocean%vmix%bkgnd_kd_min
      ocean_state%vmix%bkgnd_henyey_n0_2omega = cfg%ocean%vmix%bkgnd_henyey_n0_2omega
      ocean_state%vmix%bkgnd_henyey_max_lat = cfg%ocean%vmix%bkgnd_henyey_max_lat
      ! Backstop for the two `validate_config` guards (Bryan-Lewis and Henyey
      ! mutually exclusive; non-cartesian grid_config required) — reached only
      ! by a caller that bypassed validation, since the namelist path stops
      ! there first.  Repeated here because a Henyey run with either
      ! precondition violated is silent (wrong background field), not noisy.
      if (bkgnd_henyey_conflicts_profile(cfg%ocean%vmix%bkgnd_henyey, &
                                         cfg%ocean%vmix%bkgnd_profile)) then
         call fail("ocean_vmix_nml: bkgnd_henyey and bkgnd_profile are "// &
                   "mutually exclusive background schemes — the Henyey factor "// &
                   "scales the SCALAR background, it does not compose with the "// &
                   "Bryan-Lewis depth profile", ierr, OCEAN_STATUS_ERR_SETUP)
         return
      end if
      if (cfg%ocean%vmix%bkgnd_henyey .and. &
          trim(cfg%ocean%grid%grid_config) == "cartesian") then
         call fail("ocean_vmix_nml: bkgnd_henyey requires a non-cartesian "// &
                   "grid_config — geolatT is identically zero on cartesian, so "// &
                   "every column would take the equatorial L(0 deg)=0 and the "// &
                   "background would flatten to a uniform bkgnd_kd_min", ierr, OCEAN_STATUS_ERR_SETUP)
         return
      end if
      if (compute_rank == 0) then
         if (cfg%ocean%vmix%bkgnd_profile) then
            call logger%info("Background mixing: Bryan-Lewis profile (Kd_sfc="// &
                             to_string(cfg%ocean%vmix%bkgnd_kd_sfc)//" Kd_deep="// &
                             to_string(cfg%ocean%vmix%bkgnd_kd_deep)//" z0="// &
                             to_string(cfg%ocean%vmix%bkgnd_z0)//" m)")
         end if
         if (cfg%ocean%vmix%bkgnd_henyey) then
            call logger%info("Background mixing: Henyey IGW latitude factor on the "// &
                             "scalar background (N0_2Omega="// &
                             to_string(cfg%ocean%vmix%bkgnd_henyey_n0_2omega)// &
                             " max_lat="//to_string(cfg%ocean%vmix%bkgnd_henyey_max_lat)// &
                             " degN Kd_min="// &
                             to_string(vmix_resolve_kd_min(cfg%ocean%vmix%bkgnd_kd_min, &
                                                           cfg%ocean%vmix%pp81_kappa_bg))// &
                             " m^2/s)")
            ! `pp81_kappa_bg` (not `ocean_state%vmix%kt_bg`) because
            ! `seed_backgrounds` — which re-derives kt_bg from it — runs
            ! later in this same routine; reading the slot here would log
            ! the stale type default whenever the user retuned kappa_bg.
         end if
      end if

      ! smooth_scratch is allocated ONLY when smoothing is requested —
      ! here, after kd_smooth_iterations is known, before enter_data.
      ! The enter_data path guards on allocated() so the not-allocated
      ! case (smoothing off) is handled correctly.
      if (cfg%ocean%vmix%kd_smooth_iterations > 0) then
         if (.not. allocated(ocean_state%vmix%smooth_scratch)) then
            associate (kv => ocean_state%vmix%kv)
               allocate (ocean_state%vmix%smooth_scratch( &
                         size(kv, 1), size(kv, 2), size(kv, 3)), source=0.0_wp)
            end associate
         end if
      end if

      if (compute_rank == 0) then
         if (cfg%ocean%vmix%kd_max < huge(1.0_wp) .or. cfg%ocean%vmix%kv_max < huge(1.0_wp)) then
            call logger%info("Vmix assembly:    kv_max="//to_string(cfg%ocean%vmix%kv_max)// &
                             " kd_max="//to_string(cfg%ocean%vmix%kd_max)//" m²/s")
         end if
         if (cfg%ocean%vmix%kd_smooth_iterations > 0) then
            call logger%info("Vmix assembly:    "// &
                             to_string(cfg%ocean%vmix%kd_smooth_iterations)// &
                             " 1-2-1 smoothing pass(es) on kv/kt (wet-mask aware)")
         end if
         if (cfg%ocean%vmix%vmix_guard) then
            call logger%info("Vmix assembly:    negative/NaN guard ON")
         end if
      end if

      ! HARMONIC_VISC — vdiff face-thickness mean (better at thin layers).
      ocean_state%vdiff%use_harmonic = cfg%ocean%vmix%harmonic_visc
      ! MOM6 HARMONIC_VISC parity for the momentum face thickness (hvel) +
      ! arithmetic h_shear.  Off by default => historical arithmetic h_u.
      ocean_state%vdiff%hvel_mom6 = cfg%ocean%vdiff%hvel_mom6
      ocean_state%vdiff%hbbl_visc = cfg%ocean%vdiff%hbbl_visc
      ocean_state%vdiff%hvel_harmonic = cfg%ocean%vdiff%hvel_harmonic
      ! MOM6 bottomdraglaw coupling parity: kv_bbl botfn glue + piston bed
      ! drag (PGF_BUG.md §9), with MOM6's per-face `set_viscous_BBL`
      ! (`vdiff_set_viscous_bbl`) built from the bottom-drag configuration:
      ! the drag law, `cd`/`r`, `bg_vel`, `bbl_thick_min`, and `HBBL` =
      ! `&ocean_bdrag_nml hbbl`, falling back to `&ocean_vdiff_nml
      ! hbbl_visc` for a drag configured bed-only (`hbbl = 0`) — MOM6 has
      ! ONE HBBL and no bed-only form.  No drag configured ⇒ the latch
      ! leaves the glue OFF.  `hvel_mom6` is validated at configure.
      ocean_state%vdiff%bbl_glue = cfg%ocean%vdiff%bbl_glue
      ocean_state%vdiff%bbl_piston = cfg%ocean%vdiff%bbl_piston
      ocean_state%vdiff%hvel_upwind = cfg%ocean%vdiff%hvel_upwind
      block
         integer :: bbl_form
         real(wp) :: bbl_hbbl
         bbl_form = BBL_FORM_QUADRATIC
         if (trim(cfg%ocean%bdrag%form) == "linear") bbl_form = BBL_FORM_LINEAR
         bbl_hbbl = cfg%ocean%bdrag%hbbl
         if (bbl_hbbl <= 0.0_wp) bbl_hbbl = cfg%ocean%vdiff%hbbl_visc
         call vdiff_bbl_configure(ocean_state%vdiff, &
                                  size(ocean_state%multilayer%h_layer, 1), &
                                  size(ocean_state%multilayer%h_layer, 2), &
                                  size(ocean_state%multilayer%h_layer, 3), &
                                  bbl_form, cfg%ocean%bdrag%cd, cfg%ocean%bdrag%r, &
                                  bbl_hbbl, cfg%ocean%bdrag%bg_vel, &
                                  cfg%ocean%bdrag%bbl_thick_min, &
                                  cfg%ocean%kshear%enable, ocean_state%eos%rho0, &
                                  cfg%ocean%vmix%pp81_nu_bg)
      end block
      if (compute_rank == 0 .and. cfg%ocean%vdiff%hvel_mom6 &
          .and. .not. cfg%ocean%vdiff%hvel_upwind) then
         call logger%info("vdiff: hvel near-bed upwind blend OFF (pure harmonic hvel)")
      end if
      if (compute_rank == 0 .and. cfg%ocean%vdiff%hvel_mom6) then
         if (cfg%ocean%vdiff%hvel_harmonic) then
            call logger%info("vdiff: MOM6 hvel, HARMONIC_VISC=True branch (harmonic + "// &
                             "near-bed upwind blend, arithmetic h_shear) ON")
         else
            call logger%info("vdiff: MOM6 hvel, HARMONIC_VISC=False branch (arithmetic, "// &
                             "z_clear near-bed harmonic blend, arithmetic h_shear) ON")
         end if
      end if
      if (compute_rank == 0 .and. ocean_state%vdiff%bbl_glue) then
         call logger%info("vdiff: MOM6 bottomdraglaw BBL glue ON (per-face "// &
                          "set_viscous_BBL kv_bbl/bbl_thick, "//trim(cfg%ocean%bdrag%form)// &
                          " law; botfn interfaces + piston bed row replace the "// &
                          "explicit bed-drag apply)")
      else if (compute_rank == 0 .and. cfg%ocean%vdiff%bbl_glue) then
         call logger%info("vdiff: bbl_glue requested but no bottom drag is configured "// &
                          "(&ocean_bdrag_nml cd/r = 0, or linear with bg_vel = 0) — glue OFF")
      end if
      if (compute_rank == 0) then
         if (cfg%ocean%vmix%harmonic_visc) then
            call logger%info("Vertical visc:    harmonic-mean face thickness")
         end if
      end if

      ! Implicit stress/drag folding into the vdiff tridiagonal
      ! (`&ocean_vdiff_nml`).  When on, the corresponding explicit
      ! pre-solve apply is gated off in run_stage (see rdb_ocean_dyn), and
      ! the bottom-drag slot fills the bed-layer Rayleigh-rate field
      ! `lambda_bot_u/v` that the vdiff diagonal consumes.  Mutual
      ! exclusions validated at configure (validate_config).
      ocean_state%vdiff%implicit_stress = cfg%ocean%vdiff%implicit_stress
      ocean_state%vdiff%implicit_drag = cfg%ocean%vdiff%implicit_drag
      ocean_state%bdrag%implicit_fold = cfg%ocean%vdiff%implicit_drag
      if (compute_rank == 0) then
         if (cfg%ocean%vdiff%implicit_stress) then
            call logger%info("Vertical visc:    wind stress folded into vdiff "// &
                             "surface (k=nz) RHS (implicit_stress)")
         end if
         if (cfg%ocean%vdiff%implicit_drag) then
            call logger%info("Vertical visc:    bottom drag folded into vdiff "// &
                             "bed (k=1) diagonal (implicit_drag)")
         end if
      end if

      ! DT_THERM ratio — thermo/tracer kernels fire every Nth outer step.
      ocean_state%dyn%dt_therm_ratio = max(1, cfg%ocean%vmix%dt_therm_ratio)
      if (compute_rank == 0) then
         if (ocean_state%dyn%dt_therm_ratio > 1) then
            call logger%info("Thermo ratio:     "// &
                             to_string(ocean_state%dyn%dt_therm_ratio)// &
                             "·dt (DT_THERM = ratio · dt_dynamic)")
         end if
      end if

      ! DT_TRACER_ADVECT ratio — horizontal tracer advect fires every
      ! Nth outer step over the accumulated face transports.  Must be
      ! >= 1 and dt_therm_ratio must be an integer multiple of it so the
      ! ALE remap (which fires at DT_THERM cadence) never lands inside an
      ! open accumulation window.  Fail loud, host-side.
      if (cfg%ocean%vmix%dt_tracer_advect_ratio < 1) then
         call fail("ocean_vmix_nml: dt_tracer_advect_ratio must be >= 1 (got "// &
                   to_string(cfg%ocean%vmix%dt_tracer_advect_ratio)//")", ierr, OCEAN_STATUS_ERR_SETUP)
         return
      end if
      ! `< 1` already error-stopped above, so the value is >= 1 here.
      ocean_state%dyn%dt_tracer_advect_ratio = cfg%ocean%vmix%dt_tracer_advect_ratio
      if (.not. ocean_dt_tracer_advect_ratios_ok(ocean_state%dyn%dt_therm_ratio, &
                                                 ocean_state%dyn%dt_tracer_advect_ratio)) then
         call fail("ocean_vmix_nml: dt_therm_ratio ("// &
                   to_string(ocean_state%dyn%dt_therm_ratio)// &
                   ") must be an integer multiple of dt_tracer_advect_ratio ("// &
                   to_string(ocean_state%dyn%dt_tracer_advect_ratio)// &
                   ") so the ALE remap never fires mid-accumulation-window", ierr, OCEAN_STATUS_ERR_SETUP)
         return
      end if
      if (compute_rank == 0) then
         if (ocean_state%dyn%dt_tracer_advect_ratio > 1) then
            call logger%info("Tracer advect:    "// &
                             to_string(ocean_state%dyn%dt_tracer_advect_ratio)// &
                             "·dt (DT_TRACER_ADVECT = ratio · dt_dynamic)")
         end if
      end if

      ! Q6: windowed-drain tracer face reconstruction (ppm | weno5/7/9).
      ! The per-rung nghost minimum is validated fail-loud in validate_config
      ! (cfg%nghost is the ocean grid nghost); here we only map the parsed
      ! code onto the continuity slot.  ppm (default) ⇒ bit-identical.
      ocean_state%continuity%tracer_recon = &
         parse_tracer_recon(trim(cfg%ocean%vmix%tracer_recon))
      if (compute_rank == 0 .and. &
          ocean_state%continuity%tracer_recon /= TRACER_RECON_PPM) then
         call logger%info("Tracer recon:     "//trim(cfg%ocean%vmix%tracer_recon)// &
                          " (windowed-drain WENO-Z swept-average face)")
      end if
      if (compute_rank == 0) then
         if (cfg%ocean%thermo%enable_thermodynamics) then
            call logger%info("Thermodynamics:   on")
         else
            call logger%info("Thermodynamics:   OFF (adiabatic — EOS/tracer kernels skipped)")
         end if
      end if
      if (present(ierr)) ierr = OCEAN_STATUS_OK
   end subroutine configure_ocean_vmix