.. _SuiteGroupCaps: **************************************** 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 :numref:`Figure %s `. The CCPP *prebuild* script discussed in :numref:`Chapter %s ` 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 :numref:`Chapter %s `. These *caps* autogenerated by ``ccpp_prebuild.py`` reside in the directory defined by the ``CAPS_DIR`` variable (see example in :ref:`Listing 8.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 :numref:`Figure %s `) 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 :numref:`Section %s ` 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: .. code-block:: console ccpp/framework/doc/DevelopersGuide/CCPP_VARIABLES_FV3.tex * Makefile snippets that contain all *caps* to be compiled: .. code-block:: console ccpp/physics/CCPP_CAPS.{cmake,mk} * Makefile snippets that contain all schemes to be compiled: .. code-block:: console ccpp/physics/CCPP_SCHEMES.{cmake,mk} * List of CCPP types: .. code-block:: console ccpp/physics/CCPP_TYPEDEFS.{cmake,mk} * List of variables provided by host model: .. code-block:: console ccpp/physics/CCPP_VARIABLES_FV3.html * One *cap* per physics group (fast_physics, physics, radiation, time_vary, stochastic, …) for each suite: .. code-block:: console ccpp/physics/ccpp_{suite_name}_{group_name}_cap.F90 * *Cap* for each suite: .. code-block:: console ccpp/physics/ccpp_{suite_name}_cap.F90 * Autogenerated API (aka CCPP-Framework). .. code-block:: console 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 :ref:`Listing 5.2 `. .. _ccpp_physics_init: .. code-block:: fortran 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* :ref:`Listing 5.3 ` *and* :ref:`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: .. _FV3_GFS_v15_physics: .. code-block:: fortran 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. :ref:`Listing 5.4 ` is an example of ``FV3_GFS_v15_init_cap``. .. _FV3_GFS_v15_init_cap: .. code-block:: fortran 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.* .. _AutomaticUnitConversions: 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: .. code-block:: console 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. .. code-block:: fortran ! 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: .. code-block:: console 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.