Skip to content

CCPP Integration

This guide covers integrating CATChem inline with the Common Community Physics Package (CCPP) framework used in the UFS directly in the atmosphere (ATM) component. This option is important for research as some processes require close interactions between chemistry and physics. Considering the interactive and interdependent nature of chemistry and physics, it is useful for certain applications to insert the aerosol or chemistry modules directly inside the physics suites.

Overview

CCPP integration enables CATChem to work seamlessly with the CCPP framework, providing:

  • Standardized Interface - CCPP-compliant subroutine signatures and metadata
  • Automatic Integration - CCPP-generated calling sequences and type definitions
  • Host Model Compatibility - Works with any CCPP-enabled host model
  • Performance Optimization - Optimized data structures and memory layout

CCPP Interface Architecture

CCPP Wrapper Module

module ccpp_catchem_interface
  use ccpp_types, only: ccpp_t
  use ccpp_fields, only: ccpp_field_t
  use catchem_mod
  implicit none
  private

  public :: ccpp_catchem_init
  public :: ccpp_catchem_run
  public :: ccpp_catchem_finalize

contains

  subroutine ccpp_catchem_init(ccpp_data, errmsg, errflg)
    type(ccpp_t), intent(inout) :: ccpp_data
    character(len=*), intent(out) :: errmsg
    integer, intent(out) :: errflg

    ! Initialize CATChem using CCPP data
    call catchem_ccpp_init(ccpp_data, errmsg, errflg)
  end subroutine ccpp_catchem_init

  subroutine ccpp_catchem_run(ccpp_data, dt, errmsg, errflg)
    type(ccpp_t), intent(inout) :: ccpp_data
    real(kind_phys), intent(in) :: dt
    character(len=*), intent(out) :: errmsg
    integer, intent(out) :: errflg

    ! Run CATChem processes
    call catchem_ccpp_run(ccpp_data, dt, errmsg, errflg)
  end subroutine ccpp_catchem_run

  subroutine ccpp_catchem_finalize(ccpp_data, errmsg, errflg)
    type(ccpp_t), intent(inout) :: ccpp_data
    character(len=*), intent(out) :: errmsg
    integer, intent(out) :: errflg

    ! Finalize CATChem
    call catchem_ccpp_finalize(ccpp_data, errmsg, errflg)
  end subroutine ccpp_catchem_finalize

end module ccpp_catchem_interface

CCPP Metadata File (ccpp_catchem_interface.meta)

[ccpp-table-properties]
  name = ccpp_catchem_interface
  type = scheme
  dependencies = catchem_mod.F90,catchem_types.F90

########################################################################
[ccpp-arg-table]
  name = ccpp_catchem_init
  type = scheme
[im]
  standard_name = horizontal_loop_extent
  long_name = horizontal loop extent
  units = count
  dimensions = ()
  type = integer
  intent = in
[km]
  standard_name = vertical_layer_dimension
  long_name = vertical layer dimension
  units = count
  dimensions = ()
  type = integer
  intent = in
[dt]
  standard_name = timestep_for_chemistry
  long_name = chemistry timestep
  units = s
  dimensions = ()
  type = real
  kind = kind_phys
  intent = in
[temperature]
  standard_name = air_temperature
  long_name = layer mean air temperature
  units = K
  dimensions = (horizontal_loop_extent,vertical_layer_dimension)
  type = real
  kind = kind_phys
  intent = in
[pressure]
  standard_name = air_pressure
  long_name = layer mean air pressure
  units = Pa
  dimensions = (horizontal_loop_extent,vertical_layer_dimension)
  type = real
  kind = kind_phys
  intent = in
[specific_humidity]
  standard_name = specific_humidity
  long_name = water vapor specific humidity
  units = kg kg-1
  dimensions = (horizontal_loop_extent,vertical_layer_dimension)
  type = real
  kind = kind_phys
  intent = in
