Spaces:
Sleeping
Sleeping
| """ | |
| Component library module for HVAC Load Calculator. | |
| This module implements the preset component database and component selection interface. | |
| """ | |
| from typing import Dict, List, Any, Optional, Tuple | |
| import pandas as pd | |
| import numpy as np | |
| import os | |
| import json | |
| import uuid | |
| from dataclasses import asdict | |
| # Import data models | |
| from data.building_components import ( | |
| BuildingComponent, Wall, Roof, Floor, Window, Door, Skylight, | |
| MaterialLayer, Orientation, ComponentType, BuildingComponentFactory | |
| ) | |
| from data.reference_data import reference_data | |
| # Define paths | |
| DATA_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| class ComponentLibrary: | |
| """Class for managing building component library.""" | |
| def __init__(self): | |
| """Initialize component library.""" | |
| self.components = {} | |
| self.load_preset_components() | |
| def load_preset_components(self): | |
| """Load preset components from reference data.""" | |
| # Load preset walls | |
| for wall_id, wall_data in reference_data.wall_types.items(): | |
| # Create material layers | |
| material_layers = [] | |
| for layer_data in wall_data.get("layers", []): | |
| material_id = layer_data.get("material") | |
| thickness = layer_data.get("thickness") | |
| material = reference_data.get_material(material_id) | |
| if material: | |
| layer = MaterialLayer( | |
| name=material["name"], | |
| thickness=thickness, | |
| conductivity=material["conductivity"], | |
| density=material.get("density"), | |
| specific_heat=material.get("specific_heat") | |
| ) | |
| material_layers.append(layer) | |
| # Create wall component | |
| component_id = f"preset_wall_{wall_id}" | |
| wall = Wall( | |
| id=component_id, | |
| name=wall_data["name"], | |
| component_type=ComponentType.WALL, | |
| u_value=wall_data["u_value"], | |
| area=1.0, # Area will be set when component is used | |
| orientation=Orientation.NOT_APPLICABLE, # Orientation will be set when component is used | |
| wall_type=wall_data["name"], | |
| wall_group=wall_data["wall_group"], | |
| material_layers=material_layers | |
| ) | |
| self.components[component_id] = wall | |
| # Load preset roofs | |
| for roof_id, roof_data in reference_data.roof_types.items(): | |
| # Create material layers | |
| material_layers = [] | |
| for layer_data in roof_data.get("layers", []): | |
| material_id = layer_data.get("material") | |
| thickness = layer_data.get("thickness") | |
| material = reference_data.get_material(material_id) | |
| if material: | |
| layer = MaterialLayer( | |
| name=material["name"], | |
| thickness=thickness, | |
| conductivity=material["conductivity"], | |
| density=material.get("density"), | |
| specific_heat=material.get("specific_heat") | |
| ) | |
| material_layers.append(layer) | |
| # Create roof component | |
| component_id = f"preset_roof_{roof_id}" | |
| roof = Roof( | |
| id=component_id, | |
| name=roof_data["name"], | |
| component_type=ComponentType.ROOF, | |
| u_value=roof_data["u_value"], | |
| area=1.0, # Area will be set when component is used | |
| orientation=Orientation.HORIZONTAL, | |
| roof_type=roof_data["name"], | |
| roof_group=roof_data["roof_group"], | |
| material_layers=material_layers | |
| ) | |
| self.components[component_id] = roof | |
| # Load preset floors | |
| for floor_id, floor_data in reference_data.floor_types.items(): | |
| # Create material layers | |
| material_layers = [] | |
| for layer_data in floor_data.get("layers", []): | |
| material_id = layer_data.get("material") | |
| thickness = layer_data.get("thickness") | |
| material = reference_data.get_material(material_id) | |
| if material: | |
| layer = MaterialLayer( | |
| name=material["name"], | |
| thickness=thickness, | |
| conductivity=material["conductivity"], | |
| density=material.get("density"), | |
| specific_heat=material.get("specific_heat") | |
| ) | |
| material_layers.append(layer) | |
| # Create floor component | |
| component_id = f"preset_floor_{floor_id}" | |
| floor = Floor( | |
| id=component_id, | |
| name=floor_data["name"], | |
| component_type=ComponentType.FLOOR, | |
| u_value=floor_data["u_value"], | |
| area=1.0, # Area will be set when component is used | |
| orientation=Orientation.HORIZONTAL, | |
| floor_type=floor_data["name"], | |
| is_ground_contact=floor_data["is_ground_contact"], | |
| material_layers=material_layers | |
| ) | |
| self.components[component_id] = floor | |
| # Load preset windows | |
| for window_id, window_data in reference_data.window_types.items(): | |
| # Create window component | |
| component_id = f"preset_window_{window_id}" | |
| window = Window( | |
| id=component_id, | |
| name=window_data["name"], | |
| component_type=ComponentType.WINDOW, | |
| u_value=window_data["u_value"], | |
| area=1.0, # Area will be set when component is used | |
| orientation=Orientation.NOT_APPLICABLE, # Orientation will be set when component is used | |
| shgc=window_data["shgc"], | |
| vt=window_data["vt"], | |
| window_type=window_data["name"], | |
| glazing_layers=window_data["glazing_layers"], | |
| gas_fill=window_data["gas_fill"], | |
| low_e_coating=window_data["low_e_coating"] | |
| ) | |
| self.components[component_id] = window | |
| # Load preset doors | |
| for door_id, door_data in reference_data.door_types.items(): | |
| # Create door component | |
| component_id = f"preset_door_{door_id}" | |
| door = Door( | |
| id=component_id, | |
| name=door_data["name"], | |
| component_type=ComponentType.DOOR, | |
| u_value=door_data["u_value"], | |
| area=1.0, # Area will be set when component is used | |
| orientation=Orientation.NOT_APPLICABLE, # Orientation will be set when component is used | |
| door_type=door_data["door_type"], | |
| glazing_percentage=door_data["glazing_percentage"], | |
| shgc=door_data.get("shgc", 0.0), | |
| vt=door_data.get("vt", 0.0) | |
| ) | |
| self.components[component_id] = door | |
| def get_component(self, component_id: str) -> Optional[BuildingComponent]: | |
| """ | |
| Get a component by ID. | |
| Args: | |
| component_id: Component identifier | |
| Returns: | |
| BuildingComponent object or None if not found | |
| """ | |
| return self.components.get(component_id) | |
| def get_components_by_type(self, component_type: ComponentType) -> List[BuildingComponent]: | |
| """ | |
| Get all components of a specific type. | |
| Args: | |
| component_type: Component type | |
| Returns: | |
| List of BuildingComponent objects | |
| """ | |
| return [comp for comp in self.components.values() if comp.component_type == component_type] | |
| def get_preset_components_by_type(self, component_type: ComponentType) -> List[BuildingComponent]: | |
| """ | |
| Get all preset components of a specific type. | |
| Args: | |
| component_type: Component type | |
| Returns: | |
| List of BuildingComponent objects | |
| """ | |
| return [comp for comp in self.components.values() | |
| if comp.component_type == component_type and comp.id.startswith("preset_")] | |
| def get_custom_components_by_type(self, component_type: ComponentType) -> List[BuildingComponent]: | |
| """ | |
| Get all custom components of a specific type. | |
| Args: | |
| component_type: Component type | |
| Returns: | |
| List of BuildingComponent objects | |
| """ | |
| return [comp for comp in self.components.values() | |
| if comp.component_type == component_type and comp.id.startswith("custom_")] | |
| def add_component(self, component: BuildingComponent) -> str: | |
| """ | |
| Add a component to the library. | |
| Args: | |
| component: BuildingComponent object | |
| Returns: | |
| Component ID | |
| """ | |
| if component.id in self.components: | |
| # Generate a new ID if the component ID already exists | |
| component.id = f"custom_{component.component_type.value.lower()}_{str(uuid.uuid4())[:8]}" | |
| self.components[component.id] = component | |
| return component.id | |
| def update_component(self, component_id: str, component: BuildingComponent) -> bool: | |
| """ | |
| Update a component in the library. | |
| Args: | |
| component_id: ID of the component to update | |
| component: Updated BuildingComponent object | |
| Returns: | |
| True if the component was updated, False otherwise | |
| """ | |
| if component_id not in self.components: | |
| return False | |
| # Preserve the original ID | |
| component.id = component_id | |
| self.components[component_id] = component | |
| return True | |
| def remove_component(self, component_id: str) -> bool: | |
| """ | |
| Remove a component from the library. | |
| Args: | |
| component_id: ID of the component to remove | |
| Returns: | |
| True if the component was removed, False otherwise | |
| """ | |
| if component_id not in self.components: | |
| return False | |
| # Don't allow removing preset components | |
| if component_id.startswith("preset_"): | |
| return False | |
| del self.components[component_id] | |
| return True | |
| def clone_component(self, component_id: str, new_name: str = None) -> Optional[str]: | |
| """ | |
| Clone a component in the library. | |
| Args: | |
| component_id: ID of the component to clone | |
| new_name: Name for the cloned component (optional) | |
| Returns: | |
| ID of the cloned component or None if the original component was not found | |
| """ | |
| if component_id not in self.components: | |
| return None | |
| # Get the original component | |
| original = self.components[component_id] | |
| # Create a copy of the component | |
| component_dict = asdict(original) | |
| # Generate a new ID | |
| component_dict["id"] = f"custom_{original.component_type.value.lower()}_{str(uuid.uuid4())[:8]}" | |
| # Set new name if provided | |
| if new_name: | |
| component_dict["name"] = new_name | |
| else: | |
| component_dict["name"] = f"Copy of {original.name}" | |
| # Create a new component | |
| new_component = BuildingComponentFactory.create_component(component_dict) | |
| # Add the new component to the library | |
| self.components[new_component.id] = new_component | |
| return new_component.id | |
| def export_to_json(self, file_path: str) -> None: | |
| """ | |
| Export all components to a JSON file. | |
| Args: | |
| file_path: Path to the output JSON file | |
| """ | |
| data = {comp_id: comp.to_dict() for comp_id, comp in self.components.items()} | |
| with open(file_path, 'w') as f: | |
| json.dump(data, f, indent=4) | |
| def import_from_json(self, file_path: str) -> int: | |
| """ | |
| Import components from a JSON file. | |
| Args: | |
| file_path: Path to the input JSON file | |
| Returns: | |
| Number of components imported | |
| """ | |
| with open(file_path, 'r') as f: | |
| data = json.load(f) | |
| count = 0 | |
| for comp_id, comp_data in data.items(): | |
| try: | |
| component = BuildingComponentFactory.create_component(comp_data) | |
| self.components[comp_id] = component | |
| count += 1 | |
| except Exception as e: | |
| print(f"Error importing component {comp_id}: {e}") | |
| return count | |
| # Create a singleton instance | |
| component_library = ComponentLibrary() | |
| # Export component library to JSON if needed | |
| if __name__ == "__main__": | |
| component_library.export_to_json(os.path.join(DATA_DIR, "component_library.json")) | |