Spaces:
Sleeping
Sleeping
Update utils/ctf_calculations.py
Browse files- utils/ctf_calculations.py +88 -1
utils/ctf_calculations.py
CHANGED
@@ -17,6 +17,7 @@ import threading
|
|
17 |
from typing import List, Dict, Any, NamedTuple
|
18 |
import streamlit as st
|
19 |
from enum import Enum
|
|
|
20 |
|
21 |
# Configure logging
|
22 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
@@ -310,4 +311,90 @@ class CTFCalculator:
|
|
310 |
CTFCoefficients: Placeholder zero coefficients until implementation.
|
311 |
"""
|
312 |
logger.info(f"CTF table calculation for {component.get('type', 'Unknown')} component '{component.get('name', 'Unknown')}' not yet implemented. Returning zero coefficients.")
|
313 |
-
return CTFCoefficients(X=[0.0], Y=[0.0], Z=[0.0], F=[0.0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
from typing import List, Dict, Any, NamedTuple
|
18 |
import streamlit as st
|
19 |
from enum import Enum
|
20 |
+
from utils.solar import SolarCalculations
|
21 |
|
22 |
# Configure logging
|
23 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
311 |
CTFCoefficients: Placeholder zero coefficients until implementation.
|
312 |
"""
|
313 |
logger.info(f"CTF table calculation for {component.get('type', 'Unknown')} component '{component.get('name', 'Unknown')}' not yet implemented. Returning zero coefficients.")
|
314 |
+
return CTFCoefficients(X=[0.0], Y=[0.0], Z=[0.0], F=[0.0])
|
315 |
+
|
316 |
+
@classmethod
|
317 |
+
def calculate_heat_flux(cls, component: Dict[str, Any], hourly_data: Dict[str, Any],
|
318 |
+
solar_calc: SolarCalculations, building_info: Dict[str, Any]) -> float:
|
319 |
+
"""Calculate hourly heat flux using CTF coefficients and sol-air temperature.
|
320 |
+
|
321 |
+
Args:
|
322 |
+
component: Dictionary containing component properties.
|
323 |
+
hourly_data: Dictionary with month, day, hour, dry_bulb, dew_point, wind_speed,
|
324 |
+
total_sky_cover, global_horizontal_radiation, direct_normal_radiation,
|
325 |
+
diffuse_horizontal_radiation.
|
326 |
+
solar_calc: SolarCalculations instance for sol-air temperature.
|
327 |
+
building_info: Building information dictionary.
|
328 |
+
|
329 |
+
Returns:
|
330 |
+
float: Heat flux through the component (W/m²).
|
331 |
+
|
332 |
+
References:
|
333 |
+
ASHRAE Handbook—Fundamentals, Chapter 26.
|
334 |
+
"""
|
335 |
+
# Get component type
|
336 |
+
comp_type_str = component.get('type', '').lower()
|
337 |
+
comp_type_map = {
|
338 |
+
'walls': ComponentType.WALL,
|
339 |
+
'roofs': ComponentType.ROOF,
|
340 |
+
'floors': ComponentType.FLOOR,
|
341 |
+
'windows': ComponentType.WINDOW,
|
342 |
+
'skylights': ComponentType.SKYLIGHT
|
343 |
+
}
|
344 |
+
component_type = comp_type_map.get(comp_type_str, None)
|
345 |
+
if not component_type:
|
346 |
+
logger.warning(f"Invalid component type '{comp_type_str}' for component '{component.get('name', 'Unknown')}'. Returning zero flux.")
|
347 |
+
return 0.0
|
348 |
+
|
349 |
+
# Skip for WINDOW, SKYLIGHT (handled via SHGC)
|
350 |
+
if component_type in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
|
351 |
+
logger.info(f"Skipping heat flux calculation for {component_type.value} component '{component.get('name', 'Unknown')}'.")
|
352 |
+
return 0.0
|
353 |
+
|
354 |
+
# Get CTF coefficients
|
355 |
+
ctf = cls.calculate_ctf_coefficients(component)
|
356 |
+
if not any(ctf.X + ctf.Y + ctf.Z + ctf.F):
|
357 |
+
logger.warning(f"Zero CTF coefficients for component '{component.get('name', 'Unknown')}'. Returning zero flux.")
|
358 |
+
return 0.0
|
359 |
+
|
360 |
+
# Extract hourly data
|
361 |
+
T_out = hourly_data.get('dry_bulb', 25.0)
|
362 |
+
dew_point = hourly_data.get('dew_point', T_out - 5.0)
|
363 |
+
wind_speed = hourly_data.get('wind_speed', 4.0)
|
364 |
+
total_sky_cover = hourly_data.get('total_sky_cover', 0.5)
|
365 |
+
T_in = 24.0 # Assume constant indoor temperature (adjust as needed)
|
366 |
+
|
367 |
+
# Get surface parameters and total incident radiation
|
368 |
+
surface_tilt, surface_azimuth, h_o, emissivity, absorptivity = solar_calc.get_surface_parameters(
|
369 |
+
component, building_info, wind_speed)
|
370 |
+
|
371 |
+
# Calculate total incident radiation (simplified, reuse solar_calc logic)
|
372 |
+
ghi = hourly_data.get('global_horizontal_radiation', 0.0)
|
373 |
+
dni = hourly_data.get('direct_normal_radiation', ghi * 0.7)
|
374 |
+
dhi = hourly_data.get('diffuse_horizontal_radiation', ghi * 0.3)
|
375 |
+
if ghi <= 0:
|
376 |
+
I_t = 0.0
|
377 |
+
else:
|
378 |
+
# Simplified radiation calculation (full calc in solar.py)
|
379 |
+
view_factor = (1 - math.cos(math.radians(surface_tilt))) / 2
|
380 |
+
ground_reflectivity = 0.2
|
381 |
+
cos_theta = max(math.cos(math.radians(surface_tilt)), 0.0) # Simplified for CTF
|
382 |
+
I_t = dni * cos_theta + dhi + ground_reflectivity * ghi * view_factor
|
383 |
+
|
384 |
+
# Calculate sol-air temperature
|
385 |
+
T_sol_air = solar_calc.calculate_sol_air_temperature(
|
386 |
+
T_out, I_t, absorptivity, emissivity, h_o, dew_point, total_sky_cover)
|
387 |
+
|
388 |
+
# Calculate heat flux using CTF (q_t = Σ X_i * T_sol_air(t-i) + Σ Z_i * T_in(t-i) - Σ F_i * q(t-i))
|
389 |
+
num_ctf = len(ctf.X)
|
390 |
+
q_t = 0.0
|
391 |
+
for i in range(num_ctf):
|
392 |
+
# Assume T_sol_air and T_in are constant for history (simplified, extend with history buffer if needed)
|
393 |
+
q_t += ctf.X[i] * T_sol_air + ctf.Z[i] * T_in
|
394 |
+
# Flux history (F) requires previous q values; assume zero for first calculation
|
395 |
+
q_t -= ctf.F[i] * 0.0 # Placeholder, implement history buffer for accurate F terms
|
396 |
+
|
397 |
+
logger.info(f"Calculated heat flux for component '{component.get('name', 'Unknown')}' at "
|
398 |
+
f"{hourly_data.get('month')}/{hourly_data.get('day')}/{hourly_data.get('hour')}: "
|
399 |
+
f"{q_t:.2f} W/m² (T_sol_air={T_sol_air:.2f}°C)")
|
400 |
+
return q_t
|