[o3_mixing_ratio]
  standard_name = ozone_mixing_ratio
  long_name = ozone mixing ratio
  units = kg kg-1
  dimensions = (horizontal_loop_extent,vertical_layer_dimension)
  type = real
  kind = kind_phys
  intent = inout
[no2_mixing_ratio]
  standard_name = nitrogen_dioxide_mixing_ratio
  long_name = nitrogen dioxide mixing ratio
  units = kg kg-1
  dimensions = (horizontal_loop_extent,vertical_layer_dimension)
  type = real
  kind = kind_phys
  intent = inout
[errmsg]
  standard_name = ccpp_error_message
  long_name = error message for error handling in CCPP
  units = none
  dimensions = ()
  type = character
  kind = len=*
  intent = out
[errflg]
  standard_name = ccpp_error_code
  long_name = error code for error handling in CCPP
  units = 1
  dimensions = ()
  type = integer
  intent = out

Implementation

CATChem CCPP Wrapper Implementation

module catchem_ccpp_wrapper
  use ccpp_types
  use catchem_mod
  use catchem_types
  implicit none

contains

  subroutine catchem_ccpp_init(ccpp_data, errmsg, errflg)
    type(ccpp_t), intent(inout) :: ccpp_data
    character(len=*), intent(out) :: errmsg
    integer, intent(out) :: errflg

    type(catchem_config_type) :: config
    integer :: im, km
    integer :: rc

    errflg = 0
    errmsg = ''

    ! Extract dimensions from CCPP data
    call ccpp_field_get(ccpp_data, 'horizontal_loop_extent', im, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get horizontal_loop_extent from CCPP'
      return
    end if

    call ccpp_field_get(ccpp_data, 'vertical_layer_dimension', km, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get vertical_layer_dimension from CCPP'
      return
    end if

    ! Initialize CATChem configuration
    call setup_catchem_config_from_ccpp(ccpp_data, config, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to setup CATChem configuration'
      return
    end if

    ! Initialize CATChem
    call catchem_init(config, im, km, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'CATChem initialization failed'
      return
    end if

  end subroutine catchem_ccpp_init

  subroutine catchem_ccpp_run(ccpp_data, dt, errmsg, errflg)
    type(ccpp_t), intent(inout) :: ccpp_data
    real(kind_phys), intent(in) :: dt
    character(len=*), intent(out) :: errmsg
    integer, intent(out) :: errflg

    real(kind_phys), pointer :: temperature(:,:) => null()
    real(kind_phys), pointer :: pressure(:,:) => null()
    real(kind_phys), pointer :: specific_humidity(:,:) => null()
    real(kind_phys), pointer :: o3_mixing_ratio(:,:) => null()
    real(kind_phys), pointer :: no2_mixing_ratio(:,:) => null()

    type(catchem_state_type) :: catchem_state
    integer :: rc

    errflg = 0
    errmsg = ''

    ! Get meteorological fields from CCPP
    call ccpp_field_get(ccpp_data, 'air_temperature', temperature, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get air_temperature from CCPP'
      return
    end if

    call ccpp_field_get(ccpp_data, 'air_pressure', pressure, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get air_pressure from CCPP'
      return
    end if

    call ccpp_field_get(ccpp_data, 'specific_humidity', specific_humidity, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get specific_humidity from CCPP'
      return
    end if

    ! Get chemical species from CCPP
    call ccpp_field_get(ccpp_data, 'ozone_mixing_ratio', o3_mixing_ratio, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get ozone_mixing_ratio from CCPP'
      return
    end if

    call ccpp_field_get(ccpp_data, 'nitrogen_dioxide_mixing_ratio', no2_mixing_ratio, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to get nitrogen_dioxide_mixing_ratio from CCPP'
      return
    end if

    ! Setup CATChem state from CCPP data
    call setup_catchem_state_from_ccpp( &
      temperature, pressure, specific_humidity, &
      o3_mixing_ratio, no2_mixing_ratio, &
      catchem_state, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to setup CATChem state'
      return
    end if

    ! Run CATChem processes
    call catchem_run(catchem_state, dt, rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'CATChem run failed'
      return
    end if

    ! Update CCPP fields with CATChem results
    call update_ccpp_from_catchem_state( &
      catchem_state, &
      o3_mixing_ratio, no2_mixing_ratio, &
      rc)
    if (rc /= 0) then
      errflg = 1
      errmsg = 'Failed to update CCPP fields'
      return
    end if

  end subroutine catchem_ccpp_run

end module catchem_ccpp_wrapper

Configuration

CCPP Suite Definition File (suite_FV3_CATChem.xml)

<?xml version="1.0" encoding="UTF-8"?>
<suite name="FV3_CATChem" version="1">
  <group name="time_vary">
    <subcycle loop="1">
      <scheme>GFS_time_vary_pre</scheme>
      <scheme>GFS_rrtmg_setup</scheme>
      <scheme>GFS_rad_time_vary</scheme>
      <scheme>GFS_phys_time_vary</scheme>
    </subcycle>
  </group>

  <group name="radiation">
    <subcycle loop="1">
      <scheme>GFS_suite_interstitial_rad_reset</scheme>
      <scheme>GFS_rrtmg_pre</scheme>
      <scheme>rrtmg_sw</scheme>
      <scheme>rrtmg_lw</scheme>
      <scheme>GFS_rrtmg_post</scheme>
    </subcycle>
  </group>

  <group name="physics">
    <subcycle loop="1">
      <scheme>GFS_suite_interstitial_phys_reset</scheme>
      <scheme>GFS_suite_stateout_reset</scheme>
      <scheme>get_prs_fv3</scheme>
      <scheme>GFS_suite_interstitial_1</scheme>
      <scheme>GFS_surface_generic_pre</scheme>
      <scheme>GFS_surface_composites_pre</scheme>
      <scheme>dcyc2t3</scheme>
      <scheme>GFS_surface_composites_inter</scheme>
      <scheme>GFS_suite_interstitial_2</scheme>
    </subcycle>

    <!-- Chemistry Integration -->
    <subcycle loop="1">
      <scheme>ccpp_catchem_interface</scheme>
    </subcycle>

    <subcycle loop="1">
      <scheme>GFS_suite_interstitial_3</scheme>
      <scheme>GFS_suite_stateout_update</scheme>
      <scheme>GFS_suite_interstitial_4</scheme>
      <scheme>GFS_MP_generic_pre</scheme>
      <scheme>mp_thompson_pre</scheme>
      <scheme>mp_thompson</scheme>
      <scheme>mp_thompson_post</scheme>
      <scheme>GFS_MP_generic_post</scheme>
    </subcycle>
  </group>

  <group name="stochastics">
    <subcycle loop="1">
      <scheme>GFS_stochastics</scheme>
    </subcycle>
  </group>
</suite>

CCPP Configuration

# CCPP-specific CATChem configuration
ccpp_integration:
  enabled: true

  # Field mapping between CCPP standard names and CATChem species
  field_mapping:
    air_temperature: temperature
    air_pressure: pressure
    specific_humidity: humidity
    ozone_mixing_ratio: O3
    nitrogen_dioxide_mixing_ratio: NO2
    sulfur_dioxide_mixing_ratio: SO2

  # Process configuration for CCPP integration
  processes:
    - name: chemistry
      enabled: true
      ccpp_compliant: true
      timestep_coupling: synchronous

    - name: settling
      enabled: true
      ccpp_compliant: true

    - name: dry_deposition
      enabled: false  # Handled by host model

  # CCPP-specific optimizations
  optimizations:
    column_processing: true
    memory_layout: ccpp_optimized
    data_locality: true

Advanced Integration Features

Field Mapping and Unit Conversion

subroutine setup_ccpp_field_mapping(field_map)
  type(field_mapping_type), intent(out) :: field_map

  ! Meteorological fields
  call add_field_mapping(field_map, &
    ccpp_name='air_temperature', &
    catchem_name='temperature', &
    units_conversion=1.0_fp, &  ! K to K (no conversion)
    required=.true.)

  call add_field_mapping(field_map, &
    ccpp_name='air_pressure', &
    catchem_name='pressure', &
    units_conversion=1.0_fp, &  ! Pa to Pa (no conversion)
    required=.true.)

  ! Chemical species with unit conversion
  call add_field_mapping(field_map, &
    ccpp_name='ozone_mixing_ratio', &
    catchem_name='O3', &
    units_conversion=1.0_fp / molar_mass_o3, &  ! kg/kg to mol/mol
    required=.true.)

  call add_field_mapping(field_map, &
    ccpp_name='nitrogen_dioxide_mixing_ratio', &
    catchem_name='NO2', &
    units_conversion=1.0_fp / molar_mass_no2, &  ! kg/kg to mol/mol
    required=.true.)
end subroutine setup_ccpp_field_mapping

Performance Optimization

! CCPP-optimized column processing
subroutine catchem_ccpp_column_processing(ccpp_data, dt)
  type(ccpp_t), intent(inout) :: ccpp_data
  real(kind_phys), intent(in) :: dt

  integer :: im, km, i
  real(kind_phys), pointer :: temperature(:,:), pressure(:,:)
  real(kind_phys), pointer :: o3(:,:), no2(:,:)

  ! Get field pointers (more efficient than copying)
  call ccpp_field_get_ptr(ccpp_data, 'air_temperature', temperature)
  call ccpp_field_get_ptr(ccpp_data, 'air_pressure', pressure)
  call ccpp_field_get_ptr(ccpp_data, 'ozone_mixing_ratio', o3)
  call ccpp_field_get_ptr(ccpp_data, 'nitrogen_dioxide_mixing_ratio', no2)

  im = size(temperature, 1)
  km = size(temperature, 2)

  ! Process columns in parallel
  !$OMP PARALLEL DO PRIVATE(i)
  do i = 1, im
    call catchem_process_column( &
      temperature(i,:), pressure(i,:), &
      o3(i,:), no2(i,:), &
      dt)
  end do
  !$OMP END PARALLEL DO

end subroutine catchem_ccpp_column_processing

Testing and Validation

CCPP Integration Tests

program test_ccpp_integration
  use ccpp_types
  use ccpp_catchem_interface
  implicit none

  type(ccpp_t) :: ccpp_data
  character(len=512) :: errmsg
  integer :: errflg
  real(kind_phys) :: dt = 60.0_fp

  ! Setup test CCPP data
  call setup_test_ccpp_data(ccpp_data)

  ! Test initialization
  call ccpp_catchem_init(ccpp_data, errmsg, errflg)
  if (errflg /= 0) then
    print *, 'CCPP init failed: ', trim(errmsg)
    stop 1
  end if

  ! Test run
  call ccpp_catchem_run(ccpp_data, dt, errmsg, errflg)
  if (errflg /= 0) then
    print *, 'CCPP run failed: ', trim(errmsg)
    stop 1
  end if

  ! Test finalization
  call ccpp_catchem_finalize(ccpp_data, errmsg, errflg)
  if (errflg /= 0) then
    print *, 'CCPP finalize failed: ', trim(errmsg)
    stop 1
  end if

  print *, 'CCPP integration tests passed!'

end program test_ccpp_integration

Best Practices

1. CCPP Compliance

  • Follow CCPP metadata standards exactly
  • Use CCPP standard names for all fields
  • Implement proper error handling with errmsg/errflg

2. Performance

  • Use field pointers instead of copying data
  • Implement column-wise processing for parallelization
  • Minimize memory allocations in run routine

3. Error Handling

! Always check CCPP field operations
call ccpp_field_get(ccpp_data, 'field_name', field_ptr, rc)
if (rc /= 0) then
  errflg = 1
  errmsg = 'Failed to get field: field_name'
  return
end if

CATChem integrated inline with the CCPP framework provides close interactions between chemistry and physics while maintaining performance and standards compliance.