Update-materials-solar / data /material_library.py
mabuseif's picture
Upload material_library.py
3d36616 verified
"""
Material Library for HVAC Load Calculator
Updated 2025-05-26: Added emissivity to Material, h_o to GlazingMaterial, and expanded DoorMaterial to include all Material properties plus emissivity. Assigned values based on provided data and inferred for doors.
Updated 2025-05-17: Removed original materials and constructions, deleted ASHRAE materials starting with 'R' followed by number (e.g., R01, R25), verified constructions use valid materials.
Updated 2025-05-17: Added all materials and constructions from ASHRAE_2005_HOF_Materials.idf with Australian-specific embodied carbon and prices.
Updated 2025-05-17: Added GlazingMaterial and DoorMaterial classes, included library_glazing_materials and library_door_materials with comprehensive data.
Updated 2025-05-16: Removed mass_per_meter, added thermal_mass method, updated CSV handling.
Updated 2025-05-16: Fixed U-value calculation in Material.get_u_value.
Updated 2025-05-16: Updated MaterialCategory to Finishing Materials, Structural Materials, Sub-Structural Materials, Insulation.
Updated 2025-05-16: Fixed Construction.get_thermal_mass to use avg_specific_heat.
Developed by: Dr Majed Abuseif, Deakin University
© 2025
"""
from typing import Dict, List, Optional, Tuple
from enum import Enum
import pandas as pd
class MaterialCategory(Enum):
FINISHING_MATERIALS = "Finishing Materials"
STRUCTURAL_MATERIALS = "Structural Materials"
SUB_STRUCTURAL_MATERIALS = "Sub-Structural Materials"
INSULATION = "Insulation"
class ThermalMass(Enum):
HIGH = "High"
MEDIUM = "Medium"
LOW = "Low"
NO_MASS = "No Mass"
class Material:
def __init__(self, name: str, category: MaterialCategory, conductivity: float, density: float,
specific_heat: float, default_thickness: float, embodied_carbon: float,
solar_absorption: float, price: float, emissivity: float, is_library: bool = True):
self.name = name
self.category = category
self.conductivity = max(0.01, conductivity) # W/m·K
self.density = max(1.0, density) # kg/m³
self.specific_heat = max(100.0, specific_heat) # J/kg·K
self.default_thickness = max(0.01, default_thickness) # m
self.embodied_carbon = max(0.0, embodied_carbon) # kgCO₂e/kg
self.solar_absorption = min(max(0.0, solar_absorption), 1.0)
self.price = max(0.0, price) # USD/m²
self.emissivity = min(max(0.0, emissivity), 1.0) # Dimensionless
self.is_library = is_library
def get_thermal_mass(self) -> ThermalMass:
if self.density < 100.0 or self.specific_heat < 800.0:
return ThermalMass.NO_MASS
elif self.density > 2000.0 and self.specific_heat > 800.0:
return ThermalMass.HIGH
elif 1000.0 <= self.density <= 2000.0 and 800.0 <= self.specific_heat <= 1200.0:
return ThermalMass.MEDIUM
else:
return ThermalMass.LOW
def get_u_value(self) -> float:
return self.conductivity / self.default_thickness if self.default_thickness > 0 else 0.1
class GlazingMaterial:
def __init__(self, name: str, u_value: float, shgc: float, embodied_carbon: float, price: float, h_o: float, is_library: bool = True):
self.name = name
self.u_value = max(0.1, u_value) # W/m²·K
self.shgc = min(max(0.0, shgc), 1.0) # Solar Heat Gain Coefficient
self.embodied_carbon = max(0.0, embodied_carbon) # kgCO₂e/m²
self.price = max(0.0, price) # USD/m²
self.h_o = max(0.0, h_o) # W/m²·K, exterior convective heat transfer coefficient
self.is_library = is_library
class DoorMaterial:
def __init__(self, name: str, category: MaterialCategory, conductivity: float, density: float,
specific_heat: float, default_thickness: float, u_value: float, solar_absorption: float,
embodied_carbon: float, price: float, emissivity: float, is_library: bool = True):
self.name = name
self.category = category
self.conductivity = max(0.01, conductivity) # W/m·K
self.density = max(1.0, density) # kg/m³
self.specific_heat = max(100.0, specific_heat) # J/kg·K
self.default_thickness = max(0.01, default_thickness) # m
self.u_value = max(0.1, u_value) # W/m²·K
self.solar_absorption = min(max(0.0, solar_absorption), 1.0)
self.embodied_carbon = max(0.0, embodied_carbon) # kgCO₂e/m²
self.price = max(0.0, price) # USD/m²
self.emissivity = min(max(0.0, emissivity), 1.0) # Dimensionless
self.is_library = is_library
class Construction:
def __init__(self, name: str, component_type: str, layers: List[Dict], is_library: bool = True):
self.name = name
self.component_type = component_type
self.layers = layers or []
self.is_library = is_library
self.u_value = self.calculate_u_value()
self.total_thickness = sum(layer["thickness"] for layer in self.layers)
self.embodied_carbon = sum(layer["material"].embodied_carbon * layer["material"].density * layer["thickness"]
for layer in self.layers)
self.solar_absorption = max(layer["material"].solar_absorption for layer in self.layers) if self.layers else 0.6
self.price = sum(layer["material"].price * layer["thickness"] / layer["material"].default_thickness
for layer in self.layers)
def calculate_u_value(self) -> float:
if not self.layers:
return 0.1
r_total = sum(layer["thickness"] / layer["material"].conductivity for layer in self.layers)
return 1 / r_total if r_total > 0 else 0.1
def get_thermal_mass(self) -> ThermalMass:
if not self.layers:
return ThermalMass.NO_MASS
total_thickness = self.total_thickness
if total_thickness == 0:
return ThermalMass.NO_MASS
avg_density = sum(layer["material"].density * layer["thickness"] for layer in self.layers) / total_thickness
avg_specific_heat = sum(layer["material"].specific_heat * layer["thickness"] for layer in self.layers) / total_thickness
if avg_density < 100.0 or avg_specific_heat < 800.0:
return ThermalMass.NO_MASS
elif avg_density > 2000.0 and avg_specific_heat > 800.0:
return ThermalMass.HIGH
elif 1000.0 <= avg_density <= 2000.0 and 800.0 <= avg_specific_heat <= 1200.0:
return ThermalMass.MEDIUM
else:
return ThermalMass.LOW
class MaterialLibrary:
def __init__(self):
self.library_materials = self.initialize_materials()
self.library_constructions = self.initialize_constructions()
self.library_glazing_materials = self.initialize_glazing_materials()
self.library_door_materials = self.initialize_door_materials()
def initialize_materials(self) -> Dict[str, Material]:
materials = [
# ASHRAE 2005 HOF Materials (excluding 'R'-prefixed materials)
Material("F04 Wall air space resistance", MaterialCategory.INSULATION, 0.01, 1.0, 1000.0, 0.01, 0.01, 0.5, 0.5, 0.925),
Material("F05 Ceiling air space resistance", MaterialCategory.INSULATION, 0.01, 1.0, 1000.0, 0.01, 0.01, 0.5, 0.5, 0.925),
Material("F06 EIFS finish", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0095, 0.3, 0.5, 17.6, 0.925),
Material("F07 25mm stucco", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0254, 0.2, 0.6, 14.1, 0.91),
Material("F08 Metal surface", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 45.28, 7824.0, 500.0, 0.001, 2.2, 0.7, 25.0, 0.075),
Material("F09 25mm cement plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0254, 0.2, 0.6, 14.1, 0.91),
Material("F10 13mm gypsum board", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0127, 0.25, 0.4, 5.1, 0.925),
Material("F11 16mm gypsum board", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0159, 0.25, 0.4, 6.4, 0.925),
Material("F12 19mm gypsum board", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0191, 0.25, 0.4, 7.6, 0.925),
Material("F13 13mm cement plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0127, 0.2, 0.6, 7.1, 0.91),
Material("F14 13mm lime plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1600.0, 840.0, 0.0127, 0.2, 0.5, 6.1, 0.90),
Material("F15 22mm cement plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0222, 0.2, 0.6, 12.4, 0.91),
Material("F16 Acoustic tile", MaterialCategory.FINISHING_MATERIALS, 0.06, 368.0, 590.0, 0.0191, 1.0, 0.4, 14.0, 0.925),
Material("F17 13mm slag", MaterialCategory.FINISHING_MATERIALS, 0.16, 960.0, 1090.0, 0.0127, 0.2, 0.5, 1.0, 0.925),
Material("F18 25mm slag", MaterialCategory.FINISHING_MATERIALS, 0.16, 960.0, 1090.0, 0.0254, 0.2, 0.5, 1.9, 0.925),
Material("G01 13mm gypsum board", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0127, 0.25, 0.4, 5.1, 0.925),
Material("G01a 19mm gypsum board", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0191, 0.25, 0.4, 7.6, 0.925),
Material("G02 25mm cement plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0254, 0.2, 0.6, 14.1, 0.91),
Material("G03 13mm lime plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1600.0, 840.0, 0.0127, 0.25, 0.5, 6.1, 0.90),
Material("G04 13mm cement plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0127, 0.2, 0.6, 7.1, 0.91),
Material("G05 25mm wood", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.15, 608.0, 1630.0, 0.0254, 0.3, 0.5, 15.4, 0.925),
Material("G06 19mm wood", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.15, 608.0, 1630.0, 0.0191, 0.3, 0.5, 11.6, 0.925),
Material("I01 25mm insulation board", MaterialCategory.INSULATION, 0.03, 43.0, 1210.0, 0.0254, 2.5, 0.5, 1.1, 0.925),
Material("I02 50mm insulation board", MaterialCategory.INSULATION, 0.03, 43.0, 1210.0, 0.0508, 2.5, 0.5, 2.2, 0.925),
Material("I03 75mm insulation board", MaterialCategory.INSULATION, 0.03, 43.0, 1210.0, 0.0762, 2.5, 0.5, 3.3, 0.925),
Material("M01 100mm brick", MaterialCategory.STRUCTURAL_MATERIALS, 0.89, 1920.0, 790.0, 0.1016, 0.3, 0.7, 19.5, 0.93),
Material("M02 100mm face brick", MaterialCategory.STRUCTURAL_MATERIALS, 1.33, 2000.0, 790.0, 0.1016, 0.3, 0.7, 20.3, 0.93),
Material("M03 150mm brick", MaterialCategory.STRUCTURAL_MATERIALS, 0.89, 1920.0, 790.0, 0.1524, 0.3, 0.7, 29.3, 0.93),
Material("M04 200mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.2032, 0.2, 0.65, 13.0, 0.925),
Material("M05 200mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.2032, 0.2, 0.65, 20.8, 0.925),
Material("M06 150mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.1524, 0.2, 0.65, 9.8, 0.925),
Material("M07 100mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.1016, 0.2, 0.65, 6.5, 0.925),
Material("M08 150mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.1524, 0.2, 0.65, 15.6, 0.925),
Material("M09 100mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.1016, 0.2, 0.65, 10.4, 0.925),
Material("M10 100mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.1016, 0.15, 0.65, 7.8, 0.925),
Material("M11 100mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.1016, 0.15, 0.65, 7.8, 0.925),
Material("M12 150mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.1524, 0.15, 0.65, 11.7, 0.925),
Material("M13 200mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.2032, 0.15, 0.65, 15.6, 0.925),
Material("M14 100mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.1016, 0.2, 0.65, 18.2, 0.925),
Material("M14a 100mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.1016, 0.2, 0.65, 18.2, 0.925),
Material("M15 200mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.2032, 0.2, 0.65, 36.4, 0.925),
Material("M16 300mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.3048, 0.2, 0.65, 54.6, 0.925),
Material("M17 100mm stone", MaterialCategory.STRUCTURAL_MATERIALS, 2.10, 2240.0, 880.0, 0.1016, 0.2, 0.7, 22.8, 0.925),
Material("M18 150mm stone", MaterialCategory.STRUCTURAL_MATERIALS, 2.10, 2240.0, 880.0, 0.1524, 0.2, 0.7, 34.1, 0.925),
Material("M19 100mm limestone", MaterialCategory.STRUCTURAL_MATERIALS, 1.80, 2320.0, 880.0, 0.1016, 0.2, 0.6, 23.6, 0.925),
Material("M20 150mm limestone", MaterialCategory.STRUCTURAL_MATERIALS, 1.80, 2320.0, 880.0, 0.1524, 0.2, 0.6, 35.4, 0.925),
Material("M21 200mm limestone", MaterialCategory.STRUCTURAL_MATERIALS, 1.80, 2320.0, 880.0, 0.2032, 0.2, 0.6, 47.1, 0.925),
Material("M22 100mm granite", MaterialCategory.STRUCTURAL_MATERIALS, 2.80, 2640.0, 880.0, 0.1016, 0.2, 0.7, 26.8, 0.925),
Material("M23 150mm granite", MaterialCategory.STRUCTURAL_MATERIALS, 2.80, 2640.0, 880.0, 0.1524, 0.2, 0.7, 40.2, 0.925),
Material("M24 200mm granite", MaterialCategory.STRUCTURAL_MATERIALS, 2.80, 2640.0, 880.0, 0.2032, 0.2, 0.7, 53.6, 0.925),
Material("M25 100mm marble", MaterialCategory.STRUCTURAL_MATERIALS, 2.50, 2720.0, 880.0, 0.1016, 0.2, 0.6, 27.6, 0.925),
Material("M26 150mm marble", MaterialCategory.STRUCTURAL_MATERIALS, 2.50, 2720.0, 880.0, 0.1524, 0.2, 0.6, 41.4, 0.925),
Material("M27 200mm marble", MaterialCategory.STRUCTURAL_MATERIALS, 2.50, 2720.0, 880.0, 0.2032, 0.2, 0.6, 55.3, 0.925),
]
return {mat.name: mat for mat in materials}
def initialize_glazing_materials(self) -> Dict[str, GlazingMaterial]:
glazing_materials = [
# ASHRAE-based glazing materials with Australian pricing
GlazingMaterial("Single Clear 3mm", 5.8, 0.81, 25.0, 50.0, 17.8),
GlazingMaterial("Single Clear 6mm", 5.7, 0.78, 28.0, 60.0, 17.8),
GlazingMaterial("Single Tinted 6mm", 5.7, 0.55, 30.0, 70.0, 17.8),
GlazingMaterial("Double Clear 6mm/13mm Air", 2.7, 0.70, 40.0, 100.0, 17.8),
GlazingMaterial("Double Low-E 6mm/13mm Air", 1.8, 0.60, 45.0, 120.0, 17.8),
GlazingMaterial("Double Tinted 6mm/13mm Air", 2.7, 0.45, 42.0, 110.0, 17.8),
GlazingMaterial("Double Low-E 6mm/13mm Argon", 1.5, 0.55, 48.0, 130.0, 17.8),
GlazingMaterial("Triple Clear 4mm/12mm Air", 1.8, 0.62, 55.0, 150.0, 17.8),
GlazingMaterial("Triple Low-E 4mm/12mm Argon", 0.9, 0.50, 60.0, 180.0, 17.8),
GlazingMaterial("Single Low-E Reflective 6mm", 5.6, 0.35, 35.0, 90.0, 13.0),
GlazingMaterial("Double Reflective 6mm/13mm Air", 2.5, 0.30, 50.0, 140.0, 13.0),
GlazingMaterial("Electrochromic 6mm/13mm Air", 2.0, 0.40, 70.0, 200.0, 17.8),
]
return {mat.name: mat for mat in glazing_materials}
def initialize_door_materials(self) -> Dict[str, DoorMaterial]:
door_materials = [
# Door materials with ASHRAE-based properties, Australian pricing, and inferred properties
DoorMaterial("Solid Wood 45mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.15, 608.0, 1630.0, 0.045, 2.5, 0.50, 15.0, 200.0, 0.925),
DoorMaterial("Insulated Wood 50mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.10, 500.0, 1400.0, 0.050, 1.8, 0.45, 18.0, 250.0, 0.925),
DoorMaterial("Hollow Core Wood 40mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.15, 400.0, 1630.0, 0.040, 3.5, 0.50, 12.0, 150.0, 0.925),
DoorMaterial("Steel Uninsulated 45mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 45.28, 7824.0, 500.0, 0.045, 5.0, 0.70, 20.0, 180.0, 0.075),
DoorMaterial("Steel Insulated 50mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.10, 2000.0, 1000.0, 0.050, 2.0, 0.65, 25.0, 220.0, 0.925),
DoorMaterial("Aluminum Uninsulated 45mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 160.0, 2700.0, 900.0, 0.045, 6.0, 0.75, 22.0, 200.0, 0.075),
DoorMaterial("Aluminum Insulated 50mm", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.15, 1800.0, 1100.0, 0.050, 2.5, 0.70, 28.0, 240.0, 0.925),
DoorMaterial("Glass Single 6mm", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.006, 5.7, 0.78, 28.0, 100.0, 0.90),
DoorMaterial("Glass Double 6mm/13mm Air", MaterialCategory.STRUCTURAL_MATERIALS, 0.40, 1280.0, 840.0, 0.025, 2.7, 0.70, 40.0, 150.0, 0.90),
DoorMaterial("Fiberglass Insulated 50mm", MaterialCategory.INSULATION, 0.03, 43.0, 1210.0, 0.050, 1.5, 0.60, 20.0, 230.0, 0.925),
DoorMaterial("PVC Insulated 50mm", MaterialCategory.INSULATION, 0.05, 1400.0, 1000.0, 0.050, 1.7, 0.55, 18.0, 210.0, 0.925),
DoorMaterial("Wood with Glass Insert", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.15, 608.0, 1630.0, 0.045, 3.0, 0.65, 16.0, 190.0, 0.925),
]
return {mat.name: mat for mat in door_materials}
def initialize_constructions(self) -> Dict[str, Construction]:
constructions = [
Construction("Light Exterior Wall", "Wall", [
{"material": self.library_materials["F08 Metal surface"], "thickness": 0.001},
{"material": self.library_materials["I02 50mm insulation board"], "thickness": 0.0508},
{"material": self.library_materials["F04 Wall air space resistance"], "thickness": 0.01},
{"material": self.library_materials["G01a 19mm gypsum board"], "thickness": 0.0191}
]),
Construction("Light Roof/Ceiling", "Roof", [
{"material": self.library_materials["M11 100mm lightweight concrete"], "thickness": 0.1016},
{"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
{"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191}
]),
Construction("Light Floor", "Floor", [
{"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191},
{"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
{"material": self.library_materials["M11 100mm lightweight concrete"], "thickness": 0.1016}
]),
Construction("Medium Exterior Wall", "Wall", [
{"material": self.library_materials["M01 100mm brick"], "thickness": 0.1016},
{"material": self.library_materials["I02 50mm insulation board"], "thickness": 0.0508},
{"material": self.library_materials["F04 Wall air space resistance"], "thickness": 0.01},
{"material": self.library_materials["G01a 19mm gypsum board"], "thickness": 0.0191}
]),
Construction("Medium Roof/Ceiling", "Roof", [
{"material": self.library_materials["M14a 100mm heavyweight concrete"], "thickness": 0.1016},
{"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
{"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191}
]),
Construction("Medium Floor", "Floor", [
{"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191},
{"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
{"material": self.library_materials["M14a 100mm heavyweight concrete"], "thickness": 0.1016}
]),
Construction("Heavy Exterior Wall", "Wall", [
{"material": self.library_materials["M01 100mm brick"], "thickness": 0.1016},
{"material": self.library_materials["M15 200mm heavyweight concrete"], "thickness": 0.2032},
{"material": self.library_materials["I02 50mm insulation board"], "thickness": 0.0508},
{"material": self.library_materials["F04 Wall air space resistance"], "thickness": 0.01},
{"material": self.library_materials["G01a 19mm gypsum board"], "thickness": 0.0191}
]),
Construction("Heavy Roof/Ceiling", "Roof", [
{"material": self.library_materials["M15 200mm heavyweight concrete"], "thickness": 0.2032},
{"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
{"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191}
]),
Construction("Heavy Floor", "Floor", [
{"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191},
{"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
{"material": self.library_materials["M15 200mm heavyweight concrete"], "thickness": 0.2032}
]),
]
return {cons.name: cons for cons in constructions}
def get_all_materials(self, project_materials: Optional[Dict[str, Material]] = None) -> List[Material]:
materials = list(self.library_materials.values())
if project_materials:
materials.extend(list(project_materials.values()))
return materials
def get_all_glazing_materials(self, project_glazing_materials: Optional[Dict[str, GlazingMaterial]] = None) -> List[GlazingMaterial]:
materials = list(self.library_glazing_materials.values())
if project_glazing_materials:
materials.extend(list(project_glazing_materials.values()))
return materials
def get_all_door_materials(self, project_door_materials: Optional[Dict[str, DoorMaterial]] = None) -> List[DoorMaterial]:
materials = list(self.library_door_materials.values())
if project_door_materials:
materials.extend(list(project_door_materials.values()))
return materials
def add_project_material(self, material: Material, project_materials: Dict[str, Material]) -> Tuple[bool, str]:
if len(project_materials) >= 20:
return False, "Maximum 20 project materials allowed."
if material.name in project_materials or material.name in self.library_materials:
return False, f"Material name '{material.name}' already exists."
project_materials[material.name] = material
return True, f"Material '{material.name}' added to project materials."
def add_project_glazing_material(self, material: GlazingMaterial, project_glazing_materials: Dict[str, GlazingMaterial]) -> Tuple[bool, str]:
if len(project_glazing_materials) >= 20:
return False, "Maximum 20 project glazing materials allowed."
if material.name in project_glazing_materials or material.name in self.library_glazing_materials:
return False, f"Glazing material name '{material.name}' already exists."
project_glazing_materials[material.name] = material
return True, f"Glazing material '{material.name}' added to project glazing materials."
def add_project_door_material(self, material: DoorMaterial, project_door_materials: Dict[str, DoorMaterial]) -> Tuple[bool, str]:
if len(project_door_materials) >= 20:
return False, "Maximum 20 project door materials allowed."
if material.name in project_door_materials or material.name in self.library_door_materials:
return False, f"Door material name '{material.name}' already exists."
project_door_materials[material.name] = material
return True, f"Door material '{material.name}' added to project door materials."
def edit_project_material(self, old_name: str, new_material: Material, project_materials: Dict[str, Material],
components: Dict[str, List]) -> Tuple[bool, str]:
if old_name not in project_materials:
return False, f"Material '{old_name}' not found in project materials."
if new_material.name != old_name and (new_material.name in project_materials or new_material.name in self.library_materials):
return False, f"Material name '{new_material.name}' already exists."
for comp_list in components.values():
for comp in comp_list:
if comp.construction and any(layer["material"].name == old_name for layer in comp.layers):
comp.layers = [{"material": new_material if layer["material"].name == old_name else layer["material"],
"thickness": layer["thickness"]} for layer in comp.layers]
comp.construction = Construction(
name=comp.construction.name,
component_type=comp.construction.component_type,
layers=comp.layers,
is_library=comp.construction.is_library
)
project_materials.pop(old_name)
project_materials[new_material.name] = new_material
return True, f"Material '{old_name}' updated to '{new_material.name}'."
def edit_project_glazing_material(self, old_name: str, new_material: GlazingMaterial,
project_glazing_materials: Dict[str, GlazingMaterial],
components: Dict[str, List]) -> Tuple[bool, str]:
if old_name not in project_glazing_materials:
return False, f"Glazing material '{old_name}' not found in project glazing materials."
if new_material.name != old_name and (new_material.name in project_glazing_materials or new_material.name in self.library_glazing_materials):
return False, f"Glazing material name '{new_material.name}' already exists."
for comp_list in components.values():
for comp in comp_list:
if comp.glazing_material and comp.glazing_material.name == old_name:
comp.glazing_material = new_material
comp.u_value = new_material.u_value
comp.shgc = new_material.shgc
comp.h_o = new_material.h_o
project_glazing_materials.pop(old_name)
project_glazing_materials[new_material.name] = new_material
return True, f"Glazing material '{old_name}' updated to '{new_material.name}'."
def edit_project_door_material(self, old_name: str, new_material: DoorMaterial,
project_door_materials: Dict[str, DoorMaterial],
components: Dict[str, List]) -> Tuple[bool, str]:
if old_name not in project_door_materials:
return False, f"Door material '{old_name}' not found in project door materials."
if new_material.name != old_name and (new_material.name in project_door_materials or new_material.name in self.library_door_materials):
return False, f"Door material name '{new_material.name}' already exists."
for comp_list in components.values():
for comp in comp_list:
if comp.door_material and comp.door_material.name == old_name:
comp.door_material = new_material
comp.u_value = new_material.u_value
comp.solar_absorptivity = new_material.solar_absorption
project_door_materials.pop(old_name)
project_door_materials[new_material.name] = new_material
return True, f"Door material '{old_name}' updated to '{new_material.name}'."
def delete_project_material(self, name: str, project_materials: Dict[str, Material], components: Dict[str, List]) -> Tuple[bool, str]:
if name not in project_materials:
return False, f"Material '{name}' not found in project materials."
for cons in self.library_constructions.values():
if any(layer["material"].name == name for layer in cons.layers):
return False, f"Cannot delete '{name}' as it is used in library construction '{cons.name}'."
for comp_type, comp_list in components.items():
for comp in comp_list:
if 'layers' in comp and any(layer["material"].name == name for layer in comp["layers"]):
return False, f"Cannot delete '{name}' as it is used in component '{comp['name']}' ({comp_type})."
del project_materials[name]
return True, f"Material '{name}' deleted successfully."
def delete_project_glazing_material(self, name: str, project_glazing_materials: Dict[str, GlazingMaterial],
components: Dict[str, List]) -> Tuple[bool, str]:
if name not in project_glazing_materials:
return False, f"Glazing material '{name}' not found in project glazing materials."
for comp_list in components.values():
for comp in comp_list:
if comp.glazing_material and comp.glazing_material.name == name:
return False, f"Cannot delete '{name}' as it is used in component '{comp.name}'."
del project_glazing_materials[name]
return True, f"Glazing material '{name}' deleted successfully."
def delete_project_door_material(self, name: str, project_door_materials: Dict[str, DoorMaterial],
components: Dict[str, List]) -> Tuple[bool, str]:
if name not in project_door_materials:
return False, f"Door material '{name}' not found in project door materials."
for comp_list in components.values():
for comp in comp_list:
if comp.door_material and comp.door_material.name == name:
return False, f"Cannot delete '{name}' as it is used in component '{comp.name}'."
del project_door_materials[name]
return True, f"Door material '{name}' deleted successfully."
def add_project_construction(self, construction: Construction, project_constructions: Dict[str, Construction]) -> Tuple[bool, str]:
if len(project_constructions) >= 20:
return False, "Maximum 20 project constructions allowed."
if construction.name in project_constructions or construction.name in self.library_constructions:
return False, f"Construction name '{construction.name}' already exists."
project_constructions[construction.name] = construction
return True, f"Construction '{construction.name}' added to project constructions."
def edit_project_construction(self, old_name: str, new_construction: Construction,
project_constructions: Dict[str, Construction],
components: Dict[str, List]) -> Tuple[bool, str]:
if old_name not in project_constructions:
return False, f"Construction '{old_name}' not found in project constructions."
if new_construction.name != old_name and (new_construction.name in project_constructions or new_construction.name in self.library_constructions):
return False, f"Construction name '{new_construction.name}' already exists."
for comp_list in components.values():
for comp in comp_list:
if comp.construction and comp.construction.name == old_name:
comp.construction = new_construction
comp.layers = new_construction.layers
comp.u_value = new_construction.u_value
project_constructions.pop(old_name)
project_constructions[new_construction.name] = new_construction
return True, f"Construction '{old_name}' updated to '{new_construction.name}'."
def delete_project_construction(self, name: str, project_constructions: Dict[str, Construction],
components: Dict[str, List]) -> Tuple[bool, str]:
if name not in project_constructions:
return False, f"Construction '{name}' not found in project constructions."
for comp_list in components.values():
for comp in comp_list:
if comp.construction and comp.construction.name == name:
return False, f"Construction '{name}' is used in component '{comp.name}'."
project_constructions.pop(name)
return True, f"Construction '{name}' deleted."
def to_dataframe(self, data_type: str, project_materials: Optional[Dict[str, Material]] = None,
project_constructions: Optional[Dict[str, Construction]] = None,
project_glazing_materials: Optional[Dict[str, GlazingMaterial]] = None,
project_door_materials: Optional[Dict[str, DoorMaterial]] = None,
only_project: bool = False) -> pd.DataFrame:
if data_type == "materials":
data = []
materials = project_materials.values() if only_project else self.get_all_materials(project_materials)
for mat in materials:
data.append({
"Name": mat.name,
"Category": mat.category.value,
"Conductivity (W/m·K)": mat.conductivity,
"Density (kg/m³)": mat.density,
"Specific Heat (J/kg·K)": mat.specific_heat,
"Default Thickness (m)": mat.default_thickness,
"Embodied Carbon (kgCO₂e/kg)": mat.embodied_carbon,
"Solar Absorption": mat.solar_absorption,
"Price (USD/m²)": mat.price,
"Emissivity": mat.emissivity,
"Source": "Project" if not mat.is_library else "Library"
})
return pd.DataFrame(data)
elif data_type == "constructions":
data = []
constructions = project_constructions.values() if only_project else list(self.library_constructions.values())
if not only_project and project_constructions:
constructions.extend(list(project_constructions.values()))
for cons in constructions:
layers_str = "; ".join(f"{layer['material'].name} ({layer['thickness']}m)" for layer in cons.layers)
data.append({
"Name": cons.name,
"Component Type": cons.component_type,
"U-Value (W/m²·K)": cons.u_value,
"Total Thickness (m)": cons.total_thickness,
"Embodied Carbon (kgCO₂e/m²)": cons.embodied_carbon,
"Solar Absorption": cons.solar_absorption,
"Price (USD/m²)": cons.price,
"Layers": layers_str,
"Source": "Project" if not cons.is_library else "Library"
})
return pd.DataFrame(data)
elif data_type == "glazing_materials":
data = []
glazing_materials = project_glazing_materials.values() if only_project else self.get_all_glazing_materials(project_glazing_materials)
for mat in glazing_materials:
data.append({
"Name": mat.name,
"U-Value (W/m²·K)": mat.u_value,
"SHGC": mat.shgc,
"Embodied Carbon (kgCO₂e/m²)": mat.embodied_carbon,
"Price (USD/m²)": mat.price,
"h_o (W/m²·K)": mat.h_o,
"Source": "Project" if not mat.is_library else "Library"
})
return pd.DataFrame(data)
elif data_type == "door_materials":
data = []
door_materials = project_door_materials.values() if only_project else self.get_all_door_materials(project_door_materials)
for mat in door_materials:
data.append({
"Name": mat.name,
"Category": mat.category.value,
"Conductivity (W/m·K)": mat.conductivity,
"Density (kg/m³)": mat.density,
"Specific Heat (J/kg·K)": mat.specific_heat,
"Default Thickness (m)": mat.default_thickness,
"U-Value (W/m²·K)": mat.u_value,
"Solar Absorption": mat.solar_absorption,
"Embodied Carbon (kgCO₂e/m²)": mat.embodied_carbon,
"Price (USD/m²)": mat.price,
"Emissivity": mat.emissivity,
"Source": "Project" if not mat.is_library else "Library"
})
return pd.DataFrame(data)
return pd.DataFrame()