Source code for mapstp.extract_info

"""Extract meta information from paths given in an STP file."""

from __future__ import annotations

from typing import TYPE_CHECKING

import re

from dataclasses import dataclass

import numpy as np
import pandas as pd

_META_PATTERN = re.compile(r"\[(?P<meta>[^]]+)]")

if TYPE_CHECKING:
    from collections.abc import Iterator


[docs] @dataclass class MetaInfoCollector: """Helper to store meta information from a path.""" mnemonic: str | None = None factor: float | None = None rwcl: str | None = None
[docs] def update(self: MetaInfoCollector, pars: dict[str, str]) -> None: """Revise meta information collected on traversing along an STP branch. Args: pars: the last found meta information """ mnemonic = pars.get("m") if mnemonic is not None: if mnemonic == "void": # all subsequent components in the STP tree are void, # except linked ones, if any self.mnemonic = None self.factor = None else: self.mnemonic = mnemonic t = pars.get("f") if t is not None: self.factor = float(t) t = pars.get("r") if t is not None: self.rwcl = t
[docs] def extract_path_info(paths: list[str], material_index: pd.DataFrame) -> pd.DataFrame: """Extract meta information from `paths` and associate corresponding data with each path. Args: paths: STP paths material_index: mnemonic-material-density lookup table Returns: Table with material `number`, `density`, applied correction `factor`, and `rwcl` label corresponding to every path in paths """ return pd.DataFrame.from_records( _records(paths, material_index), columns=["material_number", "density", "factor", "rwcl"], )
def _records( paths: list[str], material_index: pd.DataFrame, ) -> Iterator[tuple[int | None, float | None, float | None, str | None]]: for path in paths: meta_info = extract_meta_info_from_path(path) if meta_info.mnemonic: density, material_number = define_material_number_and_density( material_index, meta_info, path, ) else: material_number = density = None yield material_number, density, meta_info.factor, meta_info.rwcl
[docs] def define_material_number_and_density( material_index: pd.DataFrame, meta_info: MetaInfoCollector, path: str, ) -> tuple[float | None, int | None]: """Define material number and density from a material index for given meta info. Args: material_index: table mapping material mnemonics to material number and density meta_info: ... collected from the `path` path: ... for diagnostics Returns: density and material """ try: material_number: int | None = int(material_index.loc[meta_info.mnemonic]["number"]) except KeyError: msg = ( f"The mnemonic {meta_info.mnemonic!r} " "is not specified in the material index. " f"See the STP path: {path}" ) raise KeyError(msg) from None density = material_index.loc[meta_info.mnemonic]["density"] if np.isnan(density): msg = ( f"The density for mnemonic {meta_info.mnemonic!r} " "is not specified in the material index." ) raise ValueError(msg) if density < 0.0: msg = ( f"The density for mnemonic {meta_info.mnemonic!r} " "in the material index is not to be negative." ) raise ValueError(msg) return density, material_number
[docs] def extract_meta_info_from_path(path: str) -> MetaInfoCollector: """Extract meta information from an STP path. Args: path: ... to body with `[m-...]` tags Returns: Collected meta info map. """ meta_info = MetaInfoCollector() found = _META_PATTERN.findall(path) if found: for meta in found: pairs = _extract_meta_info(meta, path) meta_info.update(pairs) return meta_info
def _extract_meta_info(meta: str, path: str) -> dict[str, str]: try: pairs: dict[str, str] = dict(_create_pair(t) for t in meta.split()) except ValueError as _ex: msg = f"On path {path}" raise ValueError(msg) from _ex return pairs def _create_pair(meta_part: str) -> tuple[str, str]: a, b = meta_part.split("-", maxsplit=1) return a, b