Spaces:
Sleeping
Sleeping
Update data/calculation.py
Browse files- data/calculation.py +39 -13
data/calculation.py
CHANGED
|
@@ -308,7 +308,11 @@ class TFMCalculations:
|
|
| 308 |
References:
|
| 309 |
ASHRAE Handbook—Fundamentals, Chapters 15 and 18.
|
| 310 |
"""
|
|
|
|
|
|
|
|
|
|
| 311 |
if mode != "cooling" or component.component_type not in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
|
|
|
|
| 312 |
return 0, None
|
| 313 |
|
| 314 |
details = {} if return_details else None
|
|
@@ -346,10 +350,8 @@ class TFMCalculations:
|
|
| 346 |
"diffuse_horizontal_radiation", "dry_bulb"]
|
| 347 |
if not all(field in hourly_data for field in required_fields):
|
| 348 |
logger.warning(f"Missing required fields in hourly_data for hour {hour}: {hourly_data}")
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
if hourly_data["global_horizontal_radiation"] <= 0:
|
| 352 |
-
logger.info(f"No solar load for hour {hour} due to GHI={hourly_data['global_horizontal_radiation']}")
|
| 353 |
return 0, details
|
| 354 |
|
| 355 |
month = hourly_data["month"]
|
|
@@ -360,10 +362,6 @@ class TFMCalculations:
|
|
| 360 |
dhi = hourly_data.get("diffuse_horizontal_radiation", ghi * 0.3)
|
| 361 |
outdoor_temp = hourly_data["dry_bulb"]
|
| 362 |
|
| 363 |
-
if ghi < 0 or dni < 0 or dhi < 0:
|
| 364 |
-
logger.error(f"Negative radiation values for {month}/{day}/{hour}")
|
| 365 |
-
raise ValueError(f"Negative radiation values for {month}/{day}/{hour}")
|
| 366 |
-
|
| 367 |
if return_details:
|
| 368 |
details["component"] = {
|
| 369 |
"name": getattr(component, 'name', 'unknown'),
|
|
@@ -388,6 +386,16 @@ class TFMCalculations:
|
|
| 388 |
"ground_reflectivity": ground_reflectivity
|
| 389 |
}
|
| 390 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 391 |
logger.info(f"Processing solar for {month}/{day}/{hour} with GHI={ghi}, DNI={dni}, DHI={dhi}, "
|
| 392 |
f"dry_bulb={outdoor_temp}")
|
| 393 |
|
|
@@ -447,9 +455,9 @@ class TFMCalculations:
|
|
| 447 |
shgc = glazing_material_obj.shgc
|
| 448 |
h_o = glazing_material_obj.h_o
|
| 449 |
else:
|
| 450 |
-
logger.warning(f"Glazing material '{glazing_material.name}' not found for {
|
| 451 |
else:
|
| 452 |
-
logger.warning(f"No glazing material defined for {
|
| 453 |
|
| 454 |
cos_theta = (math.sin(math.radians(alpha)) * math.cos(math.radians(surface_tilt)) +
|
| 455 |
math.cos(math.radians(alpha)) * math.sin(math.radians(surface_tilt)) *
|
|
@@ -463,7 +471,7 @@ class TFMCalculations:
|
|
| 463 |
"cos_incidence_angle": cos_theta
|
| 464 |
}
|
| 465 |
|
| 466 |
-
logger.info(f" Component {
|
| 467 |
f"surface_tilt={surface_tilt:.2f}, surface_azimuth={surface_azimuth:.2f}, "
|
| 468 |
f"cos_theta={cos_theta:.2f}")
|
| 469 |
|
|
@@ -489,15 +497,16 @@ class TFMCalculations:
|
|
| 489 |
"solar_heat_gain_kw": solar_heat_gain
|
| 490 |
}
|
| 491 |
|
| 492 |
-
logger.info(f"Solar heat gain for {
|
| 493 |
f"{solar_heat_gain:.2f} kW (area={component.area}, shgc_dynamic={shgc_dynamic:.2f}, "
|
| 494 |
f"I_t={I_t:.2f}, iac={iac})")
|
| 495 |
|
| 496 |
return solar_heat_gain, details
|
| 497 |
|
| 498 |
except Exception as e:
|
| 499 |
-
component_id = getattr(component, 'id', 'unknown_component')
|
| 500 |
logger.error(f"Error calculating solar load for component {component_id} at hour {hour}: {str(e)}")
|
|
|
|
|
|
|
| 501 |
return 0, details
|
| 502 |
|
| 503 |
@staticmethod
|
|
@@ -666,10 +675,21 @@ class TFMCalculations:
|
|
| 666 |
operating_periods = hvac_settings.get("operating_hours", [{"start": 8, "end": 18}])
|
| 667 |
area = building_info.get("floor_area", 100.0)
|
| 668 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 669 |
for comp_list in components.values():
|
| 670 |
for comp in comp_list:
|
| 671 |
comp.ctf = CTFCalculator.calculate_ctf_coefficients(comp)
|
| 672 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 673 |
for idx, hour_data in enumerate(filtered_data):
|
| 674 |
hour = hour_data["hour"]
|
| 675 |
outdoor_temp = hour_data["dry_bulb"]
|
|
@@ -684,6 +704,10 @@ class TFMCalculations:
|
|
| 684 |
is_operating = True
|
| 685 |
break
|
| 686 |
mode = "none" if abs(outdoor_temp - 18) < 0.01 else "cooling" if outdoor_temp > 18 else "heating"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
if is_operating and mode == "cooling":
|
| 688 |
for comp_list in components.values():
|
| 689 |
for comp in comp_list:
|
|
@@ -695,6 +719,7 @@ class TFMCalculations:
|
|
| 695 |
solar += solar_load
|
| 696 |
if idx == 0 and details:
|
| 697 |
solar_details_first_hour.append(details)
|
|
|
|
| 698 |
internal = TFMCalculations.calculate_internal_load(internal_loads, hour, max([p["end"] - p["start"] for p in operating_periods]), area)
|
| 699 |
ventilation_cooling, _ = TFMCalculations.calculate_ventilation_load(internal_loads, outdoor_temp, indoor_temp, area, building_info, mode="cooling")
|
| 700 |
infiltration_cooling, _ = TFMCalculations.calculate_infiltration_load(internal_loads, outdoor_temp, indoor_temp, area, building_info, mode="cooling")
|
|
@@ -746,6 +771,7 @@ class TFMCalculations:
|
|
| 746 |
load["total_cooling"] = 0
|
| 747 |
load["total_heating"] = 0
|
| 748 |
final_loads.append(load)
|
|
|
|
| 749 |
return {
|
| 750 |
"loads": final_loads,
|
| 751 |
"solar_details_first_hour": solar_details_first_hour
|
|
|
|
| 308 |
References:
|
| 309 |
ASHRAE Handbook—Fundamentals, Chapters 15 and 18.
|
| 310 |
"""
|
| 311 |
+
component_id = getattr(component, 'id', 'unknown_component')
|
| 312 |
+
logger.info(f"Processing solar load for component {component_id}, type: {component.component_type}, mode: {mode}, return_details: {return_details}")
|
| 313 |
+
|
| 314 |
if mode != "cooling" or component.component_type not in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
|
| 315 |
+
logger.debug(f"Skipping solar load for {component_id}: mode={mode}, type={component.component_type}")
|
| 316 |
return 0, None
|
| 317 |
|
| 318 |
details = {} if return_details else None
|
|
|
|
| 350 |
"diffuse_horizontal_radiation", "dry_bulb"]
|
| 351 |
if not all(field in hourly_data for field in required_fields):
|
| 352 |
logger.warning(f"Missing required fields in hourly_data for hour {hour}: {hourly_data}")
|
| 353 |
+
if return_details:
|
| 354 |
+
details["error"] = f"Missing required climate data fields: {required_fields}"
|
|
|
|
|
|
|
| 355 |
return 0, details
|
| 356 |
|
| 357 |
month = hourly_data["month"]
|
|
|
|
| 362 |
dhi = hourly_data.get("diffuse_horizontal_radiation", ghi * 0.3)
|
| 363 |
outdoor_temp = hourly_data["dry_bulb"]
|
| 364 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
if return_details:
|
| 366 |
details["component"] = {
|
| 367 |
"name": getattr(component, 'name', 'unknown'),
|
|
|
|
| 386 |
"ground_reflectivity": ground_reflectivity
|
| 387 |
}
|
| 388 |
|
| 389 |
+
if ghi < 0 or dni < 0 or dhi < 0:
|
| 390 |
+
logger.error(f"Negative radiation values for {month}/{day}/{hour}")
|
| 391 |
+
if return_details:
|
| 392 |
+
details["error"] = "Negative radiation values"
|
| 393 |
+
return 0, details
|
| 394 |
+
|
| 395 |
+
if ghi <= 0:
|
| 396 |
+
logger.info(f"No solar load for {component_id} at hour {hour} due to GHI={ghi}")
|
| 397 |
+
return 0, details # Return details even if GHI is zero for first hour
|
| 398 |
+
|
| 399 |
logger.info(f"Processing solar for {month}/{day}/{hour} with GHI={ghi}, DNI={dni}, DHI={dhi}, "
|
| 400 |
f"dry_bulb={outdoor_temp}")
|
| 401 |
|
|
|
|
| 455 |
shgc = glazing_material_obj.shgc
|
| 456 |
h_o = glazing_material_obj.h_o
|
| 457 |
else:
|
| 458 |
+
logger.warning(f"Glazing material '{glazing_material.name}' not found for {component_id}. Using default SHGC=0.7.")
|
| 459 |
else:
|
| 460 |
+
logger.warning(f"No glazing material defined for {component_id}. Using default SHGC=0.7.")
|
| 461 |
|
| 462 |
cos_theta = (math.sin(math.radians(alpha)) * math.cos(math.radians(surface_tilt)) +
|
| 463 |
math.cos(math.radians(alpha)) * math.sin(math.radians(surface_tilt)) *
|
|
|
|
| 471 |
"cos_incidence_angle": cos_theta
|
| 472 |
}
|
| 473 |
|
| 474 |
+
logger.info(f" Component {component_id} at {month}/{day}/{hour}: "
|
| 475 |
f"surface_tilt={surface_tilt:.2f}, surface_azimuth={surface_azimuth:.2f}, "
|
| 476 |
f"cos_theta={cos_theta:.2f}")
|
| 477 |
|
|
|
|
| 497 |
"solar_heat_gain_kw": solar_heat_gain
|
| 498 |
}
|
| 499 |
|
| 500 |
+
logger.info(f"Solar heat gain for {component_id} at {month}/{day}/{hour}: "
|
| 501 |
f"{solar_heat_gain:.2f} kW (area={component.area}, shgc_dynamic={shgc_dynamic:.2f}, "
|
| 502 |
f"I_t={I_t:.2f}, iac={iac})")
|
| 503 |
|
| 504 |
return solar_heat_gain, details
|
| 505 |
|
| 506 |
except Exception as e:
|
|
|
|
| 507 |
logger.error(f"Error calculating solar load for component {component_id} at hour {hour}: {str(e)}")
|
| 508 |
+
if return_details:
|
| 509 |
+
details["error"] = str(e)
|
| 510 |
return 0, details
|
| 511 |
|
| 512 |
@staticmethod
|
|
|
|
| 675 |
operating_periods = hvac_settings.get("operating_hours", [{"start": 8, "end": 18}])
|
| 676 |
area = building_info.get("floor_area", 100.0)
|
| 677 |
|
| 678 |
+
# Log available windows and skylights
|
| 679 |
+
windows = components.get("windows", [])
|
| 680 |
+
skylights = components.get("skylights", [])
|
| 681 |
+
logger.info(f"Windows defined: {len(windows)}, Skylights defined: {len(skylights)}")
|
| 682 |
+
for comp in windows + skylights:
|
| 683 |
+
logger.debug(f"Component: {comp.name}, Type: {comp.component_type}, Area: {comp.area}, SHGC: {comp.shgc}")
|
| 684 |
+
|
| 685 |
for comp_list in components.values():
|
| 686 |
for comp in comp_list:
|
| 687 |
comp.ctf = CTFCalculator.calculate_ctf_coefficients(comp)
|
| 688 |
|
| 689 |
+
if not filtered_data:
|
| 690 |
+
logger.warning("No filtered hourly data available for simulation")
|
| 691 |
+
return {"loads": [], "solar_details_first_hour": []}
|
| 692 |
+
|
| 693 |
for idx, hour_data in enumerate(filtered_data):
|
| 694 |
hour = hour_data["hour"]
|
| 695 |
outdoor_temp = hour_data["dry_bulb"]
|
|
|
|
| 704 |
is_operating = True
|
| 705 |
break
|
| 706 |
mode = "none" if abs(outdoor_temp - 18) < 0.01 else "cooling" if outdoor_temp > 18 else "heating"
|
| 707 |
+
if idx == 0:
|
| 708 |
+
logger.info(f"First hour: {hour_data['month']}/{hour_data['day']}/{hour}, Outdoor Temp: {outdoor_temp}°C, Mode: {mode}, Operating: {is_operating}")
|
| 709 |
+
logger.debug(f"Climate data for first hour: {hour_data}")
|
| 710 |
+
|
| 711 |
if is_operating and mode == "cooling":
|
| 712 |
for comp_list in components.values():
|
| 713 |
for comp in comp_list:
|
|
|
|
| 719 |
solar += solar_load
|
| 720 |
if idx == 0 and details:
|
| 721 |
solar_details_first_hour.append(details)
|
| 722 |
+
logger.debug(f"Solar details for {comp.name}: {details}")
|
| 723 |
internal = TFMCalculations.calculate_internal_load(internal_loads, hour, max([p["end"] - p["start"] for p in operating_periods]), area)
|
| 724 |
ventilation_cooling, _ = TFMCalculations.calculate_ventilation_load(internal_loads, outdoor_temp, indoor_temp, area, building_info, mode="cooling")
|
| 725 |
infiltration_cooling, _ = TFMCalculations.calculate_infiltration_load(internal_loads, outdoor_temp, indoor_temp, area, building_info, mode="cooling")
|
|
|
|
| 771 |
load["total_cooling"] = 0
|
| 772 |
load["total_heating"] = 0
|
| 773 |
final_loads.append(load)
|
| 774 |
+
logger.info(f"Solar details collected for first hour: {len(solar_details_first_hour)} entries")
|
| 775 |
return {
|
| 776 |
"loads": final_loads,
|
| 777 |
"solar_details_first_hour": solar_details_first_hour
|