diff --git a/src/core_atmosphere/Registry.xml b/src/core_atmosphere/Registry.xml index c0bdcdd05d..88f61285f9 100644 --- a/src/core_atmosphere/Registry.xml +++ b/src/core_atmosphere/Registry.xml @@ -1737,6 +1737,15 @@ description="Graupel mixing ratio" packages="mp_thompson_in;mp_thompson_aers_in;mp_wsm6_in"/> + + + + + + @@ -2103,6 +2112,15 @@ description="Tendency of graupel mass per unit volume divided by d(zeta)/dz" packages="mp_thompson_in;mp_thompson_aers_in;mp_wsm6_in"/> + + + + + + diff --git a/src/core_atmosphere/chemistry/mpas_atm_chemistry.F b/src/core_atmosphere/chemistry/mpas_atm_chemistry.F index 7fda1f27a8..d0d43910a1 100644 --- a/src/core_atmosphere/chemistry/mpas_atm_chemistry.F +++ b/src/core_atmosphere/chemistry/mpas_atm_chemistry.F @@ -19,6 +19,8 @@ !------------------------------------------------------------------------- module mpas_atm_chemistry + use mpas_kind_types, only : StrKIND, RKIND + implicit none private @@ -86,16 +88,31 @@ end subroutine chemistry_init !> This routine steps the chemistry packages, updating their state !> based on the current simulation time and conditions. !------------------------------------------------------------------------ - subroutine chemistry_step() + subroutine chemistry_step(dt) #ifdef MPAS_USE_MUSICA + use iso_fortran_env, only: real64 use mpas_musica, only: musica_step #endif use mpas_log, only : mpas_log_write + use mpas_derived_types, only: MPAS_LOG_CRIT + + real (kind=RKIND), intent(in) :: dt #ifdef MPAS_USE_MUSICA + real(real64) :: time_step + integer :: error_code + character(len=:), allocatable :: error_message + + time_step = dt + call mpas_log_write('Stepping chemistry packages...') - ! call musica_step() + + call musica_step(time_step, error_code, error_message) + + if (error_code /= 0) then + call mpas_log_write(error_message, messageType=MPAS_LOG_CRIT) + end if #endif end subroutine chemistry_step diff --git a/src/core_atmosphere/chemistry/musica/mpas_musica.F b/src/core_atmosphere/chemistry/musica/mpas_musica.F index a531a2414e..43156e6c38 100644 --- a/src/core_atmosphere/chemistry/musica/mpas_musica.F +++ b/src/core_atmosphere/chemistry/musica/mpas_musica.F @@ -44,21 +44,30 @@ module mpas_musica !> setting up necessary parameters and data structures for the simulation. !> For now, we will load fixed configurations for MICM and TUV-x. !> Later, we will gradually replace the fixed configuration elements - !> with runtime updates using the MUSICA API + !> with runtime updates using the MUSICA API. !------------------------------------------------------------------------ subroutine musica_init(filename_of_micm_configuration, & number_of_grid_cells, & error_code, error_message) + use iso_fortran_env, only: real64 + use musica_micm, only : RosenbrockStandardOrder use musica_util, only : error_t, string_t use mpas_log, only : mpas_log_write - character(len=*), intent(in) :: filename_of_micm_configuration - integer, intent(in) :: number_of_grid_cells - integer, intent(out) :: error_code - character(len=:), allocatable, intent(out) :: error_message + character(len=*), intent(in) :: filename_of_micm_configuration + integer, intent(in) :: number_of_grid_cells + integer, intent(out) :: error_code + character(len=:), allocatable, intent(out) :: error_message + + ! TEMPORARY: ABBA specific initial concentrations + real(real64), allocatable :: init_ab(:) + real(real64), allocatable :: init_a(:) + real(real64), allocatable :: init_b(:) + + character(len=*), parameter :: INITIAL_CONCENTRATION_PROPERTY = "__initial concentration" type(error_t) :: error type(string_t) :: micm_version @@ -99,25 +108,48 @@ end subroutine musica_init ! subroutine musica_step ! !> \brief Steps the MUSICA chemistry package - !> \author CheMPAS-A Developers - !> \date 20 August 2025 + !> \author David Fillmore + !> \date 17 November 2025 !> \details - !> This subroutine steps the MUSICA chemistry package, updating its state - !> based on the current simulation time and conditions. - !> It first calls the TUV-x package to compute photolysis rates, - !> then calls the MICM package to solve the ODEs for chemical species - !> concentrations. + !> This subroutine steps the MICM ODE solver. !------------------------------------------------------------------------ - subroutine musica_step() + subroutine musica_step(time_step, error_code, error_message) + + use iso_fortran_env, only: real64 + + use musica_micm, only : solver_stats_t + use musica_util, only : error_t, string_t use mpas_log, only : mpas_log_write + real(real64), intent(in) :: time_step + integer, intent(out) :: error_code + character(len=:), allocatable, intent(out) :: error_message + + type(string_t) :: solver_state + type(solver_stats_t) :: solver_stats + type(error_t) :: error + if (.not. musica_is_initialized) return - call mpas_log_write('Stepping MUSICA chemistry package...') + call mpas_log_write('[MUSICA] Stepping MICM solver...') + + call micm%solve(time_step, state, solver_state, solver_stats, error) + if (has_error_occurred(error, error_message, error_code)) return + + if (solver_state%get_char_array() /= 'Converged') then + call mpas_log_write('[MUSICA Warning] MICM solver failure') + end if + + call log_solver_stats(solver_stats) - ! Here we would typically call the TUV-x and MICM packages to perform - ! the necessary computations, but for now we will just log the step. + ! TEMPORARY: ABBA specific logging + call mpas_log_write('[MUSICA] MICM AB concentrations') + call log_concentrations(state, state%number_of_grid_cells, 'AB', error) + call mpas_log_write('[MUSICA] MICM A concentrations') + call log_concentrations(state, state%number_of_grid_cells, 'A', error) + call mpas_log_write('[MUSICA] MICM B concentrations') + call log_concentrations(state, state%number_of_grid_cells, 'B', error) end subroutine musica_step @@ -143,6 +175,110 @@ subroutine musica_finalize() end subroutine musica_finalize + subroutine assign_rate_parameters(state, number_of_grid_cells) + ! Provide a default value for any rate parameters (none for ABBA by default). + use iso_fortran_env, only : real64 + + type(state_t), intent(inout) :: state + integer, intent(in) :: number_of_grid_cells + + integer :: cell_stride, var_stride, rp_index, cell, idx + + if (state%number_of_rate_parameters == 0) return + + cell_stride = state%rate_parameters_strides%grid_cell + var_stride = state%rate_parameters_strides%variable + + do rp_index = 1, state%rate_parameters_ordering%size() + do cell = 1, number_of_grid_cells + idx = 1 + (cell - 1) * cell_stride + (rp_index - 1) * var_stride + state%rate_parameters(idx) = 1.0_real64 + end do + end do + end subroutine assign_rate_parameters + + subroutine set_species_profile(state, species_name, values, err) + ! Write a 1-D profile into the contiguous concentrations array. + use iso_fortran_env, only : real64 + use musica_util, only : error_t, string_t + + type(state_t), intent(inout) :: state + character(len=*), intent(in) :: species_name + real(real64), intent(in) :: values(:) + type(error_t), intent(inout) :: err + integer :: species_index, cell_stride, var_stride, cell, idx + + species_index = state%species_ordering%index(species_name, err) + if (.not. err%is_success()) return + + cell_stride = state%species_strides%grid_cell + var_stride = state%species_strides%variable + + do cell = 1, size(values) + idx = 1 + (cell - 1) * cell_stride + (species_index - 1) * var_stride + state%concentrations(idx) = values(cell) + end do + end subroutine set_species_profile + + subroutine log_concentrations(state, number_of_grid_cells, species_name, err) + use iso_fortran_env, only : real64 + use mpas_log, only : mpas_log_write + use mpas_kind_types, only : RKIND + use musica_util, only : error_t, string_t + + type(state_t), intent(in) :: state + integer, intent(in) :: number_of_grid_cells + character(len=*), intent(in) :: species_name + type(error_t), intent(inout) :: err + + integer :: species_index, cell_stride, var_stride, cell, idx + real (kind=RKIND) :: concentration + + species_index = state%species_ordering%index(species_name, err) + if (.not. err%is_success()) return + + cell_stride = state%species_strides%grid_cell + var_stride = state%species_strides%variable + + do cell = 1, number_of_grid_cells + idx = 1 + (cell - 1) * cell_stride + (species_index - 1) * var_stride + concentration = state%concentrations(idx) + call mpas_log_write(' MICM concentration: $r', realArgs=[concentration]) + end do + end subroutine log_concentrations + + subroutine log_solver_stats(solver_stats) + use mpas_log, only : mpas_log_write + use mpas_kind_types, only : RKIND + use musica_micm, only : solver_stats_t + + type(solver_stats_t), intent(in) :: solver_stats + + integer :: function_calls, jacobian_updates, number_of_steps + integer :: accepted, rejected, decompositions, solves + + real (kind=RKIND) :: final_time + + function_calls = solver_stats%function_calls() + jacobian_updates = solver_stats%jacobian_updates() + number_of_steps = solver_stats%number_of_steps() + accepted = solver_stats%accepted() + rejected = solver_stats%rejected() + decompositions = solver_stats%decompositions() + solves = solver_stats%solves() + final_time = solver_stats%final_time() + + call mpas_log_write('[MUSICA] MICM Solver statistics ...') + call mpas_log_write(' MICM function calls: $i', intArgs=[function_calls]) + call mpas_log_write(' MICM jacobian updates: $i', intArgs=[jacobian_updates]) + call mpas_log_write(' MICM number of steps: $i', intArgs=[number_of_steps]) + call mpas_log_write(' MICM accepted: $i', intArgs=[accepted]) + call mpas_log_write(' MICM rejected: $i', intArgs=[rejected]) + call mpas_log_write(' MICM LU decompositions: $i', intArgs=[decompositions]) + call mpas_log_write(' MICM linear solves: $i', intArgs=[solves]) + call mpas_log_write(' MICM final time (s): $r', realArgs=[final_time]) + end subroutine log_solver_stats + !------------------------------------------------------------------------- ! function has_error_occurred ! @@ -175,7 +311,6 @@ logical function has_error_occurred(error, error_message, error_code) error_message = '[MUSICA Error]: ' // error%category( ) // '[' // & trim( adjustl( error_code_str ) ) // ']: ' // error%message( ) has_error_occurred = .true. - end function has_error_occurred end module mpas_musica diff --git a/src/core_atmosphere/mpas_atm_core.F b/src/core_atmosphere/mpas_atm_core.F index eef1951e36..effa48beeb 100644 --- a/src/core_atmosphere/mpas_atm_core.F +++ b/src/core_atmosphere/mpas_atm_core.F @@ -1038,7 +1038,7 @@ subroutine atm_do_timestep(domain, dt, itimestep) #endif #ifdef DO_CHEMISTRY - call chemistry_step() + call chemistry_step(dt) #endif call atm_timestep(domain, dt, currTime, itimestep, exchange_halo_group) diff --git a/src/core_init_atmosphere/Registry.xml b/src/core_init_atmosphere/Registry.xml index fc9be9fb65..9c84da47cd 100644 --- a/src/core_init_atmosphere/Registry.xml +++ b/src/core_init_atmosphere/Registry.xml @@ -1163,6 +1163,15 @@ + + + + + +