Source code for materforge.catalog

# SPDX-FileCopyrightText: 2025 - 2026 Rahil Miten Doshi, Friedrich-Alexander-Universität Erlangen-Nürnberg
# SPDX-License-Identifier: BSD-3-Clause

"""Access to the example material files bundled with MaterForge.

MaterForge is built around YAML configs that users write themselves. The handful
of materials shipped with the package are reference examples, not a curated
database. These helpers load them by name so you don't have to dig the file out
of the installed package - handy for trying the API, demos, and tests.
"""

from __future__ import annotations

import logging
from importlib.resources import files
from pathlib import Path
from typing import Dict, List

import sympy as sp

from materforge.core.materials import Material
from materforge.parsing.api import create_material

logger = logging.getLogger(__name__)

_MATERIALS_PACKAGE = "materforge.data.materials"
_YAML_SUFFIXES = (".yaml", ".yml")


[docs] def list_materials() -> List[str]: """Returns the names of the bundled example materials, sorted alphabetically.""" return sorted(_discover_materials())
[docs] def get_material_path(name: str) -> Path: """Returns the path to a bundled material's YAML file. Matching is case-insensitive. Useful when you want to read or copy the YAML rather than load it directly. Args: name: Name of a bundled material (see :func:`list_materials`). Returns: Filesystem path to the material's YAML file. Raises: ValueError: If name does not match a bundled material; the message lists the available names. """ catalog = _discover_materials() target = name.strip().casefold() for material_name, path in catalog.items(): if material_name.casefold() == target: return path available = ", ".join(sorted(catalog)) or "(none found)" raise ValueError(f"Unknown material '{name}'. Available materials: {available}")
[docs] def load_material(name: str, dependency: sp.Symbol, enable_plotting: bool = False) -> Material: """Loads a bundled example material by name. A thin wrapper around :func:`materforge.create_material` for the YAML files shipped with the package. Plotting is off by default so loading an example never writes plot files next to the installed package. Args: name: Name of a bundled material (see :func:`list_materials`). dependency: SymPy symbol used as the independent variable, exactly as in :func:`materforge.create_material`. enable_plotting: Generate visualisation plots (default: False). Returns: Fully initialised Material instance. Raises: ValueError: If name does not match a bundled material. TypeError: If dependency is not a sympy Symbol. Example: >>> import sympy as sp >>> from materforge import load_material >>> steel = load_material('1.4301', sp.Symbol('T')) """ path = get_material_path(name) logger.info("Loading bundled material '%s' from %s", name, path) return create_material(path, dependency, enable_plotting=enable_plotting)
def _discover_materials() -> Dict[str, Path]: """Maps each bundled material name to its YAML path. Names are the YAML file stems (e.g. 'Al', '1.4301'). Companion files such as the Excel tables referenced by FILE_IMPORT properties live alongside the YAML and are resolved relative to it at load time. """ catalog: Dict[str, Path] = {} for entry in files(_MATERIALS_PACKAGE).iterdir(): path = Path(str(entry)) if path.suffix.lower() in _YAML_SUFFIXES: catalog[path.stem] = path return catalog