Source code for gasfir._parameter_store

"""Internal module for managing persistent parameter storage.

This module handles loading and saving of parameter data. Not for end-user use.
"""

import json
import os
import warnings
from pathlib import Path
from typing import Dict, List, Optional, Union

# Get the package's data directory
_PACKAGE_DIR = Path(__file__).parent
_PARAMS_FILE = _PACKAGE_DIR / "data" / "parameters.json"

# Default parameters that are always available
_DEFAULT_PARAMETERS = {
    "N2_Hacc5_QS": {
        "E_g": 0.5340041306217247,
        "a0": 99999.99999998882,
        "a1": 15.376194957191581,
        "a2": 0.8790068909090262,
        "a4": 2.770721571015505,
    },
    "H2_Hacc5_QS": {
        "E_g": 0.5673724982911573,
        "a0": 1937.088166540485,
        "a1": 8.491546001681295,
        "a2": 1.3543992171831474,
        "a4": 2.011176745861679,
    },
    "CO_Hacc5_QS": {
        "E_g": 0.5255150414899638,
        "a0": 0.00233014720296687,
        "a1": 2.8261859163927094,
        "a2": 2.3475286210319757,
        "a4": -3.998124088750205,
    },
    "Cs_QS": {
        "E_g": 0.14309811329075314,
        "a0": 1.101183623936075,
        "a1": 10.10193123075726,
        "a2": 5.999999850621686,
        "a4": -2.7408334445567526,
    },
    "K_QS": {
        "E_g": 0.15951770213954444,
        "a0": 0.8282884118748779,
        "a1": 8.143214583015666,
        "a2": 5.999999999965908,
        "a4": -2.767117023726227,
    },
    "Fr_QS": {
        "E_g": 0.14966888877455772,
        "a0": 1.0073373413965747,
        "a1": 9.282909141254013,
        "a2": 5.999999969726635,
        "a4": -2.741824131101126,
    },
    "Rb_QS": {
        "E_g": 0.15350551607047047,
        "a0": 0.8382418045183027,
        "a1": 8.549404330511935,
        "a2": 5.999999962739765,
        "a4": -2.797254328314558,
    },
    "H_diabatic": {
        "E_g": 0.5,
        "a0": 6.5145636716865285,
        "a1": 3.5,
        "a2": 3.412961064100051,
        "a3": 3.1901975961759295,
        "a4": -0.5,
    },
    "H_SFA": {
        "E_g": 0.5,
        "a0": 3.376633520215513,
        "a1": 3.5,
        "a2": 2.035789354025642,
        "a3": 0.27574597131861167,
        "a4": 0,
    },
    "H_QS": {
        "E_g": 0.5,
        "a1": 3.5,
        "a2": 3.8752084444900223,
        "a0": 2.8369675204664158,
        "a4": -0.5,
    },
    "Ne_Hacc4_QS": {
        "E_g": 0.792461982845425,
        "a1": 1.9131207338962406,
        "a2": 4.169719545489457,
        "a0": 0.9316816153527308,
        "a4": -0.02900758396067893,
    },
    "He_Hacc5_QS": {
        "E_g": 0.9036653755411335,
        "a1": 1.0195227787372818,
        "a2": 2.2141763280400886,
        "a0": 0.0195686829574937,
        "a4": -2.913981621862887,
    },
    "He_Hacc5": {"E_g": 0.9035551, "a1": 2.0, "a2": 3.26, "a3": 0.38, "a0": 0.82},
    "SiO2_Ulmic": {
        "E_g": 0.347,
        "a1": 1.5,
        "m_eff": 0.235,
        "a2": 0.8,
        "a3": -5.68,
        "a0_per_au3": 0.0194,
        "lattice_constant_au": 1.0,
    },
    "Diamond_TBmBJ": {
        "E_g": 0.235,
        "a1": 14,
        "m_eff": 0.115,
        "a2": 1.7,
        "a3": -3.0,
        "a0_per_au3": 0.35,
        "lattice_constant_au": 6.74,
    },
    "Ar_SAE": {"E_g": 0.5791543, "a1": 4.4, "a2": 2.8, "a3": 2.6, "a0": 16.4},
    "Si_TBmBJ": {
        "E_g": 0.108,
        "a1": 25.7,
        "m_eff": 0.080,
        "a2": -0.32,
        "a3": 7.7,
        "a0_per_au3": 25.4,
        "lattice_constant_au": 10.21,
    },
}
_DEFAULT_PARAMETERS["Hydrogen_SFA"] = _DEFAULT_PARAMETERS["H_SFA"]
_DEFAULT_PARAMETERS["Hydrogen_diabatic"] = _DEFAULT_PARAMETERS["H_diabatic"]
_DEFAULT_PARAMETERS["Hydrogen_QS"] = _DEFAULT_PARAMETERS["H_QS"]
_DEFAULT_PARAMETERS["Hydrogen"] = _DEFAULT_PARAMETERS["H_diabatic"]


def _ensure_data_dir():
    """Ensure the data directory exists."""
    data_dir = _PACKAGE_DIR / "data"
    data_dir.mkdir(exist_ok=True)
    return data_dir


def _load_parameters() -> Dict[str, Dict[str, float]]:
    """Load parameters from file, falling back to defaults if file doesn't exist."""
    try:
        with open(_PARAMS_FILE, "r") as f:
            stored_params = json.load(f)
        # Merge with defaults, stored params take precedence
        params = _DEFAULT_PARAMETERS.copy()
        params.update(stored_params)
        return params
    except (FileNotFoundError, json.JSONDecodeError):
        return _DEFAULT_PARAMETERS.copy()


