LCOV - code coverage report
Current view: top level - control - scamMod.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 7 72 9.7 %
Date: 2025-03-13 18:42:46 Functions: 1 1 100.0 %

          Line data    Source code
       1             : module scamMod
       2             :   !----------------------------------------------------------------------
       3             :   !
       4             :   ! This module provides control variables and namelist functionality
       5             :   ! for SCAM.
       6             :   !
       7             :   ! As with global CAM, SCAM is initialized with state information
       8             :   ! from the initial and boundary files. For each succeeding timestep
       9             :   ! SCAM calculates the physics tendencies and combines these with
      10             :   ! the advective tendencies provided by the IOP file to produce
      11             :   ! a forcast. Generally, the control variables in this module
      12             :   ! determine what data and parameterizations will be used to make
      13             :   ! the forecast. For instance, many of the control variables in
      14             :   ! this module provide flexibility to affect the forecast by overriding
      15             :   ! parameterization prognosed tendencies with observed tendencies
      16             :   ! of a particular field program recorded on the IOP file.
      17             :   ! 
      18             :   ! Public functions/subroutines:
      19             :   !   scam_readnl
      20             :   !-----------------------------------------------------------------------
      21             : 
      22             : use shr_kind_mod,   only: r8 => shr_kind_r8
      23             : use pmgrid,         only: plon, plat, plev, plevp
      24             : use constituents,   only: pcnst
      25             : use shr_scam_mod,   only: shr_scam_getCloseLatLon
      26             : use dycore,         only: dycore_is
      27             : use cam_logfile,    only: iulog
      28             : use cam_abortutils, only: endrun
      29             : 
      30             : implicit none
      31             : private
      32             : 
      33             : ! PUBLIC INTERFACES:
      34             : 
      35             : public scam_readnl   ! read SCAM namelist options 
      36             : 
      37             : ! PUBLIC MODULE DATA:
      38             : 
      39             : real(r8), public ::  pressure_levels(plev)
      40             : real(r8), public ::  scmlat   ! input namelist latitude for scam
      41             : real(r8), public ::  scmlon   ! input namelist longitude for scam
      42             : 
      43             : 
      44             : integer, parameter :: num_switches = 20
      45             : integer, parameter :: max_path_len = 128
      46             : 
      47             : logical, public ::  single_column         ! Using IOP file or not
      48             : logical, public ::  use_iop               ! Using IOP file or not
      49             : logical, public ::  use_pert_init         ! perturb initial values
      50             : logical, public ::  use_pert_frc          ! perturb forcing 
      51             : logical, public ::  switch(num_switches)  ! Logical flag settings from GUI
      52             : logical, public ::  l_uvphys              ! If true, update u/v after TPHYS
      53             : logical, public ::  l_uvadvect            ! If true, T, U & V will be passed to SLT
      54             : logical, public ::  l_conv                ! use flux divergence terms for T and q?     
      55             : logical, public ::  l_divtr               ! use flux divergence terms for constituents?
      56             : logical, public ::  l_diag                ! do we want available diagnostics?
      57             : 
      58             : integer, public ::  error_code            ! Error code from netCDF reads
      59             : integer, public ::  initTimeIdx
      60             : integer, public ::  seedval
      61             : 
      62             : character*(max_path_len), public ::  modelfile
      63             : character*(max_path_len), public ::  analysisfile
      64             : character*(max_path_len), public ::  sicfile
      65             : character*(max_path_len), public ::  userfile
      66             : character*(max_path_len), public ::  sstfile
      67             : character*(max_path_len), public ::  lsmpftfile
      68             : character*(max_path_len), public ::  pressfile
      69             : character*(max_path_len), public ::  topofile
      70             : character*(max_path_len), public ::  ozonefile
      71             : character*(max_path_len), public ::  iopfile
      72             : character*(max_path_len), public ::  absemsfile
      73             : character*(max_path_len), public ::  aermassfile
      74             : character*(max_path_len), public ::  aeropticsfile
      75             : character*(max_path_len), public ::  timeinvfile
      76             : character*(max_path_len), public ::  lsmsurffile
      77             : character*(max_path_len), public ::  lsminifile
      78             : 
      79             : ! note that scm_zadv_q is set to slt to be consistent with CAM BFB testing
      80             : 
      81             : 
      82             : character(len=16), public    :: scm_zadv_T  = 'eulc            '
      83             : character(len=16), public    :: scm_zadv_q  = 'slt             '
      84             : character(len=16), public    :: scm_zadv_uv = 'eulc            '
      85             : 
      86             : real(r8), public ::  fixmascam
      87             : real(r8), public ::  betacam
      88             : real(r8), public ::  alphacam(pcnst)
      89             : real(r8), public ::  dqfxcam(plon,plev,pcnst)
      90             : 
      91             : real(r8), public ::      divq3d(plev,pcnst)  ! 3D q advection
      92             : real(r8), public ::      divt3d(plev)        ! 3D T advection
      93             : real(r8), public ::      divu3d(plev)        ! 3D U advection
      94             : real(r8), public ::      divv3d(plev)        ! 3D V advection
      95             : real(r8), public ::      vertdivq(plev,pcnst)! vertical q advection
      96             : real(r8), public ::      vertdivt(plev)      ! vertical T advection
      97             : real(r8), public ::      vertdivu(plev)      ! vertical T advection
      98             : real(r8), public ::      vertdivv(plev)      ! vertical T advection
      99             : real(r8), public ::      ptend               ! surface pressure tendency
     100             : real(r8), public ::      qdiff(plev)         ! model minus observed humidity
     101             : real(r8), public ::      qobs(plev)          ! actual W.V. Mixing ratio
     102             : real(r8), public ::      qinitobs(plev,pcnst)! initial tracer field
     103             : real(r8), public ::      cldliqobs(plev)     ! actual W.V. Mixing ratio
     104             : real(r8), public ::      cldiceobs(plev)     ! actual W.V. Mixing ratio
     105             : real(r8), public ::      numliqobs(plev)     ! actual 
     106             : real(r8), public ::      numiceobs(plev)     ! actual 
     107             : real(r8), public ::      precobs(1)          ! observed precipitation 
     108             : real(r8), public ::      lhflxobs(1)         ! observed surface latent heat flux 
     109             : real(r8), public ::      shflxobs(1)         ! observed surface sensible heat flux
     110             : real(r8), public ::      q1obs(plev)         ! observed apparent heat source
     111             : real(r8), public ::      q2obs(plev)         ! observed apparent heat sink
     112             : real(r8), public ::      tdiff(plev)         ! model minus observed temp 
     113             : real(r8), public ::      tground(1)          ! ground temperature
     114             : real(r8), public ::      tobs(plev)          ! actual temperature
     115             : real(r8), public ::      tsair(1)            ! air temperature at the surface
     116             : real(r8), public ::      udiff(plev)         ! model minus observed uwind
     117             : real(r8), public ::      uobs(plev)          ! actual u wind
     118             : real(r8), public ::      vdiff(plev)         ! model minus observed vwind
     119             : real(r8), public ::      vobs(plev)          ! actual v wind
     120             : real(r8), public ::      cldobs(plev)        ! observed cld
     121             : real(r8), public ::      clwpobs(plev)       ! observed clwp
     122             : real(r8), public ::      aldirobs(1)         ! observed aldir
     123             : real(r8), public ::      aldifobs(1)         ! observed aldif
     124             : real(r8), public ::      asdirobs(1)         ! observed asdir
     125             : real(r8), public ::      asdifobs(1)         ! observed asdif
     126             : 
     127             : real(r8), public ::      wfld(plev)          ! Vertical motion (slt)
     128             : real(r8), public ::      wfldh(plevp)        ! Vertical motion (slt)
     129             : real(r8), public ::      divq(plev,pcnst)    ! Divergence of moisture
     130             : real(r8), public ::      divt(plev)          ! Divergence of temperature
     131             : real(r8), public ::      divu(plev)          ! Horiz Divergence of E/W
     132             : real(r8), public ::      divv(plev)          ! Horiz Divergence of N/S
     133             :                                              ! mo_drydep algorithm
     134             : real(r8), public, pointer :: loniop(:)
     135             : real(r8), public, pointer :: latiop(:)
     136             : 
     137             : integer, public ::     iopTimeIdx            ! index into iop dataset
     138             : integer, public ::     steplength            ! Length of time-step
     139             : integer, public ::     base_date             ! Date in (yyyymmdd) of start time
     140             : integer, public ::     base_secs             ! Time of day of start time (sec)
     141             : 
     142             : ! SCAM public data defaults
     143             : 
     144             : logical, public ::  doiopupdate            = .false. ! do we need to read next iop timepoint
     145             : logical, public ::  have_lhflx             = .false. ! dataset contains lhflx 
     146             : logical, public ::  have_shflx             = .false. ! dataset contains shflx
     147             : logical, public ::  have_tg                = .false. ! dataset contains tg
     148             : logical, public ::  have_tsair             = .false. ! dataset contains tsair
     149             : logical, public ::  have_divq              = .false. ! dataset contains divq 
     150             : logical, public ::  have_divt              = .false. ! dataset contains divt
     151             : logical, public ::  have_divq3d            = .false. ! dataset contains divq3d 
     152             : logical, public ::  have_vertdivu          = .false. ! dataset contains vertdivu
     153             : logical, public ::  have_vertdivv          = .false. ! dataset contains vertdivv
     154             : logical, public ::  have_vertdivt          = .false. ! dataset contains vertdivt
     155             : logical, public ::  have_vertdivq          = .false. ! dataset contains vertdivq 
     156             : logical, public ::  have_divt3d            = .false. ! dataset contains divt3d
     157             : logical, public ::  have_divu3d            = .false. ! dataset contains divu3d
     158             : logical, public ::  have_divv3d            = .false. ! dataset contains divv3d
     159             : logical, public ::  have_divu              = .false. ! dataset contains divu
     160             : logical, public ::  have_divv              = .false. ! dataset contains divv 
     161             : logical, public ::  have_omega             = .false. ! dataset contains omega
     162             : logical, public ::  have_phis              = .false. ! dataset contains phis
     163             : logical, public ::  have_ptend             = .false. ! dataset contains ptend
     164             : logical, public ::  have_ps                = .false. ! dataset contains ps
     165             : logical, public ::  have_q                 = .false. ! dataset contains q
     166             : logical, public ::  have_q1                = .false. ! dataset contains Q1
     167             : logical, public ::  have_q2                = .false. ! dataset contains Q2
     168             : logical, public ::  have_prec              = .false. ! dataset contains prec 
     169             : logical, public ::  have_t                 = .false. ! dataset contains t
     170             : logical, public ::  have_u                 = .false. ! dataset contains u 
     171             : logical, public ::  have_v                 = .false. ! dataset contains v 
     172             : logical, public ::  have_cld               = .false. ! dataset contains cld
     173             : logical, public ::  have_cldliq            = .false. ! dataset contains cldliq
     174             : logical, public ::  have_cldice            = .false. ! dataset contains cldice
     175             : logical, public ::  have_numliq            = .false. ! dataset contains numliq
     176             : logical, public ::  have_numice            = .false. ! dataset contains numice
     177             : logical, public ::  have_clwp              = .false. ! dataset contains clwp
     178             : logical, public ::  have_aldir             = .false. ! dataset contains aldir
     179             : logical, public ::  have_aldif             = .false. ! dataset contains aldif
     180             : logical, public ::  have_asdir             = .false. ! dataset contains asdir
     181             : logical, public ::  have_asdif             = .false. ! dataset contains asdif
     182             : logical, public ::  use_camiop             = .false. ! use cam generated forcing 
     183             : logical, public ::  use_3dfrc              = .false. ! use 3d forcing
     184             : logical, public ::  isrestart              = .false. ! If this is a restart step or not
     185             :   
     186             : ! SCAM namelist defaults
     187             : 
     188             : logical, public ::  scm_backfill_iop_w_init = .false. ! Backfill missing IOP data from initial file
     189             : logical, public ::  scm_relaxation         = .false. ! Use relaxation
     190             : logical, public ::  scm_crm_mode           = .false. ! Use column radiation mode
     191             : logical, public ::  scm_cambfb_mode        = .false. ! Use extra CAM IOP fields to assure bit for bit match with CAM run
     192             : logical, public ::  scm_use_obs_T          = .false. ! Use the SCAM-IOP specified observed T   at each time step instead of forecasting.
     193             : logical, public ::  scm_force_latlon       = .false. ! force scam to use the lat lon fields specified in the scam namelist not what is closest to iop avail lat lon
     194             : real*8, public              ::  scm_relax_top_p       = 1.e36_r8 ! upper bound for scm relaxation
     195             : real*8, public              ::  scm_relax_bot_p       = -1.e36_r8 !  lower bound for scm relaxation
     196             : real*8, public              ::  scm_relax_tau_sec       = 10800._r8  ! relaxation time constant (sec)
     197             : 
     198             : ! +++BPM:
     199             : ! modification... allow a linear ramp in relaxation time scale:
     200             : logical, public :: scm_relax_linear = .false.
     201             : real*8, public    :: scm_relax_tau_bot_sec = 10800._r8
     202             : real*8, public    :: scm_relax_tau_top_sec = 10800._r8
     203             : character(len=26), public  :: scm_relax_fincl(pcnst)
     204             : 
     205             : !
     206             : ! note that scm_use_obs_uv is set to true to be consistent with CAM BFB testing
     207             : !
     208             : 
     209             : logical, public ::  scm_use_obs_uv         = .true. ! Use the SCAM-IOP specified observed u,v at each time step instead of forecasting.
     210             : 
     211             : logical, public ::  scm_use_obs_qv         = .false. ! Use the SCAM-IOP specified observed qv  at each time step instead of forecasting.
     212             : logical, public ::  scm_iop_lhflxshflxTg   = .false. !turn off LW rad
     213             : logical, public ::  scm_iop_Tg             = .false. !turn off LW rad
     214             : 
     215             : character(len=200), public ::  scm_clubb_iop_name   ! IOP name for CLUBB
     216             : 
     217             : !=======================================================================
     218             : contains
     219             : !=======================================================================
     220             : 
     221        1536 : subroutine scam_readnl(nlfile,single_column_in,scmlat_in,scmlon_in)
     222             : 
     223             :   use namelist_utils,  only: find_group_name
     224             :   use units,           only: getunit, freeunit
     225             :   use dycore,          only: dycore_is
     226             :   use wrap_nf,         only: wrap_open
     227             :   use spmd_utils,      only : masterproc,npes
     228             :   use netcdf,          only : nf90_inquire_attribute,NF90_NOERR,NF90_GLOBAL,NF90_NOWRITE
     229             : 
     230             : 
     231             : !---------------------------Arguments-----------------------------------
     232             : 
     233             :   character(len=*), intent(in) :: nlfile  ! filepath for file containing namelist input  (nlfile=atm_in)
     234             :   logical,          intent(in) :: single_column_in
     235             :   real(r8),         intent(in) :: scmlat_in
     236             :   real(r8),         intent(in) :: scmlon_in
     237             : 
     238             :   ! Local variables
     239             :   character(len=*), parameter :: sub = 'scam_readnl'
     240             :   integer :: unitn, ierr, i
     241             :   integer  :: ncid
     242             :   integer  :: iatt
     243             :   integer  :: latidx, lonidx
     244             :   logical  :: adv
     245             :   real(r8) :: ioplat,ioplon
     246             : 
     247             : ! this list should include any variable that you might want to include in the namelist
     248             :   namelist /scam_nl/ iopfile, scm_iop_lhflxshflxTg, scm_iop_Tg, scm_relaxation, &
     249             :        scm_relax_top_p,scm_relax_bot_p,scm_relax_tau_sec, &
     250             :        scm_cambfb_mode,scm_crm_mode,scm_zadv_uv,scm_zadv_T,scm_zadv_q,&
     251             :        scm_use_obs_T, scm_use_obs_uv, scm_use_obs_qv, &
     252             :        scm_relax_linear, scm_relax_tau_top_sec, &
     253             :        scm_relax_tau_bot_sec, scm_force_latlon, scm_relax_fincl, scm_backfill_iop_w_init
     254             : 
     255        1536 :   single_column=single_column_in
     256             : 
     257        1536 :   iopfile            = ' '
     258        1536 :   scm_clubb_iop_name = ' '
     259       64512 :   scm_relax_fincl(:) = ' '
     260             :   
     261        1536 :   if( single_column ) then
     262           0 :      if( npes.gt.1) call endrun('SCAM_READNL: SCAM doesnt support using more than 1 pe.')
     263             : 
     264           0 :      if (.not. dycore_is('EUL') .or. plon /= 1 .or. plat /=1 ) then 
     265           0 :         call endrun('SCAM_SETOPTS: must compile model for SCAM mode when namelist parameter single_column is .true.')
     266             :      endif
     267             : 
     268           0 :      scmlat=scmlat_in
     269           0 :      scmlon=scmlon_in
     270             :      
     271           0 :      if( scmlat .lt. -90._r8 .or. scmlat .gt. 90._r8 ) then
     272           0 :         call endrun('SCAM_READNL: SCMLAT must be between -90. and 90. degrees.')
     273           0 :      elseif( scmlon .lt. 0._r8 .or. scmlon .gt. 360._r8 ) then
     274           0 :         call endrun('SCAM_READNL: SCMLON must be between 0. and 360. degrees.')
     275             :      end if
     276             :      
     277             :      ! Read namelist
     278           0 :      if (masterproc) then
     279           0 :         unitn = getunit()
     280           0 :         open( unitn, file=trim(nlfile), status='old' )
     281           0 :         call find_group_name(unitn, 'scam_nl', status=ierr)
     282           0 :         if (ierr == 0) then
     283           0 :            read(unitn, scam_nl, iostat=ierr)
     284           0 :            if (ierr /= 0) then
     285           0 :               call endrun(sub // ':: ERROR reading namelist')
     286             :            end if
     287             :         end if
     288           0 :         close(unitn)
     289           0 :         call freeunit(unitn)
     290             :      end if
     291             :      
     292             :      ! Error checking:
     293             :      
     294           0 :      iopfile = trim(iopfile)
     295           0 :      if( iopfile .ne. "" ) then 
     296           0 :         use_iop = .true.
     297             :      else
     298           0 :         call endrun('SCAM_READNL: must specify IOP file for single column mode')
     299             :      endif
     300             : 
     301           0 :      call wrap_open( iopfile, NF90_NOWRITE, ncid )
     302             : 
     303           0 :      if( nf90_inquire_attribute( ncid, NF90_GLOBAL, 'CAM_GENERATED_FORCING', iatt ) .EQ. NF90_NOERR ) then
     304           0 :         use_camiop = .true.
     305             :      else
     306           0 :         use_camiop = .false.
     307             :      endif
     308             :      
     309             :      ! If we are not forcing the lat and lon from the namelist use the closest lat and lon that is found in the IOP file.
     310           0 :      if (.not.scm_force_latlon) then
     311           0 :         call shr_scam_GetCloseLatLon( ncid, scmlat, scmlon, ioplat, ioplon, latidx, lonidx )
     312           0 :         write(iulog,*) 'SCAM_READNL: using closest IOP column to lat/lon specified in drv_in'
     313           0 :         write(iulog,*) '   requested lat,lon    =',scmlat,', ',scmlon
     314           0 :         write(iulog,*) '   closest IOP lat,lon  =',ioplat,', ',ioplon
     315             :      
     316           0 :         scmlat = ioplat
     317           0 :         scmlon = ioplon
     318             :      end if
     319             :      
     320           0 :      if (masterproc) then
     321           0 :         write (iulog,*) 'Single Column Model Options: '
     322           0 :         write (iulog,*) '============================='
     323           0 :         write (iulog,*) '  iopfile                     = ',trim(iopfile)
     324           0 :         write (iulog,*) '  scm_backfill_iop_w_init     = ',scm_backfill_iop_w_init
     325           0 :         write (iulog,*) '  scm_cambfb_mode             = ',scm_cambfb_mode
     326           0 :         write (iulog,*) '  scm_crm_mode                = ',scm_crm_mode
     327           0 :         write (iulog,*) '  scm_force_latlon            = ',scm_force_latlon
     328           0 :         write (iulog,*) '  scm_iop_Tg                  = ',scm_iop_Tg
     329           0 :         write (iulog,*) '  scm_iop_lhflxshflxTg        = ',scm_iop_lhflxshflxTg
     330           0 :         write (iulog,*) '  scm_relaxation              = ',scm_relaxation
     331           0 :         write (iulog,*) '  scm_relax_bot_p             = ',scm_relax_bot_p
     332           0 :         write (iulog,*) '  scm_relax_linear            = ',scm_relax_linear
     333           0 :         write (iulog,*) '  scm_relax_tau_bot_sec       = ',scm_relax_tau_bot_sec
     334           0 :         write (iulog,*) '  scm_relax_tau_sec           = ',scm_relax_tau_sec
     335           0 :         write (iulog,*) '  scm_relax_tau_top_sec       = ',scm_relax_tau_top_sec
     336           0 :         write (iulog,*) '  scm_relax_top_p             = ',scm_relax_top_p
     337           0 :         write (iulog,*) '  scm_use_obs_T               = ',scm_use_obs_T
     338           0 :         write (iulog,*) '  scm_use_obs_qv              = ',scm_use_obs_qv
     339           0 :         write (iulog,*) '  scm_use_obs_uv              = ',scm_use_obs_uv
     340           0 :         write (iulog,*) '  scm_zadv_T                  = ',trim(scm_zadv_T)
     341           0 :         write (iulog,*) '  scm_zadv_q                  = ',trim(scm_zadv_q)
     342           0 :         write (iulog,*) '  scm_zadv_uv                 = ',trim(scm_zadv_uv)
     343           0 :         write (iulog,*) '  scm_relax_finc: '
     344             :         ! output scm_relax_fincl character array
     345           0 :         do i=1,pcnst
     346           0 :            if (scm_relax_fincl(i) .ne. '') then
     347           0 :               adv = mod(i,4)==0
     348           0 :               if (adv) then
     349           0 :                  write (iulog, "(A18)") "'"//trim(scm_relax_fincl(i))//"',"
     350             :               else
     351           0 :                  write (iulog, "(A18)", ADVANCE="NO") "'"//trim(scm_relax_fincl(i))//"',"
     352             :               end if
     353             :            else
     354             :               exit
     355             :            end if
     356             :         end do
     357           0 :         print *
     358             :      end if
     359             :   end if
     360             :      
     361        1536 : end subroutine scam_readnl
     362             : 
     363             : !===============================================================================
     364             : 
     365             : end module scamMod

Generated by: LCOV version 1.14