mabuseif commited on
Commit
1072206
·
verified ·
1 Parent(s): 64370ae

Update app/components.py

Browse files
Files changed (1) hide show
  1. app/components.py +54 -180
app/components.py CHANGED
@@ -2,7 +2,7 @@
2
  BuildSustain - Building Components Module
3
 
4
  This module handles the definition of building envelope components (walls, roofs, floors,
5
- windows, doors, skylights) for the BuildSustain application. It allows users to
6
  assign constructions and fenestrations to specific components, define their areas,
7
  orientations, and other relevant properties.
8
 
@@ -23,7 +23,7 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(
23
  logger = logging.getLogger(__name__)
24
 
25
  # Define constants
26
- COMPONENT_TYPES = ["Wall", "Roof", "Floor", "Window", "Door", "Skylight"]
27
  ORIENTATION_OPTIONS = ["A (North)", "B (South)", "C (East)", "D (West)", "Custom"]
28
  ORIENTATION_MAP = {'A (North)': 0.0, 'B (South)': 180.0, 'C (East)': 90.0, 'D (West)': 270.0, 'Custom': 0.0}
29
 
@@ -33,7 +33,7 @@ def display_components_page():
33
  This is the main function called by main.py when the Building Components page is selected.
34
  """
35
  st.title("Building Components")
36
- st.write("Define walls, roofs, floors, windows, doors, and skylights based on ASHRAE 2005 Handbook of Fundamentals.")
37
 
38
  # Display help information in an expandable section
39
  with st.expander("Help & Information"):
@@ -55,7 +55,7 @@ def display_components_page():
55
 
56
  for i, component_type in enumerate(COMPONENT_TYPES):
57
  with tabs[i]:
58
- display_component_tab(component_type.lower() + "s")
59
 
60
  # Navigation buttons
61
  col1, col2 = st.columns(2)
@@ -78,7 +78,6 @@ def initialize_components():
78
  "roofs": [],
79
  "floors": [],
80
  "windows": [],
81
- "doors": [],
82
  "skylights": []
83
  }
84
 
@@ -98,7 +97,7 @@ def display_component_tab(comp_type: str):
98
  # Get available items for this component type
99
  if comp_type in ["walls", "roofs", "floors"]:
100
  available_items = get_available_constructions()
101
- else: # Windows, doors, skylights
102
  available_items = get_available_fenestrations()
103
 
104
  # Split the display into two columns
@@ -115,8 +114,6 @@ def display_component_tab(comp_type: str):
115
  display_roof_skylight_table(comp_type, components)
116
  elif comp_type == "floors":
117
  display_floor_table(comp_type, components)
118
- else: # doors
119
- display_door_table(comp_type, components)
120
  else:
121
  st.write(f"No {comp_type} defined.")
122
 
@@ -156,7 +153,7 @@ def display_component_tab(comp_type: str):
156
  )
157
 
158
  # Component-specific inputs
159
- if comp_type in ["walls", "windows", "doors", "roofs", "skylights"]:
160
  # Elevation (orientation)
161
  elevation = st.selectbox(
162
  "Elevation",
@@ -169,9 +166,10 @@ def display_component_tab(comp_type: str):
169
  rotation = st.number_input(
170
  "Rotation (°)",
171
  min_value=0.0,
172
- max_value=359.9,
173
  value=float(editor_state.get("rotation", 0.0)),
174
- format="%.1f",
 
175
  help="Rotation angle in degrees to adjust the elevation (0° = North, 90° = East, 180° = South, 270° = West)."
176
  )
177
 
@@ -186,7 +184,8 @@ def display_component_tab(comp_type: str):
186
  min_value=0.0,
187
  max_value=180.0,
188
  value=float(editor_state.get("tilt", default_tilt)),
189
- format="%.1f",
 
190
  help="Tilt angle of the component relative to horizontal (0° = horizontal, 90° = vertical)."
191
  )
192
 
@@ -197,7 +196,8 @@ def display_component_tab(comp_type: str):
197
  min_value=0.0,
198
  max_value=100.0,
199
  value=float(editor_state.get("contact_with_ground_percentage", 0.0)),
200
- format="%.1f",
 
201
  help="Percentage of the floor in contact with the ground (0% = fully above ground, 100% = fully in contact)."
202
  )
203
 
@@ -218,7 +218,6 @@ def display_component_tab(comp_type: str):
218
  # Extract material properties from the first layer
219
  absorptivity = 0.7
220
  emissivity = 0.9
221
- colour = "Unknown"
222
  if construction in available_items and "layers" in available_items[construction]:
223
  first_layer = available_items[construction]["layers"][0]
224
  if "material" in first_layer:
@@ -226,38 +225,14 @@ def display_component_tab(comp_type: str):
226
  if material_data:
227
  absorptivity = material_data.get("absorptivity", 0.7)
228
  emissivity = material_data.get("emissivity", 0.9)
229
- colour = material_data.get("colour", "Unknown")
230
-
231
- # Material property inputs
232
- absorptivity = st.number_input(
233
- "Absorptivity",
234
- min_value=0.0,
235
- max_value=1.0,
236
- value=float(editor_state.get("absorptivity", absorptivity)),
237
- format="%.2f",
238
- help="Solar absorptivity of the component's outer surface (0.0 to 1.0)."
239
- )
240
- emissivity = st.number_input(
241
- "Emissivity",
242
- min_value=0.0,
243
- max_value=1.0,
244
- value=float(editor_state.get("emissivity", emissivity)),
245
- format="%.2f",
246
- help="Thermal emissivity of the component's outer surface (0.0 to 1.0)."
247
- )
248
- colour = st.text_input(
249
- "Colour",
250
- value=editor_state.get("colour", colour),
251
- help="Colour of the component's outer surface."
252
- )
253
 
254
- else: # Windows, doors, skylights
255
  # For fenestrations, we need to select a parent component
256
  parent_components = get_parent_components(comp_type)
257
 
258
  parent_options = list(parent_components.keys())
259
  if not parent_options:
260
- st.error(f"No parent components available. Please create walls/roofs first.")
261
  parent_options = [""]
262
 
263
  parent_component = st.selectbox(
@@ -279,53 +254,15 @@ def display_component_tab(comp_type: str):
279
  help="Select the fenestration type for this component."
280
  )
281
 
282
- if comp_type == "doors":
283
- # Extract material properties from the first layer
284
- absorptivity = 0.7
285
- emissivity = 0.9
286
- colour = "Unknown"
287
- if fenestration in available_items and "layers" in available_items[fenestration]:
288
- first_layer = available_items[fenestration]["layers"][0]
289
- if "material" in first_layer:
290
- material_data = get_material_data(first_layer["material"])
291
- if material_data:
292
- absorptivity = material_data.get("absorptivity", 0.7)
293
- emissivity = material_data.get("emissivity", 0.9)
294
- colour = material_data.get("colour", "Unknown")
295
-
296
- # Material property inputs
297
- absorptivity = st.number_input(
298
- "Absorptivity",
299
- min_value=0.0,
300
- max_value=1.0,
301
- value=float(editor_state.get("absorptivity", absorptivity)),
302
- format="%.2f",
303
- help="Solar absorptivity of the door's outer surface (0.0 to 1.0)."
304
- )
305
- emissivity = st.number_input(
306
- "Emissivity",
307
- min_value=0.0,
308
- max_value=1.0,
309
- value=float(editor_state.get("emissivity", emissivity)),
310
- format="%.2f",
311
- help="Thermal emissivity of the door's outer surface (0.0 to 1.0)."
312
- )
313
- colour = st.text_input(
314
- "Colour",
315
- value=editor_state.get("colour", colour),
316
- help="Colour of the door's outer surface."
317
- )
318
-
319
- if comp_type in ["windows", "skylights"]:
320
- # Shading Coefficient
321
- shading_coefficient = st.number_input(
322
- "Shading Coefficient (SC)",
323
- min_value=0.0,
324
- max_value=1.0,
325
- value=float(editor_state.get("shading_coefficient", 1.0)),
326
- format="%.2f",
327
- help="Shading coefficient for external or internal shading devices (0.0 to 1.0)."
328
- )
329
 
330
  # Submit buttons
331
  col1, col2 = st.columns(2)
@@ -338,6 +275,17 @@ def display_component_tab(comp_type: str):
338
 
339
  # Handle form submission
340
  if submit:
 
 
 
 
 
 
 
 
 
 
 
341
  # Create component data
342
  component_data = {
343
  "id": str(uuid.uuid4()),
@@ -345,7 +293,7 @@ def display_component_tab(comp_type: str):
345
  "area": area
346
  }
347
 
348
- if comp_type in ["walls", "windows", "doors", "roofs", "skylights"]:
349
  component_data["elevation"] = elevation
350
  component_data["rotation"] = rotation
351
  component_data["surface_azimuth"] = surface_azimuth
@@ -358,36 +306,25 @@ def display_component_tab(comp_type: str):
358
  component_data["construction"] = construction
359
  component_data["absorptivity"] = absorptivity
360
  component_data["emissivity"] = emissivity
361
- component_data["colour"] = colour
362
  # Get U-value from construction if available
363
  if construction in available_items:
364
  component_data["u_value"] = available_items[construction].get("u_value", 0.0)
365
  else:
366
  component_data["u_value"] = 0.0
367
- else: # Windows, doors, skylights
368
  component_data["fenestration"] = fenestration
369
  component_data["parent_component"] = parent_component
370
  # Get U-value and other properties from fenestration if available
371
  if fenestration in available_items:
372
  component_data["u_value"] = available_items[fenestration].get("u_value", 0.0)
373
- if comp_type == "doors":
374
- component_data["absorptivity"] = absorptivity
375
- component_data["emissivity"] = emissivity
376
- component_data["colour"] = colour
377
- else:
378
- component_data["shgc"] = available_items[fenestration].get("shgc", 0.7)
379
- component_data["visible_transmittance"] = available_items[fenestration].get("visible_transmittance", 0.7)
380
- component_data["shading_coefficient"] = shading_coefficient
381
  else:
382
  component_data["u_value"] = 0.0
383
- if comp_type == "doors":
384
- component_data["absorptivity"] = absorptivity
385
- component_data["emissivity"] = emissivity
386
- component_data["colour"] = colour
387
- else:
388
- component_data["shgc"] = 0.7
389
- component_data["visible_transmittance"] = 0.7
390
- component_data["shading_coefficient"] = shading_coefficient
391
 
392
  # Handle edit mode
393
  if is_edit:
@@ -432,8 +369,8 @@ def display_wall_window_table(comp_type: str, components: List[Dict[str, Any]]):
432
  cols[0].write(comp["name"])
433
  cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
434
  cols[2].write(f"{comp['area']:.2f}")
435
- cols[3].write(f"{comp.get('surface_azimuth', 0.0):.1f}")
436
- cols[4].write(f"{comp.get('tilt', 90.0):.1f}")
437
  if comp_type == "windows":
438
  cols[5].write(f"{comp.get('shading_coefficient', 1.0):.2f}")
439
 
@@ -454,9 +391,6 @@ def display_wall_window_table(comp_type: str, components: List[Dict[str, Any]]):
454
 
455
  if comp_type == "walls":
456
  editor_data["construction"] = comp.get("construction", "")
457
- editor_data["absorptivity"] = comp.get("absorptivity", 0.7)
458
- editor_data["emissivity"] = comp.get("emissivity", 0.9)
459
- editor_data["colour"] = comp.get("colour", "Unknown")
460
  else: # windows
461
  editor_data["fenestration"] = comp.get("fenestration", "")
462
  editor_data["parent_component"] = comp.get("parent_component", "")
@@ -499,8 +433,8 @@ def display_roof_skylight_table(comp_type: str, components: List[Dict[str, Any]]
499
  cols[0].write(comp["name"])
500
  cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
501
  cols[2].write(f"{comp['area']:.2f}")
502
- cols[3].write(f"{comp.get('surface_azimuth', 0.0):.1f}")
503
- cols[4].write(f"{comp.get('tilt', 0.0):.1f}")
504
  if comp_type == "skylights":
505
  cols[5].write(f"{comp.get('shading_coefficient', 1.0):.2f}")
506
 
@@ -521,9 +455,6 @@ def display_roof_skylight_table(comp_type: str, components: List[Dict[str, Any]]
521
 
522
  if comp_type == "roofs":
523
  editor_data["construction"] = comp.get("construction", "")
524
- editor_data["absorptivity"] = comp.get("absorptivity", 0.7)
525
- editor_data["emissivity"] = comp.get("emissivity", 0.9)
526
- editor_data["colour"] = comp.get("colour", "Unknown")
527
  else: # skylights
528
  editor_data["fenestration"] = comp.get("fenestration", "")
529
  editor_data["parent_component"] = comp.get("parent_component", "")
@@ -560,7 +491,7 @@ def display_floor_table(comp_type: str, components: List[Dict[str, Any]]):
560
  cols[0].write(comp["name"])
561
  cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
562
  cols[2].write(f"{comp['area']:.2f}")
563
- cols[3].write(f"{comp.get('contact_with_ground_percentage', 0.0):.1f}")
564
 
565
  # Edit button
566
  edit_key = f"edit_{comp_type}_{comp['name']}_{idx}"
@@ -572,9 +503,6 @@ def display_floor_table(comp_type: str, components: List[Dict[str, Any]]):
572
  "area": comp["area"],
573
  "construction": comp.get("construction", ""),
574
  "contact_with_ground_percentage": comp.get("contact_with_ground_percentage", 0.0),
575
- "absorptivity": comp.get("absorptivity", 0.7),
576
- "emissivity": comp.get("emissivity", 0.9),
577
- "colour": comp.get("colour", "Unknown"),
578
  "is_edit": True
579
  }
580
 
@@ -591,59 +519,6 @@ def display_floor_table(comp_type: str, components: List[Dict[str, Any]]):
591
  st.session_state[f"{comp_type}_action"] = {"action": "delete", "id": str(uuid.uuid4())}
592
  st.session_state.components_rerun_pending = True
593
 
594
- def display_door_table(comp_type: str, components: List[Dict[str, Any]]):
595
- """Display a table of doors with appropriate columns."""
596
- # Create column headers
597
- cols = st.columns([2, 1, 1, 1, 1, 1, 1, 1])
598
- cols[0].write("**Name**")
599
- cols[1].write("**U-Value**")
600
- cols[2].write("**Absorptivity**")
601
- cols[3].write("**Surface Azimuth (°)**")
602
- cols[4].write("**Tilt (°)**")
603
- cols[5].write("**Edit**")
604
- cols[6].write("**Delete**")
605
-
606
- # Display each component
607
- for idx, comp in enumerate(components):
608
- cols = st.columns([2, 1, 1, 1, 1, 1, 1, 1])
609
- cols[0].write(comp["name"])
610
- cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
611
- cols[2].write(f"{comp.get('absorptivity', 0.7):.2f}")
612
- cols[3].write(f"{comp.get('surface_azimuth', 0.0):.1f}")
613
- cols[4].write(f"{comp.get('tilt', 90.0):.1f}")
614
-
615
- # Edit button
616
- edit_key = f"edit_{comp_type}_{comp['name']}_{idx}"
617
- with cols[5].container():
618
- if st.button("Edit", key=edit_key):
619
- editor_data = {
620
- "index": idx,
621
- "name": comp["name"],
622
- "area": comp["area"],
623
- "elevation": comp.get("elevation", ORIENTATION_OPTIONS[0]),
624
- "rotation": comp.get("rotation", 0.0),
625
- "tilt": comp.get("tilt", 90.0),
626
- "fenestration": comp.get("fenestration", ""),
627
- "parent_component": comp.get("parent_component", ""),
628
- "absorptivity": comp.get("absorptivity", 0.7),
629
- "emissivity": comp.get("emissivity", 0.9),
630
- "colour": comp.get("colour", "Unknown"),
631
- "is_edit": True
632
- }
633
-
634
- st.session_state[f"{comp_type}_editor"] = editor_data
635
- st.session_state[f"{comp_type}_action"] = {"action": "edit", "id": str(uuid.uuid4())}
636
- st.session_state.components_rerun_pending = True
637
-
638
- # Delete button
639
- delete_key = f"delete_{comp_type}_{comp['name']}_{idx}"
640
- with cols[6].container():
641
- if st.button("Delete", key=delete_key):
642
- st.session_state.project_data["components"][comp_type].pop(idx)
643
- st.success(f"{comp_type[:-1].capitalize()} '{comp['name']}' deleted!")
644
- st.session_state[f"{comp_type}_action"] = {"action": "delete", "id": str(uuid.uuid4())}
645
- st.session_state.components_rerun_pending = True
646
-
647
  def get_available_constructions() -> Dict[str, Any]:
648
  """
649
  Get all available constructions from both library and project.
@@ -700,8 +575,8 @@ def get_parent_components(comp_type: str) -> Dict[str, Any]:
700
  """
701
  parent_components = {}
702
 
703
- if comp_type == "windows" or comp_type == "doors":
704
- # Windows and doors can only be on walls
705
  if "walls" in st.session_state.project_data["components"]:
706
  for wall in st.session_state.project_data["components"]["walls"]:
707
  parent_components[wall["name"]] = wall
@@ -745,23 +620,22 @@ def display_components_help():
745
  **Key Concepts:**
746
 
747
  * **Walls, Roofs, Floors**: Opaque components that use constructions from the Construction section.
748
- * **Windows, Doors, Skylights**: Transparent components that use fenestrations from the Material Library section.
749
  * **Surface Azimuth**: Direction the component faces, calculated as elevation plus rotation (0° = North, 90° = East, 180° = South, 270° = West).
750
  * **Tilt**: Angle of the component relative to horizontal (0° = horizontal, 90° = vertical).
751
- * **Parent Component**: The wall or roof that contains a window, door, or skylight.
752
- * **Absorptivity/Emissivity**: Thermal properties of the outer surface material.
753
  * **Shading Coefficient (SC)**: Factor for shading devices on windows/skylights (0.0 to 1.0).
754
 
755
  **Workflow:**
756
 
757
  1. First define opaque components (walls, roofs, floors) using constructions.
758
- 2. Then define transparent components (windows, doors, skylights) and assign them to parent components.
759
  3. Ensure all major building envelope components are included for accurate load calculations.
760
 
761
  **Tips:**
762
 
763
  * Use elevation (A, B, C, D) and rotation to set the surface azimuth.
764
  * For roofs and skylights, set tilt to 0° for flat surfaces.
765
- * Ensure the total area of windows and doors doesn't exceed the area of their parent walls.
766
  * Ensure the total area of skylights doesn't exceed the area of their parent roofs.
767
  """)
 
