Spaces:
Sleeping
Sleeping
Update app/component_selection.py
Browse files- app/component_selection.py +168 -37
app/component_selection.py
CHANGED
|
@@ -331,7 +331,49 @@ class ComponentSelectionInterface:
|
|
| 331 |
self.shading_system = shading_system
|
| 332 |
self.reference_data = reference_data
|
| 333 |
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
def _display_add_component_form(self, session_state: Any, component_type: ComponentType) -> None:
|
| 337 |
type_name = component_type.value.lower()
|
|
@@ -456,7 +498,131 @@ class ComponentSelectionInterface:
|
|
| 456 |
if session_state[f"add_{type_name}_submitted"]:
|
| 457 |
session_state[f"add_{type_name}_submitted"] = False
|
| 458 |
|
| 459 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
|
| 461 |
def _display_u_value_calculator_tab(self, session_state: Any) -> None:
|
| 462 |
st.subheader("U-Value Calculator")
|
|
@@ -546,41 +712,6 @@ class ComponentSelectionInterface:
|
|
| 546 |
session_state.material_layers.pop()
|
| 547 |
st.rerun()
|
| 548 |
|
| 549 |
-
# ... other methods like _display_components_table, _save_components, etc. ...
|
| 550 |
-
|
| 551 |
-
with st.form("material_layer_form"):
|
| 552 |
-
col1, col2 = st.columns(2)
|
| 553 |
-
with col1:
|
| 554 |
-
material_selection = st.radio("Material Selection", ["Select from Presets", "Custom Material"])
|
| 555 |
-
if material_selection == "Select from Presets":
|
| 556 |
-
preset_materials = self.u_value_calculator.get_preset_materials()
|
| 557 |
-
material_options = {m["name"]: m for m in preset_materials}
|
| 558 |
-
material_name = st.selectbox("Material", options=list(material_options.keys()))
|
| 559 |
-
conductivity = material_options[material_name]["conductivity"]
|
| 560 |
-
st.number_input("Conductivity (W/m·K)", value=conductivity, disabled=True)
|
| 561 |
-
else:
|
| 562 |
-
material_name = st.text_input("Material Name", "Custom Material")
|
| 563 |
-
conductivity = st.number_input("Conductivity (W/m·K)", min_value=0.0, value=1.0, step=0.01)
|
| 564 |
-
|
| 565 |
-
with col2:
|
| 566 |
-
thickness = st.number_input("Thickness (mm)", min_value=0.0, value=100.0, step=1.0)
|
| 567 |
-
if conductivity > 0:
|
| 568 |
-
r_value = thickness / 1000 / conductivity
|
| 569 |
-
st.metric("Layer R-Value", f"{r_value:.3f} m²·K/W")
|
| 570 |
-
|
| 571 |
-
submitted = st.form_submit_button("Add Layer")
|
| 572 |
-
if submitted:
|
| 573 |
-
if not material_name:
|
| 574 |
-
st.error("Material name is required!")
|
| 575 |
-
elif thickness <= 0:
|
| 576 |
-
st.error("Thickness must be greater than zero!")
|
| 577 |
-
elif conductivity <= 0:
|
| 578 |
-
st.error("Conductivity must be greater than zero!")
|
| 579 |
-
else:
|
| 580 |
-
session_state.material_layers.append({"name": material_name, "thickness": thickness, "conductivity": conductivity})
|
| 581 |
-
st.success(f"Added layer: {material_name}")
|
| 582 |
-
st.rerun()
|
| 583 |
-
|
| 584 |
def _save_components(self, session_state: Any) -> None:
|
| 585 |
components_dict = {
|
| 586 |
"walls": [c.to_dict() for c in session_state.components["walls"]],
|
|
|
|
| 331 |
self.shading_system = shading_system
|
| 332 |
self.reference_data = reference_data
|
| 333 |
|
| 334 |
+
def display_component_selection(self, session_state: Any) -> None:
|
| 335 |
+
st.title("Building Components")
|
| 336 |
+
|
| 337 |
+
if 'components' not in session_state:
|
| 338 |
+
session_state.components = {'walls': [], 'roofs': [], 'floors': [], 'windows': [], 'doors': []}
|
| 339 |
+
|
| 340 |
+
tabs = st.tabs(["Walls", "Roofs", "Floors", "Windows", "Doors", "U-Value Calculator"])
|
| 341 |
+
|
| 342 |
+
with tabs[0]:
|
| 343 |
+
self._display_component_tab(session_state, ComponentType.WALL)
|
| 344 |
+
with tabs[1]:
|
| 345 |
+
self._display_component_tab(session_state, ComponentType.ROOF)
|
| 346 |
+
with tabs[2]:
|
| 347 |
+
self._display_component_tab(session_state, ComponentType.FLOOR)
|
| 348 |
+
with tabs[3]:
|
| 349 |
+
self._display_component_tab(session_state, ComponentType.WINDOW)
|
| 350 |
+
with tabs[4]:
|
| 351 |
+
self._display_component_tab(session_state, ComponentType.DOOR)
|
| 352 |
+
with tabs[5]:
|
| 353 |
+
self._display_u_value_calculator_tab(session_state)
|
| 354 |
+
|
| 355 |
+
col1, col2 = st.columns(2)
|
| 356 |
+
with col1:
|
| 357 |
+
if st.button("Save Components"):
|
| 358 |
+
self._save_components(session_state)
|
| 359 |
+
with col2:
|
| 360 |
+
uploaded_file = st.file_uploader("Load Components", type=["json"])
|
| 361 |
+
if uploaded_file and st.button("Load Uploaded Components"):
|
| 362 |
+
self._load_components(session_state, uploaded_file)
|
| 363 |
+
|
| 364 |
+
def _display_component_tab(self, session_state: Any, component_type: ComponentType) -> None:
|
| 365 |
+
type_name = component_type.value.lower()
|
| 366 |
+
st.subheader(f"{type_name.capitalize()} Components")
|
| 367 |
+
|
| 368 |
+
with st.expander(f"Add {type_name.capitalize()}", expanded=False):
|
| 369 |
+
self._display_add_component_form(session_state, component_type)
|
| 370 |
+
|
| 371 |
+
components = session_state.components.get(type_name + 's', [])
|
| 372 |
+
if components:
|
| 373 |
+
st.subheader(f"Existing {type_name.capitalize()} Components")
|
| 374 |
+
self._display_components_table(session_state, component_type, components)
|
| 375 |
+
else:
|
| 376 |
+
st.info(f"No {type_name} components defined yet.")
|
| 377 |
|
| 378 |
def _display_add_component_form(self, session_state: Any, component_type: ComponentType) -> None:
|
| 379 |
type_name = component_type.value.lower()
|
|
|
|
| 498 |
if session_state[f"add_{type_name}_submitted"]:
|
| 499 |
session_state[f"add_{type_name}_submitted"] = False
|
| 500 |
|
| 501 |
+
def _display_components_table(self, session_state: Any, component_type: ComponentType, components: List[BuildingComponent]) -> None:
|
| 502 |
+
type_name = component_type.value.lower()
|
| 503 |
+
data = []
|
| 504 |
+
for comp in components:
|
| 505 |
+
row = {"Name": comp.name, "Area (m²)": comp.area, "U-Value (W/m²·K)": comp.u_value, "Orientation": comp.orientation.value, "ID": comp.id}
|
| 506 |
+
if component_type == ComponentType.WALL:
|
| 507 |
+
row.update({"Wall Type": comp.wall_type, "Wall Group": comp.wall_group})
|
| 508 |
+
elif component_type == ComponentType.ROOF:
|
| 509 |
+
row.update({"Roof Type": comp.roof_type, "Roof Group": comp.roof_group})
|
| 510 |
+
elif component_type == ComponentType.FLOOR:
|
| 511 |
+
row.update({"Floor Type": comp.floor_type})
|
| 512 |
+
elif component_type == ComponentType.WINDOW:
|
| 513 |
+
row.update({"SHGC": comp.shgc, "VT": comp.vt, "Window Type": comp.window_type,
|
| 514 |
+
"Effective SHGC": self.shading_system.calculate_effective_shgc(comp.shgc, comp.shading_device_id)})
|
| 515 |
+
elif component_type == ComponentType.DOOR:
|
| 516 |
+
row.update({"Door Type": comp.door_type})
|
| 517 |
+
data.append(row)
|
| 518 |
+
|
| 519 |
+
df = pd.DataFrame(data)
|
| 520 |
+
display_cols = [col for col in df.columns if col != "ID"]
|
| 521 |
+
st.dataframe(df[display_cols], use_container_width=True)
|
| 522 |
+
|
| 523 |
+
for i, row in df.iterrows():
|
| 524 |
+
col1, col2 = st.columns(2)
|
| 525 |
+
with col1:
|
| 526 |
+
if st.button("Edit", key=f"edit_{row['ID']}"):
|
| 527 |
+
session_state[f"edit_{type_name}"] = row["ID"]
|
| 528 |
+
st.rerun()
|
| 529 |
+
with col2:
|
| 530 |
+
if st.button("Delete", key=f"delete_{row['ID']}"):
|
| 531 |
+
if not row["ID"].startswith("preset_"):
|
| 532 |
+
self.component_library.remove_component(row["ID"])
|
| 533 |
+
session_state.components[type_name + 's'] = [c for c in components if c.id != row["ID"]]
|
| 534 |
+
st.success(f"Deleted {row['Name']}")
|
| 535 |
+
st.rerun()
|
| 536 |
+
else:
|
| 537 |
+
st.warning("Cannot delete preset components.")
|
| 538 |
+
|
| 539 |
+
if f"edit_{type_name}" in session_state:
|
| 540 |
+
self._display_edit_component_form(session_state, component_type, session_state[f"edit_{type_name}"])
|
| 541 |
+
del session_state[f"edit_{type_name}"]
|
| 542 |
+
|
| 543 |
+
def _display_edit_component_form(self, session_state: Any, component_type: ComponentType, component_id: str) -> None:
|
| 544 |
+
type_name = component_type.value.lower()
|
| 545 |
+
component = next((c for c in session_state.components[type_name + 's'] if c.id == component_id), None)
|
| 546 |
+
if not component:
|
| 547 |
+
st.error("Component not found.")
|
| 548 |
+
return
|
| 549 |
+
|
| 550 |
+
with st.form(f"edit_{component_id}_form"):
|
| 551 |
+
col1, col2 = st.columns(2)
|
| 552 |
+
with col1:
|
| 553 |
+
name = st.text_input("Name", value=component.name)
|
| 554 |
+
area = st.number_input("Area (m²)", min_value=0.0, value=component.area, step=0.1)
|
| 555 |
+
if component_type not in [ComponentType.ROOF, ComponentType.FLOOR]:
|
| 556 |
+
orientation = st.selectbox("Orientation", [o.value for o in Orientation],
|
| 557 |
+
index=[o.value for o in Orientation].index(component.orientation.value))
|
| 558 |
+
else:
|
| 559 |
+
orientation = Orientation.HORIZONTAL.value
|
| 560 |
+
|
| 561 |
+
with col2:
|
| 562 |
+
u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=component.u_value, step=0.01)
|
| 563 |
+
if component_type == ComponentType.WALL:
|
| 564 |
+
wall_type = st.text_input("Wall Type", value=component.wall_type)
|
| 565 |
+
wall_group = st.selectbox("Wall Group", ["A", "B", "C", "D", "E", "F", "G", "H"],
|
| 566 |
+
index=["A", "B", "C", "D", "E", "F", "G", "H"].index(component.wall_group))
|
| 567 |
+
elif component_type == ComponentType.ROOF:
|
| 568 |
+
roof_type = st.text_input("Roof Type", value=component.roof_type)
|
| 569 |
+
roof_group = st.selectbox("Roof Group", ["A", "B", "C", "D", "E", "F", "G"],
|
| 570 |
+
index=["A", "B", "C", "D", "E", "F", "G"].index(component.roof_group))
|
| 571 |
+
elif component_type == ComponentType.FLOOR:
|
| 572 |
+
floor_type = st.text_input("Floor Type", value=component.floor_type)
|
| 573 |
+
elif component_type == ComponentType.WINDOW:
|
| 574 |
+
shgc = st.number_input("SHGC", min_value=0.0, max_value=1.0, value=component.shgc, step=0.01)
|
| 575 |
+
vt = st.number_input("VT", min_value=0.0, max_value=1.0, value=component.vt, step=0.01)
|
| 576 |
+
window_type = st.text_input("Window Type", value=component.window_type)
|
| 577 |
+
glazing_layers = st.selectbox("Glazing Layers", [1, 2, 3], index=[1, 2, 3].index(component.glazing_layers))
|
| 578 |
+
gas_fill = st.selectbox("Gas Fill", ["Air", "Argon", "Krypton"],
|
| 579 |
+
index=["Air", "Argon", "Krypton"].index(component.gas_fill))
|
| 580 |
+
low_e_coating = st.checkbox("Low-E Coating", value=component.low_e_coating)
|
| 581 |
+
shading_options = {d.name: d.id for d in self.shading_system.shading_devices.values()}
|
| 582 |
+
shading_device = st.selectbox("Shading Device", options=list(shading_options.keys()),
|
| 583 |
+
index=list(shading_options.keys()).index(self.shading_system.get_device(component.shading_device_id).name))
|
| 584 |
+
coverage = st.number_input("Coverage (%)", min_value=0.0, max_value=100.0,
|
| 585 |
+
value=self.shading_system.get_device(component.shading_device_id).coverage_percentage, step=5.0)
|
| 586 |
+
device = self.shading_system.get_device(shading_options[shading_device])
|
| 587 |
+
device.coverage_percentage = coverage
|
| 588 |
+
st.write(f"Effective SHGC: {self.shading_system.calculate_effective_shgc(shgc, shading_options[shading_device]):.2f}")
|
| 589 |
+
elif component_type == ComponentType.DOOR:
|
| 590 |
+
door_type = st.text_input("Door Type", value=component.door_type)
|
| 591 |
+
|
| 592 |
+
submitted = st.form_submit_button("Update Component")
|
| 593 |
+
if submitted:
|
| 594 |
+
if not name:
|
| 595 |
+
st.error(f"{type_name.capitalize()} name is required!")
|
| 596 |
+
elif area <= 0:
|
| 597 |
+
st.error(f"{type_name.capitalize()} area must be greater than zero!")
|
| 598 |
+
elif u_value <= 0:
|
| 599 |
+
st.error(f"{type_name.capitalize()} U-value must be greater than zero!")
|
| 600 |
+
else:
|
| 601 |
+
component.name = name
|
| 602 |
+
component.area = area
|
| 603 |
+
component.u_value = u_value
|
| 604 |
+
component.orientation = Orientation(orientation)
|
| 605 |
+
if component_type == ComponentType.WALL:
|
| 606 |
+
component.wall_type = wall_type
|
| 607 |
+
component.wall_group = wall_group
|
| 608 |
+
elif component_type == ComponentType.ROOF:
|
| 609 |
+
component.roof_type = roof_type
|
| 610 |
+
component.roof_group = roof_group
|
| 611 |
+
elif component_type == ComponentType.FLOOR:
|
| 612 |
+
component.floor_type = floor_type
|
| 613 |
+
elif component_type == ComponentType.WINDOW:
|
| 614 |
+
component.shgc = shgc
|
| 615 |
+
component.vt = vt
|
| 616 |
+
component.window_type = window_type
|
| 617 |
+
component.glazing_layers = glazing_layers
|
| 618 |
+
component.gas_fill = gas_fill
|
| 619 |
+
component.low_e_coating = low_e_coating
|
| 620 |
+
component.shading_device_id = shading_options[shading_device]
|
| 621 |
+
elif component_type == ComponentType.DOOR:
|
| 622 |
+
component.door_type = door_type
|
| 623 |
+
self.component_library.update_component(component_id, component)
|
| 624 |
+
st.success(f"Updated {name}")
|
| 625 |
+
st.rerun()
|
| 626 |
|
| 627 |
def _display_u_value_calculator_tab(self, session_state: Any) -> None:
|
| 628 |
st.subheader("U-Value Calculator")
|
|
|
|
| 712 |
session_state.material_layers.pop()
|
| 713 |
st.rerun()
|
| 714 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 715 |
def _save_components(self, session_state: Any) -> None:
|
| 716 |
components_dict = {
|
| 717 |
"walls": [c.to_dict() for c in session_state.components["walls"]],
|