MUSICA Julia API#
The Julia API provides access to MUSICA’s MICM chemical kinetics solver through the Musica.jl package,
which uses CxxWrap.jl to interface with the underlying C++ library.
Installation#
Prerequisites#
Julia 1.10 or 1.11
CMake 3.24 or later
A C++ compiler with C++20 support
Building from Source#
Clone and build MUSICA with Julia support:
git clone https://github.com/NCAR/musica.git
cd musica
cmake -S . -B build -D MUSICA_ENABLE_JULIA=ON -D CMAKE_BUILD_TYPE=Release
cmake --build build
Install the Julia package dependencies:
cd julia
julia --project=. -e 'using Pkg; Pkg.instantiate()'
Quick Start#
The following example runs a single-cell chemical solve using a Rosenbrock solver:
using Musica
println("MUSICA version: ", Musica.get_version())
micm = MICM(config_path = "path/to/config")
state = create_state(micm)
set_conditions!(state, temperatures = 298.0, pressures = 101325.0)
set_concentrations!(state, Dict{String,Any}("A" => 1.0, "B" => 0.0))
set_user_defined_rate_parameters!(state, Dict{String,Any}("USER.k1" => 0.001))
result = solve!(micm, state, 60.0) # integrate 60 seconds
println("Solver state: ", result.state) # Converged
println("Steps taken: ", result.stats.number_of_steps)
concs = get_concentrations(state)
println("Final [A]: ", concs["A"][1])
API Reference#
Core#
- get_version() String#
Returns the version string of the MUSICA library.
println(Musica.get_version()) # e.g. "0.15.0"
Constants#
The following physical constants are exported from the Musica module:
Name |
Value |
Description |
|---|---|---|
|
6.02214076 × 10²³ mol⁻¹ |
Avogadro’s number |
|
1.380649 × 10⁻²³ J K⁻¹ |
Boltzmann constant |
|
|
Universal gas constant (J K⁻¹ mol⁻¹) |
Types#
SolverType#
- type SolverType#
Enum controlling which MICM solver backend is used. Pass to the
MICMconstructor.Value
Int
Description
Rosenbrock1
Vector-ordered Rosenbrock solver
RosenbrockStandardOrder2
Standard-ordered Rosenbrock solver (default)
BackwardEuler3
Vector-ordered Backward Euler solver
BackwardEulerStandardOrder4
Standard-ordered Backward Euler solver
CudaRosenbrock5
GPU Rosenbrock solver (requires CUDA build)
Conditions#
- type Conditions#
Environmental conditions for a single grid cell.
Fields
temperature::Float64— Temperature in Kelvinpressure::Float64— Pressure in Pascalsair_density::Float64— Air number density in mol m⁻³
Constructor
Conditions(; temperature=0.0, pressure=0.0, air_density=nothing)
If
air_densityis not provided and bothtemperatureandpressureare positive, air density is calculated from the Ideal Gas Law:p / (R * T).c = Conditions(temperature = 298.0, pressure = 101325.0) # air_density is computed automatically c2 = Conditions(temperature = 300.0, pressure = 100000.0, air_density = 42.0)
RosenbrockSolverParameters#
- type RosenbrockSolverParameters#
Tuning parameters for the Rosenbrock solver family.
Fields
relative_tolerance::Float64— Relative tolerance (default:1e-6)absolute_tolerances::Union{Vector{Float64}, Nothing}— Per-species absolute tolerances;nothinguses solver defaultsh_min::Float64— Minimum step size in seconds (default:0.0)h_max::Float64— Maximum step size in seconds (default:0.0)h_start::Float64— Initial step size in seconds (default:0.0)max_number_of_steps::Int— Maximum number of internal steps (default:1000)
params = RosenbrockSolverParameters( relative_tolerance = 1e-8, h_start = 1e-5, max_number_of_steps = 500, ) micm = MICM(config_path = path, solver_parameters = params)
BackwardEulerSolverParameters#
- type BackwardEulerSolverParameters#
Tuning parameters for the Backward Euler solver family.
Fields
relative_tolerance::Float64— Relative tolerance (default:1e-6)absolute_tolerances::Union{Vector{Float64}, Nothing}— Per-species absolute tolerances;nothinguses solver defaultsmax_number_of_steps::Int— Maximum number of internal steps (default:11)time_step_reductions::Vector{Float64}— Five reduction factors applied after failed solves (default:[0.5, 0.5, 0.5, 0.5, 0.1])
params = BackwardEulerSolverParameters(relative_tolerance = 1e-4, max_number_of_steps = 20) micm = MICM(config_path = path, solver_type = BackwardEulerStandardOrder, solver_parameters = params)
SolverState#
- type SolverState#
Enum representing the outcome of a
solve!call.Value
Int
Meaning
NotYetCalled0
solve!has not been called yetRunning1
Solver is in progress (internal use)
Converged2
Solution accepted within tolerances
ConvergenceExceededMaxSteps3
Maximum internal steps reached before convergence
StepSizeTooSmall4
Step size fell below numerical limit
RepeatedlySingularMatrix5
Jacobian factorisation failed repeatedly
NaNDetected6
NaN appeared in the solution
InfDetected7
Inf appeared in the solution
AcceptingUnconvergedIntegration8
Solution accepted despite not fully converging
SolverStats#
- type SolverStats#
Performance counters returned by
solve!.Fields
function_calls::Int— Number of right-hand-side evaluationsjacobian_updates::Int— Number of Jacobian computationsnumber_of_steps::Int— Total internal steps takenaccepted::Int— Number of accepted stepsrejected::Int— Number of rejected stepsdecompositions::Int— Number of LU decompositionssolves::Int— Number of linear system solvesfinal_time::Float64— Simulated time reached (seconds)
SolverResult#
- type SolverResult#
Combined outcome of a
solve!call.Fields
state::SolverState— Convergence statusstats::SolverStats— Performance counters
result = solve!(micm, state, 60.0) if result.state == Converged println("Took ", result.stats.number_of_steps, " steps") end
MICM#
- type MICM#
Wrapper around the C++ MICM chemical kinetics solver.
Constructor
MICM(; config_path, solver_type=RosenbrockStandardOrder, solver_parameters=nothing)
config_path::String— Path to the mechanism configuration file or directorysolver_type::SolverType— Solver backend to use (seeSolverType)solver_parameters— OptionalRosenbrockSolverParametersorBackwardEulerSolverParameters
micm = MICM(config_path = "configs/v0/analytical") micm_be = MICM(config_path = "configs/v0/analytical", solver_type = BackwardEulerStandardOrder)
State#
- type State#
Chemical state for one or more grid cells. Created via
create_state; do not construct directly.Holds species concentrations, environmental conditions, and user-defined rate parameters. Internally uses the same vector-ordering layout as the C++ and Python APIs.
Functions#
MICM Functions#
- create_state(micm: :MICM; number_of_grid_cells = 1) State#
Create a new
Stateobject for the given solver.number_of_grid_cells— Number of independent atmospheric columns (default:1)
state = create_state(micm) state3 = create_state(micm, number_of_grid_cells = 3)
- solve!(micm::MICM, state::State, time_step::Real) -> SolverResult
Integrate the chemical system forward by
time_stepseconds. Species concentrations instateare updated in-place.result = solve!(micm, state, 60.0) @assert result.state == Converged
- solver_type(micm: :MICM) SolverType#
Return the solver type this
MICMwas created with.
- set_solver_parameters!(micm::MICM, params)
Update solver tuning parameters.
paramsmust match the solver type (RosenbrockSolverParametersfor Rosenbrock solvers,BackwardEulerSolverParametersfor Backward Euler solvers).set_solver_parameters!(micm, RosenbrockSolverParameters(relative_tolerance = 1e-8))
- get_solver_parameters(micm: :MICM)#
Return the current solver parameters as
RosenbrockSolverParametersorBackwardEulerSolverParameters, depending on the solver type.
State Functions#
- set_concentrations!(state::State, concentrations::Dict{String})
Set species concentrations. For a single grid cell, values may be scalars. For multiple grid cells, provide a
Vectorof lengthnumber_of_grid_cells.# Single grid cell set_concentrations!(state, Dict{String,Any}("A" => 1.0, "B" => 0.0)) # Multiple grid cells set_concentrations!(state, Dict{String,Any}("A" => [1.0, 2.0, 3.0]))
- get_concentrations(state: :State) Dict{String, Vector{Float64}}#
Return species concentrations for all grid cells. Keys are species names; each value is a vector of length
number_of_grid_cells.
- set_conditions!(state::State; temperatures, pressures, air_densities)
Set environmental conditions. All keyword arguments accept scalars (single grid cell) or vectors (multiple grid cells). If
air_densitiesis omitted, it is computed from the Ideal Gas Law using the provided temperature and pressure.# Single cell, auto air_density set_conditions!(state, temperatures = 298.0, pressures = 101325.0) # Multiple cells set_conditions!(state, temperatures = [298.0, 310.0, 280.0], pressures = [101325.0, 95000.0, 105000.0])
- get_conditions(state: :State) Dict{String, Vector{Float64}}#
Return environmental conditions for all grid cells. Keys are
"temperature"(K),"pressure"(Pa), and"air_density"(mol m⁻³).
- set_user_defined_rate_parameters!(state::State, params::Dict{String})
Set user-defined rate parameters (e.g. photolysis rates or emission fluxes). Values may be scalars or vectors matching the number of grid cells.
set_user_defined_rate_parameters!(state, Dict{String,Any}("USER.reaction 1" => 0.001, "USER.reaction 2" => 0.002))
- get_user_defined_rate_parameters(state: :State) Dict{String, Vector{Float64}}#
Return user-defined rate parameters for all grid cells.
- get_species_ordering(state: :State) Dict{String, Int}#
Return the mapping of species names to their 0-based indices in the internal concentration array.
- get_user_defined_rate_parameters_ordering(state: :State) Dict{String, Int}#
Return the mapping of user-defined rate parameter names to their 0-based indices.
Multi-Grid-Cell Example#
using Musica
micm = MICM(config_path = "configs/v0/analytical")
state = create_state(micm, number_of_grid_cells = 3)
set_concentrations!(state, Dict{String,Any}(
"A" => [1.0, 2.0, 3.0],
"B" => [0.0, 0.0, 0.0],
))
set_conditions!(state,
temperatures = [298.0, 310.0, 280.0],
pressures = [101325.0, 95000.0, 105000.0],
)
set_user_defined_rate_parameters!(state, Dict{String,Any}(
"USER.reaction 1" => [0.001, 0.002, 0.003],
"USER.reaction 2" => [0.004, 0.005, 0.006],
))
result = solve!(micm, state, 60.0)
@assert result.state == Converged
concs = get_concentrations(state)
println("Final [A] per cell: ", concs["A"])
Testing#
To run the Julia test suite:
cd julia
julia --project=. test/runtests.jl