2
  BuildSustain - Building Components Module
3
 
4
  This module handles the definition of building envelope components (walls, roofs, floors,
5
+ windows, skylights) for the BuildSustain application. It allows users to
6
  assign constructions and fenestrations to specific components, define their areas,
7
  orientations, and other relevant properties.
8
 
 
23
  logger = logging.getLogger(__name__)
24
 
25
  # Define constants
26
+ COMPONENT_TYPES = ["Walls", "Roofs", "Floors", "Windows", "Skylights"]
27
  ORIENTATION_OPTIONS = ["A (North)", "B (South)", "C (East)", "D (West)", "Custom"]
28
  ORIENTATION_MAP = {'A (North)': 0.0, 'B (South)': 180.0, 'C (East)': 90.0, 'D (West)': 270.0, 'Custom': 0.0}
29
 
 
33
  This is the main function called by main.py when the Building Components page is selected.
34
  """
35
  st.title("Building Components")
36
+ st.write("Define walls, roofs, floors, windows, and skylights based on ASHRAE 2005 Handbook of Fundamentals.")
37
 
38
  # Display help information in an expandable section
39
  with st.expander("Help & Information"):
 
55
 
56
  for i, component_type in enumerate(COMPONENT_TYPES):
57
  with tabs[i]:
58
+ display_component_tab(component_type.lower())
59
 
60
  # Navigation buttons
61
  col1, col2 = st.columns(2)
 
78
  "roofs": [],
79
  "floors": [],
80
  "windows": [],
 
81
  "skylights": []
82
  }
83
 
 
97
  # Get available items for this component type
98
  if comp_type in ["walls", "roofs", "floors"]:
99
  available_items = get_available_constructions()
100
+ else: # Windows, skylights
101
  available_items = get_available_fenestrations()
102
 
103
  # Split the display into two columns
 
114
  display_roof_skylight_table(comp_type, components)
115
  elif comp_type == "floors":
116
  display_floor_table(comp_type, components)
 
 
117
  else:
118
  st.write(f"No {comp_type} defined.")
119
 
 
153
  )
154
 
155
  # Component-specific inputs
156
+ if comp_type in ["walls", "windows", "roofs", "skylights"]:
157
  # Elevation (orientation)
158
  elevation = st.selectbox(
159
  "Elevation",
 
166
  rotation = st.number_input(
167
  "Rotation (°)",
168
  min_value=0.0,
169
+ max_value=359.0,
170
  value=float(editor_state.get("rotation", 0.0)),
171
+ step=1.0,
172
+ format="%.0f",
173
  help="Rotation angle in degrees to adjust the elevation (0° = North, 90° = East, 180° = South, 270° = West)."
174
  )
175
 
 
184
  min_value=0.0,
185
  max_value=180.0,
186
  value=float(editor_state.get("tilt", default_tilt)),
187
+ step=1.0,
188
+ format="%.0f",
189
  help="Tilt angle of the component relative to horizontal (0° = horizontal, 90° = vertical)."
190
  )
191
 
 
196
  min_value=0.0,
197
  max_value=100.0,
198
  value=float(editor_state.get("contact_with_ground_percentage", 0.0)),
199
+ step=1.0,
200
+ format="%.0f",
201
  help="Percentage of the floor in contact with the ground (0% = fully above ground, 100% = fully in contact)."
202
  )
203
 
 
218
  # Extract material properties from the first layer
219
  absorptivity = 0.7
220
  emissivity = 0.9
 
221
  if construction in available_items and "layers" in available_items[construction]:
222
  first_layer = available_items[construction]["layers"][0]
223
  if "material" in first_layer:
 
225
  if material_data:
226
  absorptivity = material_data.get("absorptivity", 0.7)
227
  emissivity = material_data.get("emissivity", 0.9)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
+ else: # Windows, skylights
230
  # For fenestrations, we need to select a parent component
231
  parent_components = get_parent_components(comp_type)
232
 
233
  parent_options = list(parent_components.keys())
234
  if not parent_options:
235
+ st.error(f"No parent components available. Please create {'walls' if comp_type == 'windows' else 'roofs'} first.")
236
  parent_options = [""]
237
 
238
  parent_component = st.selectbox(
 
254
  help="Select the fenestration type for this component."
255
  )
256
 
257
+ # Shading Coefficient
258
+ shading_coefficient = st.number_input(
259
+ "Shading Coefficient (SC)",
260
+ min_value=0.0,
261
+ max_value=1.0,
262
+ value=float(editor_state.get("shading_coefficient", 1.0)),
263
+ format="%.2f",
264
+ help="Shading coefficient for external or internal shading devices (0.0 to 1.0)."
265
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
  # Submit buttons
268
  col1, col2 = st.columns(2)
 
275
 
276
  # Handle form submission
277
  if submit:
278
+ # Validate name
279
+ if not name or name.strip() == "":
280
+ st.error("Component name is required.")
281
+ return
282
+
283
+ # Check for unique name
284
+ existing_names = [comp["name"] for comp in components if not (is_edit and comp["name"] == editor_state.get("name"))]
285
+ if name in existing_names:
286
+ st.error("Component name must be unique.")
287
+ return
288
+
289
  # Create component data
290
  component_data = {
291
  "id": str(uuid.uuid4()),
 
293
  "area": area
294
  }
295
 
296
+ if comp_type in ["walls", "windows", "roofs", "skylights"]:
297
  component_data["elevation"] = elevation
298
  component_data["rotation"] = rotation
299
  component_data["surface_azimuth"] = surface_azimuth
 
306
  component_data["construction"] = construction
307
  component_data["absorptivity"] = absorptivity
308
  component_data["emissivity"] = emissivity
 
309
  # Get U-value from construction if available
310
  if construction in available_items:
311
  component_data["u_value"] = available_items[construction].get("u_value", 0.0)
312
  else:
313
  component_data["u_value"] = 0.0
314
+ else: # Windows, skylights
315
  component_data["fenestration"] = fenestration
316
  component_data["parent_component"] = parent_component
317
  # Get U-value and other properties from fenestration if available
318
  if fenestration in available_items:
319
  component_data["u_value"] = available_items[fenestration].get("u_value", 0.0)
320
+ component_data["shgc"] = available_items[fenestration].get("shgc", 0.7)
321
+ component_data["visible_transmittance"] = available_items[fenestration].get("visible_transmittance", 0.7)
322
+ component_data["shading_coefficient"] = shading_coefficient
 
 
 
 
 
323
  else:
324
  component_data["u_value"] = 0.0
325
+ component_data["shgc"] = 0.7
326
+ component_data["visible_transmittance"] = 0.7
327
+ component_data["shading_coefficient"] = shading_coefficient
 
 
 
 
 
328
 
329
  # Handle edit mode
330
  if is_edit:
 
369
  cols[0].write(comp["name"])
370
  cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
371
  cols[2].write(f"{comp['area']:.2f}")
372
+ cols[3].write(f"{comp.get('surface_azimuth', 0.0):.0f}")
373
+ cols[4].write(f"{comp.get('tilt', 90.0):.0f}")
374
  if comp_type == "windows":
375
  cols[5].write(f"{comp.get('shading_coefficient', 1.0):.2f}")
376
 
 
391
 
392
  if comp_type == "walls":
393
  editor_data["construction"] = comp.get("construction", "")
 
 
 
394
  else: # windows
395
  editor_data["fenestration"] = comp.get("fenestration", "")
396
  editor_data["parent_component"] = comp.get("parent_component", "")
 
433
  cols[0].write(comp["name"])
434
  cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
435
  cols[2].write(f"{comp['area']:.2f}")
436
+ cols[3].write(f"{comp.get('surface_azimuth', 0.0):.0f}")
437
+ cols[4].write(f"{comp.get('tilt', 0.0):.0f}")
438
  if comp_type == "skylights":
439
  cols[5].write(f"{comp.get('shading_coefficient', 1.0):.2f}")
440
 
 
455
 
456
  if comp_type == "roofs":
457
  editor_data["construction"] = comp.get("construction", "")
 
 
 
458
  else: # skylights
459
  editor_data["fenestration"] = comp.get("fenestration", "")
460
  editor_data["parent_component"] = comp.get("parent_component", "")
 
491
  cols[0].write(comp["name"])
492
  cols[1].write(f"{comp.get('u_value', 0.0):.3f}")
493
  cols[2].write(f"{comp['area']:.2f}")
494
+ cols[3].write(f"{comp.get('contact_with_ground_percentage', 0.0):.0f}")
495
 
496
  # Edit button
497
  edit_key = f"edit_{comp_type}_{comp['name']}_{idx}"
 
503
  "area": comp["area"],
504
  "construction": comp.get("construction", ""),
505
  "contact_with_ground_percentage": comp.get("contact_with_ground_percentage", 0.0),
 
 
 
506
  "is_edit": True
507
  }
508
 
 
519
  st.session_state[f"{comp_type}_action"] = {"action": "delete", "id": str(uuid.uuid4())}
520
  st.session_state.components_rerun_pending = True
521
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  def get_available_constructions() -> Dict[str, Any]:
523
  """
