vdiff_apply_momentum Subroutine

public subroutine vdiff_apply_momentum(grid, this, ms, dt, kv_source, tau_u, tau_v, lambda_bot_u, lambda_bot_v, rho0, visc_rem_u, visc_rem_v, remnant_only, kv_corner_source, kv_corner_prandtl, lambda_top_u, lambda_top_v, cover_u, cover_v)

Backward-Euler vertical viscosity applied to ms%u_face_x_layer and ms%v_face_y_layer. Per-face h_face averaged from the two abutting cell columns.

Diffusivity source: either a 3D cell-centred field from a closure (PP81 / KPP) passed as kv_source (shape (nx, ny, nz+1) — interface-located, k=1 bed, k=nz+1 surface) OR the scalar this%K_v_momentum broadcast into the kv_scalar_buf workspace. No-op when neither path supplies a non-zero diffusivity.

Corner viscosity add-on (kv_corner_source, optional): a CORNER-staggered interface viscosity (nx+1, ny+1, nz+1) (corner (i,j) = SW corner of cell (i,j); k=1 bed interface, k=nz+1 surface), scaled by kv_corner_prandtl and ADDED to the face viscosity as the 2-point average of the face’s two END corners — u-face (i,j) reads corners (i,j)/(i,j+1), v-face (i,j) reads corners (i,j)/(i+1,j). This is the vertex kappa-shear Kv seam (JHL08 vertex form): the corner viscosity reaches the momentum solve WITHOUT passing through a tracer point, so it is NOT smoothed by a corner->centre->face round trip. The add lands BEFORE the BBL-glue transform (the reference formulation folds all viscosity contributions into Kv_tot ahead of the coupling-coefficient bottom-boundary blend). Absent ⇒ bit-identical.

Implicit surface-stress / bottom-drag folding (optional, gated on this%implicit_stress / this%implicit_drag): * tau_u(nu, nv) / tau_v(nu, nv) — wind stress (N/m²) on the respective faces. Divided by rho0 and added to the surface (k = nz) RHS row. Only read when implicit_stress. * lambda_bot_u / lambda_bot_v — bottom-drag Rayleigh rate λ (1/s) on the respective faces, added to the bed (k = 1) diagonal as dt·λ. Only read when implicit_drag. * lambda_top_u / lambda_top_v + cover_u / cover_v — the ICE-SHELF TOP-drag twin (implicit_top_drag): the rate is added to the SURFACE (k = nz) diagonal as dt·λ, and the wind-stress RHS on that same row is scaled by (1 − cover) so an ice-covered face takes the drag and NOT the wind. All four are read only when implicit_top_drag AND all four are supplied. All optional + device-resident; absent ⇒ the matrix is built exactly as before (bit-identical).

visc_rem_u(nu, nv, nz) / visc_rem_v(nu, nv, nz) — optional viscous-remnant γ_k output (the bt_work%visc_rem_u/v seam, PR-19). Both present ⇒ the SAME already-factorized tridiagonal solved a second time with RHS ≡ 1, written into these arrays (see diffuse_velocity_columns_impl). Absent ⇒ no remnant work is done and the momentum answer is unperturbed (bit-identical) — this is a pure add-on, not a new physics path.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_vdiff_t), intent(inout) :: this
type(multilayer_state_t), intent(inout) :: ms
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in), optional :: kv_source(grid%nx_total,grid%ny_total,ms%nz_ml+1)
real(kind=wp), intent(in), optional :: tau_u(grid%nx_total+1,grid%ny_total)
real(kind=wp), intent(in), optional :: tau_v(grid%nx_total,grid%ny_total+1)
real(kind=wp), intent(in), optional :: lambda_bot_u(grid%nx_total+1,grid%ny_total)
real(kind=wp), intent(in), optional :: lambda_bot_v(grid%nx_total,grid%ny_total+1)
real(kind=wp), intent(in), optional :: rho0

Boussinesq reference density for the implicit surface-stress fold, dt*(tau/rho0)/h_nz. READ ONLY when that fold is active (do_stress below); every other path never reaches it. There is deliberately NO defaulted value: the one rho0 of record is &ocean_ic_nml rho_0 -> eos%rho0, which the production call site hands over as surface_stress%rho0 (configure_ocean_reference_density). Omitting it WITH the fold active is a wiring bug and fails loud — it used to fall back to a silent literal 1035, which is how a run configured at a different rho_0 could fold its wind stress in on the wrong one.

real(kind=wp), intent(inout), optional :: visc_rem_u(grid%nx_total+1,grid%ny_total,ms%nz_ml)
real(kind=wp), intent(inout), optional :: visc_rem_v(grid%nx_total,grid%ny_total+1,ms%nz_ml)

Explicit-shape for the reason spelled out on the argument list above.

logical, intent(in), optional :: remnant_only

.true. = build the matrices and (re)fill visc_rem_u/v WITHOUT solving for / modifying the velocities — the pre-substep visc_rem refresh (PGF_BUG.md §9; MOM6 computes vertvisc_coef before btstep, so its BT weights never lag). Requires both visc_rem_u/v present. Default .false..

real(kind=wp), intent(in), optional :: kv_corner_source(grid%nx_total+1,grid%ny_total+1,ms%nz_ml+1)

Corner-staggered interface viscosity source, (nx+1, ny+1, nz+1) — see the corner add-on note above. Typically the vertex kappa-shear kd_corner carrier (device-resident). Explicit-shape for the reason spelled out on the argument list above.

real(kind=wp), intent(in), optional :: kv_corner_prandtl

Scale applied to kv_corner_source (Kv = Pr·Kd; the vertex kappa-shear prandtl_turb). Default 1.

real(kind=wp), intent(in), optional :: lambda_top_u(grid%nx_total+1,grid%ny_total)
real(kind=wp), intent(in), optional :: lambda_top_v(grid%nx_total,grid%ny_total+1)
real(kind=wp), intent(in), optional :: cover_u(grid%nx_total+1,grid%ny_total)
real(kind=wp), intent(in), optional :: cover_v(grid%nx_total,grid%ny_total+1)

Calls

proc~~vdiff_apply_momentum~~CallsGraph proc~vdiff_apply_momentum vdiff_apply_momentum proc~diffuse_velocity_columns_impl diffuse_velocity_columns_impl proc~vdiff_apply_momentum->proc~diffuse_velocity_columns_impl proc~fill_bbl_constants fill_bbl_constants proc~vdiff_apply_momentum->proc~fill_bbl_constants proc~fill_kv_scalar_buf fill_kv_scalar_buf proc~vdiff_apply_momentum->proc~fill_kv_scalar_buf local local proc~diffuse_velocity_columns_impl->local proc~face_thick face_thick proc~diffuse_velocity_columns_impl->proc~face_thick

Called by

proc~~vdiff_apply_momentum~~CalledByGraph proc~vdiff_apply_momentum vdiff_apply_momentum proc~visc_rem_precompute visc_rem_precompute proc~visc_rem_precompute->proc~vdiff_apply_momentum proc~vmix_apply_in_stage vmix_apply_in_stage proc~vmix_apply_in_stage->proc~vdiff_apply_momentum proc~vmix_apply_in_stage->proc~visc_rem_precompute proc~run_stage run_stage proc~run_stage->proc~vmix_apply_in_stage proc~run_stage_split run_stage_split proc~run_stage_split->proc~visc_rem_precompute proc~run_stage_split->proc~vmix_apply_in_stage proc~ocean_dyn_step ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step proc~engine_step->proc~ocean_dyn_step_split proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: corner_prandtl_l
logical, private :: do_corner
logical, private :: do_drag
logical, private :: do_remnant
logical, private :: do_stress
logical, private :: do_top
integer, private :: nx
integer, private :: nx_face
integer, private :: nx_vface
integer, private :: ny
integer, private :: ny_face
integer, private :: ny_uface
integer, private :: nz
real(kind=wp), private :: rho0_l
logical, private :: solve_mom

Source Code

   subroutine vdiff_apply_momentum(grid, this, ms, dt, kv_source, &
                                   tau_u, tau_v, lambda_bot_u, lambda_bot_v, rho0, &
                                   visc_rem_u, visc_rem_v, remnant_only, &
                                   kv_corner_source, kv_corner_prandtl, &
                                   lambda_top_u, lambda_top_v, cover_u, cover_v)
      !! Backward-Euler vertical viscosity applied to
      !! `ms%u_face_x_layer` and `ms%v_face_y_layer`.  Per-face
      !! `h_face` averaged from the two abutting cell columns.
      !!
      !! Diffusivity source: either a 3D cell-centred field from a
      !! closure (PP81 / KPP) passed as `kv_source` (shape (nx, ny,
      !! nz+1) — interface-located, k=1 bed, k=nz+1 surface) OR the
      !! scalar `this%K_v_momentum` broadcast into the
      !! `kv_scalar_buf` workspace.  No-op when neither path
      !! supplies a non-zero diffusivity.
      !!
      !! Corner viscosity add-on (`kv_corner_source`, optional): a
      !! CORNER-staggered interface viscosity `(nx+1, ny+1, nz+1)`
      !! (corner (i,j) = SW corner of cell (i,j); k=1 bed interface,
      !! k=nz+1 surface), scaled by `kv_corner_prandtl` and ADDED to
      !! the face viscosity as the 2-point average of the face's two
      !! END corners — u-face (i,j) reads corners (i,j)/(i,j+1), v-face
      !! (i,j) reads corners (i,j)/(i+1,j).  This is the vertex
      !! kappa-shear `Kv` seam (JHL08 vertex form): the corner
      !! viscosity reaches the momentum solve WITHOUT passing through a
      !! tracer point, so it is NOT smoothed by a corner->centre->face
      !! round trip.  The add lands BEFORE the BBL-glue transform (the
      !! reference formulation folds all viscosity contributions into
      !! `Kv_tot` ahead of the coupling-coefficient bottom-boundary
      !! blend).  Absent ⇒ bit-identical.
      !!
      !! Implicit surface-stress / bottom-drag folding (optional, gated
      !! on `this%implicit_stress` / `this%implicit_drag`):
      !!   * `tau_u(nu, nv)` / `tau_v(nu, nv)` — wind stress (N/m²) on the
      !!     respective faces.  Divided by `rho0` and added to the surface
      !!     (`k = nz`) RHS row.  Only read when `implicit_stress`.
      !!   * `lambda_bot_u` / `lambda_bot_v` — bottom-drag Rayleigh rate
      !!     `λ` (1/s) on the respective faces, added to the bed (`k = 1`)
      !!     diagonal as `dt·λ`.  Only read when `implicit_drag`.
      !!   * `lambda_top_u` / `lambda_top_v` + `cover_u` / `cover_v` —
      !!     the ICE-SHELF TOP-drag twin (`implicit_top_drag`): the rate
      !!     is added to the SURFACE (`k = nz`) diagonal as `dt·λ`, and
      !!     the wind-stress RHS on that same row is scaled by
      !!     `(1 − cover)` so an ice-covered face takes the drag and NOT
      !!     the wind.  All four are read only when `implicit_top_drag`
      !!     AND all four are supplied.
      !! All optional + device-resident; absent ⇒ the matrix is built
      !! exactly as before (bit-identical).
      !!
      !! `visc_rem_u(nu, nv, nz)` / `visc_rem_v(nu, nv, nz)` — optional
      !! viscous-remnant γ_k output (the `bt_work%visc_rem_u/v` seam,
      !! PR-19).  Both present ⇒ the SAME already-factorized tridiagonal
      !! solved a second time with RHS ≡ 1, written into these arrays
      !! (see `diffuse_velocity_columns_impl`).  Absent ⇒ no remnant
      !! work is done and the momentum answer is unperturbed
      !! (bit-identical) — this is a pure add-on, not a new physics
      !! path.
      type(hgrid_t), intent(in) :: grid
      type(ocean_vdiff_t), intent(inout) :: this
      type(multilayer_state_t), intent(inout) :: ms
      real(wp), intent(in) :: dt
      ! EXPLICIT SHAPE — not `(:, :)` — on every optional array below, and
      ! the same on the two `rdb_ocean_dyn` frames that FORWARD them
      ! (`vmix_apply_in_stage`, `visc_rem_precompute`).  Load-bearing, not
      ! decoration.  Each of these is handed straight on to the
      ! EXPLICIT-SHAPE `(nu, nv)` / `(nx, ny, nz)` dummies of
      ! `diffuse_velocity_columns_impl`, i.e. sequence-associated.  An
      ! ASSUMED-SHAPE actual in that position forces the compiler to decide,
      ! at the call, whether the descriptor is contiguous or has to be
      ! packed into a temporary — and gfortran 15 computes that decision
      ! UNCONDITIONALLY, from descriptor extents/strides it only loaded
      ! under the `present()` guard.  An ABSENT optional therefore leaves
      ! the packing flag uninitialised, and the post-call cleanup
      ! (`if (.not. packed .and. ptr /= NULL) free(ptr)`) branches on it:
      ! 160 valgrind "conditional jump depends on uninitialised value(s)"
      ! from 10 contexts on `test_driver_ocean`, all rooted in this
      ! routine's stack frame.  Benign (the null check still gates the free)
      ! but real, and it is the compiler reading OUR uninitialised stack.
      !
      ! Declaring the shape removes the decision at its source: an
      ! explicit-shape dummy is contiguous by definition, so there is no
      ! flag, no temporary, no free, nothing to read — and, unlike the
      ! CONTIGUOUS attribute (tried and reverted during the cavity stack), it
      ! keeps working on nvfortran.  CONTIGUOUS on an assumed-shape OPTIONAL
      ! makes nvfortran
      ! 26.5 hand the callee an offset-adjusted, NON-NULL address for an
      ! ABSENT actual; the implicit present-or-copyin the compiler emits for
      ! the impl's explicit-shape dummy then loses its null short-circuit
      ! and issues a real `cuMemcpyHtoD` off that garbage pointer
      ! (`__pgi_uacc_dataonb ... 'tau_face(:,:)'` -> SIGSEGV, 44/220 tests
      ! on `-stdpar=gpu -gpu=cc70,mem:separate`).  An explicit-shape dummy
      ! carries no descriptor at all, so an absent actual stays a clean
      ! null and the runtime skips it, exactly as it does today.
      !
      ! The bounds are the ones the body already derives and the impl
      ! already assumes: `multilayer_state_t%init` allocates the u-face
      ! arrays `(nx_total + 1, ny_total, nz_ml)` and the v-face arrays
      ! `(nx_total, ny_total + 1, nz_ml)` off this same `grid`.  Every
      ! actual is a whole allocatable slot component or a whole local array,
      ! never a strided section (the GPU path already requires that: these
      ! arrays are device-mapped whole).  Bit-identical: the packing path
      ! was never taken, only speculated about.
      real(wp), intent(in), optional :: kv_source(grid%nx_total, grid%ny_total, ms%nz_ml + 1)
      real(wp), intent(in), optional :: tau_u(grid%nx_total + 1, grid%ny_total)
      real(wp), intent(in), optional :: tau_v(grid%nx_total, grid%ny_total + 1)
      real(wp), intent(in), optional :: lambda_bot_u(grid%nx_total + 1, grid%ny_total)
      real(wp), intent(in), optional :: lambda_bot_v(grid%nx_total, grid%ny_total + 1)
      real(wp), intent(in), optional :: lambda_top_u(grid%nx_total + 1, grid%ny_total)
      real(wp), intent(in), optional :: lambda_top_v(grid%nx_total, grid%ny_total + 1)
      real(wp), intent(in), optional :: cover_u(grid%nx_total + 1, grid%ny_total)
      real(wp), intent(in), optional :: cover_v(grid%nx_total, grid%ny_total + 1)
      real(wp), intent(in), optional :: rho0
         !! Boussinesq reference density for the implicit surface-stress
         !! fold, `dt*(tau/rho0)/h_nz`.  READ ONLY when that fold is active
         !! (`do_stress` below); every other path never reaches it.  There
         !! is deliberately NO defaulted value: the one rho0 of record is
         !! `&ocean_ic_nml rho_0` -> `eos%rho0`, which the production call
         !! site hands over as `surface_stress%rho0`
         !! (`configure_ocean_reference_density`).  Omitting it WITH the
         !! fold active is a wiring bug and fails loud — it used to fall
         !! back to a silent literal 1035, which is how a run configured at
         !! a different rho_0 could fold its wind stress in on the wrong
         !! one.
      real(wp), intent(inout), optional :: visc_rem_u(grid%nx_total + 1, grid%ny_total, ms%nz_ml)
      real(wp), intent(inout), optional :: visc_rem_v(grid%nx_total, grid%ny_total + 1, ms%nz_ml)
         !! Explicit-shape for the reason spelled out on the argument list
         !! above.
      logical, intent(in), optional :: remnant_only
         !! `.true.` = build the matrices and (re)fill `visc_rem_u/v`
         !! WITHOUT solving for / modifying the velocities — the
         !! pre-substep visc_rem refresh (PGF_BUG.md §9; MOM6 computes
         !! `vertvisc_coef` before `btstep`, so its BT weights never lag).
         !! Requires both `visc_rem_u/v` present.  Default `.false.`.
      real(wp), intent(in), optional :: kv_corner_source(grid%nx_total + 1, grid%ny_total + 1, ms%nz_ml + 1)
         !! Corner-staggered interface viscosity source, `(nx+1, ny+1,
         !! nz+1)` — see the corner add-on note above.  Typically the
         !! vertex kappa-shear `kd_corner` carrier (device-resident).
         !! Explicit-shape for the reason spelled out on the argument list
         !! above.
      real(wp), intent(in), optional :: kv_corner_prandtl
         !! Scale applied to `kv_corner_source` (Kv = Pr·Kd; the vertex
         !! kappa-shear `prandtl_turb`).  Default 1.

      integer :: nx, ny, nx_face, ny_uface, nx_vface, ny_face, nz
      logical :: do_stress, do_drag, do_remnant, solve_mom, do_corner, do_top
      real(wp) :: rho0_l, corner_prandtl_l

      nx = grid%nx_total
      ny = grid%ny_total
      nx_face = size(ms%u_face_x_layer, 1)
      ny_uface = size(ms%u_face_x_layer, 2)
      nx_vface = size(ms%v_face_y_layer, 1)
      ny_face = size(ms%v_face_y_layer, 2)
      nz = ms%nz_ml

      ! Only fold the boundary BCs when the slot knob is on AND the caller
      ! supplied the corresponding face field (driver wires both together).
      do_stress = this%implicit_stress .and. present(tau_u) .and. present(tau_v)
      do_drag = this%implicit_drag .and. present(lambda_bot_u) .and. present(lambda_bot_v)
      do_top = this%implicit_top_drag .and. present(lambda_top_u) .and. &
               present(lambda_top_v) .and. present(cover_u) .and. present(cover_v)
      do_remnant = present(visc_rem_u) .and. present(visc_rem_v)
      solve_mom = .true.
      if (present(remnant_only)) solve_mom = .not. remnant_only
      if (.not. solve_mom .and. .not. do_remnant) return   ! nothing to produce
      ! Reference density for the implicit stress fold.  The placeholder is
      ! inert — `rho0_l` only reaches an answer under `do_stress`, and that
      ! branch is guarded below — so it must never be a plausible-looking
      ! seawater density that a caller could silently inherit.
      rho0_l = 1.0_wp
      if (present(rho0)) then
         rho0_l = rho0
      else if (do_stress) then
         error stop "vdiff_apply_momentum: implicit_stress is on and tau_u/tau_v "// &
            "were supplied, but rho0 was not — pass the configured reference "// &
            "density (surface_stress%rho0, from &ocean_ic_nml rho_0)"
      end if
      do_corner = present(kv_corner_source)
      corner_prandtl_l = 1.0_wp
      if (present(kv_corner_prandtl)) corner_prandtl_l = kv_corner_prandtl
      ! Glue without the per-face MOM6 BBL (`bbl_per_face`, set at
      ! configure): the historical constant piston, `kv_bbl =
      ! bbl_piston·hbbl_visc` over `bbl_thick = hbbl_visc`, refilled every
      ! call so a hand-built slot that sets the knobs after `init` sees them.
      if (this%bbl_glue .and. .not. this%bbl_per_face) then
         call fill_bbl_constants(this%kv_bbl_u, this%bbl_thick_u, &
                                 this%bbl_piston*this%hbbl_visc, this%hbbl_visc, nx_face, ny_uface)
         call fill_bbl_constants(this%kv_bbl_v, this%bbl_thick_v, &
                                 this%bbl_piston*this%hbbl_visc, this%hbbl_visc, nx_vface, ny_face)
      end if

      if (present(kv_source)) then
         call diffuse_velocity_columns_impl( &
            nx_face, ny_uface, nz, dt, &
            ms%u_face_x_layer, ms%h_layer, kv_source, ms%wet_mask, &
            .true., nx, ny, this%use_harmonic, &
            this%a_diag_u%data, this%b_diag_u%data, &
            this%c_diag_u%data, this%rhs_u%data, &
            do_stress, do_drag, rho0_l, tau_u, lambda_bot_u, &
            do_top, lambda_top_u, cover_u, &
            solve_mom, do_remnant, visc_rem_u, &
            this%hvel_mom6, this%hbbl_visc, &
            this%bbl_glue, this%hvel_upwind, this%hvel_harmonic, &
            this%kv_bbl_u, this%bbl_thick_u, this%kv_bbl_bg, &
            do_corner, corner_prandtl_l, kv_corner_source, &
            this%zlevel_faces, ms%k_top_u, ms%k_bot_u)
         call diffuse_velocity_columns_impl( &
            nx_vface, ny_face, nz, dt, &
            ms%v_face_y_layer, ms%h_layer, kv_source, ms%wet_mask, &
            .false., nx, ny, this%use_harmonic, &
            this%a_diag_v%data, this%b_diag_v%data, &
            this%c_diag_v%data, this%rhs_v%data, &
            do_stress, do_drag, rho0_l, tau_v, lambda_bot_v, &
            do_top, lambda_top_v, cover_v, &
            solve_mom, do_remnant, visc_rem_v, &
            this%hvel_mom6, this%hbbl_visc, &
            this%bbl_glue, this%hvel_upwind, this%hvel_harmonic, &
            this%kv_bbl_v, this%bbl_thick_v, this%kv_bbl_bg, &
            do_corner, corner_prandtl_l, kv_corner_source, &
            this%zlevel_faces, ms%k_top_v, ms%k_bot_v)
      else
         ! Pure-vdiff no-op short-circuit ONLY when there is also no
         ! boundary forcing to fold; stress/drag/remnant must still be
         ! applied even at K_v = 0 (the surface Ekman + bed sink don't
         ! need interior viscosity to act, and an implicit_drag-only,
         ! K_v=0 config would otherwise silently leave visc_rem stale).
         ! A corner viscosity source likewise keeps the solve alive.
         if (this%K_v_momentum <= 0.0_wp .and. .not. do_stress .and. .not. do_drag &
             .and. .not. do_top .and. .not. do_remnant .and. .not. do_corner &
             .and. .not. this%bbl_glue) return
         call fill_kv_scalar_buf(this%kv_scalar_buf%data, &
                                 this%K_v_momentum, nx, ny, nz)
         call diffuse_velocity_columns_impl( &
            nx_face, ny_uface, nz, dt, &
            ms%u_face_x_layer, ms%h_layer, this%kv_scalar_buf%data, ms%wet_mask, &
            .true., nx, ny, this%use_harmonic, &
            this%a_diag_u%data, this%b_diag_u%data, &
            this%c_diag_u%data, this%rhs_u%data, &
            do_stress, do_drag, rho0_l, tau_u, lambda_bot_u, &
            do_top, lambda_top_u, cover_u, &
            solve_mom, do_remnant, visc_rem_u, &
            this%hvel_mom6, this%hbbl_visc, &
            this%bbl_glue, this%hvel_upwind, this%hvel_harmonic, &
            this%kv_bbl_u, this%bbl_thick_u, this%kv_bbl_bg, &
            do_corner, corner_prandtl_l, kv_corner_source, &
            this%zlevel_faces, ms%k_top_u, ms%k_bot_u)
         call diffuse_velocity_columns_impl( &
            nx_vface, ny_face, nz, dt, &
            ms%v_face_y_layer, ms%h_layer, this%kv_scalar_buf%data, ms%wet_mask, &
            .false., nx, ny, this%use_harmonic, &
            this%a_diag_v%data, this%b_diag_v%data, &
            this%c_diag_v%data, this%rhs_v%data, &
            do_stress, do_drag, rho0_l, tau_v, lambda_bot_v, &
            do_top, lambda_top_v, cover_v, &
            solve_mom, do_remnant, visc_rem_v, &
            this%hvel_mom6, this%hbbl_visc, &
            this%bbl_glue, this%hvel_upwind, this%hvel_harmonic, &
            this%kv_bbl_v, this%bbl_thick_v, this%kv_bbl_bg, &
            do_corner, corner_prandtl_l, kv_corner_source, &
            this%zlevel_faces, ms%k_top_v, ms%k_bot_v)
      end if
   end subroutine vdiff_apply_momentum