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

Upload drapery.py

Browse files
Files changed (1) hide show
  1. data/drapery.py +304 -0
data/drapery.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Drapery module for HVAC Load Calculator.
3
+ This module provides classes and functions for handling drapery properties
4
+ and calculating their effects on window heat transfer.
5
+
6
+ Based on ASHRAE principles for drapery thermal characteristics.
7
+ """
8
+
9
+ from typing import Dict, Any, Optional, Tuple
10
+ from enum import Enum
11
+
12
+
13
+ class DraperyOpenness(Enum):
14
+ """Enum for drapery openness classification."""
15
+ OPEN = "Open (>25%)"
16
+ SEMI_OPEN = "Semi-open (7-25%)"
17
+ CLOSED = "Closed (0-7%)"
18
+
19
+
20
+ class DraperyColor(Enum):
21
+ """Enum for drapery color/reflectance classification."""
22
+ DARK = "Dark (0-25%)"
23
+ MEDIUM = "Medium (25-50%)"
24
+ LIGHT = "Light (>50%)"
25
+
26
+
27
+ class Drapery:
28
+ """Class for drapery properties and calculations."""
29
+
30
+ def __init__(self,
31
+ openness: float = 0.05,
32
+ reflectance: float = 0.5,
33
+ transmittance: float = 0.3,
34
+ fullness: float = 1.0,
35
+ enabled: bool = True):
36
+ """
37
+ Initialize drapery object.
38
+
39
+ Args:
40
+ openness: Openness factor (0-1), fraction of fabric area that is open
41
+ reflectance: Reflectance factor (0-1), fraction of incident radiation reflected
42
+ transmittance: Transmittance factor (0-1), fraction of incident radiation transmitted
43
+ fullness: Fullness factor (0-2), ratio of fabric width to covered width
44
+ enabled: Whether the drapery is enabled/present
45
+ """
46
+ self.openness = max(0.0, min(1.0, openness))
47
+ self.reflectance = max(0.0, min(1.0, reflectance))
48
+ self.transmittance = max(0.0, min(1.0, transmittance))
49
+ self.fullness = max(0.0, min(2.0, fullness))
50
+ self.enabled = enabled
51
+
52
+ # Calculate derived properties
53
+ self.absorptance = 1.0 - self.reflectance - self.transmittance
54
+
55
+ # Classify drapery based on openness and reflectance
56
+ self.openness_class = self._classify_openness(self.openness)
57
+ self.color_class = self._classify_color(self.reflectance)
58
+
59
+ @staticmethod
60
+ def _classify_openness(openness: float) -> DraperyOpenness:
61
+ """Classify drapery based on openness factor."""
62
+ if openness > 0.25:
63
+ return DraperyOpenness.OPEN
64
+ elif openness > 0.07:
65
+ return DraperyOpenness.SEMI_OPEN
66
+ else:
67
+ return DraperyOpenness.CLOSED
68
+
69
+ @staticmethod
70
+ def _classify_color(reflectance: float) -> DraperyColor:
71
+ """Classify drapery based on reflectance factor."""
72
+ if reflectance > 0.5:
73
+ return DraperyColor.LIGHT
74
+ elif reflectance > 0.25:
75
+ return DraperyColor.MEDIUM
76
+ else:
77
+ return DraperyColor.DARK
78
+
79
+ def get_classification(self) -> str:
80
+ """Get drapery classification string."""
81
+ openness_map = {
82
+ DraperyOpenness.OPEN: "I",
83
+ DraperyOpenness.SEMI_OPEN: "II",
84
+ DraperyOpenness.CLOSED: "III"
85
+ }
86
+
87
+ color_map = {
88
+ DraperyColor.DARK: "D",
89
+ DraperyColor.MEDIUM: "M",
90
+ DraperyColor.LIGHT: "L"
91
+ }
92
+
93
+ return f"{openness_map[self.openness_class]}{color_map[self.color_class]}"
94
+
95
+ def calculate_iac(self, glazing_shgc: float = 0.87) -> float:
96
+ """
97
+ Calculate Interior Attenuation Coefficient (IAC) for the drapery.
98
+
99
+ The IAC represents the fraction of heat flow that enters the room
100
+ after being modified by the drapery.
101
+
102
+ Args:
103
+ glazing_shgc: Solar Heat Gain Coefficient of the glazing (default: 0.87 for clear glass)
104
+
105
+ Returns:
106
+ IAC value (0-1)
107
+ """
108
+ if not self.enabled:
109
+ return 1.0 # No attenuation if drapery is not enabled
110
+
111
+ # Calculate base IAC for flat drapery (no fullness)
112
+ # This is based on the principles from the Keyes Universal Chart
113
+ # and ASHRAE's IAC calculation methods
114
+
115
+ # Calculate yarn reflectance (based on openness and reflectance)
116
+ if self.openness < 0.0001: # Prevent division by zero
117
+ yarn_reflectance = self.reflectance
118
+ else:
119
+ yarn_reflectance = self.reflectance / (1.0 - self.openness)
120
+ yarn_reflectance = min(1.0, yarn_reflectance) # Cap at 1.0
121
+
122
+ # Base IAC calculation using fabric properties
123
+ # This is a simplified version of the ASHWAT model calculations
124
+ base_iac = 1.0 - (1.0 - self.openness) * (1.0 - self.transmittance / (1.0 - self.openness)) * yarn_reflectance
125
+
126
+ # Adjust for fullness
127
+ # Fullness creates multiple reflections between adjacent fabric surfaces
128
+ if self.fullness <= 0.0:
129
+ fullness_factor = 1.0
130
+ else:
131
+ # Fullness effect increases with higher fullness values
132
+ # More fullness means more fabric area and more reflections
133
+ fullness_factor = 1.0 - 0.15 * (self.fullness / 2.0)
134
+
135
+ # Apply fullness adjustment
136
+ adjusted_iac = base_iac * fullness_factor
137
+
138
+ # Ensure IAC is within valid range
139
+ adjusted_iac = max(0.1, min(1.0, adjusted_iac))
140
+
141
+ return adjusted_iac
142
+
143
+ def calculate_shading_coefficient(self, glazing_shgc: float = 0.87) -> float:
144
+ """
145
+ Calculate shading coefficient for the drapery.
146
+
147
+ The shading coefficient is the ratio of solar heat gain through
148
+ the window with the drapery to that of standard clear glass.
149
+
150
+ Args:
151
+ glazing_shgc: Solar Heat Gain Coefficient of the glazing (default: 0.87 for clear glass)
152
+
153
+ Returns:
154
+ Shading coefficient (0-1)
155
+ """
156
+ # Calculate IAC
157
+ iac = self.calculate_iac(glazing_shgc)
158
+
159
+ # Calculate shading coefficient
160
+ # SC = IAC * SHGC / 0.87
161
+ shading_coefficient = iac * glazing_shgc / 0.87
162
+
163
+ return shading_coefficient
164
+
165
+ def calculate_u_value_adjustment(self, base_u_value: float) -> float:
166
+ """
167
+ Calculate U-value adjustment for the drapery.
168
+
169
+ The drapery adds thermal resistance to the window assembly,
170
+ reducing the overall U-value.
171
+
172
+ Args:
173
+ base_u_value: Base U-value of the window without drapery (W/m²K)
174
+
175
+ Returns:
176
+ Adjusted U-value (W/m²K)
177
+ """
178
+ if not self.enabled:
179
+ return base_u_value # No adjustment if drapery is not enabled
180
+
181
+ # Calculate additional thermal resistance based on drapery properties
182
+ # This is a simplified approach based on ASHRAE principles
183
+
184
+ # Base resistance from drapery
185
+ # More closed fabrics provide more resistance
186
+ base_resistance = 0.05 # m²K/W, typical for medium-weight drapery
187
+
188
+ # Adjust for openness (more closed = more resistance)
189
+ openness_factor = 1.0 - self.openness
190
+
191
+ # Adjust for fullness (more fullness = more resistance due to air gaps)
192
+ fullness_factor = 1.0 + 0.25 * self.fullness
193
+
194
+ # Calculate total additional resistance
195
+ additional_resistance = base_resistance * openness_factor * fullness_factor
196
+
197
+ # Convert base U-value to resistance
198
+ base_resistance = 1.0 / base_u_value
199
+
200
+ # Add drapery resistance
201
+ total_resistance = base_resistance + additional_resistance
202
+
203
+ # Convert back to U-value
204
+ adjusted_u_value = 1.0 / total_resistance
205
+
206
+ return adjusted_u_value
207
+
208
+ def to_dict(self) -> Dict[str, Any]:
209
+ """Convert drapery object to dictionary."""
210
+ return {
211
+ "openness": self.openness,
212
+ "reflectance": self.reflectance,
213
+ "transmittance": self.transmittance,
214
+ "fullness": self.fullness,
215
+ "enabled": self.enabled,
216
+ "absorptance": self.absorptance,
217
+ "openness_class": self.openness_class.value,
218
+ "color_class": self.color_class.value,
219
+ "classification": self.get_classification()
220
+ }
221
+
222
+ @classmethod
223
+ def from_dict(cls, data: Dict[str, Any]) -> 'Drapery':
224
+ """Create drapery object from dictionary."""
225
+ return cls(
226
+ openness=data.get("openness", 0.05),
227
+ reflectance=data.get("reflectance", 0.5),
228
+ transmittance=data.get("transmittance", 0.3),
229
+ fullness=data.get("fullness", 1.0),
230
+ enabled=data.get("enabled", True)
231
+ )
232
+
233
+ @classmethod
234
+ def from_classification(cls, classification: str, fullness: float = 1.0) -> 'Drapery':
235
+ """
236
+ Create drapery object from ASHRAE classification.
237
+
238
+ Args:
239
+ classification: ASHRAE classification (ID, IM, IL, IID, IIM, IIL, IIID, IIIM, IIIL)
240
+ fullness: Fullness factor (0-2)
241
+
242
+ Returns:
243
+ Drapery object
244
+ """
245
+ # Parse classification
246
+ if len(classification) < 2:
247
+ raise ValueError(f"Invalid classification: {classification}")
248
+
249
+ # Handle single-character openness class (I) vs two-character (II, III)
250
+ if classification.startswith("II") or classification.startswith("III"):
251
+ if classification.startswith("III"):
252
+ openness_class = "III"
253
+ color_class = classification[3] if len(classification) > 3 else ""
254
+ else: # II
255
+ openness_class = "II"
256
+ color_class = classification[2] if len(classification) > 2 else ""
257
+ else: # I
258
+ openness_class = "I"
259
+ color_class = classification[1] if len(classification) > 1 else ""
260
+
261
+ # Set default values
262
+ openness = 0.05
263
+ reflectance = 0.5
264
+ transmittance = 0.3
265
+
266
+ # Set openness based on class
267
+ if openness_class == "I":
268
+ openness = 0.3 # Open (>25%)
269
+ elif openness_class == "II":
270
+ openness = 0.15 # Semi-open (7-25%)
271
+ elif openness_class == "III":
272
+ openness = 0.03 # Closed (0-7%)
273
+
274
+ # Set reflectance and transmittance based on color class
275
+ if color_class == "D":
276
+ reflectance = 0.2 # Dark (0-25%)
277
+ transmittance = 0.05
278
+ elif color_class == "M":
279
+ reflectance = 0.4 # Medium (25-50%)
280
+ transmittance = 0.15
281
+ elif color_class == "L":
282
+ reflectance = 0.7 # Light (>50%)
283
+ transmittance = 0.2
284
+
285
+ return cls(
286
+ openness=openness,
287
+ reflectance=reflectance,
288
+ transmittance=transmittance,
289
+ fullness=fullness
290
+ )
291
+
292
+
293
+ # Predefined drapery types based on ASHRAE classifications
294
+ PREDEFINED_DRAPERIES = {
295
+ "ID": Drapery.from_classification("ID"),
296
+ "IM": Drapery.from_classification("IM"),
297
+ "IL": Drapery.from_classification("IL"),
298
+ "IID": Drapery.from_classification("IID"),
299
+ "IIM": Drapery.from_classification("IIM"),
300
+ "IIL": Drapery.from_classification("IIL"),
301
+ "IIID": Drapery.from_classification("IIID"),
302
+ "IIIM": Drapery.from_classification("IIIM"),
303
+ "IIIL": Drapery.from_classification("IIIL")
304
+ }