Source code for femorph_solver.mapdl_api.cdb
"""Load MAPDL CDB input decks into an femorph-solver :class:`~femorph_solver.Model`.
Thin wrapper around :mod:`mapdl_archive`. Only the file-format reader
is involved — MAPDL itself is never invoked.
"""
from __future__ import annotations
import re
from pathlib import Path
from typing import TYPE_CHECKING, Any
from femorph_solver._labels import UnitSystem, unit_system_from_mapdl
if TYPE_CHECKING:
from femorph_solver.model import Model
# ``/UNITS,<token>`` or ``/UNITS,<token>,...`` — whitespace allowed
# around the comma; token is the leading comma-separated field. CDB
# parsers (including MAPDL itself) only look at the first token.
_UNITS_RE = re.compile(r"^\s*/UNITS\s*,\s*([A-Za-z0-9_]+)", re.IGNORECASE)
def _detect_unit_system(path: str | Path) -> UnitSystem:
"""Scan the CDB for a ``/UNITS,<token>`` command and return its :class:`UnitSystem`.
Returns :attr:`UnitSystem.UNSPECIFIED` when no ``/UNITS`` command
appears in the first 8 KiB of the file (the header region). MAPDL
decks place ``/UNITS`` near the top; scanning further down would
just slow this call without improving accuracy.
"""
try:
with open(path, encoding="utf-8", errors="ignore") as fh:
head = fh.read(8192)
except OSError: # pragma: no cover - defensive; from_cdb opens again
return UnitSystem.UNSPECIFIED
for line in head.splitlines():
m = _UNITS_RE.match(line)
if m:
return unit_system_from_mapdl(m.group(1))
return UnitSystem.UNSPECIFIED
[docs]
def from_cdb(path: str, **kwargs: Any) -> Model:
"""Load a MAPDL CDB file via :mod:`mapdl_archive`.
Requires the ``mapdl`` extra: ``pip install femorph_solver[mapdl]``.
The deck's ``/UNITS`` command (if present) is parsed and stamped
onto :attr:`Model.unit_system`. Decks without a ``/UNITS`` line
get :attr:`UnitSystem.UNSPECIFIED`.
"""
try:
import mapdl_archive
except ImportError as exc: # pragma: no cover - import-guard path
raise ImportError(
"Reading MAPDL CDB decks requires the 'mapdl' extra. "
"Install with: pip install femorph_solver[mapdl]"
) from exc
from femorph_solver.model import Model
archive = mapdl_archive.Archive(path, **kwargs)
model = Model.from_grid(archive.grid)
model.set_unit_system(_detect_unit_system(path))
return model