tests_sgs_statistics Function

public function tests_sgs_statistics()

Uses

  • proc~~tests_sgs_statistics~~UsesGraph proc~tests_sgs_statistics tests_sgs_statistics decomp_2d decomp_2d proc~tests_sgs_statistics->decomp_2d module~initfac initfac proc~tests_sgs_statistics->module~initfac module~modboundary modboundary proc~tests_sgs_statistics->module~modboundary module~modfields modfields proc~tests_sgs_statistics->module~modfields module~modglobal modglobal proc~tests_sgs_statistics->module~modglobal module~modibm modibm proc~tests_sgs_statistics->module~modibm module~modstatsdump modstatsdump proc~tests_sgs_statistics->module~modstatsdump module~modsubgrid modsubgrid proc~tests_sgs_statistics->module~modsubgrid module~initfac->module~modglobal module~modmpi modmpi module~initfac->module~modmpi mpi mpi module~initfac->mpi netcdf netcdf module~initfac->netcdf module~modboundary->mpi module~modfields->decomp_2d module~modibmdata modibmdata module~modibm->module~modibmdata module~modibm->mpi module~modstatsdump->module~modglobal module~modstatsdump->module~modmpi module~modstatsdump->mpi module~modsubgriddata modsubgriddata module~modsubgrid->module~modsubgriddata module~modsubgrid->mpi module~modmpi->mpi

Arguments

None

Return Value logical


Calls

proc~~tests_sgs_statistics~~CallsGraph proc~tests_sgs_statistics tests_sgs_statistics proc~compare_real_3d compare_real_3d proc~tests_sgs_statistics->proc~compare_real_3d proc~compute_sgs_fluxes compute_sgs_fluxes proc~tests_sgs_statistics->proc~compute_sgs_fluxes proc~createmasks createmasks proc~tests_sgs_statistics->proc~createmasks proc~diffc diffc proc~tests_sgs_statistics->proc~diffc proc~diffu diffu proc~tests_sgs_statistics->proc~diffu proc~diffw diffw proc~tests_sgs_statistics->proc~diffw proc~initboundary initboundary proc~tests_sgs_statistics->proc~initboundary proc~initfields initfields proc~tests_sgs_statistics->proc~initfields proc~initibm initibm proc~tests_sgs_statistics->proc~initibm proc~initsubgrid initsubgrid proc~tests_sgs_statistics->proc~initsubgrid proc~readfacetfiles readfacetfiles proc~tests_sgs_statistics->proc~readfacetfiles zstart zstart proc~tests_sgs_statistics->zstart mpi_allreduce mpi_allreduce proc~createmasks->mpi_allreduce alloc_z alloc_z proc~initfields->alloc_z exchange_halo_z exchange_halo_z proc~initibm->exchange_halo_z proc~define_nc define_nc proc~initibm->proc~define_nc proc~initibmnorm initibmnorm proc~initibm->proc~initibmnorm proc~initibmwallfun initibmwallfun proc~initibm->proc~initibmwallfun proc~ncinfo ncinfo proc~initibm->proc~ncinfo proc~open_nc open_nc proc~initibm->proc~open_nc proc~solid solid proc~initibm->proc~solid proc~writestat_dims_nc writestat_dims_nc proc~initibm->proc~writestat_dims_nc proc~subgridnamelist subgridnamelist proc~initsubgrid->proc~subgridnamelist mpi_bcast mpi_bcast proc~readfacetfiles->mpi_bcast nf90_get_var nf90_get_var proc~readfacetfiles->nf90_get_var nf90_inq_varid nf90_inq_varid proc~readfacetfiles->nf90_inq_varid nf90_open nf90_open proc~readfacetfiles->nf90_open proc~qsat qsat proc~readfacetfiles->proc~qsat proc~define_nc->nf90_inq_varid nf90_def_var nf90_def_var proc~define_nc->nf90_def_var nf90_enddef nf90_enddef proc~define_nc->nf90_enddef nf90_inq_dimid nf90_inq_dimid proc~define_nc->nf90_inq_dimid nf90_put_att nf90_put_att proc~define_nc->nf90_put_att nf90_redef nf90_redef proc~define_nc->nf90_redef proc~nchandle_error nchandle_error proc~define_nc->proc~nchandle_error proc~read_sparse_ijk read_sparse_ijk proc~initibmnorm->proc~read_sparse_ijk proc~initibmwallfun->zstart proc~initibmwallfun->mpi_bcast mpi_abort mpi_abort proc~initibmwallfun->mpi_abort proc~alignment alignment proc~initibmwallfun->proc~alignment proc~plane_line_intersection plane_line_intersection proc~initibmwallfun->proc~plane_line_intersection proc~initibmwallfun->proc~read_sparse_ijk zend zend proc~initibmwallfun->zend proc~open_nc->nf90_get_var proc~open_nc->nf90_inq_varid proc~open_nc->nf90_open nf90_create nf90_create proc~open_nc->nf90_create nf90_def_dim nf90_def_dim proc~open_nc->nf90_def_dim proc~open_nc->nf90_def_var proc~open_nc->nf90_enddef proc~open_nc->nf90_inq_dimid nf90_inquire nf90_inquire proc~open_nc->nf90_inquire nf90_inquire_dimension nf90_inquire_dimension proc~open_nc->nf90_inquire_dimension proc~open_nc->nf90_put_att nf90_sync nf90_sync proc~open_nc->nf90_sync proc~subgridnamelist->mpi_bcast proc~writestat_dims_nc->nf90_inq_varid proc~writestat_dims_nc->nf90_inquire_dimension nf90_put_var nf90_put_var proc~writestat_dims_nc->nf90_put_var proc~is_equal is_equal proc~alignment->proc~is_equal nf90_strerror nf90_strerror proc~nchandle_error->nf90_strerror proc~read_sparse_ijk->zstart proc~read_sparse_ijk->mpi_bcast proc~read_sparse_ijk->zend

