configure_ocean_bt_split Subroutine

public subroutine configure_ocean_bt_split(cfg, ocean_state, grid, compute_rank)

Auto-derive the barotropic substep count n_inner from the external gravity-wave CFL (MOM6 set_dtbt) when requested, then latch the mode-split reference column depth bt_H_ref from the seeded bathymetry. cfg is intent(inout) because auto_n_inner writes cfg%ocean%bt%n_inner.

Arguments

Type IntentOptional Attributes Name
type(config_t), intent(inout) :: cfg
type(ocean_state_t), intent(inout) :: ocean_state
type(hgrid_t), intent(in) :: grid
integer, intent(in) :: compute_rank

Calls

proc~~configure_ocean_bt_split~~CallsGraph proc~configure_ocean_bt_split configure_ocean_bt_split info info proc~configure_ocean_bt_split->info proc~bt_auto_n_inner_from_dt bt_auto_n_inner_from_dt proc~configure_ocean_bt_split->proc~bt_auto_n_inner_from_dt proc~bt_cfl_dt_wet bt_cfl_dt_wet proc~configure_ocean_bt_split->proc~bt_cfl_dt_wet proc~cavity_datum_impl cavity_datum_impl proc~configure_ocean_bt_split->proc~cavity_datum_impl proc~halo_allreduce_min halo_allreduce_min proc~configure_ocean_bt_split->proc~halo_allreduce_min to_string to_string proc~configure_ocean_bt_split->to_string warning warning proc~configure_ocean_bt_split->warning allreduce allreduce proc~halo_allreduce_min->allreduce proc~comm_env_compute_comm comm_env_compute_comm proc~halo_allreduce_min->proc~comm_env_compute_comm comm_world comm_world proc~comm_env_compute_comm->comm_world

Called by