def _save_parameters_to_file(parameters: Dict[str, Dict[str, float]]) -> None:
    """Save parameters to file.

    Only saves parameters that differ from defaults to keep the file clean.
    """
    _ensure_data_dir()

    # Only store parameters that differ from defaults
    custom_params = {}
    for medium, params in parameters.items():
        if medium not in _DEFAULT_PARAMETERS or params != _DEFAULT_PARAMETERS[medium]:
            custom_params[medium] = params

    with open(_PARAMS_FILE, "w") as f:
        json.dump(custom_params, f, indent=4)


def _save_parameters(medium_name: str, parameters: Dict[str, float]) -> None:
    """Persist a single medium's parameters to the on-disk store.

    Args:
        medium_name: Identifier string for the medium (e.g. ``"H_SFA"``).
        parameters: Dict mapping parameter names to their float values.
    """
    param_as_dict = {medium_name: parameters}
    _write_parameters(param_as_dict)


# Global parameters dictionary, initialized on module import
_PARAMETERS = _load_parameters()


[docs] def get_parameters( medium_name: Optional[str] = None, ) -> Union[Dict[str, float], List[str]]: """Return parameters for a medium, or list all available medium names. Args: medium_name: Case-insensitive name of the medium (e.g. ``"Hydrogen_SFA"``). Pass ``None`` to get a list of all available medium names. Returns: A copy of the parameter dict when *medium_name* is given, or a list of all known medium name strings when *medium_name* is ``None``. Raises: ValueError: If *medium_name* is not found in the parameter store. Example: >>> params = get_parameters("Hydrogen_SFA") >>> names = get_parameters() # list all available media """ if medium_name is None: return list(_PARAMETERS.keys()) # Make key lookup case-insensitive for key in _PARAMETERS: if key.lower() == medium_name.lower(): return _PARAMETERS[key].copy() available = list(_PARAMETERS.keys()) raise ValueError( f"Unknown medium: {medium_name.lower()}. Available media are: {available}" )
def _write_parameters(new_parameters: Dict[str, Dict[str, float]]) -> None: """Add new parameters to the store and save them to disk. This is an internal function not meant for end-user use. """ required_params = {"E_g", "a0", "a1", "a2", "a4"} diabatic_params = {"a3", "a5"} internal_params = { "internal_param_xi1", "internal_param_pxi1", "internal_param_pxi2", } for medium, params in new_parameters.items(): # Check if all required parameters are present missing_params = required_params - set(params.keys()) if missing_params: raise ValueError( f"Parameter dictionary for {medium} is missing required parameters: {missing_params}" ) # give a warning if diabatic parameters are not present if not diabatic_params.issubset(params.keys()): warnings.warn( f"Diabatic parameters {diabatic_params} are not present for {medium}. " "This may lead to incorrect results if using these parameters to compute diabatic rates." ) # give a warning if internal parameters are being used if internal_params.intersection(params.keys()): warnings.warn( f"Internal parameters {internal_params.intersection(params.keys())} are being used for {medium}. " "These parameters are not meant for advanced users and may lead to unexpected results." ) # Add or update parameters _PARAMETERS[medium] = params.copy() # Save to disk _save_parameters_to_file(_PARAMETERS)
[docs] def return_parameter_table(): r"""Return a formatted LaTeX ``tabularx`` string of all stored parameters. Each row is ``medium_name & a0 & a1 & a2 & a3 & a4 & E_g & m_eff``. The returned string is a complete ``tabularx`` environment ready to be pasted into a LaTeX document. """ media = get_parameters() table_str = "\\begin{tabularx}{\\linewidth}{@{} l *{5}{>{\\raggedleft\\arraybackslash}X} @{} }\n" table_str += " \\toprule\n" table_str += " & $a_0$ & $a_1$ & $a_2$ & $a_3$ & $a_4$ & E_g & m_eff \\\\\n" table_str += " \\midrule\n" for medium in media: params = get_parameters(medium) a0 = params.get("a0", "N/A") a1 = params.get("a1", "N/A") a2 = params.get("a2", "N/A") a3 = params.get("a3", "N/A") a4 = params.get("a4", "N/A") E_g = params.get("E_g", "N/A") m_eff = params.get("m_eff", "N/A") # format medium name to be more presentable in latex, e.g. "H_SFA" -> "$H_\mathrm{SFA}$" # the first part is subscript the second superscript, e.g. "H_SFA_diabatic" -> "$H_\mathrm{SFA}^\mathrm{diabatic}$" replaced_medium = ( medium.replace("_diabatic", "^\\mathrm{diabatic}") .replace("_QS", "^\\mathrm{QS}") .replace("_SAE", "^\\mathrm{SAE}") .replace("_Ulmic", "^\\mathrm{Ulmic}") .replace("_TBmBJ", "^\\mathrm{TBmBJ}") ) medium = medium.replace("_", "_\\mathrm{") + "}" medium = f"${medium}$" if isinstance(E_g, (int, float)): E_g_eV = E_g * 27.21138 else: E_g_eV = "N/A" table_str += f" {medium} & {a0} & {a1} & {a2} & {a3} & {a4} & {E_g_eV} & {m_eff} \\\\\n" table_str += " \\bottomrule\n" table_str += " \\end{tabularx}" return table_str
if __name__ == "__main__": import sys print(return_parameter_table()) if len(sys.argv) > 1: output_path = sys.argv[1] with open(output_path, "w") as f: f.write(return_parameter_table())