Called by

proc~~tests_sgs_statistics~~CalledByGraph proc~tests_sgs_statistics tests_sgs_statistics proc~execute_runmode_actions execute_runmode_actions proc~execute_runmode_actions->proc~tests_sgs_statistics program~udales uDALES program~udales->proc~execute_runmode_actions

Source Code

  logical function tests_sgs_statistics()
    use modglobal,    only : ib, ie, ih, jb, je, jh, kb, ke, kh, runmode, &
                             dx, dzf, dzfi, dzh, dzhi, zf, zh
    use modfields,    only : initfields, um, vm, wm, thlm, u0, v0, w0, thl0, up, wp, thlp
    use modsubgrid,   only : initsubgrid, diffc, diffu, diffw, ekh, ekm
    use modboundary,  only : initboundary
    use modibm,       only : initibm, createmasks
    use modstatsdump, only : compute_sgs_fluxes
    use initfac,      only : readfacetfiles
    use decomp_2d,    only : zstart

    implicit none

    real, parameter :: acoef = 0.37   ! u   = acoef*z^2
    real, parameter :: bcoef = 0.21   ! w   = bcoef*x + dcoef*z
    real, parameter :: ccoef = 0.53   ! thl = ccoef*z^2
    real, parameter :: dcoef = 0.43
    real, parameter :: nuconst = 0.17 ! constant ekm for the manufactured solution
    real, parameter :: kaconst = 0.29 ! constant ekh for the manufactured solution

    real, allocatable :: usgs(:,:,:), vsgs(:,:,:), wsgs(:,:,:)
    real, allocatable :: thlsgs(:,:,:), qtsgs(:,:,:)
    real, allocatable :: sv1sgs(:,:,:), sv2sgs(:,:,:), sv3sgs(:,:,:), sv4sgs(:,:,:)
    real, allocatable :: zc(:), zw(:), got(:,:,:), exp(:,:,:)
    logical :: all_passed
    real    :: trunc_tol, scale
    integer :: i, j, k, gi

    if (myid == 0) then
      write(*, '(A)') '================================================'
      write(*, '(A, I8)') 'runmode = ', runmode
      write(*, '(A)') 'tests_sgs_statistics: SGS STATISTICS TEST'
      write(*, '(A)') '------------------------------------------------'
      write(*, '(A)') 'Using stretched-grid case 300 to validate modstatsdump SGS fluxes'
    end if

    call initfields
    call initboundary
    call initsubgrid
    call readfacetfiles
    call initibm
    call createmasks

    allocate(usgs  (ib-ih:ie+ih,jb-jh:je+jh,kb:ke+kh))
    allocate(vsgs  (ib-ih:ie+ih,jb-jh:je+jh,kb:ke+kh))
    allocate(wsgs  (ib-ih:ie+ih,jb-jh:je+jh,kb:ke+kh))
    allocate(thlsgs(ib-ih:ie+ih,jb-jh:je+jh,kb:ke+kh))
    allocate(qtsgs (ib-ih:ie+ih,jb-jh:je+jh,kb:ke+kh))
    allocate(sv1sgs(ib:ie,jb:je,kb:ke+kh))
    allocate(sv2sgs(ib:ie,jb:je,kb:ke+kh))
    allocate(sv3sgs(ib:ie,jb:je,kb:ke+kh))
    allocate(sv4sgs(ib:ie,jb:je,kb:ke+kh))
    allocate(got(ib:ie,jb:je,kb:ke+kh), exp(ib:ie,jb:je,kb:ke+kh))

    ! Cell-centre heights, extended one level below the surface by mirroring
    ! (zh(kb) = 0), which is the level the kb stencils actually reach into.
    allocate(zc(kb-kh:ke+kh), zw(kb-kh:ke+kh))
    do k = kb, ke+kh
      zc(k) = zf(k)
      zw(k) = zh(k)
    end do
    zc(kb-kh) = zf(kb) - dzh(kb)
    zw(kb-kh) = zh(kb) - dzf(kb)

    all_passed = .true.

    !----------------------------------------------------------------------
    ! (a) Manufactured solution on the stretched grid
    !----------------------------------------------------------------------
    ekm = nuconst
    ekh = kaconst

    do k = kb-kh, ke+kh
      do j = jb-jh, je+jh
        do i = ib-ih, ie+ih
          gi = zstart(1) + i - 1
          um(i,j,k)   = acoef * zc(k)**2
          vm(i,j,k)   = 0.
          wm(i,j,k)   = bcoef * (real(gi) - 0.5) * dx + dcoef * zw(k)
          thlm(i,j,k) = ccoef * zc(k)**2
        end do
      end do
    end do

    call compute_sgs_fluxes(usgs,vsgs,wsgs,thlsgs,qtsgs,sv1sgs,sv2sgs,sv3sgs,sv4sgs)

    ! (a1) exact discrete identity: the second-order stencils are exact for a
    !      quadratic in z and a linear in x, so no truncation error enters here.
    do k = kb, ke+kh
      got(:,:,k) = usgs(ib:ie,jb:je,k)
      exp(:,:,k) = -nuconst * (acoef*(zc(k) + zc(k-1)) + bcoef)
    end do
    if (.not. compare_real_3d('usgs discrete', got, exp, 1.e-10)) all_passed = .false.

    do k = kb, ke+kh
      got(:,:,k) = thlsgs(ib:ie,jb:je,k)
      exp(:,:,k) = -kaconst * ccoef * (zc(k) + zc(k-1))
    end do
    if (.not. compare_real_3d('thlsgs discrete', got, exp, 1.e-10)) all_passed = .false.

    ! wsgs is the normal stress tau_33 = -2*K_m*dw/dz at the CELL CENTRE, so with
    ! dw/dz = dcoef it is exactly -2*nu*dcoef everywhere. If wsgs were the
    ! vertical part of the diffw tendency instead (units m/s^2, not m^2/s^2) this
    ! would be identically zero for a linear w and the check would fail.
    do k = kb, ke
      got(:,:,k) = wsgs(ib:ie,jb:je,k)
      exp(:,:,k) = -2. * nuconst * dcoef
    end do
    got(:,:,ke+kh) = 0.; exp(:,:,ke+kh) = 0.
    if (.not. compare_real_3d('wsgs discrete', got, exp, 1.e-10)) all_passed = .false.

    ! (a2) second-order consistency with the analytic flux at the w-levels.
    !      The stretched grid gives zf(k)+zf(k-1) - 2*zh(k) = (dzf(k)-dzf(k-1))/2,
    !      so anything worse than that bound (a dzf/dzh swap, a missing factor,
    !      a sign error) is caught here.
    trunc_tol = 0.
    do k = kb+1, ke+kh
      trunc_tol = max(trunc_tol, abs(dzf(k) - dzf(k-1)))
    end do
    trunc_tol = 0.5 * trunc_tol

    do k = kb, ke+kh
      got(:,:,k) = usgs(ib:ie,jb:je,k)
      exp(:,:,k) = -nuconst * (2.*acoef*zh(k) + bcoef)
    end do
    if (.not. compare_real_3d('usgs vs analytic', got, exp, &
                              1.05*nuconst*acoef*trunc_tol + 1.e-10)) all_passed = .false.

    do k = kb, ke+kh
      got(:,:,k) = thlsgs(ib:ie,jb:je,k)
      exp(:,:,k) = -kaconst * 2.*ccoef * zh(k)
    end do
    if (.not. compare_real_3d('thlsgs vs analytic', got, exp, &
                              1.05*kaconst*ccoef*trunc_tol + 1.e-10)) all_passed = .false.

    !----------------------------------------------------------------------
    ! (c) Sign regression: monotone positive shear must give a negative
    !     (down-gradient) SGS momentum flux. One-line guard on the convention.
    !----------------------------------------------------------------------
    if (maxval(usgs(ib:ie,jb:je,kb+1:ke)) >= 0.) then
      if (myid == 0) write(*,'(A,1X,A,1X,ES12.4)') 'FAIL', 'usgs sign', &
           maxval(usgs(ib:ie,jb:je,kb+1:ke))
      all_passed = .false.
    end if
    if (maxval(thlsgs(ib:ie,jb:je,kb+1:ke)) >= 0.) then
      if (myid == 0) write(*,'(A,1X,A,1X,ES12.4)') 'FAIL', 'thlsgs sign', &
           maxval(thlsgs(ib:ie,jb:je,kb+1:ke))
      all_passed = .false.
    end if

    !----------------------------------------------------------------------
    ! (b) Consistency with the solver's diffusion terms.
    !     u, v and thl vary in z only and w varies in x only, so the x- and
    !     y-terms of diffu/diffc vanish identically and what is left is exactly
    !     minus the vertical divergence of the SGS fluxes.
    !----------------------------------------------------------------------
    do k = kb-kh, ke+kh
      do j = jb-jh, je+jh
        do i = ib-ih, ie+ih
          gi = zstart(1) + i - 1
          ekm(i,j,k)  = 0.5 + 0.4*sin(1.7*real(gi) + 0.9*real(j) + 0.31*real(k))
          ekh(i,j,k)  = 0.6 + 0.3*cos(0.7*real(gi) - 1.1*real(j) + 0.53*real(k))
          um(i,j,k)   = sin(0.41*real(k)) + 0.3*cos(0.17*real(k))
          vm(i,j,k)   = cos(0.23*real(k)) - 0.2*sin(0.61*real(k))
          wm(i,j,k)   = 0.7*sin(0.29*(real(gi) - 0.5)*dx)
          thlm(i,j,k) = 288. + sin(0.37*real(k)) + 0.4*cos(0.13*real(k))
        end do
      end do
    end do
    u0   = um
    v0   = vm
    w0   = wm
    thl0 = thlm

    call compute_sgs_fluxes(usgs,vsgs,wsgs,thlsgs,qtsgs,sv1sgs,sv2sgs,sv3sgs,sv4sgs)

    up   = 0.
    thlp = 0.
    call diffu(up)
    call diffc(ih,jh,kh,thl0,thlp)

    do k = kb, ke
      got(:,:,k) = up(ib:ie,jb:je,k)
      exp(:,:,k) = -(usgs(ib:ie,jb:je,k+1) - usgs(ib:ie,jb:je,k)) * dzfi(k)
    end do
    got(:,:,ke+kh) = 0.; exp(:,:,ke+kh) = 0.
    scale = max(maxval(abs(exp)), 1.)
    if (.not. compare_real_3d('diffu vs div(usgs)', got, exp, 1.e-10*scale)) all_passed = .false.

    do k = kb, ke
      got(:,:,k) = thlp(ib:ie,jb:je,k)
      exp(:,:,k) = -(thlsgs(ib:ie,jb:je,k+1) - thlsgs(ib:ie,jb:je,k)) * dzfi(k)
    end do
    got(:,:,ke+kh) = 0.; exp(:,:,ke+kh) = 0.
    scale = max(maxval(abs(exp)), 1.)
    if (.not. compare_real_3d('diffc vs div(thlsgs)', got, exp, 1.e-10*scale)) all_passed = .false.

    ! Same idea for the normal stress: with u = v = 0 and w a function of z only,
    ! the horizontal terms of diffw vanish and what is left is exactly minus the
    ! vertical divergence of wsgs. This is the check that pins wsgs to a flux
    ! (m^2/s^2) rather than a tendency (m/s^2).
    do k = kb-kh, ke+kh
      do j = jb-jh, je+jh
        do i = ib-ih, ie+ih
          um(i,j,k) = 0.
          vm(i,j,k) = 0.
          wm(i,j,k) = sin(0.33*real(k)) + 0.5*cos(0.19*real(k))
        end do
      end do
    end do
    u0 = um
    v0 = vm
    w0 = wm

    call compute_sgs_fluxes(usgs,vsgs,wsgs,thlsgs,qtsgs,sv1sgs,sv2sgs,sv3sgs,sv4sgs)

    wp = 0.
    call diffw(wp)

    got = 0.; exp = 0.
    do k = kb+1, ke
      got(:,:,k) = wp(ib:ie,jb:je,k)
      exp(:,:,k) = -(wsgs(ib:ie,jb:je,k) - wsgs(ib:ie,jb:je,k-1)) * dzhi(k)
    end do
    scale = max(maxval(abs(exp)), 1.)
    if (.not. compare_real_3d('diffw vs div(wsgs)', got, exp, 1.e-10*scale)) all_passed = .false.

    deallocate(usgs, vsgs, wsgs, thlsgs, qtsgs, sv1sgs, sv2sgs, sv3sgs, sv4sgs, got, exp, zc, zw)

    if (all_passed .and. myid == 0) then
      write(*, '(A)') '------------------------------------------------'
      write(*, '(A)') 'ALL TESTS PASSED: tests_sgs_statistics'
      write(*, '(A)') '================================================'
    else if ((.not. all_passed) .and. myid == 0) then
      write(*, '(A)') '------------------------------------------------'
      write(*, '(A)') 'TESTS FAILED: tests_sgs_statistics'
      write(*, '(A)') '================================================'
    end if

    tests_sgs_statistics = all_passed

  end function tests_sgs_statistics