Source code for torx.specializations.reax.rate_coefficient_m

"""
Base class for calculating rates based on the AMJUEL database.

Contains ionization / recombination reaction and cooling rates.

The RateCoefficient class mimics the REAX module rate_coefficients_m
to perform the same computation a posteriori, i.e. based on the xr.DataArray
that stores the GENE-X and GRILLIX simulation data.
"""
import numpy as np
import xarray as xr
from pathlib import Path

from ... import reax_resources_dir
from torx import Quantity
from torx.autodoc_decorators_m import autodoc_class
from torx.fileio import filepath_resolver

[docs] @autodoc_class class RateCoefficient: """Manages rate coefficient calculations based on AMJUEL."""
[docs] def __init__(self, coeff_type: int, file_name: str, folder_path: Path = None ): """ Initialize the RateCoefficient object. Given the coeff_type (0 for reaction or 1 for cooling). By default, the file stored in TorX is used, but one can use another file by additionally specifying a folder_path. """ if coeff_type not in (0, 1): raise ValueError("Attribute 'coeff_type' must be 0 (reaction rate) \ or 1 (cooling rate).") self.coeff_type = coeff_type # Load fit coefficient if folder_path is None: folder_path = Path(reax_resources_dir) self.file_path = Path(filepath_resolver(folder_path, file_name)) self.file_name = file_name # AMJUEL fit have T-index as row and E-index as column self.fit_coeff = xr.DataArray( np.loadtxt(self.file_path, max_rows=9), dims=["T", "E"], coords={"T": np.arange(9), "E": np.arange(9)} )
[docs] def compute(self, n: xr.DataArray, t: xr.DataArray, norm=None ) -> xr.DataArray: """Evaluate the normalized rate coefficient.""" if isinstance(n, xr.DataArray) and isinstance(t, xr.DataArray): return self._compute_xarray(n, t, norm) # Raise an error for unsupported types raise TypeError("Unsupported types for compute. \ Expected xarray.DataArray.")
def _compute_xarray(self, ne: xr.DataArray, te: xr.DataArray, norm=None ) -> xr.DataArray: """Return rate coefficient as xarray.""" # Set the rate normalization for the rate coefficient instance if norm: try: k_norm = norm.c_s0 / (norm.n0 * norm.R0) except AttributeError as e: print("Incompatible norm provided, \ using default k_norm (cm^3/s).") k_norm = Quantity(1.0, "cm^3/s") else: print("No norm provided, using default k_norm (cm^3/s).") k_norm = Quantity(1.0, "cm^3/s") # Extract normalization constants from attribute if hasattr(ne, "norm"): ne_norm = ne.norm elif norm is not None: ne_norm = norm.n0 else: ne_norm = Quantity(1.0, "1/cm^3") if hasattr(te, "norm"): te_norm = te.norm elif norm is not None: te_norm = norm.Te0 else: te_norm = Quantity(1.0, "eV") if self.coeff_type == 0: rate_norm = k_norm elif self.coeff_type == 1: rate_norm = te_norm * k_norm # Convert norms to magnitudes for computation ne_norm_value = ne_norm.to("1/cm^3").magnitude te_norm_value = te_norm.to("eV").magnitude te_fit = np.log(te * te_norm_value) ne_fit = np.log(ne * ne_norm_value * 1.0e-8) # Dask-compatible equivalent of # np.polynomial.polynomial.polyval2d(te_fit, ne_fit, self.fit) i_vals = np.arange(9) j_vals = np.arange(9) n_powers = xr.concat([ne_fit ** i for i in i_vals], dim="E") t_powers = xr.concat([te_fit ** j for j in j_vals], dim="T") rate_values = xr.dot(self.fit_coeff, n_powers, dims="E") rate_values = xr.dot(rate_values, t_powers, dims="T") rate_values = np.exp(rate_values) / rate_norm.magnitude rate_values.attrs["norm"] = rate_norm rate_values.attrs["description"] = self.file_name return rate_values