Skip to content

Spatially varying bottom drag coefficient#983

Merged
Hallberg-NOAA merged 2 commits into
NOAA-GFDL:dev/gfdlfrom
c2xu:c2xu/bottom_drag
Dec 3, 2025
Merged

Spatially varying bottom drag coefficient#983
Hallberg-NOAA merged 2 commits into
NOAA-GFDL:dev/gfdlfrom
c2xu:c2xu/bottom_drag

Conversation

@c2xu
Copy link
Copy Markdown

@c2xu c2xu commented Nov 9, 2025

A spatially varying bottom drag coefficient can be specified by providing a map of the spatially varying scaling factor.

@herrwang0
Copy link
Copy Markdown

I think some descriptions on the background would help explain why we need this addition. For example, we have the option to read a spatially varying background velocity for ustar with a tideamp file. The maths are obviously different, but I assume that must be a scenario that this new parameterization works better and/or is physically more justifiable than using a tideamp?

@awallcraft
Copy link
Copy Markdown

awallcraft commented Nov 9, 2025 via email

@c2xu c2xu marked this pull request as draft November 11, 2025 17:19
@c2xu c2xu force-pushed the c2xu/bottom_drag branch 3 times, most recently from f1f6098 to 2dc2cdd Compare November 27, 2025 19:59
@c2xu c2xu marked this pull request as ready for review November 27, 2025 20:19
@c2xu
Copy link
Copy Markdown
Author

c2xu commented Nov 27, 2025

Thanks @herrwang0 and @awallcraft. I agree that applying the scaling factor to CDRAG is the most straightforward way, and this is what's been done in the revised commit. In theory, CDRAG should be a function of the bottom roughness, and the ability to adjust CDRAG regionally could be helpful for coastal and estuarine models where the bottom roughness is spatially varying.

@awallcraft
Copy link
Copy Markdown

This looks good now, except that using a constant cdrag_2d array does not give the same answers as using CS%cdrag with the same constant value at open boundaries, as it should:

  if (.not.CS%bottomdragmap) then
    cdrag_sqrt = sqrt(CS%cdrag)
  endif

        if (CS%bottomdragmap) then
          if (m==1) then         
            cdrag_sqrt = sqrt(0.5 * (G%mask2dT(i,j) * CS%cdrag_2d(i,j) + &
                                     G%mask2dT(i+1,j) * CS%cdrag_2d(i+1,j)))
          else
            cdrag_sqrt = sqrt(0.5 * (G%mask2dT(i,j) * CS%cdrag_2d(i,j) + &
                                     G%mask2dT(i,j+1) * CS%cdrag_2d(i,j+1)))
          endif
        endif

This is the same approach used for tidal background, but it may be wrong there as well:

    ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant  
    if (CS%BBL_use_tidal_bg) then
      do i=is,ie ; if (do_i(i)) then ; if (m==1) then
        u2_bg(I) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
                         G%mask2dT(i+1,j)*(CS%tideamp(i+1,j)*CS%tideamp(i+1,j)) )
      else
        u2_bg(i) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
                         G%mask2dT(i,j+1)*(CS%tideamp(i,j+1)*CS%tideamp(i,j+1)) )
      endif ; endif ; enddo 
    else
      do i=is,ie ; if (do_i(i)) then
        u2_bg(i) = CS%drag_bg_vel * CS%drag_bg_vel
      endif ; enddo    
    endif

This edge case is handled in MOM_kappa_shear.F90:

  if (CS%vertex_shear_OBC_bug) then
    !$OMP parallel do default(shared)
    do k=1,nz
      do j=JsB,JeB+1 ; do I=IsB,IeB
        h_at_u(I,j,k) = G%mask2dCu(I,j) * (h(i,j,k) + h(i+1,j,k)) * 0.5
      enddo ; enddo
      do J=JsB,JeB ; do i=IsB,IeB+1
        h_at_v(i,J,k) = G%mask2dCv(i,J) * (h(i,j,k) + h(i,j+1,k)) * 0.5
      enddo ; enddo
    enddo
  else
    ! Because G%mask2dCu(I,j) is zero if either G%mask2dT(i,j) or G%mask2dT(i+1,j) except at OBC
    ! faces, the following form give equivalent answers to those above unless OBCs are in use,
    ! although the former is clearly less complicated and costly.
    !$OMP parallel do default(shared)
      do j=JsB,JeB+1 ; do I=IsB,IeB
        h_at_u(I,j,k) = G%mask2dCu(I,j) * (G%mask2dT(i,j) * h(i,j,k) + G%mask2dT(i+1,j) * h(i+1,j,k)) / &
                                          (G%mask2dT(i,j) + G%mask2dT(i+1,j) + 1.0e-36)
      enddo ; enddo
      do J=JsB,JeB ; do i=IsB,IeB+1
        h_at_v(i,J,k) = G%mask2dCv(i,J) * (G%mask2dT(i,j) * h(i,j,k) + G%mask2dT(i,j+1) * h(i,j+1,k)) / &
                                          (G%mask2dT(i,j) + G%mask2dT(i,j+1) + 1.0e-36)
      enddo ; enddo
    enddo
  endif

