mabuseif commited on
Commit
fc5d8bf
·
verified ·
1 Parent(s): a565314

Update app/component_selection.py

Browse files
Files changed (1) hide show
  1. app/component_selection.py +203 -228
app/component_selection.py CHANGED
@@ -331,247 +331,222 @@ class ComponentSelectionInterface:
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()
380
- preset_components = self.component_library.get_preset_components_by_type(component_type)
381
-
382
- # Add a flag to prevent infinite rerun
383
- if f"add_{type_name}_submitted" not in session_state:
384
- session_state[f"add_{type_name}_submitted"] = False
385
-
386
- with st.form(f"add_{type_name}_form"):
387
- col1, col2 = st.columns(2)
388
- with col1:
389
- name = st.text_input("Name", f"New {type_name.capitalize()}")
390
- area = st.number_input("Area (m²)", min_value=0.0, value=1.0, step=0.1)
391
- if component_type not in [ComponentType.ROOF, ComponentType.FLOOR]:
392
- orientation = st.selectbox("Orientation", [o.value for o in Orientation], index=0)
393
- else:
394
- orientation = Orientation.HORIZONTAL.value
395
 
396
- with col2:
397
- selection_method = st.radio(f"{type_name.capitalize()} Selection Method", ["Select from Presets", "Custom Properties"])
398
- if selection_method == "Select from Presets" and preset_components:
399
- preset_options = {comp.name: comp.id for comp in preset_components}
400
- selected_preset = st.selectbox(f"Select Preset {type_name.capitalize()}", options=list(preset_options.keys()))
401
- component = self.component_library.get_component(preset_options[selected_preset])
402
- u_value = st.number_input("U-Value (W/m²·K)", value=component.u_value, disabled=True)
403
- # ... preset display logic ...
404
- else:
405
- u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=0.5, step=0.01)
406
- if component_type == ComponentType.WALL:
407
- wall_type = st.text_input("Wall Type", "Custom")
408
- wall_group = st.selectbox("Wall Group", ["A", "B", "C", "D", "E", "F", "G", "H"], index=1)
409
- elif component_type == ComponentType.ROOF:
410
- roof_type = st.text_input("Roof Type", "Custom")
411
- roof_group = st.selectbox("Roof Group", ["A", "B", "C", "D", "E", "F", "G"], index=2)
412
- elif component_type == ComponentType.FLOOR:
413
- floor_type = st.text_input("Floor Type", "Custom")
414
- elif component_type == ComponentType.WINDOW:
415
- shgc = st.number_input("SHGC", min_value=0.0, max_value=1.0, value=0.7, step=0.01)
416
- vt = st.number_input("VT", min_value=0.0, max_value=1.0, value=0.7, step=0.01)
417
- window_type = st.text_input("Window Type", "Custom")
418
- glazing_layers = st.selectbox("Glazing Layers", [1, 2, 3], index=1)
419
- gas_fill = st.selectbox("Gas Fill", ["Air", "Argon", "Krypton"], index=0)
420
- low_e_coating = st.checkbox("Low-E Coating")
421
- shading_options = {d.name: d.id for d in self.shading_system.shading_devices.values()}
422
- shading_device = st.selectbox("Shading Device", options=list(shading_options.keys()), index=0)
423
- coverage = st.number_input("Coverage (%)", min_value=0.0, max_value=100.0, value=100.0, step=5.0)
424
- device = self.shading_system.get_device(shading_options[shading_device])
425
- device.coverage_percentage = coverage
426
- st.write(f"Effective SHGC: {self.shading_system.calculate_effective_shgc(shgc, shading_options[shading_device]):.2f}")
427
- elif component_type == ComponentType.DOOR:
428
- door_type = st.text_input("Door Type", "Custom")
429
 
