Spaces:
Sleeping
Sleeping
Update app/main.py
Browse files- app/main.py +33 -9
app/main.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
HVAC Calculator Code Documentation.
|
3 |
Updated 2025-05-02: Integrated skylights, surface color, glazing type, frame type, and drapery adjustments from main_new.py.
|
4 |
Updated 2025-05-02: Enhanced per Plan.txt to include winter design temperature, humidity, building height, ventilation rate, internal load enhancements, and calculation parameters.
|
|
|
5 |
"""
|
6 |
|
7 |
import streamlit as st
|
@@ -52,6 +53,10 @@ VENTILATION_RATES = {
|
|
52 |
"Custom": {"people_rate": 0.0, "area_rate": 0.0}
|
53 |
}
|
54 |
|
|
|
|
|
|
|
|
|
55 |
class HVACCalculator:
|
56 |
def __init__(self):
|
57 |
st.set_page_config(
|
@@ -225,6 +230,13 @@ class HVACCalculator:
|
|
225 |
return False, f"Glazing type missing for {component_type}: {comp.name}"
|
226 |
if getattr(comp, 'frame_type', None) is None:
|
227 |
return False, f"Frame type missing for {component_type}: {comp.name}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
|
229 |
# Validate ventilation rate
|
230 |
if building_info.get('ventilation_rate', 0) < 0:
|
@@ -279,18 +291,30 @@ class HVACCalculator:
|
|
279 |
|
280 |
return True, "Valid load."
|
281 |
|
282 |
-
def parse_latitude(self, latitude: Any) ->
|
283 |
-
"""Parse latitude from string or
|
284 |
try:
|
285 |
if isinstance(latitude, (int, float)):
|
286 |
-
|
287 |
-
|
288 |
lat_str = latitude.strip().upper().replace('N', '').replace('S', '')
|
289 |
-
|
290 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
except (ValueError, AttributeError) as e:
|
292 |
-
st.error(f"Invalid latitude: {latitude}. Using default
|
293 |
-
return
|
294 |
|
295 |
def display_internal_loads(self):
|
296 |
st.title("Internal Loads")
|
@@ -1131,7 +1155,7 @@ class HVACCalculator:
|
|
1131 |
'lights': {
|
1132 |
'power': sum(load['power'] for load in internal_loads.get('lighting', [])),
|
1133 |
'use_factor': internal_loads.get('lighting', [{}])[0].get('usage_factor', 0.8),
|
1134 |
-
'hours_operation': internal_loads.get('lighting', [{}])[0].get('hours_in_operation
|
1135 |
'zone_type': internal_loads.get('lighting', [{}])[0].get('zone_type', 'A')
|
1136 |
},
|
1137 |
'equipment': {
|
|
|
2 |
HVAC Calculator Code Documentation.
|
3 |
Updated 2025-05-02: Integrated skylights, surface color, glazing type, frame type, and drapery adjustments from main_new.py.
|
4 |
Updated 2025-05-02: Enhanced per Plan.txt to include winter design temperature, humidity, building height, ventilation rate, internal load enhancements, and calculation parameters.
|
5 |
+
Updated 2025-05-09: Fixed latitude parsing to return string (e.g., "24N") to match ASHRAE table keys and added group validation.
|
6 |
"""
|
7 |
|
8 |
import streamlit as st
|
|
|
53 |
"Custom": {"people_rate": 0.0, "area_rate": 0.0}
|
54 |
}
|
55 |
|
56 |
+
# Valid wall and roof groups for ASHRAE CLTD tables (adjust based on ashrae_tables.py)
|
57 |
+
VALID_WALL_GROUPS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
|
58 |
+
VALID_ROOF_GROUPS = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
|
59 |
+
|
60 |
class HVACCalculator:
|
61 |
def __init__(self):
|
62 |
st.set_page_config(
|
|
|
230 |
return False, f"Glazing type missing for {component_type}: {comp.name}"
|
231 |
if getattr(comp, 'frame_type', None) is None:
|
232 |
return False, f"Frame type missing for {component_type}: {comp.name}"
|
233 |
+
# NEW: Validate wall and roof groups
|
234 |
+
if component_type == 'walls':
|
235 |
+
if getattr(comp, 'wall_group', '') not in VALID_WALL_GROUPS:
|
236 |
+
return False, f"Invalid wall group '{comp.wall_group}' for {comp.name}. Valid groups: {', '.join(VALID_WALL_GROUPS)}"
|
237 |
+
if component_type == 'roofs':
|
238 |
+
if getattr(comp, 'roof_group', '') not in VALID_ROOF_GROUPS:
|
239 |
+
return False, f"Invalid roof group '{comp.roof_group}' for {comp.name}. Valid groups: {', '.join(VALID_ROOF_GROUPS)}"
|
240 |
|
241 |
# Validate ventilation rate
|
242 |
if building_info.get('ventilation_rate', 0) < 0:
|
|
|
291 |
|
292 |
return True, "Valid load."
|
293 |
|
294 |
+
def parse_latitude(self, latitude: Any) -> str:
|
295 |
+
"""Parse latitude from string or number to ASHRAE table format (e.g., '24N')."""
|
296 |
try:
|
297 |
if isinstance(latitude, (int, float)):
|
298 |
+
lat_value = float(latitude)
|
299 |
+
elif isinstance(latitude, str):
|
300 |
lat_str = latitude.strip().upper().replace('N', '').replace('S', '')
|
301 |
+
lat_value = float(lat_str)
|
302 |
+
else:
|
303 |
+
raise ValueError("Invalid latitude format")
|
304 |
+
|
305 |
+
# Convert to ASHRAE table format (e.g., '24N', '32N')
|
306 |
+
if lat_value in [24, 32, 40, 48]: # Common ASHRAE latitudes
|
307 |
+
return f"{int(lat_value)}N"
|
308 |
+
else:
|
309 |
+
# Round to nearest supported latitude
|
310 |
+
supported_latitudes = [24, 32, 40, 48]
|
311 |
+
closest_lat = min(supported_latitudes, key=lambda x: abs(x - lat_value))
|
312 |
+
st.warning(f"Latitude {lat_value} not in ASHRAE tables. Using closest: {closest_lat}N")
|
313 |
+
return f"{closest_lat}N"
|
314 |
+
|
315 |
except (ValueError, AttributeError) as e:
|
316 |
+
st.error(f"Invalid latitude: {latitude}. Using default 32N.")
|
317 |
+
return "32N" # Default to 32N
|
318 |
|
319 |
def display_internal_loads(self):
|
320 |
st.title("Internal Loads")
|
|
|
1155 |
'lights': {
|
1156 |
'power': sum(load['power'] for load in internal_loads.get('lighting', [])),
|
1157 |
'use_factor': internal_loads.get('lighting', [{}])[0].get('usage_factor', 0.8),
|
1158 |
+
'hours_operation': internal_loads.get('lighting', [{}])[0].get('hours_in_operation』, '8h'),
|
1159 |
'zone_type': internal_loads.get('lighting', [{}])[0].get('zone_type', 'A')
|
1160 |
},
|
1161 |
'equipment': {
|