File CATChem_API.F90¶
File List > api > CATChem_API.F90
Go to the documentation of this file
module catchem_api
use precision_mod, only: fp
use error_mod, only: cc_success, cc_failure, errormanagertype
use catchemcore_mod, only: catchemcoretype, catchembuildertype
use statemanager_mod, only: statemanagertype
use processmanager_mod, only: processmanagertype
use gridmanager_mod, only: gridmanagertype
use diagnosticmanager_mod, only: diagnosticmanagertype
use metstate_mod, only: metstatetype
use chemstate_mod, only: chemstatetype
use configmanager_mod, only: configdatatype
use diagnosticinterface_mod, only: diagnosticregistrytype, diagnosticfieldtype, &
diag_real_scalar, diag_real_1d, diag_real_2d, diag_real_3d, &
diag_integer_scalar, diag_integer_1d, diag_integer_2d, diag_integer_3d
use processinterface_mod, only: processinterface
! Import process registration functions
use seasaltprocesscreator_mod, only: register_seasalt_process
use drydepprocesscreator_mod, only: register_drydep_process
use wetdepprocesscreator_mod, only: register_wetdep_process
use settlingprocesscreator_mod, only: register_settling_process
use so4chemprocesscreator_mod, only: register_so4chem_process
use carbchemprocesscreator_mod, only: register_carbchem_process
implicit none
private
! Public interface types and constants
public :: catchem_model
type :: catchem_model
private
! Core engine - uses existing CATChemCore infrastructure
type(CATChemCoreType) :: core
! Configuration and status tracking
logical :: initialized = .false.
logical :: grid_setup = .false.
logical :: enable_run_phase = .false.
character(len=512) :: config_file = ''
character(len=64), allocatable, public :: required_fields(:)
type(ErrorManagerType) :: error_manager
! Grid information
integer :: nx = 0, ny = 0, nz = 0
integer :: nsoil = 4, nsoiltype = 19, nsurftype = 20
contains
! Basic lifecycle methods
procedure :: initialize => model_initialize
procedure :: finalize => model_finalize
! Grid access (no separate setup needed now)
procedure :: get_grid_dimensions => model_get_grid_dimensions
! Process management
procedure :: add_process => model_add_process
procedure :: get_process_names => model_get_process_names
procedure :: get_num_processes => model_get_num_processes
procedure, private :: model_register_process
! Run execution
procedure :: run_timestep => model_run_timestep
procedure :: run_phase => model_run_phase
procedure :: run_all_phases => model_run_all_phases
procedure :: get_phase_names => model_get_phase_names
! Data exchange methods
procedure :: set_chemistry => model_set_chemistry
procedure :: get_chemistry => model_get_chemistry
! Diagnostic methods
procedure :: get_diagnostic_names => model_get_diagnostic_names
procedure :: get_diagnostic => model_get_diagnostic
procedure :: get_all_diagnostics => model_get_all_diagnostics
! Utility methods
procedure :: is_ready => model_is_ready
procedure :: is_initialized => model_is_initialized
procedure :: get_required_met_index => model_get_required_met_index
procedure :: get_diag_index_from_field => model_get_diag_index_from_field
! Core access methods (for advanced users)
procedure :: get_state_manager => model_get_state_manager
procedure :: get_process_manager => model_get_process_manager
procedure :: get_error_manager => model_get_error_manager
procedure :: get_grid_manager => model_get_grid_manager
procedure :: get_diagnostic_manager => model_get_diagnostic_manager
end type catchem_model
contains
subroutine model_initialize(this, config_file, nx, ny, nz, nsoil, nsoiltype, nsurftype, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: config_file
integer, intent(in) :: nx, ny, nz
integer, intent(in), optional :: nsoil, nsoiltype, nsurftype
integer, intent(out) :: rc
type(CATChemBuilderType) :: builder
type(ConfigDataType), pointer :: config_data => null()
rc = cc_success
call this%error_manager%init()
! Validate inputs
if (len_trim(config_file) == 0) then
call this%error_manager%push_context('model_initialize', 'validating configuration file')
call this%error_manager%report_error(1001, 'Configuration file path is empty', rc)
call this%error_manager%pop_context()
return
endif
if (nx <= 0 .or. ny <= 0 .or. nz <= 0) then
call this%error_manager%push_context('model_initialize', 'validating grid dimensions')
call this%error_manager%report_error(1001, 'Grid dimensions must be positive', rc)
call this%error_manager%pop_context()
return
endif
! Store config file path and grid dimensions
this%config_file = trim(config_file)
this%nx = nx
this%ny = ny
this%nz = nz
! Store soil and surface parameters (use provided values or defaults)
if (present(nsoil)) this%nsoil = nsoil
if (present(nsoiltype)) this%nsoiltype = nsoiltype
if (present(nsurftype)) this%nsurftype = nsurftype
! Initialize core using builder pattern with grid information
call builder%init()
builder = builder%with_name('CATChem_API_Instance')
builder = builder%with_config(config_file)
builder = builder%with_grid(nx, ny, nz, this%nsoil, this%nsoiltype, this%nsurftype)
call builder%build(this%core, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_initialize', 'building CATChem core')
call this%error_manager%report_error(1014, 'Failed to initialize CATChem core with config: ' // trim(config_file), rc)
call this%error_manager%pop_context()
return
endif
this%initialized = .true.
this%grid_setup = .true. ! Grid is now set up during initialization
! Get configuration data from core
config_data => this%core%get_config()
if ( .not. associated(config_data)) then
call this%error_manager%push_context('model_initialize', 'accessing configuration data')
call this%error_manager%report_error(1002, 'Required managers or config data not available', rc)
call this%error_manager%pop_context()
return
endif
this%enable_run_phase = config_data%run_phases_enabled
end subroutine model_initialize
subroutine model_finalize(this, rc)
class(CATChem_Model), intent(inout) :: this
integer, intent(out) :: rc
rc = cc_success
if (.not. this%initialized) then
rc = cc_success ! Already finalized
return
endif
! Finalize core
call this%core%finalize(rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_finalize', 'finalizing CATChem core')
call this%error_manager%report_error(1015, 'Core finalization had issues', rc)
call this%error_manager%pop_context()
! Don't return failure for finalization warnings
rc = cc_success
endif
! Reset state
this%initialized = .false.
this%grid_setup = .false.
this%enable_run_phase = .false.
this%nx = 0
this%ny = 0
this%nz = 0
this%nsoil = 4
this%nsoiltype = 19
this%nsurftype = 20
this%config_file = ''
if (allocated(this%required_fields)) deallocate(this%required_fields)
end subroutine model_finalize
subroutine model_get_grid_dimensions(this, nx, ny, nz, nsoil, nsoiltype, nsurftype)
class(CATChem_Model), intent(in) :: this
integer, intent(out) :: nx, ny, nz
integer, intent(out), optional :: nsoil, nsoiltype, nsurftype
nx = this%nx
ny = this%ny
nz = this%nz
if (present(nsoil)) nsoil = this%nsoil
if (present(nsoiltype)) nsoiltype = this%nsoiltype
if (present(nsurftype)) nsurftype = this%nsurftype
end subroutine model_get_grid_dimensions
subroutine model_add_process(this, rc)
use configmanager_mod, only: configdatatype
class(CATChem_Model), intent(inout) :: this
integer, intent(out) :: rc
type(ConfigDataType), pointer :: config_data => null()
type(ProcessManagerType), pointer :: process_mgr => null()
integer :: i, reg_rc, add_rc, num_fields
rc = cc_success
if (.not. this%initialized) then
call this%error_manager%push_context('model_add_process', 'checking initialization status')
call this%error_manager%report_error(1003, 'Model must be initialized first', rc)
call this%error_manager%pop_context()
return
endif
! Get configuration data from core
config_data => this%core%get_config()
if (.not. associated(config_data)) then
call this%error_manager%push_context('model_add_process', 'accessing configuration data')
call this%error_manager%report_error(1002, 'Configuration data not available', rc)
call this%error_manager%pop_context()
return
endif
! Get process manager
process_mgr => this%core%get_process_manager()
if (.not. associated(process_mgr)) then
call this%error_manager%push_context('model_add_process', 'accessing process manager')
call this%error_manager%report_error(1014, 'ProcessManager not available from core', rc)
call this%error_manager%pop_context()
return
endif
! Check if processes are available (either run phase or direct processes)
if (.not. allocated(config_data%run_phase_processes)) then
call this%error_manager%push_context('model_add_process', 'checking process configuration')
call this%error_manager%report_error(1002, 'No processes configured', rc)
call this%error_manager%pop_context()
return
endif
! Loop through all processes in configuration and add enabled ones
do i = 1, size(config_data%run_phase_processes)
if (config_data%run_phase_processes(i)%enabled) then
! Automatically register the process based on its name
call this%model_register_process(config_data%run_phase_processes(i)%name, process_mgr, reg_rc)
if (reg_rc /= cc_success) then
write(*,'(A,A,A)') 'Warning: Failed to register enabled process: ', &
trim(config_data%run_phase_processes(i)%name), '. Skipping this process.'
cycle ! Skip this process and continue with others
endif
! Add process to core
call this%core%add_process(config_data%run_phase_processes(i)%name, add_rc)
if (add_rc /= cc_success) then
write(*,'(A,A,A)') 'Warning: Failed to add enabled process to core: ', &
trim(config_data%run_phase_processes(i)%name), '. Skipping this process.'
cycle ! Skip this process and continue with others
endif
write(*,'(A,A)') 'Successfully added enabled process: ', &
trim(config_data%run_phase_processes(i)%name)
else
write(*,'(A,A)') 'Skipping disabled process: ', &
trim(config_data%run_phase_processes(i)%name)
endif
end do
! Get required met fields from process_mgr
if (allocated(process_mgr%required_met_fields)) then
num_fields = size(process_mgr%required_met_fields)
allocate(this%required_fields(num_fields))
this%required_fields = process_mgr%required_met_fields
else
call this%error_manager%push_context('model_add_process', 'checking required met fields')
call this%error_manager%report_error(1014, 'No met fields found', rc)
call this%error_manager%pop_context()
endif
end subroutine model_add_process
subroutine model_register_process(this, process_name, process_mgr, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: process_name
type(ProcessManagerType), intent(inout) :: process_mgr
integer, intent(out) :: rc
rc = cc_success
! Dispatch to the appropriate registration function based on process name
select case (trim(process_name))
case ('seasalt')
call register_seasalt_process(process_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_register_process', 'registering seasalt process')
call this%error_manager%report_error(1014, 'Failed to register seasalt process', rc)
call this%error_manager%pop_context()
endif
! Add more processes here as they become available
! case ('dust')
! call register_dust_process(process_mgr, rc)
case ('drydep')
call register_drydep_process(process_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_register_process', 'registering drydep process')
call this%error_manager%report_error(1014, 'Failed to register drydep process', rc)
call this%error_manager%pop_context()
endif
case ('wetdep')
call register_wetdep_process(process_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_register_process', 'registering wetdep process')
call this%error_manager%report_error(1014, 'Failed to register wetdep process', rc)
call this%error_manager%pop_context()
endif
case ('settling')
call register_settling_process(process_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_register_process', 'registering settling process')
call this%error_manager%report_error(1014, 'Failed to register settling process', rc)
call this%error_manager%pop_context()
endif
case ('so4chem')
call register_so4chem_process(process_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_register_process', 'registering so4chem process')
call this%error_manager%report_error(1014, 'Failed to register so4chem process', rc)
call this%error_manager%pop_context()
endif
case ('carbchem')
call register_carbchem_process(process_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_register_process', 'registering carbchem process')
call this%error_manager%report_error(1014, 'Failed to register carbchem process', rc)
call this%error_manager%pop_context()
endif
! case ('chemistry')
! call register_chemistry_process(process_mgr, rc)
case default
call this%error_manager%push_context('model_register_process', 'validating process type')
call this%error_manager%report_error(1016, 'Unknown process type: ' // trim(process_name) // &
'. Supported processes: seasalt, drydep, wetdep, settling, so4chem, carbchem', rc)
call this%error_manager%pop_context()
end select
end subroutine model_register_process
subroutine model_get_process_names(this, process_names, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), allocatable, intent(out) :: process_names(:)
integer, intent(out) :: rc
type(ProcessManagerType), pointer :: process_mgr => null()
character(len=64) :: temp_names(50) ! Temporary array with max size
integer :: count, i
rc = cc_success
if (.not. this%initialized) then
allocate(process_names(0))
rc = cc_failure
return
endif
! Get process manager from core
process_mgr => this%core%get_process_manager()
if (.not. associated(process_mgr)) then
allocate(process_names(0))
rc = cc_failure
return
endif
! Get process list from ProcessManager
call process_mgr%list_processes(temp_names, count)
! Allocate output array with actual count
allocate(process_names(count))
! Copy the actual process names
do i = 1, count
process_names(i) = temp_names(i)
end do
end subroutine model_get_process_names
function model_get_num_processes(this) result(num_processes)
class(CATChem_Model), intent(inout) :: this
integer :: num_processes
type(ProcessManagerType), pointer :: process_mgr => null()
character(len=64) :: temp_names(50) ! Temporary array with max size
num_processes = 0
if (.not. this%initialized) return
! Get process manager from core
process_mgr => this%core%get_process_manager()
if (.not. associated(process_mgr)) return
! Get process count from ProcessManager
call process_mgr%list_processes(temp_names, num_processes)
end function model_get_num_processes
subroutine model_run_timestep(this, timestep, dt, rc)
class(CATChem_Model), intent(inout) :: this
integer, intent(in) :: timestep ! Current timestep number
real(fp), intent(in) :: dt ! Timestep size [s]
integer, intent(out) :: rc
rc = cc_success
if (.not. this%is_ready()) then
call this%error_manager%push_context('model_run_timestep', 'checking model readiness')
call this%error_manager%report_error(1003, 'Model is not ready to run timestep', rc)
call this%error_manager%pop_context()
return
endif
if (dt <= 0.0_fp) then
call this%error_manager%push_context('model_run_timestep', 'validating timestep size')
call this%error_manager%report_error(1001, 'Timestep size must be positive', rc)
call this%error_manager%pop_context()
return
endif
if (this%enable_run_phase) then
call this%run_all_phases(rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_run_timestep', 'running all phases')
call this%error_manager%report_error(1015, 'Failed to run all phases during timestep', rc)
call this%error_manager%pop_context()
return
endif
else
! Run the core timestep
call this%core%run_timestep(timestep, dt, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_run_timestep', 'running core timestep')
call this%error_manager%report_error(1015, 'Failed to run all processes during timestep', rc)
call this%error_manager%pop_context()
return
endif
endif
end subroutine model_run_timestep
subroutine model_run_phase(this, phase_name, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: phase_name
integer, intent(out) :: rc
type(ProcessManagerType), pointer :: process_mgr => null()
type(StateManagerType), pointer :: state_mgr => null()
type(ConfigDataType), pointer :: config_data => null()
rc = cc_success
if (.not. this%is_ready()) then
call this%error_manager%push_context('model_run_phase', 'checking model readiness')
call this%error_manager%report_error(1003, 'Model is not ready for phase execution', rc)
call this%error_manager%pop_context()
return
endif
! Get managers and config data
process_mgr => this%core%get_process_manager()
state_mgr => this%core%get_state_manager()
config_data => this%core%get_config()
if (.not. associated(process_mgr) .or. .not. associated(state_mgr) .or. .not. associated(config_data)) then
call this%error_manager%push_context('model_run_phase', 'accessing required managers')
call this%error_manager%report_error(1014, 'Required managers or config data not available', rc)
call this%error_manager%pop_context()
return
endif
! Run the specific phase via ProcessManager
call process_mgr%run_phase(phase_name, config_data, state_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_run_phase', 'executing phase: ' // trim(phase_name))
call this%error_manager%report_error(1015, 'Failed to run phase: ' // trim(phase_name), rc)
call this%error_manager%pop_context()
endif
end subroutine model_run_phase
subroutine model_run_all_phases(this, rc)
class(CATChem_Model), intent(inout) :: this
integer, intent(out) :: rc
type(ProcessManagerType), pointer :: process_mgr => null()
type(StateManagerType), pointer :: state_mgr => null()
type(ConfigDataType), pointer :: config_data => null()
rc = cc_success
if (.not. this%is_ready()) then
call this%error_manager%push_context('model_run_all_phases', 'checking model readiness')
call this%error_manager%report_error(1003, 'Model is not ready for phase execution', rc)
call this%error_manager%pop_context()
return
endif
! Get managers and config data
process_mgr => this%core%get_process_manager()
state_mgr => this%core%get_state_manager()
config_data => this%core%get_config()
if (.not. associated(process_mgr) .or. .not. associated(state_mgr) .or. .not. associated(config_data)) then
call this%error_manager%push_context('model_run_all_phases', 'accessing required managers')
call this%error_manager%report_error(1014, 'Required managers or config data not available', rc)
call this%error_manager%pop_context()
return
endif
! Run all phases in sequence using ConfigManager data
call process_mgr%run_all_phases(config_data, state_mgr, rc)
if (rc /= cc_success) then
call this%error_manager%push_context('model_run_all_phases', 'executing all phases')
call this%error_manager%report_error(1015, 'Failed to run all phases', rc)
call this%error_manager%pop_context()
endif
end subroutine model_run_all_phases
subroutine model_get_phase_names(this, phase_names, rc)
use configmanager_mod, only: configdatatype
class(CATChem_Model), intent(inout) :: this
character(len=*), allocatable, intent(out) :: phase_names(:)
integer, intent(out) :: rc
type(ConfigDataType), pointer :: config_data => null()
integer :: i
rc = cc_success
if (.not. this%initialized) then
allocate(phase_names(0))
rc = cc_failure
return
endif
! Get configuration data from core
config_data => this%core%get_config()
if (.not. associated(config_data)) then
allocate(phase_names(0))
rc = cc_failure
return
endif
! Check if run phases are available
if (.not. allocated(config_data%run_phases)) then
allocate(phase_names(0))
return
endif
allocate(phase_names(size(config_data%run_phases)))
do i = 1, size(config_data%run_phases)
phase_names(i) = config_data%run_phases(i)%name
end do
end subroutine model_get_phase_names
subroutine model_set_chemistry(this, species_names, concentrations, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: species_names(:)
real(fp), intent(in) :: concentrations(:,:,:,:) ! [species, nx, ny, nz]
integer, intent(out) :: rc
type(StateManagerType), pointer :: state_mgr => null()
type(ChemStateType), pointer :: chem_state => null()
integer :: num_species
rc = cc_success
! Initialize error handling
if (.not. this%is_ready()) then
! Error: 'Model is not ready for chemistry data'
rc = cc_failure
return
endif
num_species = size(species_names)
if (num_species /= size(concentrations, 1)) then
! Error: 'Number of species names does not match concentration dimensions'
rc = cc_failure
return
endif
! Get state manager and chemistry state
state_mgr => this%core%get_state_manager()
if (.not. associated(state_mgr)) then
! Error: 'State manager not available'
rc = cc_failure
return
endif
chem_state => state_mgr%get_chem_state_ptr()
if (.not. associated(chem_state)) then
! Error: 'Chemistry state not available'
rc = cc_failure
return
endif
! TODO: Implement chemistry data transfer
! This would involve mapping species names to indices and copying data
! Error: 'Chemistry data transfer not yet implemented'
rc = cc_failure
end subroutine model_set_chemistry
subroutine model_get_chemistry(this, species_names, concentrations, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), allocatable, intent(out) :: species_names(:)
real(fp), allocatable, intent(out) :: concentrations(:,:,:,:) ! [species, nx, ny, nz]
integer, intent(out) :: rc
type(StateManagerType), pointer :: state_mgr => null()
type(ChemStateType), pointer :: chem_state => null()
rc = cc_success
if (.not. this%is_ready()) then
rc = cc_failure
return
endif
! Get state manager and chemistry state
state_mgr => this%core%get_state_manager()
if (.not. associated(state_mgr)) then
rc = cc_failure
return
endif
chem_state => state_mgr%get_chem_state_ptr()
if (.not. associated(chem_state)) then
rc = cc_failure
return
endif
! TODO: Implement chemistry data retrieval
! This would involve getting species names and copying concentration data
allocate(species_names(0))
allocate(concentrations(0, this%nx, this%ny, this%nz))
end subroutine model_get_chemistry
subroutine model_get_diagnostic_names(this, diagnostic_names, diagnostic_fields, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), allocatable, intent(out) :: diagnostic_names(:)
character(len=*), allocatable, optional, intent(out) :: diagnostic_fields(:)
integer, intent(out) :: rc
type(DiagnosticManagerType), pointer :: diag_mgr => null()
type(DiagnosticRegistryType), pointer :: registry => null()
character(len=64), allocatable :: process_list(:), field_names(:)
integer :: num_processes, i, j, field_count, total_fields, name_idx
integer :: local_rc
rc = cc_success
! Initialize error handling
if (.not. this%initialized) then
allocate(diagnostic_names(0))
! Error: 'Model not initialized'
rc = cc_failure
return
endif
diag_mgr => this%core%get_diagnostic_manager()
if (.not. associated(diag_mgr)) then
allocate(diagnostic_names(0))
! Error: 'Diagnostic manager not available'
rc = cc_failure
return
endif
! Get list of all processes with diagnostics
call diag_mgr%list_processes(process_list, num_processes, local_rc)
if (local_rc /= cc_success .or. num_processes == 0) then
allocate(diagnostic_names(0))
return ! No processes or failed to get list - return empty array
endif
! Count total diagnostic fields across all processes
total_fields = 0
do i = 1, num_processes
call diag_mgr%get_process_registry(process_list(i), registry, local_rc)
if (local_rc == cc_success .and. associated(registry)) then
total_fields = total_fields + registry%get_field_count()
endif
end do
! Allocate output array
allocate(diagnostic_names(total_fields))
if (present(diagnostic_fields)) allocate( diagnostic_fields(total_fields) )
! Collect all diagnostic field names with process prefix
name_idx = 0
do i = 1, num_processes
call diag_mgr%get_process_registry(process_list(i), registry, local_rc)
if (local_rc == cc_success .and. associated(registry)) then
field_count = registry%get_field_count()
if (field_count > 0) then
allocate(field_names(field_count))
call registry%list_fields(field_names, field_count)
do j = 1, field_count
name_idx = name_idx + 1
! Create qualified name: process_name.field_name
diagnostic_names(name_idx) = trim(process_list(i)) // '.' // trim(field_names(j))
if (present(diagnostic_fields)) diagnostic_fields(name_idx) = trim(field_names(j))
end do
deallocate(field_names)
endif
endif
end do
! Clean up
if (allocated(process_list)) deallocate(process_list)
end subroutine model_get_diagnostic_names
subroutine model_get_diagnostic(this, diagnostic_name, diagnostic_data, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: diagnostic_name
real(fp), allocatable, intent(out) :: diagnostic_data(:,:,:)
integer, intent(out) :: rc
type(DiagnosticManagerType), pointer :: diag_mgr => null()
character(len=64) :: process_name, field_name
integer :: local_rc, dot_pos, data_type
real(fp) :: scalar_value
real(fp), pointer :: array_1d_ptr(:) => null()
real(fp), pointer :: array_2d_ptr(:,:) => null()
real(fp), pointer :: array_3d_ptr(:,:,:) => null()
logical :: found = .false.
rc = cc_success
! Initialize error handling
if (.not. this%initialized) then
! Error: 'Model not initialized'
rc = cc_failure
return
endif
diag_mgr => this%core%get_diagnostic_manager()
if (.not. associated(diag_mgr)) then
! Error: 'Diagnostic manager not available'
rc = cc_failure
return
endif
! Parse diagnostic name (format: process_name.field_name)
dot_pos = index(diagnostic_name, '.')
if (dot_pos <= 1) then
! Error: 'Invalid diagnostic name format. Expected: process_name.field_name'
rc = cc_failure
return
endif
process_name = diagnostic_name(1:dot_pos-1)
field_name = diagnostic_name(dot_pos+1:)
! Get field value using DiagnosticManager
call diag_mgr%get_field_value(process_name, field_name, &
scalar_value, array_1d_ptr, array_2d_ptr, array_3d_ptr, &
data_type, rc = local_rc)
if (local_rc /= cc_success) then
! Error: 'Failed to retrieve diagnostic field: ' // trim(diagnostic_name)
rc = cc_failure
return
endif
! Allocate and populate output array based on data type
allocate(diagnostic_data(this%nx, this%ny, this%nz))
diagnostic_data = 0.0_fp
select case (data_type)
case (diag_real_scalar)
diagnostic_data = scalar_value
found = .true.
case (diag_real_2d)
if (associated(array_2d_ptr)) then
! Copy 2D data to first level of 3D array
diagnostic_data(:,:,1) = array_2d_ptr
found = .true.
endif
case (diag_real_3d)
if (associated(array_3d_ptr)) then
diagnostic_data = array_3d_ptr
found = .true.
endif
case default
! Error: 'Unsupported diagnostic data type for: ' // trim(diagnostic_name)
rc = cc_failure
return
end select
if (.not. found) then
! Error: 'No data available for diagnostic: ' // trim(diagnostic_name)
rc = cc_failure
endif
end subroutine model_get_diagnostic
subroutine model_get_all_diagnostics(this, diagnostic_names, diagnostic_data, rc)
class(CATChem_Model), intent(inout) :: this
character(len=*), allocatable, intent(out) :: diagnostic_names(:)
real(fp), allocatable, intent(out) :: diagnostic_data(:,:,:,:) ! [diag, nx, ny, nz]
integer, intent(out) :: rc
type(DiagnosticManagerType), pointer :: diag_mgr => null()
integer :: local_rc
rc = cc_success
! Initialize error handling
if (.not. this%initialized) then
! Error: 'Model not initialized'
allocate(diagnostic_names(0))
allocate(diagnostic_data(0, this%nx, this%ny, this%nz))
rc = cc_failure
return
endif
diag_mgr => this%core%get_diagnostic_manager()
if (.not. associated(diag_mgr)) then
! Error: 'Diagnostic manager not available'
allocate(diagnostic_names(0))
allocate(diagnostic_data(0, this%nx, this%ny, this%nz))
rc = cc_failure
return
endif
! First get all diagnostic field names
call this%get_diagnostic_names(diagnostic_names, rc =local_rc)
if (local_rc /= cc_success) then
! Error: 'Failed to get diagnostic names'
allocate(diagnostic_data(0, this%nx, this%ny, this%nz))
rc = cc_failure
return
endif
! Collect all diagnostic data using DiagnosticManager
call diag_mgr%collect_all_diagnostics(local_rc)
if (local_rc /= cc_success) then
! Error: 'Failed to collect diagnostic data'
allocate(diagnostic_data(size(diagnostic_names), this%nx, this%ny, this%nz))
rc = cc_failure
return
endif
! For now, allocate the data array but don't populate it
! TODO: Extract individual diagnostic values and populate diagnostic_data array
! This would require iterating through diagnostic_names and calling get_field_value for each
allocate(diagnostic_data(size(diagnostic_names), this%nx, this%ny, this%nz))
diagnostic_data = 0.0_fp
! Note: The actual data collection has been performed and stored in each field's
! DiagnosticDataType structure. Individual fields can be accessed using get_diagnostic()
end subroutine model_get_all_diagnostics
function model_get_diag_index_from_field(this, field_name) result(found_index)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: field_name
character(len=128), allocatable :: diagnostic_names(:)
character(len=128), allocatable :: diagnostic_fields(:)
integer :: found_index, rc, i
found_index = 0
call this%get_diagnostic_names(diagnostic_names, diagnostic_fields, rc)
if (allocated(diagnostic_fields)) then
do i = 1, size(diagnostic_fields)
if (trim(field_name) == trim(diagnostic_fields(i))) then
found_index = i
exit !exit the loop once found; TODO: this assumes we do not have duplicated field names
end if
end do
else
write(*,'(A)') ' Warning: No diagnostic fields are registered!!'
end if
end function model_get_diag_index_from_field
function model_is_ready(this) result(is_ready)
class(CATChem_Model), intent(inout) :: this
logical :: is_ready
is_ready = this%initialized .and. this%grid_setup .and. (this%get_num_processes() > 0)
end function model_is_ready
function model_is_initialized(this) result(is_initialized)
class(CATChem_Model), intent(in) :: this
logical :: is_initialized
is_initialized = this%initialized
end function model_is_initialized
function model_get_required_met_index(this, var_name) result(found_index)
class(CATChem_Model), intent(inout) :: this
character(len=*), intent(in) :: var_name
integer :: found_index
integer :: i
found_index = 0
if (allocated(this%required_fields)) then
do i = 1, size(this%required_fields)
if (trim(var_name) == trim(this%required_fields(i))) then
found_index = i
exit !exit the loop once found
end if
end do
else
write(*,'(A)') ' Warning: No met fields are registered!!'
end if
end function model_get_required_met_index
function model_get_error_manager(this) result(error_mgr_ptr)
class(CATChem_Model), intent(inout), target :: this
type(ErrorManagerType), pointer :: error_mgr_ptr
error_mgr_ptr => this%error_manager
end function model_get_error_manager
! Core access methods for advanced users
function model_get_state_manager(this) result(state_mgr_ptr)
class(CATChem_Model), intent(inout) :: this
type(StateManagerType), pointer :: state_mgr_ptr
state_mgr_ptr => this%core%get_state_manager()
end function model_get_state_manager
function model_get_process_manager(this) result(process_mgr_ptr)
class(CATChem_Model), intent(inout) :: this
type(ProcessManagerType), pointer :: process_mgr_ptr
process_mgr_ptr => this%core%get_process_manager()
end function model_get_process_manager
function model_get_grid_manager(this) result(grid_mgr_ptr)
class(CATChem_Model), intent(inout) :: this
type(GridManagerType), pointer :: grid_mgr_ptr
grid_mgr_ptr => this%core%get_grid_manager()
end function model_get_grid_manager
function model_get_diagnostic_manager(this) result(diag_mgr_ptr)
class(CATChem_Model), intent(inout) :: this
type(DiagnosticManagerType), pointer :: diag_mgr_ptr
diag_mgr_ptr => this%core%get_diagnostic_manager()
end function model_get_diagnostic_manager
end module catchem_api