Source code for materforge.parsing.processors.property_processor

import logging
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Union

import sympy as sp

from materforge.core.materials import Material
from materforge.parsing.validation.property_type_detector import PropertyType
from materforge.parsing.processors.property_processor_base import PropertyProcessorBase
from materforge.parsing.processors.property_handlers import (
    ConstantValuePropertyHandler,
    StepFunctionPropertyHandler,
    FileImportPropertyHandler,
    TabularDataPropertyHandler,
    PiecewiseEquationPropertyHandler,
    ComputedPropertyHandler
)
from materforge.parsing.processors.post_processor import PropertyPostProcessor

logger = logging.getLogger(__name__)


[docs] class PropertyProcessor(PropertyProcessorBase): """ Main orchestrator for processing different property types for material objects. This class coordinates the processing of various property types by delegating to specialized handlers for each property type. """ def __init__(self) -> None: """Initialize processor with specialized handlers.""" super().__init__() # Initialize property handlers self.handlers = { PropertyType.CONSTANT_VALUE: ConstantValuePropertyHandler(), PropertyType.STEP_FUNCTION: StepFunctionPropertyHandler(), PropertyType.FILE_IMPORT: FileImportPropertyHandler(), PropertyType.TABULAR_DATA: TabularDataPropertyHandler(), PropertyType.PIECEWISE_EQUATION: PiecewiseEquationPropertyHandler(), PropertyType.COMPUTED_PROPERTY: ComputedPropertyHandler() } # Initialize post-processor self.post_processor = PropertyPostProcessor() # Processing state self.properties: Optional[Dict[str, Any]] = None self.categorized_properties: Optional[Dict[PropertyType, List[Tuple[str, Any]]]] = None self.base_dir: Optional[Path] = None logger.debug("PropertyProcessor initialized with %d handlers", len(self.handlers))
[docs] def process_properties(self, material: Material, dependency: Union[float, sp.Symbol], properties: Dict[str, Any], categorized_properties: Dict[PropertyType, List[Tuple[str, Any]]], base_dir: Path, visualizer) -> None: """Process all properties for the material.""" logger.info("Starting property processing for material: %s", material.name) # Set processing context self._initialize_processing_context(material, dependency, properties, categorized_properties, base_dir, visualizer) try: # Process properties by type self._process_by_category(material, dependency) # Post-process properties (regression, etc.) logger.info("Starting post-processing for material: %s", material.name) self.post_processor.post_process_properties( material, dependency, self.properties, self.categorized_properties, self.processed_properties ) logger.info("Successfully processed all properties for material: %s", material.name) except Exception as e: logger.error("Property processing failed for material '%s': %s", material.name, e, exc_info=True) raise ValueError(f"Failed to process properties \n -> {str(e)}") from e
def _initialize_processing_context(self, material: Material, dependency: Union[float, sp.Symbol], properties: Dict[str, Any], categorized_properties: Dict[PropertyType, List[Tuple[str, Any]]], base_dir: Path, visualizer) -> None: """Initialize processing context and handler dependencies.""" logger.debug("Initializing processing context for material: %s", material.name) self.properties = properties self.categorized_properties = categorized_properties self.base_dir = base_dir self.visualizer = visualizer self.processed_properties = set() # Set context for all handlers for handler_type, handler in self.handlers.items(): handler.set_processing_context(self.base_dir, visualizer, self.processed_properties) logger.debug("Set processing context for handler: %s", handler_type.name) # Initialize dependency processor for computed properties computed_handler = self.handlers.get(PropertyType.COMPUTED_PROPERTY) if computed_handler: computed_handler.set_computed_property_processor(properties) logger.debug("Dependency processor initialized for computed properties") def _process_by_category(self, material: Material, dependency: Union[float, sp.Symbol]) -> None: """Process properties grouped by category.""" total_properties = sum(len(prop_list) for prop_list in self.categorized_properties.values()) active_categories = len([cat for cat, props in self.categorized_properties.items() if props]) logger.info("Processing %d properties across %d categories", total_properties, active_categories) for prop_type, prop_list in self.categorized_properties.items(): if not prop_list: continue logger.info("Processing %d properties of type: %s", len(prop_list), prop_type.name) handler = self.handlers.get(prop_type) if handler is None: logger.error("No handler available for property type: %s", prop_type.name) raise ValueError(f"No handler available for property type: {prop_type.name}") # Sort properties to prioritize temperature references sorted_props = self._sort_properties_by_priority(prop_list) for prop_name, config in sorted_props: logger.debug("Processing property: %s", prop_name) try: handler.process_property(material, prop_name, config, dependency) logger.debug("Successfully processed property: %s", prop_name) except Exception as e: logger.error("Failed to process property '%s': %s", prop_name, e, exc_info=True) raise logger.info("Completed processing %s properties", prop_type.name) @staticmethod def _sort_properties_by_priority(prop_list: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]: """Sort properties to process temperature references first.""" from materforge.parsing.config.yaml_keys import ( MELTING_TEMPERATURE_KEY, BOILING_TEMPERATURE_KEY, SOLIDUS_TEMPERATURE_KEY, LIQUIDUS_TEMPERATURE_KEY, INITIAL_BOILING_TEMPERATURE_KEY, FINAL_BOILING_TEMPERATURE_KEY ) priority_props = { MELTING_TEMPERATURE_KEY, BOILING_TEMPERATURE_KEY, SOLIDUS_TEMPERATURE_KEY, LIQUIDUS_TEMPERATURE_KEY, INITIAL_BOILING_TEMPERATURE_KEY, FINAL_BOILING_TEMPERATURE_KEY } sorted_props = sorted(prop_list, key=lambda x: 0 if x[0] in priority_props else 1) priority_count = len([p for p in prop_list if p[0] in priority_props]) if priority_count > 0: logger.debug("Prioritized %d temperature reference properties", priority_count) return sorted_props