mabuseif commited on
Commit
4bad458
·
verified ·
1 Parent(s): 837cf13

Update data/building_components.py

Browse files
Files changed (1) hide show
  1. data/building_components.py +122 -24
data/building_components.py CHANGED
@@ -7,30 +7,31 @@ from dataclasses import dataclass, field
7
  from enum import Enum
8
  from typing import List, Dict, Optional, Union
9
  import numpy as np
 
10
 
11
 
12
  class Orientation(Enum):
13
  """Enumeration for building component orientations."""
14
- NORTH = "North"
15
- NORTHEAST = "Northeast"
16
- EAST = "East"
17
- SOUTHEAST = "Southeast"
18
- SOUTH = "South"
19
- SOUTHWEST = "Southwest"
20
- WEST = "West"
21
- NORTHWEST = "Northwest"
22
- HORIZONTAL = "Horizontal" # For roofs and floors
23
  NOT_APPLICABLE = "N/A" # For components without orientation
24
 
25
 
26
  class ComponentType(Enum):
27
  """Enumeration for building component types."""
28
- WALL = "Wall"
29
- ROOF = "Roof"
30
- FLOOR = "Floor"
31
- WINDOW = "Window"
32
- DOOR = "Door"
33
- SKYLIGHT = "Skylight"
34
 
35
 
36
  class MaterialLayer:
@@ -162,6 +163,8 @@ class BuildingComponent:
162
  class Wall(BuildingComponent):
163
  """Class representing a wall component."""
164
 
 
 
165
  has_sun_exposure: bool = True
166
  wall_type: str = "Custom" # Brick, Concrete, Wood Frame, etc.
167
  wall_group: str = "A" # ASHRAE wall group (A, B, C, D, E, F, G, H)
@@ -175,6 +178,10 @@ class Wall(BuildingComponent):
175
  super().__post_init__()
176
  self.component_type = ComponentType.WALL
177
 
 
 
 
 
178
  # Set net area equal to area if not specified
179
  if self.net_area is None:
180
  self.net_area = self.area
@@ -219,8 +226,10 @@ class Wall(BuildingComponent):
219
  class Roof(BuildingComponent):
220
  """Class representing a roof component."""
221
 
 
 
222
  roof_type: str = "Custom" # Flat, Pitched, etc.
223
- roof_group: str = "A" # ASHRAE roof group
224
  pitch: float = 0.0 # Roof pitch in degrees
225
  has_suspended_ceiling: bool = False
226
  ceiling_plenum_height: float = 0.0 # m
@@ -230,6 +239,10 @@ class Roof(BuildingComponent):
230
  super().__post_init__()
231
  self.component_type = ComponentType.ROOF
232
  self.orientation = Orientation.HORIZONTAL
 
 
 
 
233
 
234
  def to_dict(self) -> Dict:
235
  """Convert the roof to a dictionary."""
@@ -324,6 +337,7 @@ class Window(Fenestration):
324
  width: float = 1.0 # m
325
  height: float = 1.0 # m
326
  wall_id: str = None # ID of the wall containing this window
 
327
 
328
  def __post_init__(self):
329
  """Initialize window-specific attributes."""
@@ -333,6 +347,64 @@ class Window(Fenestration):
333
  # Calculate area from width and height if not provided
334
  if self.area <= 0 and self.width > 0 and self.height > 0:
335
  self.area = self.width * self.height
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
  def to_dict(self) -> Dict:
338
  """Convert the window to a dictionary."""
@@ -344,7 +416,9 @@ class Window(Fenestration):
344
  "low_e_coating": self.low_e_coating,
345
  "width": self.width,
346
  "height": self.height,
347
- "wall_id": self.wall_id
 
 
348
  })
349
  return window_dict
350
 
