Source code for torx.specializations.grillix.atomic_data_m
"""Allows interfacing with GRILLIX atomic data."""
from numba import njit
import numpy as np
from pathlib import Path
import torx
import xarray as xr
from torx.autodoc_decorators_m import autodoc_class
@njit
def poly8x8_eval(pc, x, y):
"""Return double polynomial of degree 8x8 evaluated at x and y."""
pcx = np.zeros(9)
for m in range(9):
# collapse to single polynomial
pcx[m] = poly8_eval(pc[m, :], y)
result = poly8_eval(pcx, x)
return result
@njit
def poly8_eval(pc, x):
"""Return polynomial of degree 8 evaluated at x and y."""
val = 0.0
for n in range(8, 0, -1):
val = (val + pc[n]) * x
val = val + pc[0]
return val
[docs]
@autodoc_class
class AtomicData:
"""Reads in and evaluates rate coefficients for neutrals."""
[docs]
def __init__(self, run_filepath, neutrals_params_path="params_neutrals.in"):
"""Initialize the atomic data."""
self.filepath = Path(run_filepath)
assert (self.filepath / neutrals_params_path).exists()
self.params = torx.fileio.read_fortran_namelist(
self.filepath / neutrals_params_path
)
self.ne_ref = self.params["neut_model"]["ne_ref"]
self.te_ref = self.params["neut_model"]["te_ref"]
self.k_ref = self.params["neut_model"]["k_ref"]
self.w_ref = self.params["neut_model"]["w_ref"]
self.s_cx = self.params["neut_model"]["s_cx"]
self.mach_limit = self.params["neut_model"]["mach_limit"]
self.pot_iz = self.params["neut_model"]["pot_iz"]
self._k_iz = np.loadtxt(
(
self.filepath / self.params["neut_data_paths"]["path_k_iz"]
).absolute()
)
self._k_rec = np.loadtxt(
(
self.filepath / self.params["neut_data_paths"]["path_k_rec"]
).absolute()
)
self._w_iz = np.loadtxt(
(
self.filepath / self.params["neut_data_paths"]["path_w_iz"]
).absolute()
)
self._p_rec = np.loadtxt(
(
self.filepath / self.params["neut_data_paths"]["path_p_rec"]
).absolute()
)
def _evaluate_rate_coeff(
self, ne: np.ndarray, te: np.ndarray, rate: np.ndarray
) -> np.ndarray:
"""Evaluate the normalized rate coefficient."""
# Convert inputs to log values, as required for fit (see AMJUEL manual)
te_fit = np.log(te * self.te_ref)
ne_fit = np.log(
ne * self.ne_ref * 1.0e-8
) # apply AMJUEL electron density scaling
return np.exp(poly8x8_eval(rate, te_fit, ne_fit)) / self.k_ref
[docs]
def evaluate_rate_coeff(
self, density: xr.DataArray, electron_temp: xr.DataArray, rate: np.ndarray
) -> xr.DataArray:
"""Evaluate the normalized rate coefficient (parallel)."""
return xr.apply_ufunc(
self._evaluate_rate_coeff,
density,
electron_temp,
kwargs=dict(rate=rate),
vectorize=True,
dask="parallelized",
)
[docs]
def k_iz(self, density: xr.DataArray, electron_temp: xr.DataArray) -> xr.DataArray:
"""Evaluate the k_iz rate coefficient."""
return self.evaluate_rate_coeff(density, electron_temp, rate=self._k_iz)
[docs]
def k_rec(self, density: xr.DataArray, electron_temp: xr.DataArray) -> xr.DataArray:
"""Evaluate the k_rec rate coefficient."""
return self.evaluate_rate_coeff(density, electron_temp, rate=self._k_rec)
[docs]
def w_iz(self, density: xr.DataArray, electron_temp: xr.DataArray) -> xr.DataArray:
"""Evaluate the w_iz rate coefficient."""
return self.evaluate_rate_coeff(density, electron_temp, rate=self._w_iz)
[docs]
def p_rec(self, density: xr.DataArray, electron_temp: xr.DataArray) -> xr.DataArray:
"""Evaluate the p_rec rate coefficient."""
return self.evaluate_rate_coeff(density, electron_temp, rate=self._p_rec)