5. Suite and Group Caps

The connection between the host model and the physics schemes through the CCPP-Framework is realized with caps on both sides as illustrated in Figure 1.1. The CCPP prebuild script discussed in Chapter 3 generates the caps that connect the physics schemes to the CCPP-Framework. This chapter describes the suite and group caps, while the host model caps are described in Chapter 6. These caps autogenerated by ccpp_prebuild.py reside in the directory defined by the CAPS_DIR variable (see example in Listing 8.1).

5.1. Overview

When CCPP is built, the CCPP-Framework and physics are statically linked to the executable. This allows the best performance and efficient memory use. This build requires metadata provided by the host model and variables requested from the physics scheme. Only the variables required for the specified suites are kept, requiring one or more SDFs (see left side of Figure 3.1) as arguments to the ccpp_prebuild.py script. The CCPP prebuild step performs the tasks below.

  • Check requested vs provided variables by standard_name.

  • Check units, rank, type. Perform unit conversions if a mismatch of units is detected and the required conversion has been implemented (see Section 5.2 for details).

  • Filter unused schemes and variables.

  • Create Fortran code for the static Application Programming Interface (API).

  • Create caps for groups and suite(s).

  • Populate makefiles with schemes and caps.

The prebuild step will produce the following files for the UFS Atmosphere:

  • List of variables provided by host model and required by physics:

ccpp/framework/doc/DevelopersGuide/CCPP_VARIABLES_FV3.tex
  • Makefile snippets that contain all caps to be compiled:

ccpp/physics/CCPP_CAPS.{cmake,mk}
  • Makefile snippets that contain all schemes to be compiled:

ccpp/physics/CCPP_SCHEMES.{cmake,mk}
  • List of CCPP types:

ccpp/physics/CCPP_TYPEDEFS.{cmake,mk}
  • List of variables provided by host model:

ccpp/physics/CCPP_VARIABLES_FV3.html
  • One cap per physics group (fast_physics, physics, radiation, time_vary, stochastic, …) for each suite:

ccpp/physics/ccpp_{suite_name}_{group_name}_cap.F90
  • Cap for each suite:

ccpp/physics/ccpp_{suite_name}_cap.F90
  • Autogenerated API (aka CCPP-Framework).

FV3/gfsphysics/CCPP_layer/ccpp_static_api.F90

ccpp_static_api.F90 is an interface, which contains subroutines ccpp_physics_init, ccpp_physics_run and ccpp_physics_finalize. Each subroutine uses a suite_name and an optional argument, group_name, to call the groups of a specified suite (e.g. fast_physics, physics, time_vary, radiation, stochastic, etc.), or to call the entire suite. For example, ccpp_static_api.F90 would contain module ccpp_static_api with subroutines ccpp_physics_{init, run, finalize}. The subroutine ccpp_physics_init from the autogenerated code using suites FV3_GFS_v15 and FV3_CPT_v0 is shown in Listing 5.2.

subroutine ccpp_physics_init(cdata, suite_name, group_name, ierr)
  use ccpp_types, only : ccpp_t
  implicit none
  type(ccpp_t),               intent(inout) :: cdata
  character(len=*),           intent(in)    :: suite_name
  character(len=*), optional, intent(in)    :: group_name
  integer,                    intent(out)   :: ierr
  ierr = 0
  if (trim(suite_name)=="FV3_GFS_v15") then
    if (present(group_name)) then
      if (trim(group_name)=="fast_physics") then
        ierr = FV3_GFS_v15_fast_physics_init_cap(cdata=cdata, CCPP_interstitial=CCPP_interstitial)
      else if (trim(group_name)=="time_vary") then
        ierr = FV3_GFS_v15_time_vary_init_cap(GFS_Interstitial=GFS_Interstitial, &
               cdata=cdata,GFS_Data=GFS_Data, GFS_Control=GFS_Control)
      else if (trim(group_name)=="radiation") then
        ierr = FV3_GFS_v15_radiation_init_cap()
      else if (trim(group_name)=="physics") then
        ierr = FV3_GFS_v15_physics_init_cap(cdata=cdata, GFS_Control=GFS_Control)
      else if (trim(group_name)=="stochastics") then
        ierr = FV3_GFS_v15_stochastics_init_cap()
      else
        write(cdata%errmsg, '(*(a))') "Group " // trim(group_name) // " not found"
        ierr = 1
      end if
    else
      ierr = FV3_GFS_v15_init_cap(GFS_Interstitial=GFS_Interstitial, cdata=cdata,GFS_Control=GFS_Control, &
            GFS_Data=GFS_Data, CCPP_interstitial=CCPP_interstitial)
    end if
  else if (trim(suite_name)=="FV3_CPT_v0") then
    if (present(group_name)) then
      if (trim(group_name)=="time_vary") then
        ierr = FV3_CPT_v0_time_vary_init_cap(GFS_Interstitial=GFS_Interstitial, &
               cdata=cdata,GFS_Data=GFS_Data, GFS_Control=GFS_Control)
      else if (trim(group_name)=="radiation") then
        ierr = FV3_CPT_v0_radiation_init_cap()
      else if (trim(group_name)=="physics") then
        ierr = FV3_CPT_v0_physics_init_cap(con_hfus=con_hfus, &
                  GFS_Control=GFS_Control,con_hvap=con_hvap, &
                  con_rd=con_rd,con_rv=con_rv,con_g=con_g, &
                  con_ttp=con_ttp,con_cp=con_cp,cdata=cdata)
      else if (trim(group_name)=="stochastics") then
        ierr = FV3_CPT_v0_stochastics_init_cap()
      else
        write(cdata%errmsg, '(*(a))') "Group " // trim(group_name) // " not found"
        ierr = 1
      end if
    else
      ierr = FV3_CPT_v0_init_cap(con_g=con_g, GFS_Data=GFS_Data,GFS_Control=GFS_Control, &
             con_hvap=con_hvap,GFS_Interstitial=GFS_Interstitial, con_rd=con_rd,con_rv=con_rv, &
             con_hfus=con_hfus, con_ttp=con_ttp,con_cp=con_cp,cdata=cdata)
    end if
  else
    write(cdata%errmsg,'(*(a))'), 'Invalid suite ' // trim(suite_name)
    ierr = 1
  end if
  cdata%errflg = ierr
