Spaces:
Sleeping
Sleeping
Update app/hvac_loads.py
Browse files- app/hvac_loads.py +457 -447
app/hvac_loads.py
CHANGED
@@ -1150,476 +1150,486 @@ def display_hvac_results_ui(loads: List[Dict[str, Any]], run_id: str = "default"
|
|
1150 |
def display_hvac_loads_page():
|
1151 |
"""
|
1152 |
Display the HVAC Loads page in the Streamlit application.
|
1153 |
-
|
1154 |
-
Clears previous results when a new simulation is run.
|
1155 |
"""
|
1156 |
try:
|
1157 |
st.header("HVAC Loads")
|
1158 |
st.markdown("Configure and calculate HVAC loads for the building.")
|
1159 |
|
1160 |
-
#
|
1161 |
-
if
|
1162 |
-
st.session_state.project_data
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
run_id = str(uuid.uuid4())
|
1169 |
-
|
1170 |
-
# Check for existing results
|
1171 |
-
loads = []
|
1172 |
-
if st.session_state.project_data["hvac_loads"]["cooling"]["hourly"] or \
|
1173 |
-
st.session_state.project_data["hvac_loads"]["heating"]["hourly"]:
|
1174 |
-
loads = (
|
1175 |
-
st.session_state.project_data["hvac_loads"]["cooling"]["hourly"] +
|
1176 |
-
st.session_state.project_data["hvac_loads"]["heating"]["hourly"]
|
1177 |
-
)
|
1178 |
-
# Sort loads by month, day, hour to ensure consistent display
|
1179 |
-
loads = sorted(loads, key=lambda x: (x["month"], x["day"], x["hour"]))
|
1180 |
-
if loads:
|
1181 |
-
st.info("Displaying previously calculated HVAC load results.")
|
1182 |
-
display_hvac_results_ui(loads, run_id=run_id)
|
1183 |
-
|
1184 |
-
# Location Information
|
1185 |
-
st.subheader("Location Information")
|
1186 |
-
climate_data = st.session_state.project_data["climate_data"]
|
1187 |
-
location_data = {
|
1188 |
-
"Country": climate_data.get("location", {}).get("country", ""),
|
1189 |
-
"City": climate_data.get("location", {}).get("city", ""),
|
1190 |
-
"State/Province": climate_data.get("location", {}).get("state_province", ""),
|
1191 |
-
"Latitude": climate_data.get("location", {}).get("latitude", 0.0),
|
1192 |
-
"Longitude": climate_data.get("location", {}).get("longitude", 0.0),
|
1193 |
-
"Elevation": climate_data.get("location", {}).get("elevation", 0.0),
|
1194 |
-
"Time Zone": climate_data.get("location", {}).get("timezone", "UTC"),
|
1195 |
-
"Ground Reflectivity": climate_data.get("ground_reflectivity", 0.2)
|
1196 |
-
}
|
1197 |
-
|
1198 |
-
# Create two rows with four columns each
|
1199 |
-
col1, col2, col3, col4 = st.columns(4)
|
1200 |
-
with col1:
|
1201 |
-
country = st.text_input("Country", value=location_data["Country"], key="hvac_country")
|
1202 |
-
with col2:
|
1203 |
-
city = st.text_input("City", value=location_data["City"], key="hvac_city")
|
1204 |
-
with col3:
|
1205 |
-
state_province = st.text_input("State/Province", value=location_data["State/Province"], key="hvac_state_province")
|
1206 |
-
with col4:
|
1207 |
-
latitude = st.number_input(
|
1208 |
-
"Latitude (°)",
|
1209 |
-
min_value=-90.0,
|
1210 |
-
max_value=90.0,
|
1211 |
-
value=location_data["Latitude"],
|
1212 |
-
step=0.1,
|
1213 |
-
key="hvac_latitude"
|
1214 |
)
|
1215 |
-
|
1216 |
-
col5, col6, col7, col8 = st.columns(4)
|
1217 |
-
with col5:
|
1218 |
-
longitude = st.number_input(
|
1219 |
-
"Longitude (°)",
|
1220 |
-
min_value=-180.0,
|
1221 |
-
max_value=180.0,
|
1222 |
-
value=location_data["Longitude"],
|
1223 |
-
step=0.1,
|
1224 |
-
key="hvac_longitude"
|
1225 |
-
)
|
1226 |
-
with col6:
|
1227 |
-
elevation = st.number_input(
|
1228 |
-
"Elevation (m)",
|
1229 |
-
min_value=0.0,
|
1230 |
-
max_value=10000.0,
|
1231 |
-
value=location_data["Elevation"],
|
1232 |
-
step=1.0,
|
1233 |
-
key="hvac_elevation"
|
1234 |
-
)
|
1235 |
-
with col7:
|
1236 |
-
timezone = st.number_input(
|
1237 |
-
"Time Zone (UTC offset)",
|
1238 |
-
min_value=-12.0,
|
1239 |
-
max_value=14.0,
|
1240 |
-
value=float(location_data["Time Zone"]) if isinstance(location_data["Time Zone"], (int, float, str)) else 0.0,
|
1241 |
-
step=0.5,
|
1242 |
-
key="hvac_timezone"
|
1243 |
-
)
|
1244 |
-
with col8:
|
1245 |
-
ground_reflectivity = st.number_input(
|
1246 |
-
"Ground Reflectivity",
|
1247 |
-
min_value=0.0,
|
1248 |
-
max_value=1.0,
|
1249 |
-
value=location_data["Ground Reflectivity"],
|
1250 |
-
step=0.01,
|
1251 |
-
key="hvac_ground_reflectivity"
|
1252 |
-
)
|
1253 |
-
|
1254 |
-
if st.button("Save Location"):
|
1255 |
-
st.session_state.project_data["climate_data"]["location"].update({
|
1256 |
-
"country": country,
|
1257 |
-
"city": city,
|
1258 |
-
"state_province": state_province,
|
1259 |
-
"latitude": latitude,
|
1260 |
-
"longitude": longitude,
|
1261 |
-
"elevation": elevation,
|
1262 |
-
"timezone": timezone
|
1263 |
-
})
|
1264 |
-
st.session_state.project_data["climate_data"]["ground_reflectivity"] = ground_reflectivity
|
1265 |
-
st.success("Location information saved successfully.")
|
1266 |
-
logger.info("Location information updated in session state")
|
1267 |
-
|
1268 |
-
# Simulation Period Configuration
|
1269 |
-
st.subheader("Simulation Period")
|
1270 |
-
sim_type = st.selectbox(
|
1271 |
-
"Simulation Type",
|
1272 |
-
["Full Year", "From Date to Date", "Heating Only", "Cooling Only",
|
1273 |
-
"Summer Extreme", "Summer Typical", "Winter Extreme", "Winter Typical"],
|
1274 |
-
key="hvac_sim_type",
|
1275 |
-
index=["Full Year", "From Date to Date", "Heating Only", "Cooling Only",
|
1276 |
-
"Summer Extreme", "Summer Typical", "Winter Extreme", "Winter Typical"].index(
|
1277 |
-
st.session_state.project_data["sim_period"]["type"]
|
1278 |
-
) if st.session_state.project_data["sim_period"]["type"] in
|
1279 |
-
["Full Year", "From Date to Date", "Heating Only", "Cooling Only",
|
1280 |
-
"Summer Extreme", "Summer Typical", "Winter Extreme", "Winter Typical"] else 0
|
1281 |
-
)
|
1282 |
-
st.session_state.project_data["sim_period"]["type"] = sim_type
|
1283 |
|
1284 |
-
|
1285 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1286 |
with col1:
|
1287 |
-
|
1288 |
-
"Start Date",
|
1289 |
-
value=st.session_state.project_data["sim_period"]["start_date"] or datetime(2025, 1, 1),
|
1290 |
-
key="hvac_start_date"
|
1291 |
-
)
|
1292 |
with col2:
|
1293 |
-
|
1294 |
-
|
1295 |
-
|
1296 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1297 |
)
|
1298 |
-
|
1299 |
-
|
1300 |
-
|
1301 |
-
|
1302 |
-
|
1303 |
-
|
1304 |
-
|
1305 |
-
|
1306 |
-
step=0.1,
|
1307 |
-
key="hvac_base_temp"
|
1308 |
-
)
|
1309 |
-
st.session_state.project_data["sim_period"]["base_temp"] = base_temp
|
1310 |
-
|
1311 |
-
# Indoor Conditions Configuration
|
1312 |
-
st.subheader("Indoor Conditions")
|
1313 |
-
indoor_type = st.selectbox(
|
1314 |
-
"Indoor Conditions Type",
|
1315 |
-
["Fixed Setpoints", "ASHRAE 55 Adaptive Comfort"],
|
1316 |
-
key="hvac_indoor_type",
|
1317 |
-
index=["Fixed Setpoints", "ASHRAE 55 Adaptive Comfort"].index(st.session_state.project_data["indoor_conditions"]["type"])
|
1318 |
-
)
|
1319 |
-
st.session_state.project_data["indoor_conditions"]["type"] = indoor_type
|
1320 |
-
|
1321 |
-
if indoor_type == "Fixed Setpoints":
|
1322 |
-
col1, col2 = st.columns(2)
|
1323 |
-
with col1:
|
1324 |
-
cooling_temp = st.number_input(
|
1325 |
-
"Cooling Setpoint Temperature (°C)",
|
1326 |
-
min_value=18.0,
|
1327 |
-
max_value=30.0,
|
1328 |
-
value=st.session_state.project_data["indoor_conditions"]["cooling_setpoint"]["temperature"],
|
1329 |
step=0.1,
|
1330 |
-
key="
|
1331 |
)
|
1332 |
-
|
1333 |
-
|
1334 |
-
|
1335 |
-
|
1336 |
-
|
|
|
1337 |
step=1.0,
|
1338 |
-
key="
|
1339 |
)
|
1340 |
-
with
|
1341 |
-
|
1342 |
-
"
|
1343 |
-
min_value
|
1344 |
-
max_value=
|
1345 |
-
value=
|
1346 |
-
step=0.
|
1347 |
-
key="
|
1348 |
)
|
1349 |
-
|
1350 |
-
|
1351 |
-
|
1352 |
-
|
1353 |
-
|
1354 |
-
|
1355 |
-
|
|
|
1356 |
)
|
1357 |
-
|
1358 |
-
|
1359 |
-
"
|
1360 |
-
|
1361 |
-
|
1362 |
-
|
1363 |
-
|
1364 |
-
|
1365 |
-
|
1366 |
-
|
1367 |
-
|
1368 |
-
["
|
1369 |
-
|
1370 |
-
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
|
1377 |
-
|
1378 |
-
|
1379 |
-
|
1380 |
-
|
1381 |
-
|
1382 |
-
|
1383 |
-
|
1384 |
-
|
1385 |
-
"equipment_radiative_fraction": 0.5
|
1386 |
-
}
|
1387 |
-
|
1388 |
-
# Retrieve internal loads data
|
1389 |
-
internal_loads = st.session_state.project_data.get("internal_loads", {})
|
1390 |
-
lighting_systems = internal_loads.get("lighting", [])
|
1391 |
-
equipment_systems = internal_loads.get("equipment", [])
|
1392 |
-
|
1393 |
-
# Calculate default lighting fractions from lighting systems
|
1394 |
-
if lighting_systems:
|
1395 |
-
lighting_convective_avg = sum(system.get("convective_fraction", 0.5) for system in lighting_systems) / len(lighting_systems)
|
1396 |
-
lighting_radiative_avg = sum(system.get("radiative_fraction", 0.5) for system in lighting_systems) / len(lighting_systems)
|
1397 |
-
else:
|
1398 |
-
lighting_convective_avg = st.session_state.project_data["internal_loads_conditions"].get("lighting_convective_fraction", 0.5)
|
1399 |
-
lighting_radiative_avg = st.session_state.project_data["internal_loads_conditions"].get("lighting_radiative_fraction", 0.5)
|
1400 |
-
|
1401 |
-
# Calculate default equipment fractions from equipment systems
|
1402 |
-
if equipment_systems:
|
1403 |
-
equipment_convective_avg = sum(system.get("convective_fraction", 0.5) for system in equipment_systems) / len(equipment_systems)
|
1404 |
-
equipment_radiative_avg = sum(system.get("radiative_fraction", 0.5) for system in equipment_systems) / len(equipment_systems)
|
1405 |
-
else:
|
1406 |
-
equipment_convective_avg = st.session_state.project_data["internal_loads_conditions"].get("equipment_convective_fraction", 0.5)
|
1407 |
-
equipment_radiative_avg = st.session_state.project_data["internal_loads_conditions"].get("equipment_radiative_fraction", 0.5)
|
1408 |
-
|
1409 |
-
with col1:
|
1410 |
-
air_velocity = st.number_input(
|
1411 |
-
"Air Velocity (m/s)",
|
1412 |
-
min_value=0.0,
|
1413 |
-
max_value=2.0,
|
1414 |
-
value=st.session_state.project_data["internal_loads_conditions"].get("air_velocity", 0.1),
|
1415 |
-
step=0.01,
|
1416 |
-
key="hvac_air_velocity"
|
1417 |
-
)
|
1418 |
-
if air_velocity < 0.0 or air_velocity > 2.0:
|
1419 |
-
st.error("Air velocity must be between 0 and 2 m/s.")
|
1420 |
-
air_velocity = max(0.0, min(2.0, air_velocity))
|
1421 |
-
|
1422 |
-
with col2:
|
1423 |
-
lighting_convective_fraction = st.number_input(
|
1424 |
-
"Lighting Convective Fraction",
|
1425 |
-
min_value=0.0,
|
1426 |
-
max_value=1.0,
|
1427 |
-
value=lighting_convective_avg,
|
1428 |
-
step=0.01,
|
1429 |
-
key="hvac_lighting_convective"
|
1430 |
-
)
|
1431 |
-
|
1432 |
-
with col3:
|
1433 |
-
lighting_radiative_fraction = st.number_input(
|
1434 |
-
"Lighting Radiative Fraction",
|
1435 |
-
min_value=0.0,
|
1436 |
-
max_value=1.0,
|
1437 |
-
value=lighting_radiative_avg,
|
1438 |
-
step=0.01,
|
1439 |
-
key="hvac_lighting_radiative"
|
1440 |
-
)
|
1441 |
-
# Validate lighting fractions sum to 1.0
|
1442 |
-
if abs(lighting_convective_fraction + lighting_radiative_fraction - 1.0) > 0.01:
|
1443 |
-
st.error("Lighting convective and radiative fractions must sum to 1.0.")
|
1444 |
-
lighting_radiative_fraction = 1.0 - lighting_convective_fraction # Auto-correct radiative fraction
|
1445 |
-
st.warning(f"Adjusted Lighting Radiative Fraction to {lighting_radiative_fraction:.2f} to ensure sum equals 1.0.")
|
1446 |
-
|
1447 |
-
with col4:
|
1448 |
-
equipment_convective_fraction = st.number_input(
|
1449 |
-
"Equipment Convective Fraction",
|
1450 |
-
min_value=0.0,
|
1451 |
-
max_value=1.0,
|
1452 |
-
value=equipment_convective_avg,
|
1453 |
-
step=0.01,
|
1454 |
-
key="hvac_equipment_convective"
|
1455 |
-
)
|
1456 |
-
|
1457 |
-
with col5:
|
1458 |
-
equipment_radiative_fraction = st.number_input(
|
1459 |
-
"Equipment Radiative Fraction",
|
1460 |
-
min_value=0.0,
|
1461 |
-
max_value=1.0,
|
1462 |
-
value=equipment_radiative_avg,
|
1463 |
-
step=0.01,
|
1464 |
-
key="hvac_equipment_radiative"
|
1465 |
)
|
1466 |
-
|
1467 |
-
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
|
1498 |
-
|
1499 |
-
|
1500 |
-
|
1501 |
-
|
1502 |
-
depth_options = ["0.5", "2", "4"]
|
1503 |
-
default_depth = "2"
|
1504 |
-
selected_depth = st.selectbox(
|
1505 |
-
"Ground Temperature Depth (m)",
|
1506 |
-
options=depth_options,
|
1507 |
-
index=depth_options.index(default_depth),
|
1508 |
-
key="ground_temp_depth"
|
1509 |
)
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
|
1515 |
-
|
1516 |
-
|
1517 |
-
|
1518 |
-
|
1519 |
-
|
1520 |
-
|
1521 |
-
st.write("Enter monthly ground temperatures (°C):")
|
1522 |
-
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
1523 |
-
temp_inputs = []
|
1524 |
-
cols = st.columns(12)
|
1525 |
-
for i, month in enumerate(months):
|
1526 |
-
with cols[i]:
|
1527 |
-
temp = st.number_input(
|
1528 |
-
month,
|
1529 |
-
min_value=-20.0,
|
1530 |
-
max_value=40.0,
|
1531 |
-
value=monthly_temps[i],
|
1532 |
step=0.1,
|
1533 |
-
key=
|
1534 |
)
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
|
1539 |
-
|
1540 |
-
|
1541 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1542 |
else:
|
1543 |
-
|
1544 |
-
|
1545 |
-
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
1549 |
-
|
1550 |
-
|
1551 |
-
|
1552 |
-
|
1553 |
-
|
1554 |
-
|
1555 |
-
|
1556 |
-
|
1557 |
-
|
1558 |
-
sim_period = st.session_state.project_data["sim_period"]
|
1559 |
-
hvac_settings = st.session_state.project_data["hvac_settings"]
|
1560 |
-
|
1561 |
-
if not hourly_data:
|
1562 |
-
st.error("No climate data available. Please configure climate data first.")
|
1563 |
-
logger.error("HVAC calculation failed: No climate data available")
|
1564 |
-
return
|
1565 |
-
elif not any(comp_list for comp_list in components.values()):
|
1566 |
-
st.error("No building components defined. Please configure components first.")
|
1567 |
-
logger.error("HVAC calculation failed: No building components defined")
|
1568 |
-
return
|
1569 |
-
else:
|
1570 |
-
# Clear previous HVAC loads before running new simulation
|
1571 |
-
st.session_state.project_data["hvac_loads"] = {
|
1572 |
-
"cooling": {"hourly": [], "peak": 0, "charts": {}, "breakdown": {}},
|
1573 |
-
"heating": {"hourly": [], "peak": 0, "charts": {}, "breakdown": {}}
|
1574 |
-
}
|
1575 |
-
|
1576 |
-
loads = TFMCalculations.calculate_tfm_loads(
|
1577 |
-
components=components,
|
1578 |
-
hourly_data=hourly_data,
|
1579 |
-
indoor_conditions=indoor_conditions,
|
1580 |
-
internal_loads=internal_loads,
|
1581 |
-
building_info=building_info,
|
1582 |
-
sim_period=sim_period,
|
1583 |
-
hvac_settings=hvac_settings
|
1584 |
)
|
|
|
1585 |
|
1586 |
-
|
1587 |
-
|
1588 |
-
|
1589 |
-
|
1590 |
-
st.
|
1591 |
-
|
1592 |
-
st.session_state.project_data["
|
1593 |
-
st.
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
|
1601 |
-
|
1602 |
-
|
1603 |
-
|
1604 |
-
|
1605 |
-
|
1606 |
-
|
1607 |
-
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
1611 |
-
|
1612 |
-
|
1613 |
-
|
1614 |
-
|
1615 |
-
|
1616 |
-
|
1617 |
-
|
1618 |
-
|
1619 |
-
|
1620 |
-
|
1621 |
-
|
1622 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1623 |
|
1624 |
except Exception as e:
|
1625 |
st.error(f"Error rendering HVAC Loads page: {str(e)}")
|
|
|
1150 |
def display_hvac_loads_page():
|
1151 |
"""
|
1152 |
Display the HVAC Loads page in the Streamlit application.
|
1153 |
+
Organizes input configuration and results in separate tabs, with clearing and updating of session state.
|
|
|
1154 |
"""
|
1155 |
try:
|
1156 |
st.header("HVAC Loads")
|
1157 |
st.markdown("Configure and calculate HVAC loads for the building.")
|
1158 |
|
1159 |
+
# Notify if HVAC load data exists in session state
|
1160 |
+
if (
|
1161 |
+
st.session_state.project_data.get("hvac_loads", {}).get("cooling", {}).get("hourly")
|
1162 |
+
or st.session_state.project_data.get("hvac_loads", {}).get("heating", {}).get("hourly")
|
1163 |
+
):
|
1164 |
+
st.info(
|
1165 |
+
f"HVAC load results already calculated. "
|
1166 |
+
f"View details in the 'HVAC Load Results' tab or configure new calculations below."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1167 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1168 |
|
1169 |
+
# Create tabs for input and results
|
1170 |
+
tab1, tab2 = st.tabs(["HVAC Load Input", "HVAC Load Results"])
|
1171 |
+
|
1172 |
+
# HVAC Load Input tab
|
1173 |
+
with tab1:
|
1174 |
+
# Generate a unique run ID for this session
|
1175 |
+
import uuid
|
1176 |
+
run_id = str(uuid.uuid4())
|
1177 |
+
|
1178 |
+
# Location Information
|
1179 |
+
st.subheader("Location Information")
|
1180 |
+
climate_data = st.session_state.project_data["climate_data"]
|
1181 |
+
location_data = {
|
1182 |
+
"Country": climate_data.get("location", {}).get("country", ""),
|
1183 |
+
"City": climate_data.get("location", {}).get("city", ""),
|
1184 |
+
"State/Province": climate_data.get("location", {}).get("state_province", ""),
|
1185 |
+
"Latitude": climate_data.get("location", {}).get("latitude", 0.0),
|
1186 |
+
"Longitude": climate_data.get("location", {}).get("longitude", 0.0),
|
1187 |
+
"Elevation": climate_data.get("location", {}).get("elevation", 0.0),
|
1188 |
+
"Time Zone": climate_data.get("location", {}).get("timezone", "UTC"),
|
1189 |
+
"Ground Reflectivity": climate_data.get("ground_reflectivity", 0.2)
|
1190 |
+
}
|
1191 |
+
|
1192 |
+
# Create two rows with four columns each
|
1193 |
+
col1, col2, col3, col4 = st.columns(4)
|
1194 |
with col1:
|
1195 |
+
country = st.text_input("Country", value=location_data["Country"], key="hvac_country")
|
|
|
|
|
|
|
|
|
1196 |
with col2:
|
1197 |
+
city = st.text_input("City", value=location_data["City"], key="hvac_city")
|
1198 |
+
with col3:
|
1199 |
+
state_province = st.text_input("State/Province", value=location_data["State/Province"], key="hvac_state_province")
|
1200 |
+
with col4:
|
1201 |
+
latitude = st.number_input(
|
1202 |
+
"Latitude (°)",
|
1203 |
+
min_value=-90.0,
|
1204 |
+
max_value=90.0,
|
1205 |
+
value=location_data["Latitude"],
|
1206 |
+
step=0.1,
|
1207 |
+
key="hvac_latitude"
|
1208 |
)
|
1209 |
+
|
1210 |
+
col5, col6, col7, col8 = st.columns(4)
|
1211 |
+
with col5:
|
1212 |
+
longitude = st.number_input(
|
1213 |
+
"Longitude (°)",
|
1214 |
+
min_value=-180.0,
|
1215 |
+
max_value=180.0,
|
1216 |
+
value=location_data["Longitude"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1217 |
step=0.1,
|
1218 |
+
key="hvac_longitude"
|
1219 |
)
|
1220 |
+
with col6:
|
1221 |
+
elevation = st.number_input(
|
1222 |
+
"Elevation (m)",
|
1223 |
+
min_value=0.0,
|
1224 |
+
max_value=10000.0,
|
1225 |
+
value=location_data["Elevation"],
|
1226 |
step=1.0,
|
1227 |
+
key="hvac_elevation"
|
1228 |
)
|
1229 |
+
with col7:
|
1230 |
+
timezone = st.number_input(
|
1231 |
+
"Time Zone (UTC offset)",
|
1232 |
+
min_value=-12.0,
|
1233 |
+
max_value=14.0,
|
1234 |
+
value=float(location_data["Time Zone"]) if isinstance(location_data["Time Zone"], (int, float, str)) else 0.0,
|
1235 |
+
step=0.5,
|
1236 |
+
key="hvac_timezone"
|
1237 |
)
|
1238 |
+
with col8:
|
1239 |
+
ground_reflectivity = st.number_input(
|
1240 |
+
"Ground Reflectivity",
|
1241 |
+
min_value=0.0,
|
1242 |
+
max_value=1.0,
|
1243 |
+
value=location_data["Ground Reflectivity"],
|
1244 |
+
step=0.01,
|
1245 |
+
key="hvac_ground_reflectivity"
|
1246 |
)
|
1247 |
+
|
1248 |
+
if st.button("Save Location"):
|
1249 |
+
st.session_state.project_data["climate_data"]["location"].update({
|
1250 |
+
"country": country,
|
1251 |
+
"city": city,
|
1252 |
+
"state_province": state_province,
|
1253 |
+
"latitude": latitude,
|
1254 |
+
"longitude": longitude,
|
1255 |
+
"elevation": elevation,
|
1256 |
+
"timezone": timezone
|
1257 |
+
})
|
1258 |
+
st.session_state.project_data["climate_data"]["ground_reflectivity"] = ground_reflectivity
|
1259 |
+
st.success("Location information saved successfully.")
|
1260 |
+
logger.info("Location information updated in session state")
|
1261 |
+
|
1262 |
+
# Simulation Period Configuration
|
1263 |
+
st.subheader("Simulation Period")
|
1264 |
+
sim_type = st.selectbox(
|
1265 |
+
"Simulation Type",
|
1266 |
+
["Full Year", "From Date to Date", "Heating Only", "Cooling Only",
|
1267 |
+
"Summer Extreme", "Summer Typical", "Winter Extreme", "Winter Typical"],
|
1268 |
+
key="hvac_sim_type",
|
1269 |
+
index=["Full Year", "From Date to Date", "Heating Only", "Cooling Only",
|
1270 |
+
"Summer Extreme", "Summer Typical", "Winter Extreme", "Winter Typical"].index(
|
1271 |
+
st.session_state.project_data["sim_period"]["type"]
|
1272 |
+
) if st.session_state.project_data["sim_period"]["type"] in
|
1273 |
+
["Full Year", "From Date to Date", "Heating Only", "Cooling Only",
|
1274 |
+
"Summer Extreme", "Summer Typical", "Winter Extreme", "Winter Typical"] else 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1275 |
)
|
1276 |
+
st.session_state.project_data["sim_period"]["type"] = sim_type
|
1277 |
+
|
1278 |
+
if sim_type == "From Date to Date":
|
1279 |
+
col1, col2 = st.columns(2)
|
1280 |
+
with col1:
|
1281 |
+
start_date = st.date_input(
|
1282 |
+
"Start Date",
|
1283 |
+
value=st.session_state.project_data["sim_period"]["start_date"] or datetime(2025, 1, 1),
|
1284 |
+
key="hvac_start_date"
|
1285 |
+
)
|
1286 |
+
with col2:
|
1287 |
+
end_date = st.date_input(
|
1288 |
+
"End Date",
|
1289 |
+
value=st.session_state.project_data["sim_period"]["end_date"] or datetime(2025, 12, 31),
|
1290 |
+
key="hvac_end_date"
|
1291 |
+
)
|
1292 |
+
st.session_state.project_data["sim_period"]["start_date"] = start_date
|
1293 |
+
st.session_state.project_data["sim_period"]["end_date"] = end_date
|
1294 |
+
elif sim_type in ["Heating Only", "Cooling Only"]:
|
1295 |
+
base_temp = st.number_input(
|
1296 |
+
"Base Temperature (°C)",
|
1297 |
+
min_value=0.0,
|
1298 |
+
max_value=40.0,
|
1299 |
+
value=st.session_state.project_data["sim_period"].get("base_temp", 18.3 if sim_type == "Heating Only" else 23.9),
|
1300 |
+
step=0.1,
|
1301 |
+
key="hvac_base_temp"
|
1302 |
+
)
|
1303 |
+
st.session_state.project_data["sim_period"]["base_temp"] = base_temp
|
1304 |
+
|
1305 |
+
# Indoor Conditions Configuration
|
1306 |
+
st.subheader("Indoor Conditions")
|
1307 |
+
indoor_type = st.selectbox(
|
1308 |
+
"Indoor Conditions Type",
|
1309 |
+
["Fixed Setpoints", "ASHRAE 55 Adaptive Comfort"],
|
1310 |
+
key="hvac_indoor_type",
|
1311 |
+
index=["Fixed Setpoints", "ASHRAE 55 Adaptive Comfort"].index(st.session_state.project_data["indoor_conditions"]["type"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1312 |
)
|
1313 |
+
st.session_state.project_data["indoor_conditions"]["type"] = indoor_type
|
1314 |
+
|
1315 |
+
if indoor_type == "Fixed Setpoints":
|
1316 |
+
col1, col2 = st.columns(2)
|
1317 |
+
with col1:
|
1318 |
+
cooling_temp = st.number_input(
|
1319 |
+
"Cooling Setpoint Temperature (°C)",
|
1320 |
+
min_value=18.0,
|
1321 |
+
max_value=30.0,
|
1322 |
+
value=st.session_state.project_data["indoor_conditions"]["cooling_setpoint"]["temperature"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1323 |
step=0.1,
|
1324 |
+
key="hvac_cooling_temp"
|
1325 |
)
|
1326 |
+
cooling_rh = st.number_input(
|
1327 |
+
"Cooling Setpoint Relative Humidity (%)",
|
1328 |
+
min_value=30.0,
|
1329 |
+
max_value=70.0,
|
1330 |
+
value=st.session_state.project_data["indoor_conditions"]["cooling_setpoint"]["rh"],
|
1331 |
+
step=1.0,
|
1332 |
+
key="hvac_cooling_rh"
|
1333 |
+
)
|
1334 |
+
with col2:
|
1335 |
+
heating_temp = st.number_input(
|
1336 |
+
"Heating Setpoint Temperature (°C)",
|
1337 |
+
min_value=16.0,
|
1338 |
+
max_value=26.0,
|
1339 |
+
value=st.session_state.project_data["indoor_conditions"]["heating_setpoint"]["temperature"],
|
1340 |
+
step=0.1,
|
1341 |
+
key="hvac_heating_temp"
|
1342 |
+
)
|
1343 |
+
heating_rh = st.number_input(
|
1344 |
+
"Heating Setpoint Relative Humidity (%)",
|
1345 |
+
min_value=30.0,
|
1346 |
+
max_value=70.0,
|
1347 |
+
value=st.session_state.project_data["indoor_conditions"]["heating_setpoint"]["rh"],
|
1348 |
+
step=1.0,
|
1349 |
+
key="hvac_heating_rh"
|
1350 |
+
)
|
1351 |
+
st.session_state.project_data["indoor_conditions"]["cooling_setpoint"] = {
|
1352 |
+
"temperature": cooling_temp,
|
1353 |
+
"rh": cooling_rh
|
1354 |
+
}
|
1355 |
+
st.session_state.project_data["indoor_conditions"]["heating_setpoint"] = {
|
1356 |
+
"temperature": heating_temp,
|
1357 |
+
"rh": heating_rh
|
1358 |
+
}
|
1359 |
+
elif indoor_type == "ASHRAE 55 Adaptive Comfort":
|
1360 |
+
acceptability = st.selectbox(
|
1361 |
+
"Adaptive Comfort Acceptability (%)",
|
1362 |
+
["80", "85", "90", "95"],
|
1363 |
+
index=["80", "85", "90", "95"].index(st.session_state.project_data["indoor_conditions"].get("adaptive_acceptability", "90")),
|
1364 |
+
key="adaptive_acceptability"
|
1365 |
+
)
|
1366 |
+
st.session_state.project_data["indoor_conditions"]["adaptive_acceptability"] = acceptability
|
1367 |
+
|
1368 |
+
# Internal Loads Conditions Configuration
|
1369 |
+
st.subheader("Internal Loads Conditions")
|
1370 |
+
col1, col2, col3, col4, col5 = st.columns(5)
|
1371 |
+
|
1372 |
+
# Initialize internal_loads_conditions if not present
|
1373 |
+
if "internal_loads_conditions" not in st.session_state.project_data:
|
1374 |
+
st.session_state.project_data["internal_loads_conditions"] = {
|
1375 |
+
"air_velocity": 0.1,
|
1376 |
+
"lighting_convective_fraction": 0.5,
|
1377 |
+
"lighting_radiative_fraction": 0.5,
|
1378 |
+
"equipment_convective_fraction": 0.5,
|
1379 |
+
"equipment_radiative_fraction": 0.5
|
1380 |
+
}
|
1381 |
+
|
1382 |
+
# Retrieve internal loads data
|
1383 |
+
internal_loads = st.session_state.project_data.get("internal_loads", {})
|
1384 |
+
lighting_systems = internal_loads.get("lighting", [])
|
1385 |
+
equipment_systems = internal_loads.get("equipment", [])
|
1386 |
+
|
1387 |
+
# Calculate default lighting fractions from lighting systems
|
1388 |
+
if lighting_systems:
|
1389 |
+
lighting_convective_avg = sum(system.get("convective_fraction", 0.5) for system in lighting_systems) / len(lighting_systems)
|
1390 |
+
lighting_radiative_avg = sum(system.get("radiative_fraction", 0.5) for system in lighting_systems) / len(lighting_systems)
|
1391 |
+
else:
|
1392 |
+
lighting_convective_avg = st.session_state.project_data["internal_loads_conditions"].get("lighting_convective_fraction", 0.5)
|
1393 |
+
lighting_radiative_avg = st.session_state.project_data["internal_loads_conditions"].get("lighting_radiative_fraction", 0.5)
|
1394 |
+
|
1395 |
+
# Calculate default equipment fractions from equipment systems
|
1396 |
+
if equipment_systems:
|
1397 |
+
equipment_convective_avg = sum(system.get("convective_fraction", 0.5) for system in equipment_systems) / len(equipment_systems)
|
1398 |
+
equipment_radiative_avg = sum(system.get("radiative_fraction", 0.5) for system in equipment_systems) / len(equipment_systems)
|
1399 |
+
else:
|
1400 |
+
equipment_convective_avg = st.session_state.project_data["internal_loads_conditions"].get("equipment_convective_fraction", 0.5)
|
1401 |
+
equipment_radiative_avg = st.session_state.project_data["internal_loads_conditions"].get("equipment_radiative_fraction", 0.5)
|
1402 |
+
|
1403 |
+
with col1:
|
1404 |
+
air_velocity = st.number_input(
|
1405 |
+
"Air Velocity (m/s)",
|
1406 |
+
min_value=0.0,
|
1407 |
+
max_value=2.0,
|
1408 |
+
value=st.session_state.project_data["internal_loads_conditions"].get("air_velocity", 0.1),
|
1409 |
+
step=0.01,
|
1410 |
+
key="hvac_air_velocity"
|
1411 |
+
)
|
1412 |
+
if air_velocity < 0.0 or air_velocity > 2.0:
|
1413 |
+
st.error("Air velocity must be between 0 and 2 m/s.")
|
1414 |
+
air_velocity = max(0.0, min(2.0, air_velocity))
|
1415 |
+
|
1416 |
+
with col2:
|
1417 |
+
lighting_convective_fraction = st.number_input(
|
1418 |
+
"Lighting Convective Fraction",
|
1419 |
+
min_value=0.0,
|
1420 |
+
max_value=1.0,
|
1421 |
+
value=lighting_convective_avg,
|
1422 |
+
step=0.01,
|
1423 |
+
key="hvac_lighting_convective"
|
1424 |
+
)
|
1425 |
+
|
1426 |
+
with col3:
|
1427 |
+
lighting_radiative_fraction = st.number_input(
|
1428 |
+
"Lighting Radiative Fraction",
|
1429 |
+
min_value=0.0,
|
1430 |
+
max_value=1.0,
|
1431 |
+
value=lighting_radiative_avg,
|
1432 |
+
step=0.01,
|
1433 |
+
key="hvac_lighting_radiative"
|
1434 |
+
)
|
1435 |
+
# Validate lighting fractions sum to 1.0
|
1436 |
+
if abs(lighting_convective_fraction + lighting_radiative_fraction - 1.0) > 0.01:
|
1437 |
+
st.error("Lighting convective and radiative fractions must sum to 1.0.")
|
1438 |
+
lighting_radiative_fraction = 1.0 - lighting_convective_fraction # Auto-correct radiative fraction
|
1439 |
+
st.warning(f"Adjusted Lighting Radiative Fraction to {lighting_radiative_fraction:.2f} to ensure sum equals 1.0.")
|
1440 |
+
|
1441 |
+
with col4:
|
1442 |
+
equipment_convective_fraction = st.number_input(
|
1443 |
+
"Equipment Convective Fraction",
|
1444 |
+
min_value=0.0,
|
1445 |
+
max_value=1.0,
|
1446 |
+
value=equipment_convective_avg,
|
1447 |
+
step=0.01,
|
1448 |
+
key="hvac_equipment_convective"
|
1449 |
+
)
|
1450 |
+
|
1451 |
+
with col5:
|
1452 |
+
equipment_radiative_fraction = st.number_input(
|
1453 |
+
"Equipment Radiative Fraction",
|
1454 |
+
min_value=0.0,
|
1455 |
+
max_value=1.0,
|
1456 |
+
value=equipment_radiative_avg,
|
1457 |
+
step=0.01,
|
1458 |
+
key="hvac_equipment_radiative"
|
1459 |
+
)
|
1460 |
+
# Validate equipment fractions sum to 1.0
|
1461 |
+
if abs(equipment_convective_fraction + equipment_radiative_fraction - 1.0) > 0.01:
|
1462 |
+
st.error("Equipment convective and radiative fractions must sum to 1.0.")
|
1463 |
+
equipment_radiative_fraction = 1.0 - equipment_convective_fraction # Auto-correct radiative fraction
|
1464 |
+
st.warning(f"Adjusted Equipment Radiative Fraction to {equipment_radiative_fraction:.2f} to ensure sum equals 1.0.")
|
1465 |
+
|
1466 |
+
if st.button("Save Internal Loads Conditions"):
|
1467 |
+
# Update internal loads conditions in session state
|
1468 |
+
st.session_state.project_data["internal_loads_conditions"].update({
|
1469 |
+
"air_velocity": air_velocity,
|
1470 |
+
"lighting_convective_fraction": lighting_convective_fraction,
|
1471 |
+
"lighting_radiative_fraction": lighting_radiative_fraction,
|
1472 |
+
"equipment_convective_fraction": equipment_convective_fraction,
|
1473 |
+
"equipment_radiative_fraction": equipment_radiative_fraction
|
1474 |
+
})
|
1475 |
+
# Update lighting systems with new fractions
|
1476 |
+
for system in lighting_systems:
|
1477 |
+
system["convective_fraction"] = lighting_convective_fraction
|
1478 |
+
system["radiative_fraction"] = lighting_radiative_fraction
|
1479 |
+
# Update equipment systems with new fractions
|
1480 |
+
for system in equipment_systems:
|
1481 |
+
system["convective_fraction"] = equipment_convective_fraction
|
1482 |
+
system["radiative_fraction"] = equipment_radiative_fraction
|
1483 |
+
st.success("Internal loads conditions saved successfully.")
|
1484 |
+
logger.info("Internal loads conditions updated in session state.")
|
1485 |
+
|
1486 |
+
# Ground Temperature Configuration
|
1487 |
+
st.subheader("Ground Temperature Configuration")
|
1488 |
+
has_ground_contact = any(
|
1489 |
+
comp.get('ground_contact', False)
|
1490 |
+
for comp_list in st.session_state.project_data["components"].values()
|
1491 |
+
for comp in comp_list
|
1492 |
+
)
|
1493 |
+
if has_ground_contact:
|
1494 |
+
st.markdown("Configure monthly ground temperatures for components in contact with the ground (e.g., floors, walls, roofs). Typical ranges are 10–20°C at 2 m depth (ASHRAE Fundamentals, Chapter 18).")
|
1495 |
+
|
1496 |
+
depth_options = ["0.5", "2", "4"]
|
1497 |
+
default_depth = "2"
|
1498 |
+
selected_depth = st.selectbox(
|
1499 |
+
"Ground Temperature Depth (m)",
|
1500 |
+
options=depth_options,
|
1501 |
+
index=depth_options.index(default_depth),
|
1502 |
+
key="ground_temp_depth"
|
1503 |
+
)
|
1504 |
+
|
1505 |
+
climate_data = st.session_state.project_data.get("climate_data", {})
|
1506 |
+
ground_temperatures = climate_data.get("ground_temperatures", {})
|
1507 |
+
default_temps = {"0.5": [20.0]*12, "2": [18.0]*12, "4": [16.0]*12}
|
1508 |
+
|
1509 |
+
if selected_depth not in ground_temperatures or not ground_temperatures[selected_depth]:
|
1510 |
+
st.warning(f"No ground temperature data available for depth {selected_depth} m. Using default temperatures: {default_temps[selected_depth][0]}°C.")
|
1511 |
+
monthly_temps = default_temps[selected_depth]
|
1512 |
else:
|
1513 |
+
monthly_temps = ground_temperatures[selected_depth]
|
1514 |
+
|
1515 |
+
st.write("Enter monthly ground temperatures (°C):")
|
1516 |
+
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
1517 |
+
temp_inputs = []
|
1518 |
+
cols = st.columns(12)
|
1519 |
+
for i, month in enumerate(months):
|
1520 |
+
with cols[i]:
|
1521 |
+
temp = st.number_input(
|
1522 |
+
month,
|
1523 |
+
min_value=-20.0,
|
1524 |
+
max_value=40.0,
|
1525 |
+
value=monthly_temps[i],
|
1526 |
+
step=0.1,
|
1527 |
+
key=f"ground_temp_{selected_depth}_{month}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1528 |
)
|
1529 |
+
temp_inputs.append(temp)
|
1530 |
|
1531 |
+
if st.button("Save Ground Temperatures"):
|
1532 |
+
if len(temp_inputs) != 12:
|
1533 |
+
st.error("Please provide temperatures for all 12 months.")
|
1534 |
+
elif any(not -20.0 <= t <= 40.0 for t in temp_inputs):
|
1535 |
+
st.error("All temperatures must be between -20°C and 40°C.")
|
1536 |
+
else:
|
1537 |
+
st.session_state.project_data["climate_data"]["ground_temperatures"][selected_depth] = temp_inputs
|
1538 |
+
st.success(f"Ground temperatures for depth {selected_depth} m saved successfully.")
|
1539 |
+
logger.info(f"Ground temperatures for depth {selected_depth} m updated in session state")
|
1540 |
+
else:
|
1541 |
+
st.info("No ground-contact components detected. Ground temperature configuration is not required.")
|
1542 |
+
|
1543 |
+
# Calculate HVAC Loads
|
1544 |
+
if st.button("Calculate HVAC Loads"):
|
1545 |
+
try:
|
1546 |
+
with st.spinner("Running simulation... this may take up to a minute depending on data size."):
|
1547 |
+
components = st.session_state.project_data["components"]
|
1548 |
+
hourly_data = st.session_state.project_data["climate_data"]["hourly_data"]
|
1549 |
+
indoor_conditions = st.session_state.project_data["indoor_conditions"]
|
1550 |
+
internal_loads = st.session_state.project_data["internal_loads"]
|
1551 |
+
building_info = st.session_state.project_data["building_info"]
|
1552 |
+
sim_period = st.session_state.project_data["sim_period"]
|
1553 |
+
hvac_settings = st.session_state.project_data["hvac_settings"]
|
1554 |
+
|
1555 |
+
if not hourly_data:
|
1556 |
+
st.error("No climate data available. Please configure climate data first.")
|
1557 |
+
logger.error("HVAC calculation failed: No climate data available")
|
1558 |
+
return
|
1559 |
+
elif not any(comp_list for comp_list in components.values()):
|
1560 |
+
st.error("No building components defined. Please configure components first.")
|
1561 |
+
logger.error("HVAC calculation failed: No building components defined")
|
1562 |
+
return
|
1563 |
+
else:
|
1564 |
+
loads = TFMCalculations.calculate_tfm_loads(
|
1565 |
+
components=components,
|
1566 |
+
hourly_data=hourly_data,
|
1567 |
+
indoor_conditions=indoor_conditions,
|
1568 |
+
internal_loads=internal_loads,
|
1569 |
+
building_info=building_info,
|
1570 |
+
sim_period=sim_period,
|
1571 |
+
hvac_settings=hvac_settings
|
1572 |
+
)
|
1573 |
+
|
1574 |
+
# Clear previous HVAC loads from session state
|
1575 |
+
st.session_state.project_data["hvac_loads"] = {
|
1576 |
+
"cooling": {"hourly": [], "peak": 0, "charts": {}, "breakdown": {}},
|
1577 |
+
"heating": {"hourly": [], "peak": 0, "charts": {}, "breakdown": {}}
|
1578 |
+
}
|
1579 |
+
|
1580 |
+
# Update session state with new results
|
1581 |
+
cooling_loads = [load for load in loads if load["total_cooling"] > 0]
|
1582 |
+
heating_loads = [load for load in loads if load["total_heating"] > 0]
|
1583 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["hourly"] = cooling_loads
|
1584 |
+
st.session_state.project_data["hvac_loads"]["heating"]["hourly"] = heating_loads
|
1585 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["peak"] = max([load["total_cooling"] for load in cooling_loads], default=0)
|
1586 |
+
st.session_state.project_data["hvac_loads"]["heating"]["peak"] = max([load["total_heating"] for load in heating_loads], default=0)
|
1587 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"] = {}
|
1588 |
+
st.session_state.project_data["hvac_loads"]["heating"]["charts"] = {}
|
1589 |
+
|
1590 |
+
# Store breakdown
|
1591 |
+
cooling_breakdown = {
|
1592 |
+
"Conduction": sum(load["conduction_cooling"] for load in cooling_loads),
|
1593 |
+
"Solar Gains": sum(load["solar"] for load in cooling_loads),
|
1594 |
+
"Internal": sum(load["internal"] for load in cooling_loads),
|
1595 |
+
"Ventilation Sensible": sum(system.get("sensible_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("ventilation", [])),
|
1596 |
+
"Ventilation Latent": sum(system.get("latent_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("ventilation", [])),
|
1597 |
+
"Infiltration Sensible": sum(system.get("sensible_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("infiltration", [])),
|
1598 |
+
"Infiltration Latent": sum(system.get("latent_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("infiltration", []))
|
1599 |
+
}
|
1600 |
+
heating_breakdown = {
|
1601 |
+
"conduction": sum(load["conduction_heating"] for load in heating_loads),
|
1602 |
+
"ventilation": sum(load["ventilation_heating"] for load in heating_loads),
|
1603 |
+
"infiltration": sum(load["infiltration_heating"] for load in heating_loads)
|
1604 |
+
}
|
1605 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_component"] = cooling_breakdown
|
1606 |
+
st.session_state.project_data["hvac_loads"]["heating"]["breakdown"] = heating_breakdown
|
1607 |
+
|
1608 |
+
st.success("HVAC loads calculated successfully.")
|
1609 |
+
logger.info("HVAC loads calculated and stored in session state")
|
1610 |
+
st.button("View HVAC Load Results", on_click=lambda: st.session_state.update({"hvac_tab": "HVAC Load Results"}))
|
1611 |
+
|
1612 |
+
except Exception as e:
|
1613 |
+
st.error(f"Error calculating HVAC loads: {str(e)}")
|
1614 |
+
logger.error(f"HVAC calculation error: {str(e)}")
|
1615 |
+
|
1616 |
+
# HVAC Load Results tab
|
1617 |
+
with tab2:
|
1618 |
+
if (
|
1619 |
+
st.session_state.project_data.get("hvac_loads", {}).get("cooling", {}).get("hourly")
|
1620 |
+
or st.session_state.project_data.get("hvac_loads", {}).get("heating", {}).get("hourly")
|
1621 |
+
):
|
1622 |
+
loads = []
|
1623 |
+
cooling_loads = st.session_state.project_data["hvac_loads"]["cooling"].get("hourly", [])
|
1624 |
+
heating_loads = st.session_state.project_data["hvac_loads"]["heating"].get("hourly", [])
|
1625 |
+
loads.extend(cooling_loads)
|
1626 |
+
loads.extend(heating_loads)
|
1627 |
+
# Sort loads by month, day, hour to ensure consistent display
|
1628 |
+
loads = sorted(loads, key=lambda x: (x["month"], x["day"], x["hour"]))
|
1629 |
+
if loads:
|
1630 |
+
display_hvac_results_ui(loads, run_id=run_id)
|
1631 |
+
else:
|
1632 |
+
st.info("Please configure and calculate HVAC loads in the 'HVAC Load Input' tab to view results.")
|
1633 |
|
1634 |
except Exception as e:
|
1635 |
st.error(f"Error rendering HVAC Loads page: {str(e)}")
|