430
- submitted = st.form_submit_button("Add Component")
431
- if submitted and not session_state[f"add_{type_name}_submitted"]:
432
- if not name:
433
- st.error(f"{type_name.capitalize()} name is required!")
434
- elif area <= 0:
435
- st.error(f"{type_name.capitalize()} area must be greater than zero!")
436
- elif u_value <= 0:
437
- st.error(f"{type_name.capitalize()} U-value must be greater than zero!")
438
- else:
439
- try:
440
- if selection_method == "Select from Presets" and preset_components:
441
- component_id = preset_options[selected_preset]
442
- component = self.component_library.get_component(component_id)
443
- new_component = component.__class__(
444
- id=str(uuid.uuid4()), name=name, area=area,
445
- orientation=Orientation(orientation), **component.__dict__
446
- )
447
- del new_component.__dict__["id"]
448
- else:
449
- if component_type == ComponentType.WALL:
450
- new_component = Wall(
451
- id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
452
- orientation=Orientation(orientation), wall_type=wall_type, wall_group=wall_group
453
- )
454
- elif component_type == ComponentType.ROOF:
455
- new_component = Roof(
456
- id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
457
- orientation=Orientation(orientation), roof_type=roof_type, roof_group=roof_group
458
- )
459
- elif component_type == ComponentType.FLOOR:
460
- new_component = Floor(
461
- id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
462
- orientation=Orientation(orientation), floor_type=floor_type
463
- )
464
- elif component_type == ComponentType.WINDOW:
465
- new_component = Window(
466
- id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
467
- orientation=Orientation(orientation), shgc=shgc, vt=vt, window_type=window_type,
468
- glazing_layers=glazing_layers, gas_fill=gas_fill, low_e_coating=low_e_coating,
469
- shading_device_id=shading_options[shading_device]
470
- )
471
- elif component_type == ComponentType.DOOR:
472
- new_component = Door(
473
- id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
474
- orientation=Orientation(orientation), door_type=door_type
475
- )
476
- self.component_library.add_component(new_component)
477
- session_state.components[type_name + 's'].append(new_component)
478
- st.success(f"Added {new_component.name}")
479
- session_state[f"add_{type_name}_submitted"] = True # Set flag to prevent rerun loop
480
- st.rerun()
481
- except ValueError as e:
482
- st.error(f"Error: {str(e)}")
483
-
484
- # Reset the flag after rerun to allow new submissions
485
- if session_state[f"add_{type_name}_submitted"]:
486
- session_state[f"add_{type_name}_submitted"] = False
487
-
488
- def _display_u_value_calculator_tab(self, session_state: Any) -> None:
489
- st.subheader("U-Value Calculator")
490
-
491
- if "material_layers" not in session_state:
492
- session_state.material_layers = []
493
-
494
- if "u_value_applied" not in session_state:
495
- session_state.u_value_applied = False
496
-
497
- components = []
498
- for type_name in ['walls', 'roofs', 'floors', 'windows', 'doors']:
499
- components.extend(session_state.components[type_name])
500
-
501
- if not components:
502
- st.warning("No components available. Add components first.")
503
- return
504
-
505
- selected_component = st.selectbox("Select Component", options=[c.name for c in components])
506
- component = next((c for c in components if c.name == selected_component), None)
507
- if not component:
508
- st.error("Component not found.")
509
- return
510
-
511
- if session_state.material_layers:
512
- st.write("Current Material Layers (outside to inside):")
513
- layer_data = [{"Layer": i+1, "Material": l["name"], "Thickness (mm)": l["thickness"],
514
- "Conductivity (W/m·K)": l["conductivity"], "R-Value (m²·K/W)": l["thickness"] / 1000 / l["conductivity"]}
515
- for i, l in enumerate(session_state.material_layers)]
516
- st.dataframe(pd.DataFrame(layer_data), use_container_width=True)
517
 
518
- u_value = self.u_value_calculator.calculate_u_value(session_state.material_layers)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
 
520
- col1, col2, col3 = st.columns(3)
521
- with col1:
522
- st.metric("Total R-Value", f"{1/u_value if u_value > 0 else 0:.3f} m²·K/W")
523
- with col2:
524
- st.metric("U-Value", f"{u_value:.3f} W/m²·K")
525
- with col3:
526
- if st.button("Use This U-Value") and not session_state.u_value_applied:
527
- component.u_value = u_value
528
- self.component_library.update_component(component.id, component)
529
- session_state.material_layers = []
530
- st.success(f"U-Value set to {u_value:.3f} W/m²·K for {component.name}")
531
- session_state.u_value_applied = True # Set flag to prevent rerun loop
532
- st.rerun()
533
-
534
- # Reset the flag after rerun to allow new calculations
535
- if session_state.u_value_applied:
536
- session_state.u_value_applied = False
537
-
538
- with st.form("material_layer_form"):
539
- col1, col2 = st.columns(2)
540
- with col1:
541
- material_selection = st.radio("Material Selection", ["Select from Presets", "Custom Material"])
542
- if material_selection == "Select from Presets":
543
- preset_materials = self.u_value_calculator.get_preset_materials()
544
- material_options = {m["name"]: m for m in preset_materials}
545
- material_name = st.selectbox("Material", options=list(material_options.keys()))
546
- conductivity = material_options[material_name]["conductivity"]
547
- st.number_input("Conductivity (W/m·K)", value=conductivity, disabled=True)
548
- else:
549
- material_name = st.text_input("Material Name", "Custom Material")
550
- conductivity = st.number_input("Conductivity (W/m·K)", min_value=0.0, value=1.0, step=0.01)
551
 
