Spaces:
Sleeping
Sleeping
| """ | |
| Shading system module for HVAC Load Calculator. | |
| This module implements shading type selection and coverage percentage interface. | |
| """ | |
| from typing import Dict, List, Any, Optional, Tuple | |
| import pandas as pd | |
| import numpy as np | |
| import os | |
| import json | |
| from enum import Enum | |
| from dataclasses import dataclass | |
| # Define paths | |
| DATA_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| class ShadingType(Enum): | |
| """Enumeration for shading types.""" | |
| NONE = "None" | |
| INTERNAL = "Internal" | |
| EXTERNAL = "External" | |
| BETWEEN_GLASS = "Between-glass" | |
| class ShadingDevice: | |
| """Class representing a shading device.""" | |
| id: str | |
| name: str | |
| shading_type: ShadingType | |
| shading_coefficient: float # 0-1 (1 = no shading) | |
| coverage_percentage: float = 100.0 # 0-100% | |
| description: str = "" | |
| def __post_init__(self): | |
| """Validate shading device data after initialization.""" | |
| if self.shading_coefficient < 0 or self.shading_coefficient > 1: | |
| raise ValueError("Shading coefficient must be between 0 and 1") | |
| if self.coverage_percentage < 0 or self.coverage_percentage > 100: | |
| raise ValueError("Coverage percentage must be between 0 and 100") | |
| def effective_shading_coefficient(self) -> float: | |
| """Calculate the effective shading coefficient considering coverage percentage.""" | |
| # If coverage is less than 100%, the effective coefficient is a weighted average | |
| # between the device coefficient and 1.0 (no shading) | |
| coverage_factor = self.coverage_percentage / 100.0 | |
| return self.shading_coefficient * coverage_factor + 1.0 * (1 - coverage_factor) | |
| def to_dict(self) -> Dict[str, Any]: | |
| """Convert the shading device to a dictionary.""" | |
| return { | |
| "id": self.id, | |
| "name": self.name, | |
| "shading_type": self.shading_type.value, | |
| "shading_coefficient": self.shading_coefficient, | |
| "coverage_percentage": self.coverage_percentage, | |
| "description": self.description, | |
| "effective_shading_coefficient": self.effective_shading_coefficient | |
| } | |
| class ShadingSystem: | |
| """Class for managing shading devices and calculations.""" | |
| def __init__(self): | |
| """Initialize shading system.""" | |
| self.shading_devices = {} | |
| self.load_preset_devices() | |
| def load_preset_devices(self) -> None: | |
| """Load preset shading devices.""" | |
| # Internal shading devices | |
| self.shading_devices["preset_venetian_blinds"] = ShadingDevice( | |
| id="preset_venetian_blinds", | |
| name="Venetian Blinds", | |
| shading_type=ShadingType.INTERNAL, | |
| shading_coefficient=0.6, | |
| description="Standard internal venetian blinds" | |
| ) | |
| self.shading_devices["preset_roller_shade"] = ShadingDevice( | |
| id="preset_roller_shade", | |
| name="Roller Shade", | |
| shading_type=ShadingType.INTERNAL, | |
| shading_coefficient=0.7, | |
| description="Standard internal roller shade" | |
| ) | |
| self.shading_devices["preset_drapes_light"] = ShadingDevice( | |
| id="preset_drapes_light", | |
| name="Light Drapes", | |
| shading_type=ShadingType.INTERNAL, | |
| shading_coefficient=0.8, | |
| description="Light-colored internal drapes" | |
| ) | |
| self.shading_devices["preset_drapes_dark"] = ShadingDevice( | |
| id="preset_drapes_dark", | |
| name="Dark Drapes", | |
| shading_type=ShadingType.INTERNAL, | |
| shading_coefficient=0.5, | |
| description="Dark-colored internal drapes" | |
| ) | |
| # External shading devices | |
| self.shading_devices["preset_overhang"] = ShadingDevice( | |
| id="preset_overhang", | |
| name="Overhang", | |
| shading_type=ShadingType.EXTERNAL, | |
| shading_coefficient=0.4, | |
| description="External overhang" | |
| ) | |
| self.shading_devices["preset_louvers"] = ShadingDevice( | |
| id="preset_louvers", | |
| name="Louvers", | |
| shading_type=ShadingType.EXTERNAL, | |
| shading_coefficient=0.3, | |
| description="External louvers" | |
| ) | |
| self.shading_devices["preset_exterior_screen"] = ShadingDevice( | |
| id="preset_exterior_screen", | |
| name="Exterior Screen", | |
| shading_type=ShadingType.EXTERNAL, | |
| shading_coefficient=0.5, | |
| description="External screen" | |
| ) | |
| # Between-glass shading devices | |
| self.shading_devices["preset_between_glass_blinds"] = ShadingDevice( | |
| id="preset_between_glass_blinds", | |
| name="Between-glass Blinds", | |
| shading_type=ShadingType.BETWEEN_GLASS, | |
| shading_coefficient=0.5, | |
| description="Blinds between glass panes" | |
| ) | |
| def get_device(self, device_id: str) -> Optional[ShadingDevice]: | |
| """ | |
| Get a shading device by ID. | |
| Args: | |
| device_id: Device identifier | |
| Returns: | |
| ShadingDevice object or None if not found | |
| """ | |
| return self.shading_devices.get(device_id) | |
| def get_devices_by_type(self, shading_type: ShadingType) -> List[ShadingDevice]: | |
| """ | |
| Get all shading devices of a specific type. | |
| Args: | |
| shading_type: Shading type | |
| Returns: | |
| List of ShadingDevice objects | |
| """ | |
| return [device for device in self.shading_devices.values() | |
| if device.shading_type == shading_type] | |
| def get_preset_devices(self) -> List[ShadingDevice]: | |
| """ | |
| Get all preset shading devices. | |
| Returns: | |
| List of ShadingDevice objects | |
| """ | |
| return [device for device_id, device in self.shading_devices.items() | |
| if device_id.startswith("preset_")] | |
| def get_custom_devices(self) -> List[ShadingDevice]: | |
| """ | |
| Get all custom shading devices. | |
| Returns: | |
| List of ShadingDevice objects | |
| """ | |
| return [device for device_id, device in self.shading_devices.items() | |
| if device_id.startswith("custom_")] | |
| def add_device(self, name: str, shading_type: ShadingType, | |
| shading_coefficient: float, coverage_percentage: float = 100.0, | |
| description: str = "") -> str: | |
| """ | |
| Add a custom shading device. | |
| Args: | |
| name: Device name | |
| shading_type: Shading type | |
| shading_coefficient: Shading coefficient (0-1) | |
| coverage_percentage: Coverage percentage (0-100) | |
| description: Device description | |
| Returns: | |
| Device ID | |
| """ | |
| import uuid | |
| device_id = f"custom_shading_{str(uuid.uuid4())[:8]}" | |
| device = ShadingDevice( | |
| id=device_id, | |
| name=name, | |
| shading_type=shading_type, | |
| shading_coefficient=shading_coefficient, | |
| coverage_percentage=coverage_percentage, | |
| description=description | |
| ) | |
| self.shading_devices[device_id] = device | |
| return device_id | |
| def update_device(self, device_id: str, name: str = None, | |
| shading_coefficient: float = None, | |
| coverage_percentage: float = None, | |
| description: str = None) -> bool: | |
| """ | |
| Update a shading device. | |
| Args: | |
| device_id: Device identifier | |
| name: New device name (optional) | |
| shading_coefficient: New shading coefficient (optional) | |
| coverage_percentage: New coverage percentage (optional) | |
| description: New device description (optional) | |
| Returns: | |
| True if the device was updated, False otherwise | |
| """ | |
| if device_id not in self.shading_devices: | |
| return False | |
| # Don't allow updating preset devices | |
| if device_id.startswith("preset_"): | |
| return False | |
| device = self.shading_devices[device_id] | |
| if name is not None: | |
| device.name = name | |
| if shading_coefficient is not None: | |
| if shading_coefficient < 0 or shading_coefficient > 1: | |
| return False | |
| device.shading_coefficient = shading_coefficient | |
| if coverage_percentage is not None: | |
| if coverage_percentage < 0 or coverage_percentage > 100: | |
| return False | |
| device.coverage_percentage = coverage_percentage | |
| if description is not None: | |
| device.description = description | |
| return True | |
| def remove_device(self, device_id: str) -> bool: | |
| """ | |
| Remove a shading device. | |
| Args: | |
| device_id: Device identifier | |
| Returns: | |
| True if the device was removed, False otherwise | |
| """ | |
| if device_id not in self.shading_devices: | |
| return False | |
| # Don't allow removing preset devices | |
| if device_id.startswith("preset_"): | |
| return False | |
| del self.shading_devices[device_id] | |
| return True | |
| def calculate_effective_shgc(self, base_shgc: float, device_id: str) -> float: | |
| """ | |
| Calculate the effective SHGC (Solar Heat Gain Coefficient) with shading. | |
| Args: | |
| base_shgc: Base SHGC of the window | |
| device_id: Shading device identifier | |
| Returns: | |
| Effective SHGC with shading | |
| """ | |
| if device_id not in self.shading_devices: | |
| return base_shgc | |
| device = self.shading_devices[device_id] | |
| return base_shgc * device.effective_shading_coefficient | |
| def export_to_json(self, file_path: str) -> None: | |
| """ | |
| Export all shading devices to a JSON file. | |
| Args: | |
| file_path: Path to the output JSON file | |
| """ | |
| data = {device_id: device.to_dict() for device_id, device in self.shading_devices.items()} | |
| with open(file_path, 'w') as f: | |
| json.dump(data, f, indent=4) | |
| def import_from_json(self, file_path: str) -> int: | |
| """ | |
| Import shading devices from a JSON file. | |
| Args: | |
| file_path: Path to the input JSON file | |
| Returns: | |
| Number of devices imported | |
| """ | |
| with open(file_path, 'r') as f: | |
| data = json.load(f) | |
| count = 0 | |
| for device_id, device_data in data.items(): | |
| try: | |
| shading_type = ShadingType(device_data["shading_type"]) | |
| device = ShadingDevice( | |
| id=device_id, | |
| name=device_data["name"], | |
| shading_type=shading_type, | |
| shading_coefficient=device_data["shading_coefficient"], | |
| coverage_percentage=device_data.get("coverage_percentage", 100.0), | |
| description=device_data.get("description", "") | |
| ) | |
| self.shading_devices[device_id] = device | |
| count += 1 | |
| except Exception as e: | |
| print(f"Error importing shading device {device_id}: {e}") | |
| return count | |
| # Create a singleton instance | |
| shading_system = ShadingSystem() | |
| # Export shading system to JSON if needed | |
| if __name__ == "__main__": | |
| shading_system.export_to_json(os.path.join(DATA_DIR, "data", "shading_system.json")) | |