-
Notifications
You must be signed in to change notification settings - Fork 1
MPAS MUSICA MICM State
Implement chemistry_from_MPAS and chemistry_to_MPAS routines to couple MICM chemistry state with MPAS model state, following the physics interface pattern. Focus on ABBA test species (qAB, qA, qB) with abstraction layer for future extension.
This implementation focuses exclusively on the ABBA test chemistry model (species: A, B, AB). Full tracer mappings for production chemistry mechanisms will come later, pending the MPAS runtime variables feature (see below).
A new MPAS feature, runtime variables, is under development but not yet ready. This feature will allow MPAS tracers to be defined at runtime (via configuration) rather than at compile-time (via Registry.xml).
Once available:
- MICM will use runtime variables to dynamically register its species as MPAS tracers
- No consistency check between YAML and Registry.xml will be needed
- Full tracer mappings for arbitrary chemistry mechanisms will be supported
Current placeholder: The existing check_registry_tracer_consistency() routine validates that MICM species match MPAS tracers at initialization. This is a temporary solution until runtime variables are available.
-
MICM species (runtime, YAML):
A,B,AB -
MPAS tracers (compile-time, Registry.xml):
qA,qB,qAB(prepend "q") -
check_registry_tracer_consistency()validates this mapping at init (placeholder)
mpas_atm_chemistry.F (Chemistry-Agnostic Layer)
├── chemistry_from_MPAS() - Extract MPAS state, call MICM-specific routine
├── chemistry_to_MPAS() - Update MPAS state from MICM results
└── chemistry_step() - Modified to orchestrate coupling
mpas_musica.F (MICM-Specific Layer)
├── MICM_from_chemistry() - MPAS scalars → MICM concentrations (with unit conversion)
└── MICM_to_chemistry() - MICM concentrations → MPAS scalars (with unit conversion)
chemistry_step(dt, mesh, state, diag, dimensions, time_lev)
│
├─> chemistry_from_MPAS()
│ └─> MICM_from_chemistry() # q [kg/kg] → C [mol/m³]
│
├─> musica_step() # MICM solves chemistry
│
└─> chemistry_to_MPAS()
└─> MICM_to_chemistry() # C [mol/m³] → q [kg/kg]
-
MPAS → MICM:
C = q × rho_air / M_species -
MICM → MPAS:
q = C × M_species / rho_air
Where:
-
rho_air = zz × rho_zz × (1 + qv)[kg/m³] -
M_species= molar mass [kg/mol] (e.g., M_AB=0.058, M_A=M_B=0.029)
Add new subroutines:
subroutine MICM_from_chemistry(scalars, rho_air, temperature, pressure, &
nCells, nVertLevels, &
index_qAB, index_qA, index_qB, &
error_code, error_message)
! Convert MPAS scalars to MICM state
! - Set state%conditions (T, P, air_density)
! - Convert mixing ratios to concentrations using q->C formula
! - Store in state%concentrations using strided access
! - Uses existing species mapping validated by check_registry_tracer_consistencysubroutine MICM_to_chemistry(scalars, rho_air, nCells, nVertLevels, &
index_qAB, index_qA, index_qB, &
error_code, error_message)
! Convert MICM state to MPAS scalars
! - Read state%concentrations using strided access
! - Convert concentrations to mixing ratios using C->q formula
! - Update scalars array (qA, qB, qAB)Add module constants:
real(real64), parameter :: M_AB = 0.058_real64 ! kg/mol
real(real64), parameter :: M_A = 0.029_real64 ! kg/mol
real(real64), parameter :: M_B = 0.029_real64 ! kg/mol
real(real64), parameter :: M_AIR = 0.0289644_real64 ! kg/molModify chemistry_step signature:
subroutine chemistry_step(dt, mesh, state, diag, dimensions, time_lev)Add new subroutines:
subroutine chemistry_from_MPAS(mesh, state, diag, dimensions, time_lev, &
nCells, nVertLevels)
! 1. Get scalars, rho_zz, zz, theta_m, exner from pools
! 2. Calculate rho_air and temperature
! 3. Call MICM_from_chemistry()subroutine chemistry_to_MPAS(mesh, state, diag, dimensions, time_lev, &
nCells, nVertLevels)
! 1. Get scalars and rho_air
! 2. Call MICM_to_chemistry()Modify chemistry_step implementation:
call chemistry_from_MPAS(...) ! Before solver - calls MICM_from_chemistry
call musica_step(dt, ...) ! Solve
call chemistry_to_MPAS(...) ! After solver - calls MICM_to_chemistryModify call to chemistry_step (around line 1045-1058):
block_chem => domain % blocklist
do while (associated(block_chem))
call mpas_pool_get_subpool(block_chem % structs, 'mesh', mesh_chem)
call mpas_pool_get_subpool(block_chem % structs, 'state', state_chem)
call mpas_pool_get_subpool(block_chem % structs, 'diag', diag_chem)
time_lev_chem = 1
call chemistry_step(dt, mesh_chem, state_chem, diag_chem, &
block_chem % dimensions, time_lev_chem)
block_chem => block_chem % next
end docell_stride = state%species_strides%grid_cell
var_stride = state%species_strides%variable
spec_idx = state%species_ordering%index("AB", err)
do k = 1, nVertLevels
idx = 1 + (k-1)*cell_stride + (spec_idx-1)*var_stride
state%concentrations(idx) = conc_value
end docall mpas_pool_get_array(state, 'scalars', scalars, time_lev)
call mpas_pool_get_array(state, 'rho_zz', rho_zz, time_lev)
call mpas_pool_get_array(mesh, 'zz', zz)
call mpas_pool_get_array(diag, 'exner', exner)
call mpas_pool_get_dimension(state, 'index_qAB', index_qAB)! Moist air density [kg/m³]
rho_air(k, iCell) = zz(k, iCell) * rho_zz(k, iCell) * (1.0 + qv(k, iCell))
! Temperature [K]
theta = theta_m(k, iCell) / (1.0 + rvord * qv(k, iCell))
T = theta * exner(k, iCell)Column-by-column approach:
- Loop over cells
- For each cell, copy column data to/from MICM state
- MICM state sized for nVertLevels (single column)
This matches current MICM initialization and is simpler to implement correctly.
- Add
MICM_from_chemistryandMICM_to_chemistrytompas_musica.F - Add
chemistry_from_MPASandchemistry_to_MPAStompas_atm_chemistry.F - Modify
chemistry_stepsignature and implementation - Update call site in
mpas_atm_core.F - Build and test with supercell case
- Build:
make-mpas - Run:
$HOME/bin/run-mpas - Verify in log: ABBA concentrations evolve over timesteps
- Inspect output:
ncks -v qAB,qA,qB output.nc