@@ -449,17 +523,41 @@ class BuildingComponentFactory:
449
  """
450
  component_type = component_data.get("component_type")
451
 
452
- if component_type == ComponentType.WALL.value:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  return Wall(**component_data)
454
- elif component_type == ComponentType.ROOF.value:
455
  return Roof(**component_data)
456
- elif component_type == ComponentType.FLOOR.value:
457
  return Floor(**component_data)
458
- elif component_type == ComponentType.WINDOW.value:
459
  return Window(**component_data)
460
- elif component_type == ComponentType.DOOR.value:
461
  return Door(**component_data)
462
- elif component_type == ComponentType.SKYLIGHT.value:
463
  return Skylight(**component_data)
464
  else:
465
- raise ValueError(f"Unknown component type: {component_type}")
 
7
  from enum import Enum
8
  from typing import List, Dict, Optional, Union
9
  import numpy as np
10
+ from data.drapery import Drapery
11
 
12
 
13
  class Orientation(Enum):
14
  """Enumeration for building component orientations."""
15
+ NORTH = "NORTH"
16
+ NORTHEAST = "NORTHEAST"
17
+ EAST = "EAST"
18
+ SOUTHEAST = "SOUTHEAST"
19
+ SOUTH = "SOUTH"
20
+ SOUTHWEST = "SOUTHWEST"
21
+ WEST = "WEST"
22
+ NORTHWEST = "NORTHWEST"
23
+ HORIZONTAL = "HORIZONTAL" # For roofs and floors
24
  NOT_APPLICABLE = "N/A" # For components without orientation
25
 
26
 
27
  class ComponentType(Enum):
28
  """Enumeration for building component types."""
29
+ WALL = "WALL"
30
+ ROOF = "ROOF"
31
+ FLOOR = "FLOOR"
32
+ WINDOW = "WINDOW"
33
+ DOOR = "DOOR"
34
+ SKYLIGHT = "SKYLIGHT"
35
 
36
 
37
  class MaterialLayer:
 
163
  class Wall(BuildingComponent):
164
  """Class representing a wall component."""
165
 
166
+ VALID_WALL_GROUPS = {"A", "B", "C", "D", "E", "F", "G", "H"} # ASHRAE wall groups for CLTD
167
+
168
  has_sun_exposure: bool = True
169
  wall_type: str = "Custom" # Brick, Concrete, Wood Frame, etc.
170
  wall_group: str = "A" # ASHRAE wall group (A, B, C, D, E, F, G, H)
 
178
  super().__post_init__()
179
  self.component_type = ComponentType.WALL
180
 
181
+ # Validate wall_group
182
+ if self.wall_group not in self.VALID_WALL_GROUPS:
183
+ raise ValueError(f"Invalid wall_group: {self.wall_group}. Must be one of {self.VALID_WALL_GROUPS}")
184
+
185
  # Set net area equal to area if not specified
186
  if self.net_area is None:
187
  self.net_area = self.area
 
226
  class Roof(BuildingComponent):
227
  """Class representing a roof component."""
228
 
229
+ VALID_ROOF_GROUPS = {"1", "2", "3", "4", "5", "6", "7", "8"} # ASHRAE roof groups for CLTD
230
+
231
  roof_type: str = "Custom" # Flat, Pitched, etc.
232
+ roof_group: str = "1" # ASHRAE roof group
233
  pitch: float = 0.0 # Roof pitch in degrees
234
  has_suspended_ceiling: bool = False
235
  ceiling_plenum_height: float = 0.0 # m
 
239
  super().__post_init__()
240
  self.component_type = ComponentType.ROOF
241
  self.orientation = Orientation.HORIZONTAL
242
+
243
+ # Validate roof_group
244
+ if self.roof_group not in self.VALID_ROOF_GROUPS:
245
+ raise ValueError(f"Invalid roof_group: {self.roof_group}. Must be one of {self.VALID_ROOF_GROUPS}")
246
 
247
  def to_dict(self) -> Dict:
248
  """Convert the roof to a dictionary."""
 
337
  width: float = 1.0 # m
338
  height: float = 1.0 # m
339
  wall_id: str = None # ID of the wall containing this window
340
+ drapery: Optional[Drapery] = None # Drapery object
341
 
342
  def __post_init__(self):
343
  """Initialize window-specific attributes."""
 
347
  # Calculate area from width and height if not provided
348
  if self.area <= 0 and self.width > 0 and self.height > 0:
349
  self.area = self.width * self.height
350
+
351
+ # Initialize drapery if not provided
352
+ if self.drapery is None:
353
+ self.drapery = Drapery(enabled=False)
354
+
355
+ @classmethod
356
+ def from_classification(cls, id: str, name: str, u_value: float, area: float,
357
+ shgc: float, orientation: Orientation, wall_id: str,
358
+ drapery_classification: str, fullness: float = 1.0, **kwargs) -> 'Window':
359
+ """
360
+ Create window object with drapery from ASHRAE classification.
361
+
362
+ Args:
363
+ id: Unique identifier
364
+ name: Window name
365
+ u_value: Window U-value in W/m²K
366
+ area: Window area in m²
367
+ shgc: Solar Heat Gain Coefficient (0-1)
368
+ orientation: Window orientation
369
+ wall_id: ID of the wall containing this window
370
+ drapery_classification: ASHRAE drapery classification (e.g., ID, IM, IIL)
371
+ fullness: Fullness factor (0-2)
372
+ **kwargs: Additional arguments for Window attributes
373
+
374
+ Returns:
375
+ Window object
376
+ """
377
+ drapery = Drapery.from_classification(drapery_classification, fullness)
378
+ return cls(
379
+ id=id,
380
+ name=name,
381
+ component_type=ComponentType.WINDOW,
382
+ u_value=u_value,
383
+ area=area,
384
+ shgc=shgc,
385
+ orientation=orientation,
386
+ drapery=drapery,
387
+ wall_id=wall_id,
388
+ **kwargs
389
+ )
390
+
391
+ def get_effective_u_value(self) -> float:
392
+ """Get effective U-value with drapery adjustment."""
393
+ if self.drapery and self.drapery.enabled:
394
+ return self.drapery.calculate_u_value_adjustment(self.u_value)
395
+ return self.u_value
396
+
397
+ def get_shading_coefficient(self) -> float:
398
+ """Get shading coefficient with drapery."""
399
+ if self.drapery and self.drapery.enabled:
400
+ return self.drapery.calculate_shading_coefficient(self.shgc)
401
+ return self.shading_coefficient
402
+
403
+ def get_iac(self) -> float:
404
+ """Get Interior Attenuation Coefficient with drapery."""
405
+ if self.drapery and self.drapery.enabled:
406
+ return self.drapery.calculate_iac(self.shgc)
407
+ return 1.0 # No attenuation
408
 
409
  def to_dict(self) -> Dict:
410
  """Convert the window to a dictionary."""
 
416
  "low_e_coating": self.low_e_coating,
417
  "width": self.width,
418
  "height": self.height,
419
+ "wall_id": self.wall_id,
420
+ "drapery": self.drapery.to_dict() if self.drapery else None,
421
+ "drapery_classification": self.drapery.get_classification() if self.drapery and self.drapery.enabled else None
422
  })
423
  return window_dict
424
 
 
523
  """
