seed_geostrophic_adjustment_ic Subroutine

public subroutine seed_geostrophic_adjustment_ic(state, grid, cfg, ierr)

Rossby’s classic geostrophic-adjustment problem. Overlays a Gaussian SSH bump on a flat-bottom, single-layer (barotropic) state at rest: η(x, y) = A · exp(-r² / L²) h(i, j) = b(i, j) + η(i, j) u = v = 0 where r is the radial distance from the bump centre.

Expected evolution (analytical): * Length-scale ratio L / Rd with Rd = √(gH)/f governs the split between radiated IG-wave energy and retained balanced geostrophic ring. L >> Rd → most energy retained, surface stays bumped with a balanced anticyclonic flow around it. L << Rd → most energy radiates, the bump flattens out. L ~ Rd → partial, ~50/50. * Time-series of η at the centre oscillates with period 2π/f, damping toward the residual value. * IG-wave fronts propagate outward at c = √(gH).

Diagnostic interpretation: * Bump amplitude growing in time → numerical instability (wrong PGF sign, bad continuity). * Oscillation period != 2π/f → wrong Coriolis coupling. * Energy not conserved (after damping out the IG transient) → spurious sources / sinks in the bt step or Coriolis-adv.

Requires topo_config = "flat" so the bump sits on a uniform reference depth. Single-layer is recommended (nz=1) to isolate the barotropic adjustment; multi-layer works but the bump distributes uniformly across layers.

MPI: the default bump centre uses the GLOBAL extents grid%nx_global / grid%ny_global, and local cell indices are mapped to global physical positions with grid%i_offset_global / grid%j_offset_global. Built from the LOCAL extents instead, every rank would drop a full-amplitude bump in the middle of its own tile. On a single rank the offsets are 0 and global == local, so the seed is byte-identical.

Arguments

Type IntentOptional Attributes Name
type(ocean_state_t), intent(inout) :: state
type(hgrid_t), intent(in) :: grid
type(config_t), intent(in) :: cfg
integer, intent(out), optional :: ierr

Non-zero on a geostrophic-adjustment IC configuration conflict when present; absent behaves as today (error stop).


Calls

proc~~seed_geostrophic_adjustment_ic~~CallsGraph proc~seed_geostrophic_adjustment_ic seed_geostrophic_adjustment_ic proc~fail fail proc~seed_geostrophic_adjustment_ic->proc~fail proc~seed_h_layer_uniform_impl seed_h_layer_uniform_impl proc~seed_geostrophic_adjustment_ic->proc~seed_h_layer_uniform_impl proc~seed_tracer_uniform_impl seed_tracer_uniform_impl proc~seed_geostrophic_adjustment_ic->proc~seed_tracer_uniform_impl error error proc~fail->error proc~error_ring_push error_ring_push proc~fail->proc~error_ring_push

Called by

proc~~seed_geostrophic_adjustment_ic~~CalledByGraph proc~seed_geostrophic_adjustment_ic seed_geostrophic_adjustment_ic proc~ocean_state_seed_from_cfg ocean_state_seed_from_cfg proc~ocean_state_seed_from_cfg->proc~seed_geostrophic_adjustment_ic proc~engine_setup engine_setup proc~engine_setup->proc~ocean_state_seed_from_cfg 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

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: eta_local
integer, private :: i
integer, private :: i_phys
real(kind=wp), private :: inv_L2
integer, private :: ioff
integer, private :: j
integer, private :: j_phys
integer, private :: joff
integer, private :: ng
integer, private :: nx_total
integer, private :: ny_total
integer, private :: nz_ml
real(kind=wp), private :: r2
real(kind=wp), private :: x_c
real(kind=wp), private :: x_phys
real(kind=wp), private :: y_c
real(kind=wp), private :: y_phys