552
- with col2:
553
- thickness = st.number_input("Thickness (mm)", min_value=0.0, value=100.0, step=1.0)
554
- if conductivity > 0:
555
- r_value = thickness / 1000 / conductivity
556
- st.metric("Layer R-Value", f"{r_value:.3f} m²·K/W")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
- submitted = st.form_submit_button("Add Layer")
559
- if submitted:
560
- if not material_name:
561
- st.error("Material name is required!")
562
- elif thickness <= 0:
563
- st.error("Thickness must be greater than zero!")
564
- elif conductivity <= 0:
565
- st.error("Conductivity must be greater than zero!")
566
- else:
567
- session_state.material_layers.append({"name": material_name, "thickness": thickness, "conductivity": conductivity})
568
- st.success(f"Added layer: {material_name}")
569
  st.rerun()
570
-
571
- if st.button("Remove Last Layer"):
572
- if session_state.material_layers:
573
- session_state.material_layers.pop()
574
- st.rerun()
575
 
576
  with st.form("material_layer_form"):
577
  col1, col2 = st.columns(2)
 
331
  self.shading_system = shading_system
332
  self.reference_data = reference_data
333
 
334
+ # ... other methods like display_component_selection, _display_component_tab, etc. ...
335
+
336
+ def _display_add_component_form(self, session_state: Any, component_type: ComponentType) -> None:
337
+ type_name = component_type.value.lower()
338
+ preset_components = self.component_library.get_preset_components_by_type(component_type)
339
 
340
+ # Add a flag to prevent infinite rerun
341
+ if f"add_{type_name}_submitted" not in session_state:
342
+ session_state[f"add_{type_name}_submitted"] = False
343
 
