File size: 5,665 Bytes
3284726 19d55c2 3284726 19d55c2 3284726 6fed30b 3284726 19d55c2 3284726 19d55c2 3284726 19d55c2 3284726 19d55c2 3284726 19d55c2 3284726 19d55c2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | import json
from pathlib import Path
import numpy as np
import pandas as pd
import xgboost as xgb
import gradio as gr
import spaces
MODEL_PATH = Path("surrogate_peak_temp.json")
BENCHMARK_JSON = Path("benchmark_results.json")
def load_surrogate():
if not MODEL_PATH.exists():
return None
model = xgb.XGBRegressor()
model.load_model(str(MODEL_PATH))
return model
model = load_surrogate()
@spaces.GPU
def predict_thermal_performance(wall_t, roof_alb, shading, wwr, vent_ach):
if model is None:
return "Model missing", "Model missing"
# Order: [wall_t, vent_ach, roof_alb, shading, wwr]
x_in = np.array([[wall_t, vent_ach, roof_alb, shading, wwr]])
pred_peak = float(model.predict(x_in)[0])
peak_suppression = 45.52 - pred_peak
return f"{pred_peak:.2f} °C", f"{peak_suppression:.2f} °C"
def get_benchmark_table(mode_choice):
if not BENCHMARK_JSON.exists():
return pd.DataFrame([{"Message": "Run `python cli.py --benchmark` to generate ablation results."}])
with open(BENCHMARK_JSON) as f:
bench_data = json.load(f)
mode_key = "as_built" if "Mode A" in mode_choice else "shared_interior"
table_rows = []
# Ambient row
table_rows.append({
"Archetype": bench_data["ambient"]["label"],
"Peak Temp [°C]": f"{bench_data['ambient']['peak']:.2f} °C",
"Mean Temp [°C]": f"{bench_data['ambient']['mean']:.2f} °C",
"DDH (>30°C)": f"{bench_data['ambient']['ddh_30']:.1f}",
"Daily Decrement (f)": f"{bench_data['ambient']['decrement_f']:.2f}"
})
for k, v in bench_data[mode_key].items():
table_rows.append({
"Archetype": v["label"],
"Peak Temp [°C]": f"{v['peak']:.2f} °C",
"Mean Temp [°C]": f"{v['mean']:.2f} °C",
"DDH (>30°C)": f"{v['ddh_30']:.1f}",
"Daily Decrement (f)": f"{v['decrement_f']:.2f}"
})
return pd.DataFrame(table_rows)
def get_image_path(filename):
path = Path(filename)
return str(path) if path.exists() else None
with gr.Blocks(title="HeatBee - Vernacular Thermal Surrogate") as demo:
gr.Markdown("# 🏛️ HeatBee: Vernacular Thermal Physics & AI Surrogate")
gr.Markdown("**Surrogate Modeling of Moroccan Rammed Earth (*Pisé*) Architecture**\n*EnergyPlus 26.2 1D Conduction Finite Difference | Marrakesh Menara EPW (Hottest Outdoor Hour: 45.52°C)*")
if model is None:
gr.Warning("Surrogate model not found. Please run `python cli.py --train` first.")
with gr.Row():
with gr.Column():
gr.Markdown("### 🛠️ Design Envelope Controls")
wall_t = gr.Slider(0.25, 0.60, value=0.50, step=0.05, label="Pisé Wall Thickness [m]", info="Analytical diurnal penetration depth is ~11 cm. Beyond 45 cm, mass yields diminishing returns.")
roof_alb = gr.Slider(0.20, 0.85, value=0.60, step=0.05, label="Roof Solar Reflectance (Albedo)", info="Solar reflectance of horizontal roof finish. Dominant thermal parameter.")
shading = gr.Slider(0.10, 0.80, value=0.40, step=0.05, label="Mashrabiya Glazing SHGC", info="Solar Heat Gain Coefficient. Lower value represents denser shading.")
wwr = gr.Slider(0.20, 0.50, value=0.25, step=0.05, label="Courtyard Window-to-Wall Ratio")
vent_ach = gr.Slider(1.0, 12.0, value=4.0, step=0.5, label="Ventilation Rate [ACH]", info="Free cooling ventilation rate when outdoor air is cooler than indoors.")
with gr.Column():
gr.Markdown("### ⚡ Real-Time Physics Inference (< 1 ms)")
pred_out = gr.Textbox(label="Predicted Peak Operative Temp")
supp_out = gr.Textbox(label="Peak Suppression vs Ambient", info="Difference between peak outdoor ambient (45.52°C) and indoor peak operative temperature.")
gr.Markdown("---")
gr.Markdown("### 🔬 Benchmark Comparison")
mode_choice = gr.Radio(
choices=["Mode A: As-Built Typologies (Archetype Partitions)", "Mode B: Shared Interior Mass (15cm Concrete Slab for All)"],
value="Mode A: As-Built Typologies (Archetype Partitions)",
label="Benchmark Mode:"
)
bench_table = gr.Dataframe(interactive=False)
gr.Markdown("*Shared boundary conditions: Roof albedo 0.60, external wall solar absorptance 0.50, glazing SHGC 0.40, 4.0 ACH free cooling, adiabatic floor and party walls.*")
gr.Markdown("---")
gr.Markdown("### 📊 Visualizations")
with gr.Tabs():
with gr.Tab("1-to-1 Heatwave Ablation Profile"):
gr.Image(value=get_image_path("rigorous_ablation_heatwave_profile.png"), show_label=False)
with gr.Tab("Direct Physical Sweeps"):
gr.Image(value=get_image_path("direct_physics_sweeps.png"), show_label=False)
with gr.Tab("Surrogate Parity"):
gr.Image(value=get_image_path("surrogate_parity_plot.png"), show_label=False)
# Reactive Bindings
inputs = [wall_t, roof_alb, shading, wwr, vent_ach]
outputs = [pred_out, supp_out]
for component in inputs:
component.change(fn=predict_thermal_performance, inputs=inputs, outputs=outputs)
demo.load(fn=predict_thermal_performance, inputs=inputs, outputs=outputs)
mode_choice.change(fn=get_benchmark_table, inputs=[mode_choice], outputs=[bench_table])
demo.load(fn=get_benchmark_table, inputs=[mode_choice], outputs=[bench_table])
if __name__ == "__main__":
demo.launch() |