Chapter 2#

An MICM Box Model Fortran Example#

In this next MUSICA Fortran example, we will setup a MICM solver, starting with a set of MICM configuration files, and run the solver for a single integration time step.

The MICM configuration is specified in a top-level config.json file, which simply lists the chemical species configuration file followed by the reactions configuration file.

{"camp-files": ["species.json", "reactions.json"]}

For this example, we will have a system of three chemical species A, B, and C, defined in the JSON file species.json as follows:

{
  "camp-data":
  [
    {
      "name": "A", "type": "CHEM_SPEC"
    },
    {
      "name": "B", "type": "CHEM_SPEC"
    },
    {
      "name": "C", "type": "CHEM_SPEC"
    },
    {
      "name": "D", "type": "CHEM_SPEC"
    },
    {
      "name": "E", "type": "CHEM_SPEC"
    },
    {
      "name": "F", "type": "CHEM_SPEC"
    }
  ]
}

The reactions.json specifies a mechanism, or a set of reactions for the system. Here, we will introduce two Arrhenius type reactions, the first with B evolving to C, and specifying all five reaction parameters, and the second reaction with A evolving to B and using only two reaction parameters. The mechanism configuration might then be set up as:

{
  "camp-data":
  [
    {
      "type": "MECHANISM",
      "name": "music box interactive configuration",
      "reactions":
      [
        {
          "type": "USER_DEFINED",
          "MUSICA name" : "reaction 2",
          "reactants": {"E": {"qty": 1}},
          "products": {"F": {"yield": 1}}
        },
        {
          "type": "USER_DEFINED",
          "MUSICA name" : "reaction 1",
          "reactants": {"D": {"qty": 1}},
          "products": {"E": {"yield": 1}}
        },
        {
          "type": "ARRHENIUS", "A": 0.004, "C" : 50,
          "reactants": {"A": {"qty": 1}},
          "products": {"B": {"yield": 1}}
        },
        {
          "type": "ARRHENIUS", "A": 0.012, "B": -2, "C" : 75, "D": 50, "E": 1.0e-6,
          "reactants": {"B": {"qty": 1}},
          "products": {"C": {"yield": 1}}
        }
      ]
    }
  ]
}

More information on MICM configurations and reactions can be found in the MICM documentation at `https://ncar.github.io/micm/user_guide/`_

The Fortran example code is shown below in full:

program test_micm_box_model

  use, intrinsic :: iso_c_binding
  use, intrinsic :: ieee_arithmetic

  use musica_util, only: error_t, string_t, mapping_t
  use musica_micm, only: micm_t, solver_stats_t
  use musica_micm, only: Rosenbrock, RosenbrockStandardOrder

  implicit none

  call box_model()

contains

  subroutine box_model()

    character(len=256) :: config_path
    integer(c_int)     :: solver_type
    integer(c_int)     :: num_grid_cells

    real(c_double), parameter :: GAS_CONSTANT = 8.31446261815324_c_double ! J mol-1 K-1

    real(c_double) :: time_step
    real(c_double), target :: temperature(1)
    real(c_double), target :: pressure(1)
    real(c_double), target :: air_density(1)
    real(c_double), target :: concentrations(6)
    real(c_double), target :: user_defined_reaction_rates(2)

    type(string_t)       :: solver_state
    type(solver_stats_t) :: solver_stats
    type(error_t)        :: error

    type(micm_t), pointer :: micm

    integer :: i

    config_path = "configs/analytical"
    solver_type = RosenbrockStandardOrder
    num_grid_cells = 1

    time_step = 200
    temperature(1) = 273.0
    pressure(1) = 1.0e5
    air_density(:) = pressure(:) / (GAS_CONSTANT * temperature(:))

    concentrations = (/ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 /)
    user_defined_reaction_rates = (/ 0.001, 0.002 /)

    write(*,*) "Creating MICM solver..."
    micm => micm_t(config_path, solver_type, num_grid_cells, error)

    do i = 1, micm%species_ordering%size()
      print *, "Species Name:", micm%species_ordering%name(i), &
               ", Index:", micm%species_ordering%index(i)
    end do

    write(*,*) "Solving starts..."
    call micm%solve(time_step, temperature, pressure, air_density, concentrations, &
        user_defined_reaction_rates, solver_state, solver_stats, error)
    write(*,*) "After solving, concentrations", concentrations

    deallocate( micm )

  end subroutine box_model

end program test_micm_box_model

From the musica_util module we need the Fortran types error_t, string_t, and mapping_t. A pointer to a musica_micm::micm_t will serve as the interface to the MICM solver (in the example the pointer name is micm). Note that the config_path in the code sample has been set to configs/analytical, so that subdir should be created relative to the main program and contain the MICM JSON configuration files, or otherwise the config_path should be modified appropriately. The initial species concentrations are initialized in the concentrations array, which is an argument to the MICM solver.

Finally, a single time step solution is obtained through a call to micm%solve, after which the updated concentrations may be displayed.

$ ./test_micm_box_model
  Creating MICM solver...
  Species Name:A, Index:           1
  Species Name:B, Index:           2
  Species Name:C, Index:           3
  Solving starts...
  After solving, concentrations  0.38  1.61E-009  2.62
$