end subroutine ccpp_physics_init

Listing 5.2: Code sample of subroutine ccpp_physics_init contained in the autogenerated file ccpp_static_api.F90 for the multi-suite build. This cap was generated using suites FV3_GFS_v15 and FV3_CPT_v0. Examples of the highlighted functions are shown below in Listing 5.3 and Listing 5.4.

Note that if group_name is set, specified groups (i.e. FV3_GFS_v15_physics_init_cap) are called for the specified suite_name. These functions are defined in ccpp_{suite_name}_{group_name}_cap.F90, in this case ccpp_FV3_GFS_v15_physics_cap.F90. For example:

function FV3_GFS_v15_physics_init_cap(cdata,GFS_Control)&
        result(ierr)
   use ccpp_types, only: ccpp_t
   use GFS_typedefs, only: GFS_control_type
   implicit none
   integer                     :: ierr
   type(ccpp_t), intent(inout) :: cdata
   type(GFS_control_type), intent(in) :: GFS_Control
   ierr = 0
   if (initialized) return
   call lsm_noah_init(me=GFS_Control%me,isot=GFS_Control%isot,&
         ivegsrc=GFS_Control%ivegsrc,nlunit=GFS_Control%nlunit, &
         errmsg=cdata%errmsg,errflg=cdata%errflg)
   if (cdata%errflg/=0) then
     write(cdata%errmsg,'(a)') "An error occured in lsm_noah_init"
     ierr=cdata%errflg
     return
   end if
   call gfdl_cloud_microphys_init(me=GFS_Control%me, &
        master=GFS_Control%master,nlunit=GFS_Control%nlunit, &
        input_nml_file=GFS_Control%input_nml_file, &
        logunit=GFS_Control%logunit,fn_nml=GFS_Control%fn_nml, &
        imp_physics=GFS_Control%imp_physics, &
        imp_physics_gfdl=GFS_Control%imp_physics_gfdl, &
        do_shoc=GFS_Control%do_shoc, &
        errmsg=cdata%errmsg,errflg=cdata%errflg)
   if (cdata%errflg/=0) then
     write(cdata%errmsg,'(a)') "An error occured in &
           gfdl_cloud_microphys_init"
     ierr=cdata%errflg
     return
   end if
   initialized = .true.
end function FV3_GFS_v15_physics_init_cap

Listing 5.3: The FV3_GFS_v15_physics_init_cap contained in the autogenerated file ccpp_FV3_GFS_v15_physics_cap.F90 showing calls to the lsm_noah_init , and gfdl_cloud_microphys_init subroutines for the build for suite ‘FV3_GFS_v15’ and group ‘physics’.

If the group_name is not specified for a specified suite_name, the suite is called from the autogenerated ccpp_static_api.F90, which calls the init, run and finalize routines for each group. Listing 5.4 is an example of FV3_GFS_v15_init_cap.

