mabuseif commited on
Commit
3d36616
verified
1 Parent(s): 59dc95c

Upload material_library.py

Browse files
Files changed (1) hide show
  1. data/material_library.py +342 -278
data/material_library.py CHANGED
@@ -1,20 +1,9 @@
1
  """
2
  Material Library for HVAC Load Calculator
3
- Updated 2025-05-28: Consolidated door materials into main materials library, prefixed with 'D'.
4
- Updated references to solar_absorption to solar_absorptance.
5
- Renamed 'conductivity' to 'U-value' with units W/m虏路K in Material class.
6
- Added thermal properties to door materials to align with Material class.
7
- Removed DoorMaterial class and library_door_materials.
8
- Updated 2025-05-27: Renamed solar_absorption to solar_absorptance, moved door materials to materials with 'D' prefix,
9
- renamed conductivity to U-value (W/m虏路K), and integrated door thermal properties into Material class.
10
- Updated 2025-05-26: Added emissivity to Material, h_o to GlazingMaterial, and expanded DoorMaterial to include all Material
11
- properties plus emissivity. Assigned values based on provided data and inferred for doors.
12
- Updated 2025-05-17: Removed original materials and constructions, deleted ASHRAE materials starting with 'R' followed by number
13
- (e.g., R01, R25), verified constructions use valid materials.
14
- Updated 2025-05-17: Added all materials and constructions from ASHRAE_2005_HOF_Materials.idf with Australian-specific
15
- embodied carbon and prices.
16
- Updated 2025-05-17: Added GlazingMaterial and DoorMaterial classes, included library_glazing_materials and
17
- library_door_materials with comprehensive data.
18
  Updated 2025-05-16: Removed mass_per_meter, added thermal_mass method, updated CSV handling.
19
  Updated 2025-05-16: Fixed U-value calculation in Material.get_u_value.
20
  Updated 2025-05-16: Updated MaterialCategory to Finishing Materials, Structural Materials, Sub-Structural Materials, Insulation.
@@ -33,7 +22,6 @@ class MaterialCategory(Enum):
33
  STRUCTURAL_MATERIALS = "Structural Materials"
34
  SUB_STRUCTURAL_MATERIALS = "Sub-Structural Materials"
35
  INSULATION = "Insulation"
36
- DOOR_MATERIALS = "Door Materials" # Keep this category for classification purposes within Material class
37
 
38
  class ThermalMass(Enum):
39
  HIGH = "High"
@@ -42,25 +30,22 @@ class ThermalMass(Enum):
42
  NO_MASS = "No Mass"
43
 
44
  class Material:
45
- def __init__(self, name: str, category: MaterialCategory, u_value: float, density: float,
46
  specific_heat: float, default_thickness: float, embodied_carbon: float,
47
- solar_absorptance: float, price: float, emissivity: float, is_library: bool = True):
48
  self.name = name
49
  self.category = category
50
- # u_value here represents the thermal conductivity (k) of the material in W/m路K.
51
- # The method get_u_value() then calculates the overall U-value (U = k/L) for a given thickness.
52
- self.u_value = max(0.01, u_value) # W/m路K (formerly conductivity, now named u_value for clarity)
53
  self.density = max(1.0, density) # kg/m鲁
54
  self.specific_heat = max(100.0, specific_heat) # J/kg路K
55
  self.default_thickness = max(0.01, default_thickness) # m
56
  self.embodied_carbon = max(0.0, embodied_carbon) # kgCO鈧俥/kg
57
- self.solar_absorptance = min(max(0.0, solar_absorptance), 1.0) # Dimensionless
58
- self.price = max(0.0, price) # USD/m虏 (price per square meter of material at default thickness)
59
  self.emissivity = min(max(0.0, emissivity), 1.0) # Dimensionless
60
  self.is_library = is_library
61
 
62
  def get_thermal_mass(self) -> ThermalMass:
63
- # This method calculates the thermal mass category based on density and specific heat.
64
  if self.density < 100.0 or self.specific_heat < 800.0:
65
  return ThermalMass.NO_MASS
66
  elif self.density > 2000.0 and self.specific_heat > 800.0:
@@ -71,58 +56,63 @@ class Material:
71
  return ThermalMass.LOW
72
 
73
  def get_u_value(self) -> float:
74
- # This method calculates the U-value (overall heat transfer coefficient) for a layer
75
- # of this material based on its conductivity (self.u_value) and default thickness.
76
- return self.u_value / self.default_thickness if self.default_thickness > 0 else 0.1 # U = k/L
77
 
78
  class GlazingMaterial:
79
  def __init__(self, name: str, u_value: float, shgc: float, embodied_carbon: float, price: float, h_o: float, is_library: bool = True):
80
  self.name = name
81
- self.u_value = max(0.1, u_value) # W/m虏路K (overall U-value for glazing)
82
- self.shgc = min(max(0.0, shgc), 1.0) # Solar Heat Gain Coefficient (dimensionless)
83
  self.embodied_carbon = max(0.0, embodied_carbon) # kgCO鈧俥/m虏
84
  self.price = max(0.0, price) # USD/m虏
85
  self.h_o = max(0.0, h_o) # W/m虏路K, exterior convective heat transfer coefficient
86
  self.is_library = is_library
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  class Construction:
89
  def __init__(self, name: str, component_type: str, layers: List[Dict], is_library: bool = True):
90
  self.name = name
91
  self.component_type = component_type
92
  self.layers = layers or []
93
  self.is_library = is_library
94
- # Calculate U-value, total thickness, embodied carbon, solar absorptance, and price for the construction
95
  self.u_value = self.calculate_u_value()
96
  self.total_thickness = sum(layer["thickness"] for layer in self.layers)
97
- # Embodied carbon for construction is sum of (material embodied carbon per kg * material density * layer thickness)
98
  self.embodied_carbon = sum(layer["material"].embodied_carbon * layer["material"].density * layer["thickness"]
99
  for layer in self.layers)
100
- # Solar absorptance for construction is typically determined by the outermost layer.
101
- # Here, it takes the maximum of all layers, assuming the highest absorptance dominates.
102
- self.solar_absorptance = max(layer["material"].solar_absorptance for layer in self.layers) if self.layers else 0.6
103
- # Price for construction is sum of (material price per m虏 * layer thickness / material default thickness)
104
  self.price = sum(layer["material"].price * layer["thickness"] / layer["material"].default_thickness
105
  for layer in self.layers)
106
 
107
  def calculate_u_value(self) -> float:
108
- # Calculates the overall U-value for the construction based on the sum of thermal resistances of its layers.
109
  if not self.layers:
110
- return 0.1 # Default U-value if no layers
111
- # Sum of thermal resistances (R = L/k) for each layer
112
- r_total = sum(layer["thickness"] / layer["material"].u_value for layer in self.layers) # layer["material"].u_value is conductivity (k)
113
- return 1 / r_total if r_total > 0 else 0.1 # U = 1/R_total
114
 
115
  def get_thermal_mass(self) -> ThermalMass:
116
- # Determines the thermal mass category of the entire construction.
117
  if not self.layers:
118
  return ThermalMass.NO_MASS
119
  total_thickness = self.total_thickness
120
  if total_thickness == 0:
121
  return ThermalMass.NO_MASS
122
- # Calculate average density and specific heat for the construction
123
  avg_density = sum(layer["material"].density * layer["thickness"] for layer in self.layers) / total_thickness
124
  avg_specific_heat = sum(layer["material"].specific_heat * layer["thickness"] for layer in self.layers) / total_thickness
125
-
126
  if avg_density < 100.0 or avg_specific_heat < 800.0:
127
  return ThermalMass.NO_MASS
128
  elif avg_density > 2000.0 and avg_specific_heat > 800.0:
@@ -137,6 +127,7 @@ class MaterialLibrary:
137
  self.library_materials = self.initialize_materials()
138
  self.library_constructions = self.initialize_constructions()
139
  self.library_glazing_materials = self.initialize_glazing_materials()
 
140
 
141
  def initialize_materials(self) -> Dict[str, Material]:
142
  materials = [
@@ -171,257 +162,309 @@ class MaterialLibrary:
171
  Material("M03 150mm brick", MaterialCategory.STRUCTURAL_MATERIALS, 0.89, 1920.0, 790.0, 0.1524, 0.3, 0.7, 29.3, 0.93),
172
  Material("M04 200mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.2032, 0.2, 0.65, 13.0, 0.925),
173
  Material("M05 200mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.2032, 0.2, 0.65, 20.8, 0.925),
174
- Material("M06 200mm lightweight concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.2032, 0.2, 0.6, 10.4, 0.925),
175
- Material("M07 200mm concrete block (filled)", MaterialCategory.STRUCTURAL_MATERIALS, 0.84, 1600.0, 920.0, 0.2032, 0.2, 0.65, 26.0, 0.925),
176
- Material("M08 100mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.1016, 0.2, 0.65, 6.5, 0.925),
177
- Material("M09 100mm concrete block (filled)", MaterialCategory.STRUCTURAL_MATERIALS, 0.84, 1600.0, 920.0, 0.1016, 0.2, 0.65, 13.0, 0.925),
178
- Material("M10 100mm lightweight concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.1016, 0.2, 0.6, 5.2, 0.925),
179
- Material("M11 50mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.0508, 0.15, 0.6, 10.0, 0.90),
180
- Material("M12 75mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.0762, 0.15, 0.6, 15.0, 0.90),
181
- Material("M13 100mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.1016, 0.15, 0.6, 20.0, 0.90),
182
- Material("M14 150mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.1524, 0.15, 0.6, 30.0, 0.90),
183
- Material("M15 200mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.2032, 0.15, 0.6, 40.0, 0.90),
184
- Material("M16 250mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.254, 0.15, 0.6, 50.0, 0.90),
185
- Material("M17 300mm concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 1.73, 2240.0, 840.0, 0.3048, 0.15, 0.6, 60.0, 0.90),
186
- Material("M18 50mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.0508, 0.1, 0.5, 3.0, 0.925),
187
- Material("M19 75mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.0762, 0.1, 0.5, 4.5, 0.925),
188
- Material("M20 100mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.1016, 0.1, 0.5, 6.0, 0.925),
189
- Material("M21 150mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.1524, 0.1, 0.5, 9.0, 0.925),
190
- Material("M22 200mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.2032, 0.1, 0.5, 12.0, 0.925),
191
- Material("M23 250mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.254, 0.1, 0.5, 15.0, 0.925),
192
- Material("M24 300mm lightweight concrete slab", MaterialCategory.STRUCTURAL_MATERIALS, 0.19, 640.0, 920.0, 0.3048, 0.1, 0.5, 18.0, 0.925),
193
- Material("M25 100mm concrete slab (heavy)", MaterialCategory.STRUCTURAL_MATERIALS, 2.0, 2400.0, 900.0, 0.1016, 0.18, 0.6, 22.0, 0.90),
194
- Material("M26 150mm concrete slab (heavy)", MaterialCategory.STRUCTURAL_MATERIALS, 2.0, 2400.0, 900.0, 0.1524, 0.18, 0.6, 33.0, 0.90),
195
- Material("M27 200mm concrete slab (heavy)", MaterialCategory.STRUCTURAL_MATERIALS, 2.0, 2400.0, 900.0, 0.2032, 0.18, 0.6, 44.0, 0.90),
196
- Material("M28 250mm concrete slab (heavy)", MaterialCategory.STRUCTURAL_MATERIALS, 2.0, 2400.0, 900.0, 0.254, 0.18, 0.6, 55.0, 0.90),
197
- Material("M29 300mm concrete slab (heavy)", MaterialCategory.STRUCTURAL_MATERIALS, 2.0, 2400.0, 900.0, 0.3048, 0.18, 0.6, 66.0, 0.90),
198
- Material("P01 13mm plywood", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 540.0, 1210.0, 0.0127, 0.2, 0.5, 6.5, 0.90),
199
- Material("P02 19mm plywood", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 540.0, 1210.0, 0.0191, 0.2, 0.5, 9.7, 0.90),
200
- Material("P03 25mm plywood", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 540.0, 1210.0, 0.0254, 0.2, 0.5, 13.0, 0.90),
201
- Material("P04 13mm particle board", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 640.0, 1300.0, 0.0127, 0.2, 0.5, 5.5, 0.90),
202
- Material("P05 19mm particle board", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 640.0, 1300.0, 0.0191, 0.2, 0.5, 8.3, 0.90),
203
- Material("P06 25mm particle board", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 640.0, 1300.0, 0.0254, 0.2, 0.5, 11.0, 0.90),
204
- Material("P07 13mm hardboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 960.0, 1300.0, 0.0127, 0.2, 0.5, 7.0, 0.90),
205
- Material("P08 19mm hardboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 960.0, 1300.0, 0.0191, 0.2, 0.5, 10.5, 0.90),
206
- Material("P09 25mm hardboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 960.0, 1300.0, 0.0254, 0.2, 0.5, 14.0, 0.90),
207
- Material("P10 13mm fibreboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.04, 240.0, 1300.0, 0.0127, 0.2, 0.5, 3.0, 0.90),
208
- Material("P11 19mm fibreboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.04, 240.0, 1300.0, 0.0191, 0.2, 0.5, 4.5, 0.90),
209
- Material("P12 25mm fibreboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.04, 240.0, 1300.0, 0.0254, 0.2, 0.5, 6.0, 0.90),
210
- Material("S01 13mm stucco", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0127, 0.2, 0.6, 7.1, 0.91),
211
- Material("S02 25mm stucco", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0254, 0.2, 0.6, 14.1, 0.91),
212
- Material("S03 13mm plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1600.0, 840.0, 0.0127, 0.2, 0.5, 6.1, 0.90),
213
- Material("S04 25mm plaster", MaterialCategory.FINISHING_MATERIALS, 0.72, 1600.0, 840.0, 0.0254, 0.2, 0.5, 12.2, 0.90),
214
- Material("S05 13mm gypsum plaster", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0127, 0.25, 0.4, 5.1, 0.925),
215
- Material("S06 25mm gypsum plaster", MaterialCategory.FINISHING_MATERIALS, 0.16, 800.0, 1090.0, 0.0254, 0.25, 0.4, 10.2, 0.925),
216
- Material("S07 13mm wood siding", MaterialCategory.FINISHING_MATERIALS, 0.14, 500.0, 1600.0, 0.0127, 0.25, 0.5, 8.0, 0.90),
217
- Material("S08 19mm wood siding", MaterialCategory.FINISHING_MATERIALS, 0.14, 500.0, 1600.0, 0.0191, 0.25, 0.5, 12.0, 0.90),
218
- Material("S09 25mm wood siding", MaterialCategory.FINISHING_MATERIALS, 0.14, 500.0, 1600.0, 0.0254, 0.25, 0.5, 16.0, 0.90),
219
- Material("S10 13mm brick veneer", MaterialCategory.FINISHING_MATERIALS, 0.89, 1920.0, 790.0, 0.0127, 0.3, 0.7, 2.4, 0.93),
220
- Material("S11 25mm brick veneer", MaterialCategory.FINISHING_MATERIALS, 0.89, 1920.0, 790.0, 0.0254, 0.3, 0.7, 4.8, 0.93),
221
- Material("S12 50mm brick veneer", MaterialCategory.FINISHING_MATERIALS, 0.89, 1920.0, 790.0, 0.0508, 0.3, 0.7, 9.6, 0.93),
222
- Material("S13 13mm concrete siding", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0127, 0.2, 0.6, 7.1, 0.91),
223
- Material("S14 25mm concrete siding", MaterialCategory.FINISHING_MATERIALS, 0.72, 1856.0, 840.0, 0.0254, 0.2, 0.6, 14.1, 0.91),
224
- Material("S15 13mm asphalt shingle", MaterialCategory.FINISHING_MATERIALS, 0.06, 1120.0, 920.0, 0.0127, 0.5, 0.9, 10.0, 0.90),
225
- Material("S16 25mm asphalt shingle", MaterialCategory.FINISHING_MATERIALS, 0.06, 1120.0, 920.0, 0.0254, 0.5, 0.9, 20.0, 0.90),
226
- Material("T01 13mm ceramic tile", MaterialCategory.FINISHING_MATERIALS, 1.0, 2200.0, 880.0, 0.0127, 0.4, 0.6, 15.0, 0.90),
227
- Material("T02 25mm ceramic tile", MaterialCategory.FINISHING_MATERIALS, 1.0, 2200.0, 880.0, 0.0254, 0.4, 0.6, 30.0, 0.90),
228
- Material("T03 13mm vinyl tile", MaterialCategory.FINISHING_MATERIALS, 0.17, 1600.0, 1200.0, 0.0032, 0.1, 0.5, 4.0, 0.90),
229
- Material("T04 25mm vinyl tile", MaterialCategory.FINISHING_MATERIALS, 0.17, 1600.0, 1200.0, 0.0064, 0.1, 0.5, 8.0, 0.90),
230
- Material("T05 13mm carpet", MaterialCategory.FINISHING_MATERIALS, 0.06, 100.0, 1300.0, 0.0127, 0.8, 0.7, 25.0, 0.90),
231
- Material("T06 25mm carpet", MaterialCategory.FINISHING_MATERIALS, 0.06, 100.0, 1300.0, 0.0254, 0.8, 0.7, 50.0, 0.90),
232
- Material("W01 13mm wood", MaterialCategory.FINISHING_MATERIALS, 0.15, 608.0, 1630.0, 0.0127, 0.3, 0.5, 7.7, 0.925),
233
- Material("W02 19mm wood", MaterialCategory.FINISHING_MATERIALS, 0.15, 608.0, 1630.0, 0.0191, 0.3, 0.5, 11.6, 0.925),
234
- Material("W03 25mm wood", MaterialCategory.FINISHING_MATERIALS, 0.15, 608.0, 1630.0, 0.0254, 0.3, 0.5, 15.4, 0.925),
235
- Material("W04 13mm wood fiberboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.04, 240.0, 1300.0, 0.0127, 0.2, 0.5, 3.0, 0.90),
236
- Material("W05 19mm wood fiberboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.04, 240.0, 1300.0, 0.0191, 0.2, 0.5, 4.5, 0.90),
237
- Material("W06 25mm wood fiberboard", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.04, 240.0, 1300.0, 0.0254, 0.2, 0.5, 6.0, 0.90),
238
- Material("W07 13mm wood sheathing", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 540.0, 1210.0, 0.0127, 0.2, 0.5, 6.5, 0.90),
239
- Material("W08 19mm wood sheathing", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 540.0, 1210.0, 0.0191, 0.2, 0.5, 9.7, 0.90),
240
- Material("W09 25mm wood sheathing", MaterialCategory.SUB_STRUCTURAL_MATERIALS, 0.12, 540.0, 1210.0, 0.0254, 0.2, 0.5, 13.0, 0.90),
241
- Material("W10 13mm wood paneling", MaterialCategory.FINISHING_MATERIALS, 0.15, 608.0, 1630.0, 0.0127, 0.3, 0.5, 7.7, 0.925),
242
- Material("W11 19mm wood paneling", MaterialCategory.FINISHING_MATERIALS, 0.15, 608.0, 1630.0, 0.0191, 0.3, 0.5, 11.6, 0.925),
243
- Material("W12 25mm wood paneling", MaterialCategory.FINISHING_MATERIALS, 0.15, 608.0, 1630.0, 0.0254, 0.3, 0.5, 15.4, 0.925),
244
- Material("Z99 Other", MaterialCategory.FINISHING_MATERIALS, 0.1, 100.0, 1000.0, 0.01, 0.1, 0.5, 1.0, 0.9),
245
-
246
- # Door Materials (moved from separate section, prefixed with 'D')
247
- Material("D01 Wood Door - Solid Core", MaterialCategory.DOOR_MATERIALS, 0.14, 700.0, 1700.0, 0.045, 0.25, 0.5, 80.0, 0.90),
248
- Material("D02 Wood Door - Hollow Core", MaterialCategory.DOOR_MATERIALS, 0.08, 200.0, 1500.0, 0.035, 0.15, 0.5, 50.0, 0.90),
249
- Material("D03 Steel Door - Insulated", MaterialCategory.DOOR_MATERIALS, 45.0, 7850.0, 500.0, 0.045, 2.0, 0.7, 120.0, 0.15),
250
- Material("D04 Steel Door - Non-insulated", MaterialCategory.DOOR_MATERIALS, 45.0, 7850.0, 500.0, 0.035, 1.8, 0.7, 90.0, 0.15),
251
- Material("D05 Fiberglass Door", MaterialCategory.DOOR_MATERIALS, 0.04, 150.0, 1200.0, 0.045, 0.3, 0.6, 100.0, 0.85),
252
- Material("D06 Aluminum Door - Insulated", MaterialCategory.DOOR_MATERIALS, 205.0, 2700.0, 900.0, 0.045, 1.5, 0.8, 150.0, 0.05),
253
- Material("D07 Aluminum Door - Non-insulated", MaterialCategory.DOOR_MATERIALS, 205.0, 2700.0, 900.0, 0.035, 1.2, 0.8, 110.0, 0.05),
254
- Material("D08 Glass Door - Single Pane", MaterialCategory.DOOR_MATERIALS, 1.0, 2500.0, 750.0, 0.006, 0.5, 0.1, 70.0, 0.84),
255
- Material("D09 Glass Door - Double Pane", MaterialCategory.DOOR_MATERIALS, 1.0, 2500.0, 750.0, 0.012, 1.0, 0.1, 140.0, 0.84),
256
- Material("D10 PVC Door", MaterialCategory.DOOR_MATERIALS, 0.17, 1400.0, 1000.0, 0.04, 0.4, 0.5, 75.0, 0.90),
257
- Material("D11 Composite Door", MaterialCategory.DOOR_MATERIALS, 0.06, 300.0, 1300.0, 0.045, 0.35, 0.6, 95.0, 0.88),
258
- Material("D12 Plywood Door", MaterialCategory.DOOR_MATERIALS, 0.12, 540.0, 1210.0, 0.0254, 0.2, 0.5, 13.0, 0.90), # Reusing P03
259
- Material("D13 Particle Board Door", MaterialCategory.DOOR_MATERIALS, 0.12, 640.0, 1300.0, 0.0254, 0.2, 0.5, 11.0, 0.90), # Reusing P06
260
- Material("D14 Hardboard Door", MaterialCategory.DOOR_MATERIALS, 0.12, 960.0, 1300.0, 0.0254, 0.2, 0.5, 14.0, 0.90), # Reusing P09
261
- Material("D15 Fibreboard Door", MaterialCategory.DOOR_MATERIALS, 0.04, 240.0, 1300.0, 0.0254, 0.2, 0.5, 6.0, 0.90), # Reusing P12
262
  ]
263
- return {m.name: m for m in materials}
264
 
265
  def initialize_constructions(self) -> Dict[str, Construction]:
266
- # This method defines a set of standard building constructions.
267
- # Each construction is composed of layers of materials defined in initialize_materials().
268
- materials = self.library_materials
269
  constructions = [
270
- # Walls
271
- Construction("Wall-100mm Brick-13mm Gypsum", "Wall", [
272
- {"material": materials["M01 100mm brick"], "thickness": 0.1016},
273
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
 
274
  ]),
275
- Construction("Wall-100mm Concrete Block-13mm Cement Plaster", "Wall", [
276
- {"material": materials["M08 100mm concrete block"], "thickness": 0.1016},
277
- {"material": materials["F13 13mm cement plaster"], "thickness": 0.0127}
 
278
  ]),
279
- Construction("Wall-200mm Concrete Block-25mm Stucco", "Wall", [
280
- {"material": materials["M04 200mm concrete block"], "thickness": 0.2032},
281
- {"material": materials["F07 25mm stucco"], "thickness": 0.0254}
 
282
  ]),
283
- Construction("Wall-EIFS-13mm Gypsum", "Wall", [
284
- {"material": materials["F06 EIFS finish"], "thickness": 0.0095},
285
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
 
 
286
  ]),
287
- Construction("Wall-Wood Siding-19mm Plywood-13mm Gypsum", "Wall", [
288
- {"material": materials["S08 19mm wood siding"], "thickness": 0.0191},
289
- {"material": materials["P02 19mm plywood"], "thickness": 0.0191},
290
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
291
  ]),
292
- Construction("Wall-Metal-13mm Gypsum", "Wall", [
293
- {"material": materials["F08 Metal surface"], "thickness": 0.001},
294
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
 
295
  ]),
296
- Construction("Wall-Brick Veneer-Air Space-13mm Gypsum", "Wall", [
297
- {"material": materials["S10 13mm brick veneer"], "thickness": 0.0127},
298
- {"material": materials["F04 Wall air space resistance"], "thickness": 0.01}, # Represents air gap
299
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
 
 
300
  ]),
301
- Construction("Wall-200mm Heavy Concrete-13mm Gypsum", "Wall", [
302
- {"material": materials["M27 200mm concrete slab (heavy)"], "thickness": 0.2032},
303
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
 
304
  ]),
305
- Construction("Wall-200mm Lightweight Concrete-13mm Gypsum", "Wall", [
306
- {"material": materials["M22 200mm lightweight concrete slab"], "thickness": 0.2032},
307
- {"material": materials["F10 13mm gypsum board"], "thickness": 0.0127}
 
308
  ]),
 
 
309
 
310
- # Roofs
311
- Construction("Roof-Asphalt Shingle-19mm Plywood-50mm Insulation", "Roof", [
312
- {"material": materials["S15 13mm asphalt shingle"], "thickness": 0.0127},
313
- {"material": materials["P02 19mm plywood"], "thickness": 0.0191},
314
- {"material": materials["I02 50mm insulation board"], "thickness": 0.0508}
315
- ]),
316
- Construction("Roof-Metal-75mm Insulation", "Roof", [
317
- {"material": materials["F08 Metal surface"], "thickness": 0.001},
318
- {"material": materials["I03 75mm insulation board"], "thickness": 0.0762}
319
- ]),
320
- Construction("Roof-Concrete Slab 150mm-25mm Insulation", "Roof", [
321
- {"material": materials["M14 150mm concrete slab"], "thickness": 0.1524},
322
- {"material": materials["I01 25mm insulation board"], "thickness": 0.0254}
323
- ]),
324
- Construction("Roof-Acoustic Tile-Air Space", "Roof", [
325
- {"material": materials["F16 Acoustic tile"], "thickness": 0.0191},
326
- {"material": materials["F05 Ceiling air space resistance"], "thickness": 0.01} # Represents ceiling air space
327
- ]),
328
 
329
- # Floors
330
- Construction("Floor-100mm Concrete Slab-13mm Ceramic Tile", "Floor", [
331
- {"material": materials["M13 100mm concrete slab"], "thickness": 0.1016},
332
- {"material": materials["T01 13mm ceramic tile"], "thickness": 0.0127}
333
- ]),
334
- Construction("Floor-150mm Concrete Slab-13mm Vinyl Tile", "Floor", [
335
- {"material": materials["M14 150mm concrete slab"], "thickness": 0.1524},
336
- {"material": materials["T03 13mm vinyl tile"], "thickness": 0.0032}
337
- ]),
338
- Construction("Floor-Wood-13mm Carpet", "Floor", [
339
- {"material": materials["G05 25mm wood"], "thickness": 0.0254}, # Using 25mm wood as subfloor
340
- {"material": materials["T05 13mm carpet"], "thickness": 0.0127}
341
- ]),
342
- Construction("Floor-200mm Lightweight Concrete-25mm Carpet", "Floor", [
343
- {"material": materials["M22 200mm lightweight concrete slab"], "thickness": 0.2032},
344
- {"material": materials["T06 25mm carpet"], "thickness": 0.0254}
345
- ]),
346
 
347
- # Doors (using materials from the main materials dictionary)
348
- Construction("Door-Solid Wood", "Door", [
349
- {"material": materials["D01 Wood Door - Solid Core"], "thickness": materials["D01 Wood Door - Solid Core"].default_thickness}
350
- ]),
351
- Construction("Door-Hollow Wood", "Door", [
352
- {"material": materials["D02 Wood Door - Hollow Core"], "thickness": materials["D02 Wood Door - Hollow Core"].default_thickness}
353
- ]),
354
- Construction("Door-Insulated Steel", "Door", [
355
- {"material": materials["D03 Steel Door - Insulated"], "thickness": materials["D03 Steel Door - Insulated"].default_thickness}
356
- ]),
357
- Construction("Door-Fiberglass", "Door", [
358
- {"material": materials["D05 Fiberglass Door"], "thickness": materials["D05 Fiberglass Door"].default_thickness}
359
- ]),
360
- Construction("Door-Single Pane Glass", "Door", [
361
- {"material": materials["D08 Glass Door - Single Pane"], "thickness": materials["D08 Glass Door - Single Pane"].default_thickness}
362
- ]),
363
- Construction("Door-Double Pane Glass", "Door", [
364
- {"material": materials["D09 Glass Door - Double Pane"], "thickness": materials["D09 Glass Door - Double Pane"].default_thickness}
365
- ]),
366
- Construction("Door-PVC", "Door", [
367
- {"material": materials["D10 PVC Door"], "thickness": materials["D10 PVC Door"].default_thickness}
368
- ]),
369
- Construction("Door-Composite", "Door", [
370
- {"material": materials["D11 Composite Door"], "thickness": materials["D11 Composite Door"].default_thickness}
371
- ]),
372
- ]
373
- return {c.name: c for c in constructions}
374
 
375
- def initialize_glazing_materials(self) -> Dict[str, GlazingMaterial]:
376
- # This method defines a set of standard glazing materials.
377
- glazing_materials = [
378
- GlazingMaterial("Single Pane Clear", 5.8, 0.81, 15.0, 20.0, 25.0),
379
- GlazingMaterial("Double Pane Clear (6mm air)", 2.8, 0.76, 30.0, 40.0, 25.0),
380
- GlazingMaterial("Double Pane Low-E (6mm air)", 1.8, 0.5, 35.0, 60.0, 25.0),
381
- GlazingMaterial("Triple Pane Clear (6mm air)", 1.8, 0.7, 45.0, 70.0, 25.0),
382
- GlazingMaterial("Triple Pane Low-E (6mm air)", 1.0, 0.4, 50.0, 90.0, 25.0),
383
- ]
384
- return {gm.name: gm for gm in glazing_materials}
385
 
386
- def get_all_materials(self, project_materials: Optional[Dict[str, Material]] = None) -> Dict[str, Material]:
387
- # Combines library materials with any project-specific materials.
388
- all_materials = self.library_materials.copy()
389
- if project_materials:
390
- all_materials.update(project_materials)
391
- return all_materials
392
-
393
- def get_all_constructions(self, project_constructions: Optional[Dict[str, Construction]] = None) -> Dict[str, Construction]:
394
- # Combines library constructions with any project-specific constructions.
395
- all_constructions = self.library_constructions.copy()
396
- if project_constructions:
397
- all_constructions.update(project_constructions)
398
- return all_constructions
399
-
400
- def get_all_glazing_materials(self, project_glazing_materials: Optional[Dict[str, GlazingMaterial]] = None) -> Dict[str, GlazingMaterial]:
401
- # Combines library glazing materials with any project-specific glazing materials.
402
- all_glazing_materials = self.library_glazing_materials.copy()
403
- if project_glazing_materials:
404
- all_glazing_materials.update(project_glazing_materials)
405
- return all_glazing_materials
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
 
407
- def to_dataframe(self, data_type: str, only_project: bool = False,
408
- project_materials: Optional[Dict[str, Material]] = None,
 
 
 
 
 
 
 
 
 
 
409
  project_constructions: Optional[Dict[str, Construction]] = None,
410
- project_glazing_materials: Optional[Dict[str, GlazingMaterial]] = None) -> pd.DataFrame:
411
- # Converts library data (or project data) into a pandas DataFrame for easy viewing and analysis.
 
412
  if data_type == "materials":
413
  data = []
414
- materials = project_materials.values() if only_project else self.get_all_materials(project_materials).values()
415
  for mat in materials:
416
  data.append({
417
  "Name": mat.name,
418
  "Category": mat.category.value,
419
- "U-Value (W/m路K)": mat.u_value, # This is conductivity (k)
420
  "Density (kg/m鲁)": mat.density,
421
  "Specific Heat (J/kg路K)": mat.specific_heat,
422
  "Default Thickness (m)": mat.default_thickness,
423
  "Embodied Carbon (kgCO鈧俥/kg)": mat.embodied_carbon,
424
- "Solar Absorptance": mat.solar_absorptance,
425
  "Price (USD/m虏)": mat.price,
426
  "Emissivity": mat.emissivity,
427
  "Source": "Project" if not mat.is_library else "Library"
@@ -429,23 +472,26 @@ class MaterialLibrary:
429
  return pd.DataFrame(data)
430
  elif data_type == "constructions":
431
  data = []
432
- constructions = project_constructions.values() if only_project else self.get_all_constructions(project_constructions).values()
433
- for const in constructions:
 
 
 
434
  data.append({
435
- "Name": const.name,
436
- "Component Type": const.component_type,
437
- "U-Value (W/m虏路K)": const.u_value,
438
- "Total Thickness (m)": const.total_thickness,
439
- "Embodied Carbon (kgCO鈧俥)": const.embodied_carbon,
440
- "Solar Absorptance": const.solar_absorptance,
441
- "Price (USD)": const.price,
442
- "Thermal Mass": const.get_thermal_mass().value,
443
- "Source": "Project" if not const.is_library else "Library"
444
  })
445
  return pd.DataFrame(data)
446
  elif data_type == "glazing_materials":
447
  data = []
448
- glazing_materials = project_glazing_materials.values() if only_project else self.get_all_glazing_materials(project_glazing_materials).values()
449
  for mat in glazing_materials:
450
  data.append({
451
  "Name": mat.name,
@@ -457,5 +503,23 @@ class MaterialLibrary:
457
  "Source": "Project" if not mat.is_library else "Library"
458
  })
459
  return pd.DataFrame(data)
460
- else:
461
- raise ValueError("Invalid data_type specified. Choose 'materials', 'constructions', or 'glazing_materials'.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  Material Library for HVAC Load Calculator
3
+ 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.
4
+ 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.
5
+ Updated 2025-05-17: Added all materials and constructions from ASHRAE_2005_HOF_Materials.idf with Australian-specific embodied carbon and prices.
6
+ Updated 2025-05-17: Added GlazingMaterial and DoorMaterial classes, included library_glazing_materials and library_door_materials with comprehensive data.
 
 
 
 
 
 
 
 
 
 
 
7
  Updated 2025-05-16: Removed mass_per_meter, added thermal_mass method, updated CSV handling.
8
  Updated 2025-05-16: Fixed U-value calculation in Material.get_u_value.
9
  Updated 2025-05-16: Updated MaterialCategory to Finishing Materials, Structural Materials, Sub-Structural Materials, Insulation.
 
22
  STRUCTURAL_MATERIALS = "Structural Materials"
23
  SUB_STRUCTURAL_MATERIALS = "Sub-Structural Materials"
24
  INSULATION = "Insulation"
 
25
 
26
  class ThermalMass(Enum):
27
  HIGH = "High"
 
30
  NO_MASS = "No Mass"
31
 
32
  class Material:
33
+ def __init__(self, name: str, category: MaterialCategory, conductivity: float, density: float,
34
  specific_heat: float, default_thickness: float, embodied_carbon: float,
35
+ solar_absorption: float, price: float, emissivity: float, is_library: bool = True):
36
  self.name = name
37
  self.category = category
38
+ self.conductivity = max(0.01, conductivity) # W/m路K
 
 
39
  self.density = max(1.0, density) # kg/m鲁
40
  self.specific_heat = max(100.0, specific_heat) # J/kg路K
41
  self.default_thickness = max(0.01, default_thickness) # m
42
  self.embodied_carbon = max(0.0, embodied_carbon) # kgCO鈧俥/kg
43
+ self.solar_absorption = min(max(0.0, solar_absorption), 1.0)
44
+ self.price = max(0.0, price) # USD/m虏
45
  self.emissivity = min(max(0.0, emissivity), 1.0) # Dimensionless
46
  self.is_library = is_library
47
 
48
  def get_thermal_mass(self) -> ThermalMass:
 
49
  if self.density < 100.0 or self.specific_heat < 800.0:
50
  return ThermalMass.NO_MASS
51
  elif self.density > 2000.0 and self.specific_heat > 800.0:
 
56
  return ThermalMass.LOW
57
 
58
  def get_u_value(self) -> float:
59
+ return self.conductivity / self.default_thickness if self.default_thickness > 0 else 0.1
 
 
60
 
61
  class GlazingMaterial:
62
  def __init__(self, name: str, u_value: float, shgc: float, embodied_carbon: float, price: float, h_o: float, is_library: bool = True):
63
  self.name = name
64
+ self.u_value = max(0.1, u_value) # W/m虏路K
65
+ self.shgc = min(max(0.0, shgc), 1.0) # Solar Heat Gain Coefficient
66
  self.embodied_carbon = max(0.0, embodied_carbon) # kgCO鈧俥/m虏
67
  self.price = max(0.0, price) # USD/m虏
68
  self.h_o = max(0.0, h_o) # W/m虏路K, exterior convective heat transfer coefficient
69
  self.is_library = is_library
70
 
71
+ class DoorMaterial:
72
+ def __init__(self, name: str, category: MaterialCategory, conductivity: float, density: float,
73
+ specific_heat: float, default_thickness: float, u_value: float, solar_absorption: float,
74
+ embodied_carbon: float, price: float, emissivity: float, is_library: bool = True):
75
+ self.name = name
76
+ self.category = category
77
+ self.conductivity = max(0.01, conductivity) # W/m路K
78
+ self.density = max(1.0, density) # kg/m鲁
79
+ self.specific_heat = max(100.0, specific_heat) # J/kg路K
80
+ self.default_thickness = max(0.01, default_thickness) # m
81
+ self.u_value = max(0.1, u_value) # W/m虏路K
82
+ self.solar_absorption = min(max(0.0, solar_absorption), 1.0)
83
+ self.embodied_carbon = max(0.0, embodied_carbon) # kgCO鈧俥/m虏
84
+ self.price = max(0.0, price) # USD/m虏
85
+ self.emissivity = min(max(0.0, emissivity), 1.0) # Dimensionless
86
+ self.is_library = is_library
87
+
88
  class Construction:
89
  def __init__(self, name: str, component_type: str, layers: List[Dict], is_library: bool = True):
90
  self.name = name
91
  self.component_type = component_type
92
  self.layers = layers or []
93
  self.is_library = is_library
 
94
  self.u_value = self.calculate_u_value()
95
  self.total_thickness = sum(layer["thickness"] for layer in self.layers)
 
96
  self.embodied_carbon = sum(layer["material"].embodied_carbon * layer["material"].density * layer["thickness"]
97
  for layer in self.layers)
98
+ self.solar_absorption = max(layer["material"].solar_absorption for layer in self.layers) if self.layers else 0.6
 
 
 
99
  self.price = sum(layer["material"].price * layer["thickness"] / layer["material"].default_thickness
100
  for layer in self.layers)
101
 
102
  def calculate_u_value(self) -> float:
 
103
  if not self.layers:
104
+ return 0.1
105
+ r_total = sum(layer["thickness"] / layer["material"].conductivity for layer in self.layers)
106
+ return 1 / r_total if r_total > 0 else 0.1
 
107
 
108
  def get_thermal_mass(self) -> ThermalMass:
 
109
  if not self.layers:
110
  return ThermalMass.NO_MASS
111
  total_thickness = self.total_thickness
112
  if total_thickness == 0:
113
  return ThermalMass.NO_MASS
 
114
  avg_density = sum(layer["material"].density * layer["thickness"] for layer in self.layers) / total_thickness
115
  avg_specific_heat = sum(layer["material"].specific_heat * layer["thickness"] for layer in self.layers) / total_thickness
 
116
  if avg_density < 100.0 or avg_specific_heat < 800.0:
117
  return ThermalMass.NO_MASS
118
  elif avg_density > 2000.0 and avg_specific_heat > 800.0:
 
127
  self.library_materials = self.initialize_materials()
128
  self.library_constructions = self.initialize_constructions()
129
  self.library_glazing_materials = self.initialize_glazing_materials()
130
+ self.library_door_materials = self.initialize_door_materials()
131
 
132
  def initialize_materials(self) -> Dict[str, Material]:
133
  materials = [
 
162
  Material("M03 150mm brick", MaterialCategory.STRUCTURAL_MATERIALS, 0.89, 1920.0, 790.0, 0.1524, 0.3, 0.7, 29.3, 0.93),
163
  Material("M04 200mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.2032, 0.2, 0.65, 13.0, 0.925),
164
  Material("M05 200mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.2032, 0.2, 0.65, 20.8, 0.925),
165
+ Material("M06 150mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.1524, 0.2, 0.65, 9.8, 0.925),
166
+ Material("M07 100mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 0.51, 800.0, 920.0, 0.1016, 0.2, 0.65, 6.5, 0.925),
167
+ Material("M08 150mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.1524, 0.2, 0.65, 15.6, 0.925),
168
+ Material("M09 100mm concrete block", MaterialCategory.STRUCTURAL_MATERIALS, 1.11, 1280.0, 920.0, 0.1016, 0.2, 0.65, 10.4, 0.925),
169
+ Material("M10 100mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.1016, 0.15, 0.65, 7.8, 0.925),
170
+ Material("M11 100mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.1016, 0.15, 0.65, 7.8, 0.925),
171
+ Material("M12 150mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.1524, 0.15, 0.65, 11.7, 0.925),
172
+ Material("M13 200mm lightweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 0.53, 1280.0, 840.0, 0.2032, 0.15, 0.65, 15.6, 0.925),
173
+ Material("M14 100mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.1016, 0.2, 0.65, 18.2, 0.925),
174
+ Material("M14a 100mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.1016, 0.2, 0.65, 18.2, 0.925),
175
+ Material("M15 200mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.2032, 0.2, 0.65, 36.4, 0.925),
176
+ Material("M16 300mm heavyweight concrete", MaterialCategory.STRUCTURAL_MATERIALS, 1.95, 2240.0, 900.0, 0.3048, 0.2, 0.65, 54.6, 0.925),
177
+ Material("M17 100mm stone", MaterialCategory.STRUCTURAL_MATERIALS, 2.10, 2240.0, 880.0, 0.1016, 0.2, 0.7, 22.8, 0.925),
178
+ Material("M18 150mm stone", MaterialCategory.STRUCTURAL_MATERIALS, 2.10, 2240.0, 880.0, 0.1524, 0.2, 0.7, 34.1, 0.925),
179
+ Material("M19 100mm limestone", MaterialCategory.STRUCTURAL_MATERIALS, 1.80, 2320.0, 880.0, 0.1016, 0.2, 0.6, 23.6, 0.925),
180
+ Material("M20 150mm limestone", MaterialCategory.STRUCTURAL_MATERIALS, 1.80, 2320.0, 880.0, 0.1524, 0.2, 0.6, 35.4, 0.925),
181
+ Material("M21 200mm limestone", MaterialCategory.STRUCTURAL_MATERIALS, 1.80, 2320.0, 880.0, 0.2032, 0.2, 0.6, 47.1, 0.925),
182
+ Material("M22 100mm granite", MaterialCategory.STRUCTURAL_MATERIALS, 2.80, 2640.0, 880.0, 0.1016, 0.2, 0.7, 26.8, 0.925),
183
+ Material("M23 150mm granite", MaterialCategory.STRUCTURAL_MATERIALS, 2.80, 2640.0, 880.0, 0.1524, 0.2, 0.7, 40.2, 0.925),
184
+ Material("M24 200mm granite", MaterialCategory.STRUCTURAL_MATERIALS, 2.80, 2640.0, 880.0, 0.2032, 0.2, 0.7, 53.6, 0.925),
185
+ Material("M25 100mm marble", MaterialCategory.STRUCTURAL_MATERIALS, 2.50, 2720.0, 880.0, 0.1016, 0.2, 0.6, 27.6, 0.925),
186
+ Material("M26 150mm marble", MaterialCategory.STRUCTURAL_MATERIALS, 2.50, 2720.0, 880.0, 0.1524, 0.2, 0.6, 41.4, 0.925),
187
+ Material("M27 200mm marble", MaterialCategory.STRUCTURAL_MATERIALS, 2.50, 2720.0, 880.0, 0.2032, 0.2, 0.6, 55.3, 0.925),
188
+ ]
189
+ return {mat.name: mat for mat in materials}
190
+
191
+ def initialize_glazing_materials(self) -> Dict[str, GlazingMaterial]:
192
+ glazing_materials = [
193
+ # ASHRAE-based glazing materials with Australian pricing
194
+ GlazingMaterial("Single Clear 3mm", 5.8, 0.81, 25.0, 50.0, 17.8),
195
+ GlazingMaterial("Single Clear 6mm", 5.7, 0.78, 28.0, 60.0, 17.8),
196
+ GlazingMaterial("Single Tinted 6mm", 5.7, 0.55, 30.0, 70.0, 17.8),
197
+ GlazingMaterial("Double Clear 6mm/13mm Air", 2.7, 0.70, 40.0, 100.0, 17.8),
198
+ GlazingMaterial("Double Low-E 6mm/13mm Air", 1.8, 0.60, 45.0, 120.0, 17.8),
199
+ GlazingMaterial("Double Tinted 6mm/13mm Air", 2.7, 0.45, 42.0, 110.0, 17.8),
200
+ GlazingMaterial("Double Low-E 6mm/13mm Argon", 1.5, 0.55, 48.0, 130.0, 17.8),
201
+ GlazingMaterial("Triple Clear 4mm/12mm Air", 1.8, 0.62, 55.0, 150.0, 17.8),
202
+ GlazingMaterial("Triple Low-E 4mm/12mm Argon", 0.9, 0.50, 60.0, 180.0, 17.8),
203
+ GlazingMaterial("Single Low-E Reflective 6mm", 5.6, 0.35, 35.0, 90.0, 13.0),
204
+ GlazingMaterial("Double Reflective 6mm/13mm Air", 2.5, 0.30, 50.0, 140.0, 13.0),
205
+ GlazingMaterial("Electrochromic 6mm/13mm Air", 2.0, 0.40, 70.0, 200.0, 17.8),
206
+ ]
207
+ return {mat.name: mat for mat in glazing_materials}
208
+
209
+ def initialize_door_materials(self) -> Dict[str, DoorMaterial]:
210
+ door_materials = [
211
+ # Door materials with ASHRAE-based properties, Australian pricing, and inferred properties
212
+ 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),
213
+ 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),
214
+ 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),
215
+ 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),
216
+ 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),
217
+ 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),
218
+ 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),
219
+ 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),
220
+ 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),
221
+ DoorMaterial("Fiberglass Insulated 50mm", MaterialCategory.INSULATION, 0.03, 43.0, 1210.0, 0.050, 1.5, 0.60, 20.0, 230.0, 0.925),
222
+ DoorMaterial("PVC Insulated 50mm", MaterialCategory.INSULATION, 0.05, 1400.0, 1000.0, 0.050, 1.7, 0.55, 18.0, 210.0, 0.925),
223
+ 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),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  ]
225
+ return {mat.name: mat for mat in door_materials}
226
 
227
  def initialize_constructions(self) -> Dict[str, Construction]:
 
 
 
228
  constructions = [
229
+ Construction("Light Exterior Wall", "Wall", [
230
+ {"material": self.library_materials["F08 Metal surface"], "thickness": 0.001},
231
+ {"material": self.library_materials["I02 50mm insulation board"], "thickness": 0.0508},
232
+ {"material": self.library_materials["F04 Wall air space resistance"], "thickness": 0.01},
233
+ {"material": self.library_materials["G01a 19mm gypsum board"], "thickness": 0.0191}
234
  ]),
235
+ Construction("Light Roof/Ceiling", "Roof", [
236
+ {"material": self.library_materials["M11 100mm lightweight concrete"], "thickness": 0.1016},
237
+ {"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
238
+ {"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191}
239
  ]),
240
+ Construction("Light Floor", "Floor", [
241
+ {"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191},
242
+ {"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
243
+ {"material": self.library_materials["M11 100mm lightweight concrete"], "thickness": 0.1016}
244
  ]),
245
+ Construction("Medium Exterior Wall", "Wall", [
246
+ {"material": self.library_materials["M01 100mm brick"], "thickness": 0.1016},
247
+ {"material": self.library_materials["I02 50mm insulation board"], "thickness": 0.0508},
248
+ {"material": self.library_materials["F04 Wall air space resistance"], "thickness": 0.01},
249
+ {"material": self.library_materials["G01a 19mm gypsum board"], "thickness": 0.0191}
250
  ]),
251
+ Construction("Medium Roof/Ceiling", "Roof", [
252
+ {"material": self.library_materials["M14a 100mm heavyweight concrete"], "thickness": 0.1016},
253
+ {"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
254
+ {"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191}
255
  ]),
256
+ Construction("Medium Floor", "Floor", [
257
+ {"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191},
258
+ {"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
259
+ {"material": self.library_materials["M14a 100mm heavyweight concrete"], "thickness": 0.1016}
260
  ]),
261
+ Construction("Heavy Exterior Wall", "Wall", [
262
+ {"material": self.library_materials["M01 100mm brick"], "thickness": 0.1016},
263
+ {"material": self.library_materials["M15 200mm heavyweight concrete"], "thickness": 0.2032},
264
+ {"material": self.library_materials["I02 50mm insulation board"], "thickness": 0.0508},
265
+ {"material": self.library_materials["F04 Wall air space resistance"], "thickness": 0.01},
266
+ {"material": self.library_materials["G01a 19mm gypsum board"], "thickness": 0.0191}
267
  ]),
268
+ Construction("Heavy Roof/Ceiling", "Roof", [
269
+ {"material": self.library_materials["M15 200mm heavyweight concrete"], "thickness": 0.2032},
270
+ {"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
271
+ {"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191}
272
  ]),
273
+ Construction("Heavy Floor", "Floor", [
274
+ {"material": self.library_materials["F16 Acoustic tile"], "thickness": 0.0191},
275
+ {"material": self.library_materials["F05 Ceiling air space resistance"], "thickness": 0.01},
276
+ {"material": self.library_materials["M15 200mm heavyweight concrete"], "thickness": 0.2032}
277
  ]),
278
+ ]
279
+ return {cons.name: cons for cons in constructions}
280
 
281
+ def get_all_materials(self, project_materials: Optional[Dict[str, Material]] = None) -> List[Material]:
282
+ materials = list(self.library_materials.values())
283
+ if project_materials:
284
+ materials.extend(list(project_materials.values()))
285
+ return materials
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
+ def get_all_glazing_materials(self, project_glazing_materials: Optional[Dict[str, GlazingMaterial]] = None) -> List[GlazingMaterial]:
288
+ materials = list(self.library_glazing_materials.values())
289
+ if project_glazing_materials:
290
+ materials.extend(list(project_glazing_materials.values()))
291
+ return materials
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
+ def get_all_door_materials(self, project_door_materials: Optional[Dict[str, DoorMaterial]] = None) -> List[DoorMaterial]:
294
+ materials = list(self.library_door_materials.values())
295
+ if project_door_materials:
296
+ materials.extend(list(project_door_materials.values()))
297
+ return materials
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
+ def add_project_material(self, material: Material, project_materials: Dict[str, Material]) -> Tuple[bool, str]:
300
+ if len(project_materials) >= 20:
301
+ return False, "Maximum 20 project materials allowed."
302
+ if material.name in project_materials or material.name in self.library_materials:
303
+ return False, f"Material name '{material.name}' already exists."
304
+ project_materials[material.name] = material
305
+ return True, f"Material '{material.name}' added to project materials."
 
 
 
306
 
307
+ def add_project_glazing_material(self, material: GlazingMaterial, project_glazing_materials: Dict[str, GlazingMaterial]) -> Tuple[bool, str]:
308
+ if len(project_glazing_materials) >= 20:
309
+ return False, "Maximum 20 project glazing materials allowed."
310
+ if material.name in project_glazing_materials or material.name in self.library_glazing_materials:
311
+ return False, f"Glazing material name '{material.name}' already exists."
312
+ project_glazing_materials[material.name] = material
313
+ return True, f"Glazing material '{material.name}' added to project glazing materials."
314
+
315
+ def add_project_door_material(self, material: DoorMaterial, project_door_materials: Dict[str, DoorMaterial]) -> Tuple[bool, str]:
316
+ if len(project_door_materials) >= 20:
317
+ return False, "Maximum 20 project door materials allowed."
318
+ if material.name in project_door_materials or material.name in self.library_door_materials:
319
+ return False, f"Door material name '{material.name}' already exists."
320
+ project_door_materials[material.name] = material
321
+ return True, f"Door material '{material.name}' added to project door materials."
322
+
323
+ def edit_project_material(self, old_name: str, new_material: Material, project_materials: Dict[str, Material],
324
+ components: Dict[str, List]) -> Tuple[bool, str]:
325
+ if old_name not in project_materials:
326
+ return False, f"Material '{old_name}' not found in project materials."
327
+ if new_material.name != old_name and (new_material.name in project_materials or new_material.name in self.library_materials):
328
+ return False, f"Material name '{new_material.name}' already exists."
329
+ for comp_list in components.values():
330
+ for comp in comp_list:
331
+ if comp.construction and any(layer["material"].name == old_name for layer in comp.layers):
332
+ comp.layers = [{"material": new_material if layer["material"].name == old_name else layer["material"],
333
+ "thickness": layer["thickness"]} for layer in comp.layers]
334
+ comp.construction = Construction(
335
+ name=comp.construction.name,
336
+ component_type=comp.construction.component_type,
337
+ layers=comp.layers,
338
+ is_library=comp.construction.is_library
339
+ )
340
+ project_materials.pop(old_name)
341
+ project_materials[new_material.name] = new_material
342
+ return True, f"Material '{old_name}' updated to '{new_material.name}'."
343
+
344
+ def edit_project_glazing_material(self, old_name: str, new_material: GlazingMaterial,
345
+ project_glazing_materials: Dict[str, GlazingMaterial],
346
+ components: Dict[str, List]) -> Tuple[bool, str]:
347
+ if old_name not in project_glazing_materials:
348
+ return False, f"Glazing material '{old_name}' not found in project glazing materials."
349
+ if new_material.name != old_name and (new_material.name in project_glazing_materials or new_material.name in self.library_glazing_materials):
350
+ return False, f"Glazing material name '{new_material.name}' already exists."
351
+ for comp_list in components.values():
352
+ for comp in comp_list:
353
+ if comp.glazing_material and comp.glazing_material.name == old_name:
354
+ comp.glazing_material = new_material
355
+ comp.u_value = new_material.u_value
356
+ comp.shgc = new_material.shgc
357
+ comp.h_o = new_material.h_o
358
+ project_glazing_materials.pop(old_name)
359
+ project_glazing_materials[new_material.name] = new_material
360
+ return True, f"Glazing material '{old_name}' updated to '{new_material.name}'."
361
+
362
+ def edit_project_door_material(self, old_name: str, new_material: DoorMaterial,
363
+ project_door_materials: Dict[str, DoorMaterial],
364
+ components: Dict[str, List]) -> Tuple[bool, str]:
365
+ if old_name not in project_door_materials:
366
+ return False, f"Door material '{old_name}' not found in project door materials."
367
+ if new_material.name != old_name and (new_material.name in project_door_materials or new_material.name in self.library_door_materials):
368
+ return False, f"Door material name '{new_material.name}' already exists."
369
+ for comp_list in components.values():
370
+ for comp in comp_list:
371
+ if comp.door_material and comp.door_material.name == old_name:
372
+ comp.door_material = new_material
373
+ comp.u_value = new_material.u_value
374
+ comp.solar_absorptivity = new_material.solar_absorption
375
+ project_door_materials.pop(old_name)
376
+ project_door_materials[new_material.name] = new_material
377
+ return True, f"Door material '{old_name}' updated to '{new_material.name}'."
378
+
379
+ def delete_project_material(self, name: str, project_materials: Dict[str, Material], components: Dict[str, List]) -> Tuple[bool, str]:
380
+ if name not in project_materials:
381
+ return False, f"Material '{name}' not found in project materials."
382
+ for cons in self.library_constructions.values():
383
+ if any(layer["material"].name == name for layer in cons.layers):
384
+ return False, f"Cannot delete '{name}' as it is used in library construction '{cons.name}'."
385
+ for comp_type, comp_list in components.items():
386
+ for comp in comp_list:
387
+ if 'layers' in comp and any(layer["material"].name == name for layer in comp["layers"]):
388
+ return False, f"Cannot delete '{name}' as it is used in component '{comp['name']}' ({comp_type})."
389
+ del project_materials[name]
390
+ return True, f"Material '{name}' deleted successfully."
391
+
392
+ def delete_project_glazing_material(self, name: str, project_glazing_materials: Dict[str, GlazingMaterial],
393
+ components: Dict[str, List]) -> Tuple[bool, str]:
394
+ if name not in project_glazing_materials:
395
+ return False, f"Glazing material '{name}' not found in project glazing materials."
396
+ for comp_list in components.values():
397
+ for comp in comp_list:
398
+ if comp.glazing_material and comp.glazing_material.name == name:
399
+ return False, f"Cannot delete '{name}' as it is used in component '{comp.name}'."
400
+ del project_glazing_materials[name]
401
+ return True, f"Glazing material '{name}' deleted successfully."
402
+
403
+ def delete_project_door_material(self, name: str, project_door_materials: Dict[str, DoorMaterial],
404
+ components: Dict[str, List]) -> Tuple[bool, str]:
405
+ if name not in project_door_materials:
406
+ return False, f"Door material '{name}' not found in project door materials."
407
+ for comp_list in components.values():
408
+ for comp in comp_list:
409
+ if comp.door_material and comp.door_material.name == name:
410
+ return False, f"Cannot delete '{name}' as it is used in component '{comp.name}'."
411
+ del project_door_materials[name]
412
+ return True, f"Door material '{name}' deleted successfully."
413
+
414
+ def add_project_construction(self, construction: Construction, project_constructions: Dict[str, Construction]) -> Tuple[bool, str]:
415
+ if len(project_constructions) >= 20:
416
+ return False, "Maximum 20 project constructions allowed."
417
+ if construction.name in project_constructions or construction.name in self.library_constructions:
418
+ return False, f"Construction name '{construction.name}' already exists."
419
+ project_constructions[construction.name] = construction
420
+ return True, f"Construction '{construction.name}' added to project constructions."
421
+
422
+ def edit_project_construction(self, old_name: str, new_construction: Construction,
423
+ project_constructions: Dict[str, Construction],
424
+ components: Dict[str, List]) -> Tuple[bool, str]:
425
+ if old_name not in project_constructions:
426
+ return False, f"Construction '{old_name}' not found in project constructions."
427
+ if new_construction.name != old_name and (new_construction.name in project_constructions or new_construction.name in self.library_constructions):
428
+ return False, f"Construction name '{new_construction.name}' already exists."
429
+ for comp_list in components.values():
430
+ for comp in comp_list:
431
+ if comp.construction and comp.construction.name == old_name:
432
+ comp.construction = new_construction
433
+ comp.layers = new_construction.layers
434
+ comp.u_value = new_construction.u_value
435
+ project_constructions.pop(old_name)
436
+ project_constructions[new_construction.name] = new_construction
437
+ return True, f"Construction '{old_name}' updated to '{new_construction.name}'."
438
 
439
+ def delete_project_construction(self, name: str, project_constructions: Dict[str, Construction],
440
+ components: Dict[str, List]) -> Tuple[bool, str]:
441
+ if name not in project_constructions:
442
+ return False, f"Construction '{name}' not found in project constructions."
443
+ for comp_list in components.values():
444
+ for comp in comp_list:
445
+ if comp.construction and comp.construction.name == name:
446
+ return False, f"Construction '{name}' is used in component '{comp.name}'."
447
+ project_constructions.pop(name)
448
+ return True, f"Construction '{name}' deleted."
449
+
450
+ def to_dataframe(self, data_type: str, project_materials: Optional[Dict[str, Material]] = None,
451
  project_constructions: Optional[Dict[str, Construction]] = None,
452
+ project_glazing_materials: Optional[Dict[str, GlazingMaterial]] = None,
453
+ project_door_materials: Optional[Dict[str, DoorMaterial]] = None,
454
+ only_project: bool = False) -> pd.DataFrame:
455
  if data_type == "materials":
456
  data = []
457
+ materials = project_materials.values() if only_project else self.get_all_materials(project_materials)
458
  for mat in materials:
459
  data.append({
460
  "Name": mat.name,
461
  "Category": mat.category.value,
462
+ "Conductivity (W/m路K)": mat.conductivity,
463
  "Density (kg/m鲁)": mat.density,
464
  "Specific Heat (J/kg路K)": mat.specific_heat,
465
  "Default Thickness (m)": mat.default_thickness,
466
  "Embodied Carbon (kgCO鈧俥/kg)": mat.embodied_carbon,
467
+ "Solar Absorption": mat.solar_absorption,
468
  "Price (USD/m虏)": mat.price,
469
  "Emissivity": mat.emissivity,
470
  "Source": "Project" if not mat.is_library else "Library"
 
472
  return pd.DataFrame(data)
473
  elif data_type == "constructions":
474
  data = []
475
+ constructions = project_constructions.values() if only_project else list(self.library_constructions.values())
476
+ if not only_project and project_constructions:
477
+ constructions.extend(list(project_constructions.values()))
478
+ for cons in constructions:
479
+ layers_str = "; ".join(f"{layer['material'].name} ({layer['thickness']}m)" for layer in cons.layers)
480
  data.append({
481
+ "Name": cons.name,
482
+ "Component Type": cons.component_type,
483
+ "U-Value (W/m虏路K)": cons.u_value,
484
+ "Total Thickness (m)": cons.total_thickness,
485
+ "Embodied Carbon (kgCO鈧俥/m虏)": cons.embodied_carbon,
486
+ "Solar Absorption": cons.solar_absorption,
487
+ "Price (USD/m虏)": cons.price,
488
+ "Layers": layers_str,
489
+ "Source": "Project" if not cons.is_library else "Library"
490
  })
491
  return pd.DataFrame(data)
492
  elif data_type == "glazing_materials":
493
  data = []
494
+ glazing_materials = project_glazing_materials.values() if only_project else self.get_all_glazing_materials(project_glazing_materials)
495
  for mat in glazing_materials:
496
  data.append({
497
  "Name": mat.name,
 
503
  "Source": "Project" if not mat.is_library else "Library"
504
  })
505
  return pd.DataFrame(data)
506
+ elif data_type == "door_materials":
507
+ data = []
508
+ door_materials = project_door_materials.values() if only_project else self.get_all_door_materials(project_door_materials)
509
+ for mat in door_materials:
510
+ data.append({
511
+ "Name": mat.name,
512
+ "Category": mat.category.value,
513
+ "Conductivity (W/m路K)": mat.conductivity,
514
+ "Density (kg/m鲁)": mat.density,
515
+ "Specific Heat (J/kg路K)": mat.specific_heat,
516
+ "Default Thickness (m)": mat.default_thickness,
517
+ "U-Value (W/m虏路K)": mat.u_value,
518
+ "Solar Absorption": mat.solar_absorption,
519
+ "Embodied Carbon (kgCO鈧俥/m虏)": mat.embodied_carbon,
520
+ "Price (USD/m虏)": mat.price,
521
+ "Emissivity": mat.emissivity,
522
+ "Source": "Project" if not mat.is_library else "Library"
523
+ })
524
+ return pd.DataFrame(data)
525
+ return pd.DataFrame()