Source code for torx.equilibrium.io.equilibrium_from_file_m

"""Contains functionality to read equilibrium files into RawNumericalEquilibrium."""
import numpy as np
from pathlib import Path
from typing import Union

from .eqdsk_io_m import read_eqdsk_file
from .xml_io_m import read_xml_file
from torx.equilibrium import RawNumericalEquilibrium
from torx.specializations.aug import AUG_KEY_MAP, load_aug_shotfile
from torx.decorators import autodoc_function
from torx.geometry import Polygon2D

_EQDSK_KEY_MAP = {
    "axis_r":         "rmaxis",
    "axis_z":         "zmaxis",
    "axis_Btor":      "bcentr",
    "psi_axis":       "simag",
    "psi_separatrix": "sibry",
    "spline_basis_r": "r",
    "spline_basis_z": "z",
    "psi_data":       "psirz",
    "x_point_r":      None,
    "x_point_z":      None,
}

_XML_KEY_MAP = {
    "axis_r":         ("op", 1),
    "axis_z":         ("op", 2),
    "axis_Btor":      "B0",
    "psi_axis":       None,
    "psi_separatrix": None,
    "spline_basis_r": "r",
    "spline_basis_z": "z",
    "psi_data":       "psi",
    "x_point_r":      ("xp", 1),
    "x_point_z":      ("xp", 2),
}

_SUPPORTED_FILETYPES = ["eqdsk", "xml", "pickle", "pkl"]

def _apply_key_map(raw: dict, key_map: dict) -> dict:
    """
    Apply a key map to a raw dictionary.

    Parameters
    ----------
    raw : dict
        Raw dictionary from a file reader.
    key_map : dict
        Mapping from standardized keys to raw keys. Values can be:
            - str: direct key lookup
            - tuple: (key, index) for array indexing
            - None: key is not available in this format

    Returns
    -------
    dict
        Dictionary with standardized keys.
    """
    result = {}
    for std_key, raw_key in key_map.items():
        if raw_key is None:
            continue
        if isinstance(raw_key, tuple):
            key, idx = raw_key
            result[std_key] = raw[key][idx]
        else:
            result[std_key] = raw[raw_key]

    result["raw"] = raw

    return result

def _psi_at_location(
    psi: np.ndarray,
    r: np.ndarray,
    z: np.ndarray,
    r_loc: float,
    z_loc: float,
) -> float:
    """
    Look up psi at the nearest grid point to a given location.

    Parameters
    ----------
    psi : np.ndarray
        2D array of poloidal flux values with shape (nz, nr).
    r : np.ndarray
        R grid vector.
    z : np.ndarray
        Z grid vector.
    r_loc : float
        R location to look up.
    z_loc : float
        Z location to look up.

    Returns
    -------
    float
        Poloidal flux at the nearest grid point.
    """
    r_idx = np.argmin(np.abs(r - r_loc))
    z_idx = np.argmin(np.abs(z - z_loc))
    return float(psi[z_idx, r_idx])

[docs] @autodoc_function def equilibrium_from_file( filepath: Union[str, Path], filetype: str = "auto", ) -> RawNumericalEquilibrium: """ Read an equilibrium file and return a RawNumericalEquilibrium. The returned equilibrium is not normalized. Call normalize(B0) before use as a NumericalEquilibrium. Currently supported formats are: - eqdsk (G-EQDSK format) - xml (as generated by pyequil) - pickle/pkl (AUG shotfile saved via save_aug_shotfile) Parameters ---------- filepath : Union[str, Path] Path to the equilibrium file. filetype : str, optional File format, by default "auto" which detects from extension. Returns ------- RawNumericalEquilibrium An unnormalized equilibrium ready for preprocessing. """ filepath = Path(filepath) assert filepath.exists(), f"{filepath.absolute()} does not exist!" if filetype == "auto": filetype = filepath.suffix[1:] assert filetype in _SUPPORTED_FILETYPES, ( f"Unsupported filetype '{filetype}'. " f"Currently supported are: {_SUPPORTED_FILETYPES}" ) if filetype == "eqdsk": raw = read_eqdsk_file(filepath) d = _apply_key_map(raw, _EQDSK_KEY_MAP) d["divertor_polygon"] = Polygon2D(raw["rlim"], raw["zlim"]) elif filetype == "xml": raw = read_xml_file(filepath) d = _apply_key_map(raw, _XML_KEY_MAP) d["psi_axis"] = _psi_at_location( raw["psi"], raw["r"], raw["z"], d["axis_r"], d["axis_z"], ) d["psi_separatrix"] = _psi_at_location( raw["psi"], raw["r"], raw["z"], d["x_point_r"], d["x_point_z"], ) elif filetype in ["pickle", "pkl"]: raw = load_aug_shotfile(filepath) d = _apply_key_map(raw, AUG_KEY_MAP) return RawNumericalEquilibrium.from_dict(d)