344
+ with st.form(f"add_{type_name}_form"):
345
+ col1, col2 = st.columns(2)
346
+ with col1:
347
+ name = st.text_input("Name", f"New {type_name.capitalize()}")
348
+ area = st.number_input("Area (m²)", min_value=0.0, value=1.0, step=0.1)
349
+ if component_type not in [ComponentType.ROOF, ComponentType.FLOOR]:
350
+ orientation = st.selectbox("Orientation", [o.value for o in Orientation], index=0)
351
+ else:
352
+ orientation = Orientation.HORIZONTAL.value
353
+
354
+ with col2:
355
+ selection_method = st.radio(f"{type_name.capitalize()} Selection Method", ["Select from Presets", "Custom Properties"])
356
+ if selection_method == "Select from Presets" and preset_components:
357
+ preset_options = {comp.name: comp.id for comp in preset_components}
358
+ selected_preset = st.selectbox(f"Select Preset {type_name.capitalize()}", options=list(preset_options.keys()))
359
+ component = self.component_library.get_component(preset_options[selected_preset])
360
+ u_value = st.number_input("U-Value (W/m²·K)", value=component.u_value, disabled=True)
361
+ if component_type == ComponentType.WALL:
362
+ st.text_input("Wall Type", value=component.wall_type, disabled=True)
363
+ st.text_input("Wall Group", value=component.wall_group, disabled=True)
364
+ elif component_type == ComponentType.ROOF:
365
+ st.text_input("Roof Type", value=component.roof_type, disabled=True)
366
+ st.text_input("Roof Group", value=component.roof_group, disabled=True)
367
+ elif component_type == ComponentType.FLOOR:
368
+ st.text_input("Floor Type", value=component.floor_type, disabled=True)
369
+ elif component_type == ComponentType.WINDOW:
370
+ st.number_input("SHGC", value=component.shgc, disabled=True)
371
+ st.number_input("VT", value=component.vt, disabled=True)
372
+ st.text_input("Window Type", value=component.window_type, disabled=True)
373
+ elif component_type == ComponentType.DOOR:
374
+ st.text_input("Door Type", value=component.door_type, disabled=True)
375
+ else:
376
+ u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=0.5, step=0.01)
377
+ if component_type == ComponentType.WALL:
378
+ wall_type = st.text_input("Wall Type", "Custom")
379
+ wall_group = st.selectbox("Wall Group", ["A", "B", "C", "D", "E", "F", "G", "H"], index=1)
380
+ elif component_type == ComponentType.ROOF:
381
+ roof_type = st.text_input("Roof Type", "Custom")
382
+ roof_group = st.selectbox("Roof Group", ["A", "B", "C", "D", "E", "F", "G"], index=2)
383
+ elif component_type == ComponentType.FLOOR:
384
+ floor_type = st.text_input("Floor Type", "Custom")
385
+ elif component_type == ComponentType.WINDOW:
386
+ shgc = st.number_input("SHGC", min_value=0.0, max_value=1.0, value=0.7, step=0.01)
387
+ vt = st.number_input("VT", min_value=0.0, max_value=1.0, value=0.7, step=0.01)
388
+ window_type = st.text_input("Window Type", "Custom")
389
+ glazing_layers = st.selectbox("Glazing Layers", [1, 2, 3], index=1)
390
+ gas_fill = st.selectbox("Gas Fill", ["Air", "Argon", "Krypton"], index=0)
391
+ low_e_coating = st.checkbox("Low-E Coating")
392
+ shading_options = {d.name: d.id for d in self.shading_system.shading_devices.values()}
393
+ shading_device = st.selectbox("Shading Device", options=list(shading_options.keys()), index=0)
394
+ coverage = st.number_input("Coverage (%)", min_value=0.0, max_value=100.0, value=100.0, step=5.0)
395
+ device = self.shading_system.get_device(shading_options[shading_device])
396
+ device.coverage_percentage = coverage
397
+ st.write(f"Effective SHGC: {self.shading_system.calculate_effective_shgc(shgc, shading_options[shading_device]):.2f}")
398
+ elif component_type == ComponentType.DOOR:
399
+ door_type = st.text_input("Door Type", "Custom")
400
+
401
+ submitted = st.form_submit_button("Add Component")
402
+ if submitted and not session_state[f"add_{type_name}_submitted"]:
403
+ if not name:
404
+ st.error(f"{type_name.capitalize()} name is required!")
405
+ elif area <= 0:
406
+ st.error(f"{type_name.capitalize()} area must be greater than zero!")
407
+ elif u_value <= 0:
408
+ st.error(f"{type_name.capitalize()} U-value must be greater than zero!")
409
+ else:
410
+ try:
411
+ if selection_method == "Select from Presets" and preset_components:
412
+ component_id = preset_options[selected_preset]
413
+ component = self.component_library.get_component(component_id)
414
+ new_component = component.__class__(
415
+ id=str(uuid.uuid4()), name=name, area=area,
416
+ orientation=Orientation(orientation), **component.__dict__
417
+ )
418
+ del new_component.__dict__["id"]
419
+ else:
420
+ if component_type == ComponentType.WALL:
421
+ new_component = Wall(
422
+ id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
423
+ orientation=Orientation(orientation), wall_type=wall_type, wall_group=wall_group
424
+ )
425
+ elif component_type == ComponentType.ROOF:
426
+ new_component = Roof(
427
+ id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
428
+ orientation=Orientation(orientation), roof_type=roof_type, roof_group=roof_group
429
+ )
430
+ elif component_type == ComponentType.FLOOR:
431
+ new_component = Floor(
432
+ id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
433
+ orientation=Orientation(orientation), floor_type=floor_type
434
+ )
435
+ elif component_type == ComponentType.WINDOW:
436
+ new_component = Window(
437
+ id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
438
+ orientation=Orientation(orientation), shgc=shgc, vt=vt, window_type=window_type,
439
+ glazing_layers=glazing_layers, gas_fill=gas_fill, low_e_coating=low_e_coating,
440
+ shading_device_id=shading_options[shading_device]
441
+ )
442
+ elif component_type == ComponentType.DOOR:
443
+ new_component = Door(
444
+ id=str(uuid.uuid4()), name=name, u_value=u_value, area=area,
445
+ orientation=Orientation(orientation), door_type=door_type
446
+ )
447
+ self.component_library.add_component(new_component)
448
+ session_state.components[type_name + 's'].append(new_component)
449
+ st.success(f"Added {new_component.name}")
450
+ session_state[f"add_{type_name}_submitted"] = True # Set flag to prevent rerun loop
451
+ st.rerun()
452
+ except ValueError as e:
453
+ st.error(f"Error: {str(e)}")
454
 
