This section describes the CAM interface module for a generic physics package. We assume that the physics package is implemented in a module, and that the CAM specific interfaces are implemented in a separate module. To obtain maximum benefit from the data encapsulation capability provided by Fortran 90 modules we recommend that the default access of the interface module be set to private via use of the private statement. Use of the only qualifier in all use statements is also recommended. This prevents a used module from introducing unwanted names into a scope, and also provides documentation of where variables and procedures come from.
The public methods of the CAM interface are summarized here, and described in detail below. The prefix param_XXXX is used to indicate a generic parameterization. Section 6 provides the specific names used for the CAM parameterizations.
This interface breaks the package initialization into several parts and is closely tied to CAM's initialization. We expect that a redesign of CAM's initialization will allow a simpler interface for the package initialization. But as that task is yet to be accomplished, we present a flexible interface that works with the current (as of CAM-2.0.1) CAM code.
The calling sequence of these methods is as follows. Implications of this sequence will be discussed in the sections that detail the method interfaces.
cam2 parse_namelist register param_XXXX_register inital ! dynamics and physics grids initialized read_inidat param_XXXX_init_cnst inti param_XXXX_init stepon physpkg advnce param_XXXX_timestep_init tphysbc ! moist physics, chemistry, and radiation packages param_XXXX_timestep_tend ! surface processes tphysac ! PBL, turbulence, gravity wave drag param_XXXX_timestep_tend
The only input facility currently available is Fortran's namelist. A package gains access to namelist input via public data in the interface module. The module variables are placed in the namelist, which is declared in procedure parse_namelist, and are made available via use association. This technique requires code modifications in parse_namelist unless the user is replacing a standard CAM package and can make use of the namelist variables already in place (these are detailed in section 6 on the specific CAM interfaces).
This template illustrates how a package would obtain namelist input for the variable var_in. The default value of the namelist variable is set with an initialization statement in the module; not in an executable statement in parse_namelist. A separate namelist statement is used for the variables used by each package. The namelist group is ccmexp for all namelist statements.
module param_XXXX ! Public data for namelist input integer, public ::& var_in = 0 ! set default values with initialization statements end module param_XXXX subroutine parse_namelist use param_XXXX, only: var_in namelist /ccmexp/ ... namelist /ccmexp/ var_in ! input for param_XXXX read (5,ccmexp,iostat=ierr) end subroutine parse_namelist
When the model runs in a distributed memory mode there is one extra step that must be taken. Since the namelist is only read on the master processor, subroutine distnl must be modified as follows to communicate var_in to the other MPI tasks:
subroutine distnl use param_XXXX, only: var_in call mpibcast(var_in, 1, mpiint, 0, mpicom) end subroutine distnl
This section provides detailed examples of each of the public interface methods. These methods provide the CAM specific interface, and contain the calls to the physics package's public methods. We assume that the physics package is contained in its own module and that its public methods are the only means by which the it communicates with the interface module, as required by the column physics interchange standards [1] [2].
The examples will assume that the interface is implemented in a module called param_XXXX, and that the physics package is implemented in a module called pkg_XXXX. The public interface methods will be presented with only the relevent declarations of the containing module.
The param_XXXX_register procedure is provided by packages to register constituent names and the corresponding physical properties, or to register the names and shapes of fields to be maintained across timesteps by the physics buffer. The registration method is separate from the initialization method because the constituent names must be known prior to reading initial condition data, and the package initialization procedure isn't called until after the initial file has been read. The registering of fields in the physics buffer must also happen early in the initialization process because the buffer must be allocated before the restart file is read during a restart run.
This routine is called from the CAM routine register which is called after parse_namelist. The following sample code registers an advected constituent and space for both a global field and a field which is local to physpkg in the physics buffer.
module param_XXXX use shr_kind_mod, only: r8=>shr_kind_r8 private public :: param_XXXX_register() ! Local variables integer :: & ixcnst1, &! global constituent index ixbuffld1, &! physics buffer index for BUFFLD1 ixbuffld2 ! physics buffer index for BUFFLD2 contains subroutine param_XXXX_register() use constituents, only: cnst_add, advected use phys_buffer, only: pbuf_add use ppgrid, only: pver implicit none ! request space in constituent array call cnst_add('CNST1', advected, 44., 666., 0., ixcnst1) ! request space in phys buffer for fields that persist across timesteps call pbuf_add('BUFFLD1', 'global', 1,1,1, .false., ixbuffld1) ! Request phys buffer space for fields that are local to physpkg. call pbuf_add('BUFFLD2', 'physpkg', 1,pver,1, .false., ixbuffld2) end subroutine param_XXXX_register end module param_XXXX
CAM initializes constituents in subroutine read_inidat. To make efficient use of memory only a single global constituent array is allocated and this array is subsequently scattered to the individual MPI processes when running in SPMD mode. read_inidat contains a loop over constituents and depending on the value of the namelist parameter readtrace either reads values from the initial file or calls the param_XXXX_init_cnst subroutines. Since read_inidat does not know which physics package is responsible for initializing each constituent it calls all the initializing subroutines with the name of the constituent as an argument and it is the responsibility of each param_XXXX_init_cnst subroutine to check whether or not it can intialize the requested constituent, and to return a global constituent array when appropriate.
The following sample code calls the physics package constituent initializer pkg_XXXX_init_cnst when the constituent that it knows how to initialize (CNST1) is requested. Note that the fld argument of param_XXXX_init_cnst has intent inout because the input values must be preserved when it is not known how to initialize the requested constituent.
module param_XXXX use shr_kind_mod, only: r8=>shr_kind_r8 private public :: param_XXXX_init_cnst contains subroutine param_XXXX_init_cnst(cnst_name, fld) use pmgrid, only: plon, plev, plat use pkg_XXXX, only: pkg_XXXX_init_cnst implicit none character(len=*), intent(in) :: cnst_name real(r8), dimension(plon,plev,plat), intent(inout) :: fld if (cnst_name == 'CNST1') then call pkg_XXXX_init_cnst(fld) end if end subroutine param_XXXX_init_cnst end module param_XXXX
The param_XXXX_init procedure is intended to perform time independent initializations. This procedure is called from subroutine inti after the initial data has been read and the model's prognostic variables have been initialized. Typically param_XXXX_init calls the physics package's initialization routine and registers the names of its output fields with the history module.
The following sample code calls the physics package initializer pkg_XXXX_init which does package specific initializations and sets the values of some physical constants. We assume that this package will produce tendencies for the dry static energy and all the constituents. It must therefore record those tendencies on the output history file. The names given to those tendencies in this example are arbitrary. However, if the tendencies have standard names (section 6), those names should be used to aid the post-processing of the output files. In the example we have also called add_default for each field to add the field to the default list for the primary history file.
module param_XXXX use ppgrid, only: ppcnst private public :: param_XXXX_init ! Local variables character(len=32) :: htendnam, qtendnam(ppcnst) ! tendency names contains subroutine param_XXXX_init() use physconst, only: cpair, cpwv, gravit, rair use history, only: addfld, physics_decomp, add_default use constituents, only: cnst_name use pkg_XXXX, only: pkg_XXXX_init implicit none call pkg_XXXX_init(cpair, cpwv, gravit, rair) ! Register output fields with the history module. htendnam = 'H_DTphys' do i = 1, ppcnst qtendnam(i) = cnst_name(i)//'_DTphys' end do call addfld(htendnam, 'J/kg/s', pver, 'A', & 'heating rate due to phys', physics_decomp) call add_default(htendnam, 1, ' ') do i = 1, ppcnst call addfld(qtendnam(i), 'kg/kg/s', pver, 'A', & cnst_name(i)//' tendency due to phys', physics_decomp) call add_default(qtendnam(i), 1, ' ') end do end subroutine param_XXXX_init end module param_XXXX
Subroutine param_XXXX_timestep_init is called at the top of the physics driver from subroutine advnce on each timestep. The reason this subroutine is separated from param_XXXX_timestep_tend is to provide an opportunity in a section of code that is not threaded for a package to perform per timestep tasks, such as interpolation of boundary data. This may involve reading data from files which only happens on the master MPI process, and communicating results to all MPI processes. This type of communication must occur in a non-threaded code region.
The param_XXXX_timestep_tend procedure provides the interface to the package's run procedure. It is called each timestep from either tphysbc or tphysac. The called package is expected to return tendencies of the model state for one model timestep. It is possible that the package subdivides the model timestep, or does nothing during a given timestep.
We illustrate wrapping the following generic run procedure for a package that returns tendencies for the dry static energy and constituent fields.
module pkg_XXXX private public :: pkg_XXXX_run contains subroutine pkg_XXXX_run(dt, pcol, ncol, plev, ppcnst, & dse, q, dhdt, dqdt ) implicit none real(rkind), intent(in) ::& dt ! timestep in seconds integer, intent(in) ::& pcol, &! column dimension ncol, &! number of columns plev, &! level dimension ppcnst ! constituent dimension real(rkind), dimension(pcol,plev), intent(in) ::& t ! temperature (K) real(rkind), dimension(pcol,plev,ppcnst), intent(in) ::& q ! constituent mixing ratio (kg/kg moist air) real(rkind), dimension(pcol,plev), intent(out) ::& dhdt ! heating rate (J/kg/s) real(rkind), dimension(pcol,plev,ppcnst), intent(out) ::& dqdt ! constituent tendency (kg/kg moist air/s) : end subroutine pkg_XXXX_run end module pkg_XXXX
The physics package real kind can be set using the kind parameter from the shr_kind_mod module. This is not necessary if rkind has been independently set to ensure 8-byte real values.
The interface routine translates between the CAM derived types and the primitive types passed through the physics package run procedure.
module param_XXXX use shr_kind_mod, only: r8=>shr_kind_r8 use ppgrid, only: pcols, pver, ppcnst private public :: param_XXXX_timestep_tend ! Local variables character(len=32) :: htendnam, qtendnam(ppcnst) ! tendency names contains subroutine param_XXXX_timestep_tend(state, ptend, dt, pbuf) use physics_types, only: physics_state, physics_ptend, physics_ptend_reset use phys_buffer, only: pbuf_size_max, pbuf_fld use history, only: outfld use pkg_XXXX, only: pkg_XXXX_run implicit none ! Arguments type(physics_state), intent(in) :: state ! state variables type(physics_ptend), intent(out) :: ptend ! package tendencies real(r8), intent(in) :: dt ! timestep type(pbuf_fld), intent(inout), dimension(pbuf_size_max) :: pbuf ! physics buffer ! Local variables integer :: lchnk ! chunk identifier integer :: ncol ! number of atmospheric columns in chunk real(r8), pointer, dimension(:) :: buffld1 ! physics buffer field1 real(r8), pointer, dimension(:,:) :: buffld2 ! physics buffer field2 ! Initialize output tendency structure call physics_ptend_reset(ptend) ptend%name = 'param_XXXX' ptend%ls = .true. ptend%lq = .true. ! Initialize chunk id and size lchnk = state%lchnk ncol = state%ncol ! associate local pointers with fields in the physics buffer buffld1 => pbuf(ixbuffld1)%fld_ptr(1,1:pcols,1, lchnk,1) buffld2 => pbuf(ixbuffld2)%fld_ptr(1,1:pcols,1:pver,lchnk,1) ! set up and call physics package driver call pkg_XXXX_run(dt, pcols, ncol, pver, ppcnst, & state%t, state%q, ptend%dhdt, ptend%dqdt, & buffld1, buffld2 ) ! write tendencies to history file call outfld(htendnam, ptend%dhdt, lchnk) do i = 1, ppcnst call outfld(qtendnam(i), ptend%dqdt(1,1,i), lchnk) end do ! update boundary quantities ptend%hflx_srf = 0. ptend%hflx_top = 0. ptend%cflx_srf = 0. ptend%cflx_top = 0. end subroutine param_XXXX_timestep_tend end module param_XXXX
The logical flags of the physics_ptend type are set to indicate which tendencies are returned by the physics package. The physics package's run routine is called with actual arguments from the CAM modules that specify array dimensions, and from the components of the CAM's derived types. On return from pkg_XXXX_run the outfld calls are made to put the package's forcings onto the output history file.