524
  component_type = component_data.get("component_type")
525
 
526
+ # Convert string component_type to ComponentType enum
527
+ if isinstance(component_type, str):
528
+ component_type = ComponentType[component_type]
529
+
530
+ # Handle drapery for Window components
531
+ if component_type == ComponentType.WINDOW:
532
+ drapery_data = component_data.pop("drapery", None)
533
+ drapery_classification = component_data.pop("drapery_classification", None)
534
+ if drapery_classification:
535
+ fullness = drapery_data.get("fullness", 1.0) if drapery_data else 1.0
536
+ component_data["drapery"] = Drapery.from_classification(drapery_classification, fullness)
537
+ elif drapery_data:
538
+ component_data["drapery"] = Drapery.from_dict(drapery_data)
539
+
540
+ # Convert orientation to Orientation enum
541
+ if "orientation" in component_data and isinstance(component_data["orientation"], str):
542
+ component_data["orientation"] = Orientation[component_data["orientation"]]
543
+
544
+ # Convert material_layers to MaterialLayer objects
545
+ if "material_layers" in component_data:
546
+ component_data["material_layers"] = [
547
+ MaterialLayer(**layer) for layer in component_data["material_layers"]
548
+ ]
549
+
550
+ if component_type == ComponentType.WALL:
551
  return Wall(**component_data)
552
+ elif component_type == ComponentType.ROOF:
553
  return Roof(**component_data)
554
+ elif component_type == ComponentType.FLOOR:
555
  return Floor(**component_data)
556
+ elif component_type == ComponentType.WINDOW:
557
  return Window(**component_data)
558
+ elif component_type == ComponentType.DOOR:
559
  return Door(**component_data)
560
+ elif component_type == ComponentType.SKYLIGHT:
561
  return Skylight(**component_data)
562
  else:
563
+ raise ValueError(f"Unknown component type: {component_type}")