Instead of doing this, I suggest requiring cdrag_2d be defined over land.

        if (CS%bottomdragmap) then
          if (m==1) then         
            cdrag_sqrt = sqrt(0.5 * (CS%cdrag_2d(i,j) + CS%cdrag_2d(i+1,j)))
          else
            cdrag_sqrt = sqrt(0.5 * (CS%cdrag_2d(i,j) + CS%cdrag_2d(i,j+1)))
          endif
        endif
    call get_param(param_file, mdl, "CDRAG_VAR", cdrag_var, &
                 "The name of the variable in CDRAG_FILE with the spatially "//&
                 "varying bottom drag scaling factor at h points."//&
                 "Must be defined over land to allow averaging to velocity "//&
                 "points at open boundaries.", default="", &
                 do_not_log=.not.CS%bottomdragmap)

@c2xu
Copy link
Copy Markdown
Author

c2xu commented Nov 28, 2025

This looks good now, except that using a constant cdrag_2d array does not give the same answers as using CS%cdrag with the same constant value at open boundaries, as it should:

  if (.not.CS%bottomdragmap) then
    cdrag_sqrt = sqrt(CS%cdrag)
  endif

        if (CS%bottomdragmap) then
          if (m==1) then         
            cdrag_sqrt = sqrt(0.5 * (G%mask2dT(i,j) * CS%cdrag_2d(i,j) + &
                                     G%mask2dT(i+1,j) * CS%cdrag_2d(i+1,j)))
          else
            cdrag_sqrt = sqrt(0.5 * (G%mask2dT(i,j) * CS%cdrag_2d(i,j) + &
                                     G%mask2dT(i,j+1) * CS%cdrag_2d(i,j+1)))
          endif
        endif

This is the same approach used for tidal background, but it may be wrong there as well:

    ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant  
    if (CS%BBL_use_tidal_bg) then
      do i=is,ie ; if (do_i(i)) then ; if (m==1) then
        u2_bg(I) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
                         G%mask2dT(i+1,j)*(CS%tideamp(i+1,j)*CS%tideamp(i+1,j)) )
      else
        u2_bg(i) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
                         G%mask2dT(i,j+1)*(CS%tideamp(i,j+1)*CS%tideamp(i,j+1)) )
      endif ; endif ; enddo 
    else
      do i=is,ie ; if (do_i(i)) then
        u2_bg(i) = CS%drag_bg_vel * CS%drag_bg_vel
      endif ; enddo    
    endif

This edge case is handled in MOM_kappa_shear.F90:

  if (CS%vertex_shear_OBC_bug) then
    !$OMP parallel do default(shared)
    do k=1,nz
      do j=JsB,JeB+1 ; do I=IsB,IeB
        h_at_u(I,j,k) = G%mask2dCu(I,j) * (h(i,j,k) + h(i+1,j,k)) * 0.5
      enddo ; enddo
      do J=JsB,JeB ; do i=IsB,IeB+1
        h_at_v(i,J,k) = G%mask2dCv(i,J) * (h(i,j,k) + h(i,j+1,k)) * 0.5
      enddo ; enddo
    enddo
  else
    ! Because G%mask2dCu(I,j) is zero if either G%mask2dT(i,j) or G%mask2dT(i+1,j) except at OBC
    ! faces, the following form give equivalent answers to those above unless OBCs are in use,
    ! although the former is clearly less complicated and costly.
    !$OMP parallel do default(shared)
      do j=JsB,JeB+1 ; do I=IsB,IeB
        h_at_u(I,j,k) = G%mask2dCu(I,j) * (G%mask2dT(i,j) * h(i,j,k) + G%mask2dT(i+1,j) * h(i+1,j,k)) / &
                                          (G%mask2dT(i,j) + G%mask2dT(i+1,j) + 1.0e-36)
      enddo ; enddo
      do J=JsB,JeB ; do i=IsB,IeB+1
        h_at_v(i,J,k) = G%mask2dCv(i,J) * (G%mask2dT(i,j) * h(i,j,k) + G%mask2dT(i,j+1) * h(i,j+1,k)) / &
                                          (G%mask2dT(i,j) + G%mask2dT(i,j+1) + 1.0e-36)
      enddo ; enddo
    enddo
  endif