455
+ # Reset the flag after rerun to allow new submissions
456
+ if session_state[f"add_{type_name}_submitted"]:
457
+ session_state[f"add_{type_name}_submitted"] = False
458
+
459
+ # ... other methods ...
460
+
461
+ def _display_u_value_calculator_tab(self, session_state: Any) -> None:
462
+ st.subheader("U-Value Calculator")
 
 
 
 
463
 
464
+ if "material_layers" not in session_state:
465
+ session_state.material_layers = []
 
 
 
 
 
 
 
 
 
 
466
 
467
+ if "u_value_applied" not in session_state:
468
+ session_state.u_value_applied = False
469
 
470
+ components = []
471
+ for type_name in ['walls', 'roofs', 'floors', 'windows', 'doors']:
472
+ components.extend(session_state.components[type_name])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
 
474
+ if not components:
475
+ st.warning("No components available. Add components first.")
476
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
 
478
+ selected_component = st.selectbox("Select Component", options=[c.name for c in components])
479
+ component = next((c for c in components if c.name == selected_component), None)
480
+ if not component:
481
+ st.error("Component not found.")
482
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
 
484
+ if session_state.material_layers:
485
+ st.write("Current Material Layers (outside to inside):")
486
+ layer_data = [{"Layer": i+1, "Material": l["name"], "Thickness (mm)": l["thickness"],
487
+ "Conductivity (W/m·K)": l["conductivity"], "R-Value (m²·K/W)": l["thickness"] / 1000 / l["conductivity"]}
488
+ for i, l in enumerate(session_state.material_layers)]
489
+ st.dataframe(pd.DataFrame(layer_data), use_container_width=True)
490
+
491
+ u_value = self.u_value_calculator.calculate_u_value(session_state.material_layers)
492
+
493
+ col1, col2, col3 = st.columns(3)
494
+ with col1:
495
+ st.metric("Total R-Value", f"{1/u_value if u_value > 0 else 0:.3f} m²·K/W")
496
+ with col2:
497
+ st.metric("U-Value", f"{u_value:.3f} W/m²·K")
498
+ with col3:
499
+ if st.button("Use This U-Value") and not session_state.u_value_applied:
500
+ component.u_value = u_value
501
+ self.component_library.update_component(component.id, component)
502
+ session_state.material_layers = []
503
+ st.success(f"U-Value set to {u_value:.3f} W/m²·K for {component.name}")
504
+ session_state.u_value_applied = True # Set flag to prevent rerun loop
505
+ st.rerun()
506
 
507
+ # Reset the flag after rerun to allow new calculations
508
+ if session_state.u_value_applied:
509
+ session_state.u_value_applied = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
 
511
+ with st.form("material_layer_form"):
512
+ col1, col2 = st.columns(2)
513
+ with col1:
514
+ material_selection = st.radio("Material Selection", ["Select from Presets", "Custom Material"])
515
+ if material_selection == "Select from Presets":
516
+ preset_materials = self.u_value_calculator.get_preset_materials()
517
+ material_options = {m["name"]: m for m in preset_materials}
518
+ material_name = st.selectbox("Material", options=list(material_options.keys()))
519
+ conductivity = material_options[material_name]["conductivity"]
520
+ st.number_input("Conductivity (W/m·K)", value=conductivity, disabled=True)
521
+ else:
522
+ material_name = st.text_input("Material Name", "Custom Material")
523
+ conductivity = st.number_input("Conductivity (W/m·K)", min_value=0.0, value=1.0, step=0.01)
524
+
525
+ with col2:
526
+ thickness = st.number_input("Thickness (mm)", min_value=0.0, value=100.0, step=1.0)
527
+ if conductivity > 0:
528
+ r_value = thickness / 1000 / conductivity
529
+ st.metric("Layer R-Value", f"{r_value:.3f} m²·K/W")
530
+
531
+ submitted = st.form_submit_button("Add Layer")
532
+ if submitted:
533
+ if not material_name:
534
+ st.error("Material name is required!")
535
+ elif thickness <= 0:
536
+ st.error("Thickness must be greater than zero!")
537
+ elif conductivity <= 0:
538
+ st.error("Conductivity must be greater than zero!")
539
+ else:
540
+ session_state.material_layers.append({"name": material_name, "thickness": thickness, "conductivity": conductivity})
541
+ st.success(f"Added layer: {material_name}")
542
+ st.rerun()
543
 
544
+ if st.button("Remove Last Layer"):
545
+ if session_state.material_layers:
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)