Source code for pycif.plugins.models.lmdz_ico.chemistry.chemical_scheme
from dataclasses import dataclass
from typing import Any, List, Literal, Tuple
import numpy as np
import xarray as xr
from ...lmdz_acc.chemistry.chemical_scheme import P_REF, Species
from .....utils.check.errclass import CifNotImplementedError, CifValueError
Model = Any
[docs]
@dataclass
class Reaction:
"""A class representing a chemical reaction
Args:
active_reactants (list of Species): Active reactants
prescribed_reactants (list of Species): Prescribed reactants
active_products (list of Species): Active products
active_product_stoi (list of int): Active product stoichiometric numbers
reac_type (int): Reaction type
rate_constants (list of float) Reaction rate constants
"""
active_reactants: List[Species] # Active reactants
prescribed_reactants: List[Species] # Prescribed reactants
active_products: List[Species] # Active products
active_product_stoi: List[int] # Active product stoichiometric numbers
reac_type: Literal[
"constant", "simplified_arrhenius", "arrhenius", "pressure"
] # Reaction type
rate_constants: List[float] # Reaction rate constants
[docs]
def rates(self, temp: xr.DataArray, pmid: xr.DataArray) -> xr.DataArray:
"""Compute reaction rates
Args:
temp (xr.DataArray): temperature field [K]
pmid (xr.DataArray): pressure field [Pa]
Returns:
xr.DataArray: rates [molec/cm2/s2]
"""
# Constant rates
if self.reac_type == "constant":
da = self.rate_constants[0] * temp.copy(data=np.ones(temp.shape))
# Arrhenius simplified rates
elif self.reac_type == "simplified_arrhenius":
a, b = self.rate_constants
da = a * np.exp(-b / temp)
# Arrhenius complete rates
elif self.reac_type == "arrhenius":
a, b, c = self.rate_constants
da = a * np.exp(-b / temp) * (300.0 / temp) ** c
# Pressure rates
elif self.reac_type == "pressure":
a, b, c = self.rate_constants
da = a * (b + c * pmid / P_REF)
# Photolysis rates
elif self.reac_type == "photolysis":
raise CifNotImplementedError("Photolysis reaction rates are not supported")
else:
raise CifValueError(f"unexpected reaction type '{self.reac_type}'")
return da # type: ignore
[docs]
def parse_chemical_scheme(self: Model) -> Tuple[List[Reaction], xr.DataArray]:
"""Parse the chemical scheme, partial reimplementation of LMDZ's
"read_chemical_scheme" subroutine
Args:
self (Model)
Returns:
(list of Reaction, xr.DataArray): list of reactions, molar masses
"""
species_dict = {}
for i, spec in enumerate(self.chemistry.active_species):
species_dict[spec] = Species(name=spec, type="ac", index=i)
for i, spec in enumerate(self.chemistry.prescribed_species):
species_dict[spec] = Species(name=spec, type="pr", index=i)
molar_masses = xr.DataArray(
data=[spec.molar_mass for spec in self.chemistry.active_species.values()],
dims=["spec"],
)
if self.chemistry.prodloss_species:
raise CifNotImplementedError("prodloss species are not implemented")
if self.chemistry.deposition_species:
raise CifNotImplementedError("deposition species are not implemented")
reaction_list = []
for reac_str in self.chemistry.reactions:
reactants, products, stoi, reac_type, rate_constants = (
self.chemistry.parse_reaction(self, reac_str)
)
reactants = [species_dict[spec] for spec in reactants]
products = [species_dict[spec] for spec in products]
ac_reactants = [spec for spec in reactants if spec.is_active()]
pr_reactants = [spec for spec in reactants if spec.is_prescribed()]
ac_products = [spec for spec in products if spec.is_active()]
if ac_products:
raise CifNotImplementedError(
"reactions with active products are not implemented"
)
reaction_list.append(
Reaction(
active_reactants=ac_reactants,
prescribed_reactants=pr_reactants,
active_products=[],
active_product_stoi=[],
reac_type=reac_type,
rate_constants=rate_constants,
)
)
return reaction_list, molar_masses