Instead of doing this, I suggest requiring cdrag_2d be defined over land.

        if (CS%bottomdragmap) then
          if (m==1) then         
            cdrag_sqrt = sqrt(0.5 * (CS%cdrag_2d(i,j) + CS%cdrag_2d(i+1,j)))
          else
            cdrag_sqrt = sqrt(0.5 * (CS%cdrag_2d(i,j) + CS%cdrag_2d(i,j+1)))
          endif
        endif
   call get_param(param_file, mdl, "CDRAG_VAR", cdrag_var, &
                "The name of the variable in CDRAG_FILE with the spatially "//&
                "varying bottom drag scaling factor at h points."//&
                "Must be defined over land to allow averaging to velocity "//&
                "points at open boundaries.", default="", &
                do_not_log=.not.CS%bottomdragmap)

Thanks, I did not know these details. I've made changes as suggested in a separate commit, and also fixed the same issue in tidal background velocity

Copy link
Copy Markdown

@herrwang0 herrwang0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification and I agree this addition is a useful and valuable feature.

I agree with Alan that OBC mask needs to be handled properly. I like the simplicity and generality of the current solution, but I was wondering if it would make sense for a more defensive approach, which does not rely on the user's due diligence to include land points in input files.

I think something like the following should do the trick, and you can move it to set_visc_init to precalculate cdrag_2d_[uv].

if (G%mask2du(I,j) > 0) then
  CS%c_drag_u(I,j) = 1.0 / (G%mask2dT(i,j) + G%mask2dT(i+1,j)) &
      * (G%mask2dT(i,j) * cdrag_2d(i,j) + G%mask2dT(i+1,j) * cdrag_2d(i+1,j))
endif

Note that there would still be an issue if OBC_PROJECTION_BUG = True (tracer point outside of OBC would be unmasked), but I am not sure if it worths the trouble to do cover that scenairo.

Comment thread src/parameterizations/vertical/MOM_set_viscosity.F90
Comment thread src/parameterizations/vertical/MOM_set_viscosity.F90 Outdated
Comment thread src/parameterizations/vertical/MOM_set_viscosity.F90 Outdated
@c2xu c2xu force-pushed the c2xu/bottom_drag branch from 030bc1b to 2b2f6b5 Compare December 1, 2025 15:26
@c2xu c2xu requested a review from herrwang0 December 1, 2025 18:46
Copy link
Copy Markdown

@herrwang0 herrwang0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@Hallberg-NOAA
Copy link
Copy Markdown
Member

Overall this PR is looking good, but do we need to add error handling for the case where CDRAG_MAP is set to true but no values are provided for CDRAG_FILE or CDRAG_VAR? As written, I think that the model will fail in that case but there will not necessarily be a very useful error message that points back to the unspecified input parameters.

Copy link
Copy Markdown
Member

@Hallberg-NOAA Hallberg-NOAA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please test what happens in the case when CDRAG_MAP is set to true but no values are provided for CDRAG_FILE or CDRAG_VAR, and if the resulting error messages are not very informative, please add appropriate error handling for these cases.

@c2xu c2xu force-pushed the c2xu/bottom_drag branch from 2b2f6b5 to 017d1b0 Compare December 3, 2025 12:35
@c2xu
Copy link
Copy Markdown
Author

c2xu commented Dec 3, 2025

Please test what happens in the case when CDRAG_MAP is set to true but no values are provided for CDRAG_FILE or CDRAG_VAR, and if the resulting error messages are not very informative, please add appropriate error handling for these cases.

Thanks, an error message has been added.

@c2xu c2xu force-pushed the c2xu/bottom_drag branch from 017d1b0 to 06e5128 Compare December 3, 2025 12:45
@c2xu c2xu requested a review from Hallberg-NOAA December 3, 2025 12:46
Copy link
Copy Markdown
Member

@Hallberg-NOAA Hallberg-NOAA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is now looking good to me.

c2xu added 2 commits December 3, 2025 11:08
The spatially varying bottom drag coefficient can be specified by
providing a map of the spatially varying scaling factor.
Fixed the inconsistency at open boundaries when CDRAG_MAP is true.
@Hallberg-NOAA
Copy link
Copy Markdown
Member

This PR has passed pipeline testing at https://gitlab.gfdl.noaa.gov/ogrp/mom6ci/MOM6/-/pipelines/29412 with the expected warnings about a new runtime parameter.

@Hallberg-NOAA Hallberg-NOAA merged commit f2c8917 into NOAA-GFDL:dev/gfdl Dec 3, 2025
52 checks passed
@c2xu c2xu deleted the c2xu/bottom_drag branch December 12, 2025 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants