HVAC-text-02 / utils /shading_system.py
mabuseif's picture
Upload 27 files
ca54a52 verified
"""
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"
@dataclass
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")
@property
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"))