Source Code

   subroutine seed_geostrophic_adjustment_ic(state, grid, cfg, ierr)
      !! Rossby's classic geostrophic-adjustment problem.  Overlays a
      !! Gaussian SSH bump on a flat-bottom, single-layer (barotropic)
      !! state at rest:
      !!     η(x, y) = A · exp(-r² / L²)
      !!     h(i, j) = b(i, j) + η(i, j)
      !!     u = v = 0
      !! where r is the radial distance from the bump centre.
      !!
      !! Expected evolution (analytical):
      !!   * Length-scale ratio L / Rd with Rd = √(gH)/f governs the
      !!     split between radiated IG-wave energy and retained
      !!     balanced geostrophic ring.
      !!     L >> Rd → most energy retained, surface stays bumped with
      !!                 a balanced anticyclonic flow around it.
      !!     L << Rd → most energy radiates, the bump flattens out.
      !!     L ~ Rd  → partial, ~50/50.
      !!   * Time-series of η at the centre oscillates with period
      !!     2π/f, damping toward the residual value.
      !!   * IG-wave fronts propagate outward at c = √(gH).
      !!
      !! Diagnostic interpretation:
      !!   * Bump amplitude growing in time → numerical instability
      !!     (wrong PGF sign, bad continuity).
      !!   * Oscillation period != 2π/f → wrong Coriolis coupling.
      !!   * Energy not conserved (after damping out the IG transient)
      !!     → spurious sources / sinks in the bt step or Coriolis-adv.
      !!
      !! Requires `topo_config = "flat"` so the bump sits on a
      !! uniform reference depth.  Single-layer is recommended (nz=1)
      !! to isolate the barotropic adjustment; multi-layer works but
      !! the bump distributes uniformly across layers.
      !!
      !! MPI: the default bump centre uses the GLOBAL extents
      !! `grid%nx_global` / `grid%ny_global`, and local cell indices are
      !! mapped to global physical positions with `grid%i_offset_global` /
      !! `grid%j_offset_global`.  Built from the LOCAL extents instead,
      !! every rank would drop a full-amplitude bump in the middle of its
      !! own tile.  On a single rank the offsets are 0 and global == local,
      !! so the seed is byte-identical.
      type(ocean_state_t), intent(inout) :: state
      type(hgrid_t), intent(in) :: grid
      type(config_t), intent(in) :: cfg
      integer, intent(out), optional :: ierr
         !! Non-zero on a geostrophic-adjustment IC configuration conflict
         !! when present; absent behaves as today (`error stop`).

      integer :: i, j, nx_total, ny_total, nz_ml, ng
      integer :: i_phys, j_phys, ioff, joff
      real(wp) :: x_phys, y_phys, x_c, y_c, r2, eta_local, inv_L2

      if (trim(cfg%ocean%topo%topo_config) /= "flat") then
         call fail("seed_geostrophic_adjustment_ic: requires topo_config='flat'", ierr, OCEAN_STATUS_ERR_IC_SEED)
         return
      end if
      if (cfg%ocean%ic%ga_length_scale <= 0.0_wp) then
         call fail("seed_geostrophic_adjustment_ic: ga_length_scale must be > 0", ierr, OCEAN_STATUS_ERR_IC_SEED)
         return
      end if

      nx_total = size(state%barotropic%b, 1)
      ny_total = size(state%barotropic%b, 2)
      nz_ml = state%multilayer%nz_ml
      ng = grid%nghost
      ioff = grid%i_offset_global
      joff = grid%j_offset_global

      ! Default centre = GLOBAL basin midpoint (negative cfg values).
      x_c = cfg%ocean%ic%ga_x_center
      y_c = cfg%ocean%ic%ga_y_center
      if (x_c < 0.0_wp) x_c = 0.5_wp*real(grid%nx_global, wp)*grid%dx
      if (y_c < 0.0_wp) y_c = 0.5_wp*real(grid%ny_global, wp)*grid%dy

      inv_L2 = 1.0_wp/(cfg%ocean%ic%ga_length_scale*cfg%ocean%ic%ga_length_scale)

      ! Override h_total = b + η(x, y) and redistribute the per-layer
      ! thickness evenly.  Velocities + face fluxes left at zero from
      ! the default seed.  Velocities are unchanged at zero, so hu/hv
      ! stay at zero too.
      do j = 1, ny_total
         ! GLOBAL physical indices of this local cell.
         j_phys = j - ng + joff
         y_phys = (real(j_phys, wp) - 0.5_wp)*grid%dy
         do i = 1, nx_total
            i_phys = i - ng + ioff
            x_phys = (real(i_phys, wp) - 0.5_wp)*grid%dx
            r2 = (x_phys - x_c)**2 + (y_phys - y_c)**2
            eta_local = cfg%ocean%ic%ga_eta_amp*exp(-r2*inv_L2)
            state%barotropic%h(i, j) = state%barotropic%b(i, j) + eta_local
         end do
      end do

      call seed_h_layer_uniform_impl(state%multilayer%h_layer, &
                                     state%barotropic%h, nz_ml)
      ! Re-seed S, T so hTr/h_layer stays at the configured uniform
      ! values regardless of the η bump.
      if (state%multilayer%idx_salinity > 0) then
         call seed_tracer_uniform_impl( &
            state%multilayer%tracers(state%multilayer%idx_salinity)%hTr, &
            state%multilayer%h_layer, cfg%initial_salinity, nz_ml)
      end if
      if (state%multilayer%idx_temperature > 0) then
         call seed_tracer_uniform_impl( &
            state%multilayer%tracers(state%multilayer%idx_temperature)%hTr, &
            state%multilayer%h_layer, cfg%initial_temperature, nz_ml)
      end if
      if (present(ierr)) ierr = OCEAN_STATUS_OK
   end subroutine seed_geostrophic_adjustment_ic