subroutine ocean_sponge_apply_tracers(grid, bc, ms, dt)
!! Relax tracer concentrations (hTr/h) toward per-edge targets in any
!! sponge band with `sponge_relax_tracers = .true.`. Same cosine ramp
!! and `sponge_strength` as the momentum kernel. No-op otherwise.
!! Per-tracer loop outside the inner loops (outer-shim for the
!! array-of-derived-types registry).
type(hgrid_t), intent(in) :: grid
type(ocean_bc_state_t), intent(in) :: bc
type(multilayer_state_t), intent(inout) :: ms
real(wp), intent(in) :: dt
integer :: it
integer :: i0, i1, j0, j1
integer :: wall_face, band, nz, nxt, nyt
real(wp) :: strength, C_bc
nz = ms%nz_ml
nxt = grid%nx_total
nyt = grid%ny_total
i0 = grid%nghost + 1
i1 = grid%nghost + grid%nx_phys
j0 = grid%nghost + 1
j1 = grid%nghost + grid%ny_phys
if (.not. allocated(ms%tracers)) return
! Per-tracer `it` loop stays OUTSIDE the `do concurrent` kernels
! (outer-shim): the hTr slice + h_layer reach the device as top-level
! explicit-shape allocatables in the `_impl` routines, never
! dereferenced as `ms%tracers(it)%hTr(...)` inside a device loop.
! ---- West edge ----
! has_* gate: a subdomain seam never carries a sponge band (O0).
if (bc%west%bc_type == OBC_SPONGE .and. &
bc%west%sponge_relax_tracers .and. &
bc%west%sponge_width > 0 .and. &
allocated(bc%west%clamped_tracer) .and. &
bc%has_west) then
band = bc%west%sponge_width
strength = bc%west%sponge_strength
wall_face = grid%nghost + 1
do it = 1, size(ms%tracers)
if (.not. allocated(ms%tracers(it)%hTr)) cycle
if (it > size(bc%west%clamped_tracer)) cycle
C_bc = bc%west%clamped_tracer(it)
call sponge_relax_band_x_tracer(ms, it, nxt, nyt, nz, &
j0, j1, wall_face, band, +1, strength, C_bc, dt)
end do
end if
! ---- East edge ----
if (bc%east%bc_type == OBC_SPONGE .and. &
bc%east%sponge_relax_tracers .and. &
bc%east%sponge_width > 0 .and. &
allocated(bc%east%clamped_tracer) .and. &
bc%has_east) then
band = bc%east%sponge_width
strength = bc%east%sponge_strength
wall_face = grid%nghost + grid%nx_phys + 1
do it = 1, size(ms%tracers)
if (.not. allocated(ms%tracers(it)%hTr)) cycle
if (it > size(bc%east%clamped_tracer)) cycle
C_bc = bc%east%clamped_tracer(it)
call sponge_relax_band_x_tracer(ms, it, nxt, nyt, nz, &
j0, j1, wall_face, band, -1, strength, C_bc, dt)
end do
end if
! ---- South edge ----
if (bc%south%bc_type == OBC_SPONGE .and. &
bc%south%sponge_relax_tracers .and. &
bc%south%sponge_width > 0 .and. &
allocated(bc%south%clamped_tracer) .and. &
bc%has_south) then
band = bc%south%sponge_width
strength = bc%south%sponge_strength
wall_face = grid%nghost + 1
do it = 1, size(ms%tracers)
if (.not. allocated(ms%tracers(it)%hTr)) cycle
if (it > size(bc%south%clamped_tracer)) cycle
C_bc = bc%south%clamped_tracer(it)
call sponge_relax_band_y_tracer(ms, it, nxt, nyt, nz, &
i0, i1, wall_face, band, +1, strength, C_bc, dt)
end do
end if
! ---- North edge ----
if (bc%north%bc_type == OBC_SPONGE .and. &
bc%north%sponge_relax_tracers .and. &
bc%north%sponge_width > 0 .and. &
allocated(bc%north%clamped_tracer) .and. &
bc%has_north) then
band = bc%north%sponge_width
strength = bc%north%sponge_strength
wall_face = grid%nghost + grid%ny_phys + 1
do it = 1, size(ms%tracers)
if (.not. allocated(ms%tracers(it)%hTr)) cycle
if (it > size(bc%north%clamped_tracer)) cycle
C_bc = bc%north%clamped_tracer(it)
call sponge_relax_band_y_tracer(ms, it, nxt, nyt, nz, &
i0, i1, wall_face, band, -1, strength, C_bc, dt)
end do
end if
end subroutine ocean_sponge_apply_tracers