Process Architecture¶
CATChem uses a modern, extensible process architecture that enables easy development and integration of atmospheric physics and chemistry processes.
Overview¶
The process architecture is built around three key concepts:
- ProcessInterface: Abstract base class defining the standard interface
- Process Modules: Concrete implementations of specific atmospheric processes
- Scheme Modules: Interchangeable algorithmic implementations within processes
graph TB
A[ProcessInterface] --> B[settlingProcess]
A --> C[chemistryProcess]
A --> D[emissionProcess]
B --> E[StokesScheme]
B --> F[IntermediateReynoldsScheme]
C --> G[CB6Scheme]
C --> H[RACM2Scheme] ProcessInterface Base Class¶
All processes extend the abstract ProcessInterface type defined in ProcessInterface_Mod.F90:
type, abstract :: ProcessInterface
private
character(len=64) :: name = ''
character(len=64) :: version = ''
character(len=256) :: description = ''
logical :: is_initialized = .false.
logical :: is_active = .false.
real(fp) :: dt = 0.0_fp
! Species and size bin management
integer :: n_species = 0
character(len=32), allocatable :: species_names(:)
integer :: n_size_bins = 0
real(fp), allocatable :: size_bin_bounds(:)
contains
! Required interface methods
procedure(init_interface), deferred :: init
procedure(run_interface), deferred :: run
procedure(finalize_interface), deferred :: finalize
! Common utility methods
procedure :: is_ready
procedure :: get_name
procedure :: get_version
end type ProcessInterface
Required Methods¶
Every process must implement three core methods:
init(container, rc)¶
- Initialize the process with configuration and state
- Validate inputs and allocate resources
- Set up diagnostic outputs
- Register with the state container
run(container, rc)¶
- Execute the main process logic
- Access state data through the container
- Update species concentrations or emissions
- Generate diagnostic output
finalize(rc)¶
- Clean up allocated resources
- Close files and finalize diagnostics
- Graceful shutdown procedures
Process Development Workflow¶
1. Create Process Structure¶
# Create process directory
mkdir src/process/myprocess
mkdir src/process/myprocess/schemes
# Create required files
touch src/process/myprocess/myprocessProcess_Mod.F90
touch src/process/myprocess/myprocessCommon_Mod.F90
touch src/process/myprocess/CMakeLists.txt
touch src/process/myprocess/schemes/CMakeLists.txt
2. Implement Process Module¶
module myprocessProcess_Mod
use precision_mod
use state_mod, only : StateContainerType
use error_mod
use ProcessInterface_Mod
use myprocessCommon_Mod
implicit none
private
public :: myprocessProcessType
type, extends(ProcessInterface) :: myprocessProcessType
private
! Process-specific configuration
character(len=32) :: selected_scheme = 'default'
real(fp) :: process_parameter = 1.0_fp
contains
procedure :: init => myprocess_init
procedure :: run => myprocess_run
procedure :: finalize => myprocess_finalize
end type myprocessProcessType
contains
subroutine myprocess_init(this, container, rc)
class(myprocessProcessType), intent(inout) :: this
type(StateContainerType), intent(inout) :: container
integer, intent(out) :: rc
! Set process metadata
this%name = 'myprocess'
this%version = '1.0'
this%description = 'My atmospheric process description'
! Process-specific initialization
! ... implementation details ...
this%is_initialized = .true.
this%is_active = .true.
rc = CC_SUCCESS
end subroutine myprocess_init
subroutine myprocess_run(this, container, rc)
class(myprocessProcessType), intent(inout) :: this
type(StateContainerType), intent(inout) :: container
integer, intent(out) :: rc
! Get state pointers
type(MetStateType), pointer :: met_state
type(ChemStateType), pointer :: chem_state
met_state => container%get_met_state_ptr()
chem_state => container%get_chem_state_ptr()
! Execute process logic
! ... implementation details ...
rc = CC_SUCCESS
end subroutine myprocess_run
subroutine myprocess_finalize(this, rc)
class(myprocessProcessType), intent(inout) :: this
integer, intent(out) :: rc
! Cleanup resources
! ... implementation details ...
rc = CC_SUCCESS
end subroutine myprocess_finalize
end module myprocessProcess_Mod
3. Implement Schemes¶
Schemes provide interchangeable algorithms within a process:
module MyScheme_Mod
use precision_mod
use state_mod, only : StateContainerType
use error_mod
implicit none
private
public :: myscheme_calculate
contains
subroutine myscheme_calculate(container, parameters, rc)
type(StateContainerType), intent(inout) :: container
real(fp), intent(in) :: parameters(:)
integer, intent(out) :: rc
! Scheme-specific calculations
! ... implementation details ...
rc = CC_SUCCESS
end subroutine myscheme_calculate
end module MyScheme_Mod
State Container Integration¶
Processes interact with model state through the StateContainerType:
Accessing State Data¶
! Get meteorological state
type(MetStateType), pointer :: met_state
met_state => container%get_met_state_ptr()
! Access temperature, pressure, etc.
real(fp), pointer :: temperature(:,:,:)
temperature => met_state%get_field('temperature')
! Get chemical state
type(ChemStateType), pointer :: chem_state
chem_state => container%get_chem_state_ptr()
! Access species concentrations
real(fp), pointer :: o3_conc(:,:,:)
o3_conc => chem_state%get_species_ptr('O3')
Error Handling¶
type(ErrorManagerType), pointer :: error_mgr
error_mgr => container%get_error_manager()
! Report errors
call error_mgr%report_error("Process calculation failed")
! Report warnings
call error_mgr%report_warning("Using default parameter value")
! Report informational messages
call error_mgr%report_info("Process completed successfully")
Diagnostics¶
type(DiagnosticManagerType), pointer :: diag_mgr
diag_mgr => container%get_diagnostic_manager()
! Register diagnostic variables
call diag_mgr%register_field('settling_velocity', &
'Particle settling velocity', &
'm/s', shape(settling_vel))
! Update diagnostic values
call diag_mgr%update_field('settling_velocity', settling_vel)
Column Virtualization¶
CATChem processes operate on 1D columns for optimal performance:
subroutine process_column(this, column, rc)
class(myprocessProcessType), intent(inout) :: this
type(VirtualColumnType), intent(inout) :: column
integer, intent(out) :: rc
! Process operates on single column
real(fp) :: temp_profile(column%nz)
real(fp) :: conc_profile(column%nz, this%n_species)
! Get column data
temp_profile = column%get_met_field('temperature')
conc_profile = column%get_chem_concentrations()
! Process calculations on 1D profiles
! ... calculations ...
! Update column data
call column%set_chem_concentrations(conc_profile)
rc = CC_SUCCESS
end subroutine process_column
Configuration Integration¶
Processes are configured through YAML files:
processes:
- name: myprocess
enabled: true
scheme: MyScheme
timestep: 60.0
parameters:
process_parameter: 2.5
enable_diagnostics: true
diagnostics:
- name: process_rate
output_frequency: hourly
- name: process_flux
output_frequency: daily
Testing and Validation¶
Unit Testing¶
Create unit tests for individual process components:
program test_myprocess
use myprocessProcess_Mod
use testing_mod
type(myprocessProcessType) :: process
type(StateContainerType) :: container
integer :: rc
! Test initialization
call process%init(container, rc)
call assert_equal(rc, CC_SUCCESS, "Process initialization failed")
! Test calculation
call process%run(container, rc)
call assert_equal(rc, CC_SUCCESS, "Process run failed")
! Verify results
! ... test assertions ...
end program test_myprocess
Integration Testing¶
Test process integration with the full model:
! Create test configuration
call create_test_state_container(container)
! Initialize and run process
call process%init(container, rc)
call process%run(container, rc)
! Validate conservation laws
call validate_mass_conservation(container)
call validate_energy_conservation(container)
Performance Considerations¶
Memory Management¶
- Use pointer associations for large arrays
- Avoid unnecessary allocations in run() method
- Clean up temporary arrays in finalize()
Computational Efficiency¶
- Vectorize inner loops where possible
- Minimize conditional branches in tight loops
- Use compiler optimization flags
Parallel Scalability¶
- Design for column-parallel execution
- Avoid global reductions where possible
- Use thread-safe operations only
Best Practices¶
- Keep processes focused: Each process should handle one physical phenomenon
- Use descriptive names: Process and variable names should be self-documenting
- Validate inputs: Always check state data consistency in init()
- Handle errors gracefully: Use the error manager for all error reporting
- Document thoroughly: Include Doxygen comments for all public interfaces
- Test extensively: Write unit tests for all major functionality
- Follow naming conventions: Use consistent naming patterns across processes
Example: Settling Process¶
The settling process demonstrates best practices:
- Clear interface: Extends ProcessInterface with required methods
- Multiple schemes: Supports Stokes and intermediate Reynolds number schemes
- Comprehensive diagnostics: Outputs settling velocity and particle flux
- Error handling: Validates inputs and reports meaningful errors
- Column processing: Optimized for 1D column calculations
See the Creating New Processes guide for step-by-step instructions on developing your own process modules.