Source code for upxo.material.processing

"""
processing.py
==============
A material's processing history, modeled as an ordered sequence of typed
steps -- replaces the old, flat ``ProcessingCondition`` (a single
``ht``/``pro``/``app``/``appLoc`` snapshot with no way to represent a
multi-step route). Texture is history- and sequence-dependent, so knowing
*which* step happened *and in what order* -- not just "some processing
happened" -- is what downstream consumers like ``tcVf_twinned_fcc`` need.
"""

import warnings
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Set

# The four deformation-route types tcVf_twinned_fcc's processing-route ->
# texture-model mapping distinguishes (see
# admin/twinnedFccGui/texIntegration/tcVf_scoping.md §3.2), plus common
# non-deformation steps a route might also include.
KNOWN_PROCESSING_STEP_TYPES: Set[str] = {
    "rolling_symmetric", "rolling_asymmetric", "extrusion", "forging",
    "heat_treatment", "casting", "machining", "aging",
}

# The subset of KNOWN_PROCESSING_STEP_TYPES that ProcessingRoute's
# last_deformation_step looks for -- every other type is a non-deformation
# step it skips over when searching.
_DEFORMATION_STEP_TYPES: Set[str] = {
    "rolling_symmetric", "rolling_asymmetric", "extrusion", "forging",
}


[docs] @dataclass class ProcessingStep: """One step in a material's processing route. Parameters ---------- step_type : str What kind of step this is. Soft-validated against `KNOWN_PROCESSING_STEP_TYPES` at construction time -- an unrecognized value warns but is still accepted. This validates itself here, in `__post_init__`, rather than relying on `MaterialRegistry`'s `known_values` mechanism (`registry.py`): that mechanism only checks direct fields of a *registered* category's own top-level instance (e.g. `MaterialIdentity.name`) via `getattr`, and can't reach into a `ProcessingRoute`'s nested `steps` list to validate each step's `step_type` individually. params : dict Step-specific parameters (e.g. reduction ratio, temperature). notes : str Free-text notes. """ step_type: str params: Dict[str, Any] = field(default_factory=dict) notes: str = "" def __post_init__(self) -> None: if self.step_type not in KNOWN_PROCESSING_STEP_TYPES: warnings.warn( f'ProcessingStep: "{self.step_type}" is not a recognized ' f'step type (known: {sorted(KNOWN_PROCESSING_STEP_TYPES)}). ' f'Accepted anyway -- soft validation only.', stacklevel=2)
[docs] @dataclass class ProcessingRoute: """A material's processing history as an ordered sequence of steps. Replaces the old, flat ``ProcessingCondition``. Order matters -- texture depends on which step happened, and in what sequence, not just which step types occurred somewhere in the material's history. """ steps: List[ProcessingStep] = field(default_factory=list)
[docs] def append_step(self, step_type: str, **params: Any) -> None: """Append a new ``ProcessingStep(step_type, params)`` to the route.""" self.steps.append(ProcessingStep(step_type=step_type, params=params))
@property def last_deformation_step(self) -> Optional[ProcessingStep]: """The most recent step whose type is one of the four deformation routes (rolling_symmetric/rolling_asymmetric/extrusion/forging), skipping non-deformation steps (heat_treatment, machining, ...) that may follow it. Returns ``None`` if the route has no deformation step at all. This is what ``tcVf_twinned_fcc``'s processing-route -> texture-model mapping (admin/twinnedFccGui/texIntegration/tcVf_scoping.md §3.2) is meant to call. """ for step in reversed(self.steps): if step.step_type in _DEFORMATION_STEP_TYPES: return step return None