524
  Get all available constructions from both library and project.
 
575
  """
576
  parent_components = {}
577
 
578
+ if comp_type == "windows":
579
+ # Windows can only be on walls
580
  if "walls" in st.session_state.project_data["components"]:
581
  for wall in st.session_state.project_data["components"]["walls"]:
582
  parent_components[wall["name"]] = wall
 
620
  **Key Concepts:**
621
 
622
  * **Walls, Roofs, Floors**: Opaque components that use constructions from the Construction section.
623
+ * **Windows, Skylights**: Transparent components that use fenestrations from the Material Library section.
624
  * **Surface Azimuth**: Direction the component faces, calculated as elevation plus rotation (0° = North, 90° = East, 180° = South, 270° = West).
625
  * **Tilt**: Angle of the component relative to horizontal (0° = horizontal, 90° = vertical).
626
+ * **Parent Component**: The wall or roof that contains a window or skylight.
 
627
  * **Shading Coefficient (SC)**: Factor for shading devices on windows/skylights (0.0 to 1.0).
628
 
629
  **Workflow:**
630
 
631
  1. First define opaque components (walls, roofs, floors) using constructions.
632
+ 2. Then define transparent components (windows, skylights) and assign them to parent components.
633
  3. Ensure all major building envelope components are included for accurate load calculations.
634
 
635
  **Tips:**
636
 
637
  * Use elevation (A, B, C, D) and rotation to set the surface azimuth.
638
  * For roofs and skylights, set tilt to 0° for flat surfaces.
639
+ * Ensure the total area of windows doesn't exceed the area of their parent walls.
640
  * Ensure the total area of skylights doesn't exceed the area of their parent roofs.
641
  """)