proc~~configure_ocean_bt_split~~CalledByGraph proc~configure_ocean_bt_split configure_ocean_bt_split proc~engine_setup engine_setup proc~engine_setup->proc~configure_ocean_bt_split 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_bt_split(cfg, ocean_state, grid, compute_rank)
      !! Auto-derive the barotropic substep count n_inner from the external
      !! gravity-wave CFL (MOM6 set_dtbt) when requested, then latch the
      !! mode-split reference column depth bt_H_ref from the seeded bathymetry.
      !! `cfg` is intent(inout) because auto_n_inner writes cfg%ocean%bt%n_inner.
      type(config_t), intent(inout) :: cfg
      type(ocean_state_t), intent(inout) :: ocean_state
      type(hgrid_t), intent(in) :: grid
      integer, intent(in) :: compute_rank

      ! Auto-derive n_inner from the external gravity-wave CFL (MOM6 set_dtbt),
      ! evaluated PER WET CELL: the local depth with the local 2-D CFL length
      ! `l = 1/sqrt(1/dx^2+1/dy^2)` (the cross-direction term matters — on
      ! square cells it is the sqrt(2) the legacy 1-D estimate omitted), over
      ! ocean only.  See `bt_cfl_dt_wet` for why the old "deepest anywhere x
      ! smallest anywhere, land included" combination was wrong on a real
      ! global grid, and why this reproduces it bit-for-bit wherever the two
      ! extremes coincide on a wet cell.
      !
      ! `multilayer%wet_mask`, not `metrics%wet_T`: the metric mask is built
      ! later, by `configure_ocean_land_mask`; its interior IS this array.
      if (cfg%ocean%bt%auto_n_inner) then
         block
            integer :: ng_, n_inner_derived, n_wet_local
            real(wp) :: dt_bt_local, dt_bt_safe, h_at, l_at
            ng_ = grid%nghost
            call bt_cfl_dt_wet(size(ocean_state%barotropic%b, 1), &
                               size(ocean_state%barotropic%b, 2), &
                               ng_ + 1, ng_ + grid%nx_phys, &
                               ng_ + 1, ng_ + grid%ny_phys, &
                               ocean_state%barotropic%b, &
                               ocean_state%multilayer%wet_mask, &
                               ocean_state%metrics%dxT, ocean_state%metrics%dyT, &
                               cfg%ocean%bt%cfl_bt_safety, &
                               dt_bt_local, h_at, l_at, n_wet_local)
            ! Reduce to the GLOBAL per-point minimum so every rank derives the
            ! SAME n_inner.  Different n_inner per rank means a different
            ! number of barotropic substeps, which desyncs the per-substep
            ! grouped halo exchanges (Isend/Irecv pairing crosses between
            ! substeps -> MPI_ERR_TRUNCATE at the first N/S exchange; seen on
            ! a spherical py>1 split).  A rank with no wet cell contributes
            ! `huge`, the identity of min.  Single rank: identity stub.
            call halo_allreduce_min(dt_bt_local, dt_bt_safe)
            n_inner_derived = bt_auto_n_inner_from_dt(cfg%dt_fixed, dt_bt_safe)
            if (compute_rank == 0) then
               if (dt_bt_safe >= huge(1.0_wp)) then
                  call logger%warning("Auto n_inner: no wet cell anywhere — "// &
                                      "n_inner = 1 (was "// &
                                      to_string(cfg%ocean%bt%n_inner)//")")
               else if (dt_bt_local == dt_bt_safe) then
                  call logger%info("Auto n_inner (per wet cell): limiting cell H = "// &
                                   to_string(h_at)//" m, l_cfl = "// &
                                   to_string(l_at)//" m, c_ext = "// &
                                   to_string(sqrt(GRAVITY*max(h_at, 1.0_wp)))// &
                                   " m/s, dt_bt = "//to_string(dt_bt_safe)// &
                                   " s → n_inner = "//to_string(n_inner_derived)// &
                                   " (was "//to_string(cfg%ocean%bt%n_inner)//")")
               else
                  call logger%info("Auto n_inner (per wet cell): dt_bt = "// &
                                   to_string(dt_bt_safe)//" s (limited on another "// &
                                   "rank) → n_inner = "//to_string(n_inner_derived)// &
                                   " (was "//to_string(cfg%ocean%bt%n_inner)//")")
               end if
            end if
            cfg%ocean%bt%n_inner = n_inner_derived
         end block
      end if

      ! Mode-split contract: bt_H_ref is the reference WATER-COLUMN
      ! thickness.  Without an ice shelf that is the seeded bathymetry
      ! (Σh_layer = b); under one it is `b − z_draft`, which is what makes
      ! `bt_eta = Σh_layer − bt_H_ref` the deviation from the LOADED
      ! equilibrium (zero at rest) rather than a permanent −z_draft.  That
      ! is Losch (2008) §2.1's own convention, and it is why every
      ! consumer of `D = bt_H_ref + bt_eta` — the BT continuity face
      ! thickness, the Chapman phase speed, the ALE `remap_h_ref` — needs
      ! no cavity branch of its own.  Knob off ⇒ `z_draft ≡ 0` ⇒ the
      ! literal `bt_H_ref = b` this always was.
      !
      ! The snapshot is taken from the UNWRAPPED `b`/`z_draft`; the engine
      ! re-wraps + halo-exchanges `bt_H_ref` right after, alongside both
      ! of its sources.
      !
      ! NOTE on `auto_n_inner` above: it derives each wet cell's external
      ! gravity-wave speed from `b`, the BED depth, not from `b − z_draft`.
      ! Under a shelf that OVERESTIMATES `c_ext` and so buys more
      ! barotropic substeps than the CFL needs — conservative, never
      ! unstable, and at a calving front (where the draft is 0) it is
      ! exactly right.  Left as-is deliberately: tightening it would
      ! change `n_inner` for cavity runs only, which is a separate,
      ! answer-changing decision.
      if (cfg%ocean%bt%n_inner >= 1) then
         if (ocean_state%metrics%use_cavity) then
            ! `cavity_datum_impl`, not the raw `b - z_draft`: a GROUNDED
            ! column has no water column, and its datum is 0 rather than
            ! a negative thickness.  See that routine for why — in short,
            ! it is land, nothing downstream reads its datum, and the
            ! negative value put a phantom few-hundred-metre `bt_eta` on
            ! it and turned the ALE land target into a cancellation.
            call cavity_datum_impl(ocean_state%dyn%bt_work%bt_H_ref, &
                                   ocean_state%barotropic%b, &
                                   ocean_state%metrics%z_draft, &
                                   cfg%ocean%cavity_dyn%h_min_cavity, &
                                   size(ocean_state%barotropic%b, 1), &
                                   size(ocean_state%barotropic%b, 2))
         else
            ocean_state%dyn%bt_work%bt_H_ref = ocean_state%barotropic%b
         end if
      end if
   end subroutine configure_ocean_bt_split