function FV3_GFS_v15_init_cap(GFS_Interstitial, &
  cdata,GFS_Control,GFS_Data,CCPP_interstitial) result(ierr)
  use GFS_typedefs, only: GFS_interstitial_type
  use ccpp_types, only: ccpp_t
  use GFS_typedefs, only: GFS_control_type
  use GFS_typedefs, only: GFS_data_type
  use CCPP_typedefs, only: CCPP_interstitial_type

  implicit none

  integer :: ierr
  type(GFS_interstitial_type), intent(inout) :: GFS_Interstitial(:)
  type(ccpp_t), intent(inout) :: cdata
  type(GFS_control_type), intent(inout) :: GFS_Control
  type(GFS_data_type), intent(inout) :: GFS_Data(:)
  type(CCPP_interstitial_type), intent(in) :: CCPP_interstitial

  ierr = 0
  ierr = FV3_GFS_v15_fast_physics_init_cap(cdata=cdata, CCPP_interstitial=CCPP_interstitial)
  if (ierr/=0) return

  ierr = FV3_GFS_v15_time_vary_init_cap (GFS_Interstitial=GFS_Interstitial,cdata=cdata, &
         GFS_Data=GFS_Data,GFS_Control=GFS_Control)
  if (ierr/=0) return

  ierr = FV3_GFS_v15_radiation_init_cap()
  if (ierr/=0) return
  ierr = FV3_GFS_v15_physics_init_cap(cdata=cdata, &
      GFS_Control=GFS_Control)
  if (ierr/=0) return

  ierr = FV3_GFS_v15_stochastics_init_cap()
  if (ierr/=0) return
end function FV3_GFS_v15_init_cap

Listing 5.4: Condensed version of the FV3_GFS_v15_init_cap function contained in the autogenerated file ccpp_FV3_GFS_v15_cap.F90 showing calls to the group caps FV3_GFS_v15_fast_physics_init_cap, FV3_GFS_v15_time_vary_init_cap , etc. for the build where a group name is not specified.

5.2. Automatic unit conversions

The CCPP framework is capable of performing automatic unit conversions if a mismatch of units between the host model and a physics scheme is detected, provided that the required unit conversion has been implemented.

If a mismatch of units is detected and an automatic unit conversion can be performed, the CCPP prebuild script will document this with a log message as in the following example:

INFO: Comparing metadata for requested and provided variables ...
INFO: Automatic unit conversion from m to um for effective_radius_of_stratiform_cloud_ice_particle_in_um after returning from MODULE_mp_thompson SCHEME_mp_thompson SUBROUTINE_mp_thompson_run
INFO: Automatic unit conversion from m to um for effective_radius_of_stratiform_cloud_liquid_water_particle_in_um after returning from MODULE_mp_thompson SCHEME_mp_thompson SUBROUTINE_mp_thompson_run
INFO: Automatic unit conversion from m to um for effective_radius_of_stratiform_cloud_snow_particle_in_um after returning from MODULE_mp_thompson SCHEME_mp_thompson SUBROUTINE_mp_thompson_run
INFO: Generating schemes makefile/cmakefile snippet ...

The CCPP framework is performing only the minimum unit conversions necessary, depending on the intent information of the variable in the parameterization’s metadata table. In the above example, the cloud effective radii are intent(out) variables, which means that no unit conversion is required before entering the subroutine mp_thompson_run. Below are examples for auto-generated code performing automatic unit conversions from m to um or back, depending on the intent of the variable. The conversions are performed in the individual physics scheme caps for the dynamic build, or the group caps for the build.

! var1 is intent(in)
        call mp_thompson_run(...,recloud=1.0E-6_kind_phys*re_cloud,...,errmsg=cdata%errmsg,errflg=cdata%errflg)
        ierr=cdata%errflg

! var1 is intent(inout)
        allocate(tmpvar1, source=re_cloud)
        tmpvar1 = 1.0E-6_kind_phys*re_cloud
        call mp_thompson_run(...,re_cloud=tmpvar1,...,errmsg=cdata%errmsg,errflg=cdata%errflg)
        ierr=cdata%errflg
        re_cloud = 1.0E+6_kind_phys*tmpvar1
        deallocate(tmpvar1)

! var1 is intent(out)
        allocate(tmpvar1, source=re_cloud)
        call mp_thompson_run(...,re_cloud=tmpvar1,...,errmsg=cdata%errmsg,errflg=cdata%errflg)
        ierr=cdata%errflg
        re_cloud = 1.0E+6_kind_phys*tmpvar1
        deallocate(tmpvar1)

If a required unit conversion has not been implemented the CCPP prebuild script will generate an error message as follows:

INFO: Comparing metadata for requested and provided variables ...
ERROR: Error, automatic unit conversion from m to pc for effective_radius_of_stratiform_cloud_ice_particle_in_um in MODULE_mp_thompson SCHEME_mp_thompson SUBROUTINE_mp_thompson_run not implemented

All automatic unit conversions are implemented in ccpp/framework/scripts/conversion_tools/unit_conversion.py, new unit conversions can be added to this file by following the existing examples.