mabuseif commited on
Commit
aa676d7
·
verified ·
1 Parent(s): b06f043

Update app/hvac_loads.py

Browse files
Files changed (1) hide show
  1. app/hvac_loads.py +25 -19
app/hvac_loads.py CHANGED
@@ -273,7 +273,7 @@ class TFMCalculations:
273
  if component_type == ComponentType.FLOOR or component.get('adiabatic', False) or component.get('ground_contact', False):
274
  logger.info(f"Skipping solar load calculation for {component_name} at hour {hour} (type={component_type.value}, adiabatic={component.get('adiabatic', False)}, ground_contact={component.get('ground_contact', False)})")
275
  return 0
276
-
277
  try:
278
  material_library = st.session_state.get("material_library")
279
  if not material_library:
@@ -281,7 +281,7 @@ class TFMCalculations:
281
  material_library = MaterialLibrary()
282
  st.session_state.material_library = material_library
283
  logger.info(f"Created new MaterialLibrary for {component_name}")
284
-
285
  project_materials = st.session_state.get("project_data", {}).get("materials", {}).get("project", {})
286
  project_constructions = st.session_state.get("project_data", {}).get("constructions", {}).get("project", {})
287
  project_glazing_materials = st.session_state.get("project_data", {}).get("fenestrations", {}).get("project", {})
@@ -290,7 +290,7 @@ class TFMCalculations:
290
  longitude = climate_data.get("longitude", 0.0)
291
  timezone = climate_data.get("time_zone", 0.0)
292
  ground_reflectivity = climate_data.get("ground_reflectivity", 0.2)
293
-
294
  if not -90 <= latitude <= 90:
295
  logger.warning(f"Invalid latitude {latitude} for {component_name}. Using default 0.0.")
296
  latitude = 0.0
@@ -303,17 +303,17 @@ class TFMCalculations:
303
  if not 0 <= ground_reflectivity <= 1:
304
  logger.warning(f"Invalid ground_reflectivity {ground_reflectivity} for {component_name}. Using default 0.2.")
305
  ground_reflectivity = 0.2
306
-
307
  required_fields = ["month", "day", "hour", "global_horizontal_radiation", "direct_normal_radiation",
308
  "diffuse_horizontal_radiation", "dry_bulb"]
309
  if not all(field in hourly_data for field in required_fields):
310
  logger.warning(f"Missing required fields in hourly_data for hour {hour} for {component_name}: {hourly_data}")
311
  return 0
312
-
313
  if hourly_data["global_horizontal_radiation"] <= 0:
314
  logger.info(f"No solar load for hour {hour} due to GHI={hourly_data['global_horizontal_radiation']} for {component_name}")
315
  return 0
316
-
317
  month = hourly_data["month"]
318
  day = hourly_data["day"]
319
  hour = hourly_data["hour"]
@@ -321,30 +321,30 @@ class TFMCalculations:
321
  dni = hourly_data.get("direct_normal_radiation", ghi * 0.7)
322
  dhi = hourly_data.get("diffuse_horizontal_radiation", ghi * 0.3)
323
  outdoor_temp = hourly_data["dry_bulb"]
324
-
325
  if ghi < 0 or dni < 0 or dhi < 0:
326
  logger.error(f"Negative radiation values for {month}/{day}/{hour} for {component_name}")
327
  raise ValueError(f"Negative radiation values for {month}/{day}/{hour}")
328
-
329
  logger.info(f"Processing solar for {month}/{day}/{hour} with GHI={ghi}, DNI={dni}, DHI={dhi}, "
330
  f"dry_bulb={outdoor_temp} for {component_name}")
331
-
332
  year = 2025
333
  n = TFMCalculations.day_of_year(month, day, year)
334
  EOT = TFMCalculations.equation_of_time(n)
335
  lambda_std = 15 * timezone
336
  standard_time = hour - 1 + 0.5
337
  LST = standard_time + (4 * (lambda_std - longitude) + EOT) / 60
338
-
339
  delta = 23.45 * math.sin(math.radians(360 / 365 * (284 + n)))
340
  phi = math.radians(latitude)
341
  delta_rad = math.radians(delta)
342
  hra = 15 * (LST - 12)
343
  hra_rad = math.radians(hra)
344
-
345
  sin_alpha = math.sin(phi) * math.sin(delta_rad) + math.cos(phi) * math.cos(delta_rad) * math.cos(hra_rad)
346
  alpha = math.degrees(math.asin(sin_alpha))
347
-
348
  if abs(math.cos(math.radians(alpha))) < 0.01:
349
  azimuth = 0
350
  else:
@@ -353,10 +353,10 @@ class TFMCalculations:
353
  azimuth = math.degrees(math.atan2(sin_az, cos_az))
354
  if hra > 0:
355
  azimuth = 360 - azimuth if azimuth > 0 else -azimuth
356
-
357
  logger.info(f"Solar angles for {month}/{day}/{hour}: declination={delta:.2f}, LST={LST:.2f}, "
358
  f"HRA={hra:.2f}, altitude={alpha:.2f}, azimuth={azimuth:.2f} for {component_name}")
359
-
360
  building_info = {"orientation_angle": building_orientation}
361
  try:
362
  surface_tilt, surface_azimuth, h_o, emissivity, absorptivity = \
@@ -385,7 +385,7 @@ class TFMCalculations:
385
  else: # WINDOW, SKYLIGHT
386
  absorptivity = 0.0
387
  h_o = DEFAULT_WINDOW_PROPERTIES["h_o"]
388
-
389
  alpha_rad = math.radians(alpha)
390
  surface_tilt_rad = math.radians(surface_tilt)
391
  azimuth_rad = math.radians(azimuth)
@@ -400,7 +400,7 @@ class TFMCalculations:
400
  logger.info(f" Component {component_name} at {month}/{day}/{hour}: "
401
  f"surface_tilt={surface_tilt:.2f}, surface_azimuth={surface_azimuth:.2f}, "
402
  f"cos_theta={cos_theta:.4f}")
403
-
404
  view_factor = (1 - math.cos(surface_tilt_rad)) / 2
405
  ground_reflected = ground_reflectivity * ghi * view_factor
406
 
@@ -415,14 +415,20 @@ class TFMCalculations:
415
  shgc = component.get('shgc', 0.7) # Use component-stored shgc
416
  glazing_type = SolarCalculations.GLAZING_TYPE_MAPPING.get(component.get('fenestration', ''), "Single Clear")
417
  shading_coeff = component.get('shading_coefficient', 1.0) # Aligned with components.py
418
-
 
 
 
 
 
 
419
  shgc_dynamic = shgc * SolarCalculations.calculate_dynamic_shgc(glazing_type, cos_theta)
420
 
421
  solar_heat_gain = component.get('area', 0.0) * shgc_dynamic * I_t * shading_coeff / 1000
422
 
423
  logger.info(f"Fenestration solar heat gain for {component_name} at {month}/{day}/{hour}: "
424
  f"{solar_heat_gain:.4f} kW (area={component.get('area', 0.0)}, shgc_dynamic={shgc_dynamic:.4f}, "
425
- f"I_t={I_t:.2f}, shading_coeff={shading_coeff})")
426
 
427
  elif component_type in [ComponentType.WALL, ComponentType.ROOF]:
428
  surface_resistance = 1/h_o
@@ -434,7 +440,7 @@ class TFMCalculations:
434
  f"I_t={I_t:.2f}, surface_resistance={surface_resistance:.4f})")
435
 
436
  return solar_heat_gain
437
-
438
  except Exception as e:
439
  logger.error(f"Error calculating solar load for {component_name} at hour {hour}: {str(e)}")
440
  return 0
 
273
  if component_type == ComponentType.FLOOR or component.get('adiabatic', False) or component.get('ground_contact', False):
274
  logger.info(f"Skipping solar load calculation for {component_name} at hour {hour} (type={component_type.value}, adiabatic={component.get('adiabatic', False)}, ground_contact={component.get('ground_contact', False)})")
275
  return 0
276
+
277
  try:
278
  material_library = st.session_state.get("material_library")
279
  if not material_library:
 
281
  material_library = MaterialLibrary()
282
  st.session_state.material_library = material_library
283
  logger.info(f"Created new MaterialLibrary for {component_name}")
284
+
285
  project_materials = st.session_state.get("project_data", {}).get("materials", {}).get("project", {})
286
  project_constructions = st.session_state.get("project_data", {}).get("constructions", {}).get("project", {})
287
  project_glazing_materials = st.session_state.get("project_data", {}).get("fenestrations", {}).get("project", {})
 
290
  longitude = climate_data.get("longitude", 0.0)
291
  timezone = climate_data.get("time_zone", 0.0)
292
  ground_reflectivity = climate_data.get("ground_reflectivity", 0.2)
293
+
294
  if not -90 <= latitude <= 90:
295
  logger.warning(f"Invalid latitude {latitude} for {component_name}. Using default 0.0.")
296
  latitude = 0.0
 
303
  if not 0 <= ground_reflectivity <= 1:
304
  logger.warning(f"Invalid ground_reflectivity {ground_reflectivity} for {component_name}. Using default 0.2.")
305
  ground_reflectivity = 0.2
306
+
307
  required_fields = ["month", "day", "hour", "global_horizontal_radiation", "direct_normal_radiation",
308
  "diffuse_horizontal_radiation", "dry_bulb"]
309
  if not all(field in hourly_data for field in required_fields):
310
  logger.warning(f"Missing required fields in hourly_data for hour {hour} for {component_name}: {hourly_data}")
311
  return 0
312
+
313
  if hourly_data["global_horizontal_radiation"] <= 0:
314
  logger.info(f"No solar load for hour {hour} due to GHI={hourly_data['global_horizontal_radiation']} for {component_name}")
315
  return 0
316
+
317
  month = hourly_data["month"]
318
  day = hourly_data["day"]
319
  hour = hourly_data["hour"]
 
321
  dni = hourly_data.get("direct_normal_radiation", ghi * 0.7)
322
  dhi = hourly_data.get("diffuse_horizontal_radiation", ghi * 0.3)
323
  outdoor_temp = hourly_data["dry_bulb"]
324
+
325
  if ghi < 0 or dni < 0 or dhi < 0:
326
  logger.error(f"Negative radiation values for {month}/{day}/{hour} for {component_name}")
327
  raise ValueError(f"Negative radiation values for {month}/{day}/{hour}")
328
+
329
  logger.info(f"Processing solar for {month}/{day}/{hour} with GHI={ghi}, DNI={dni}, DHI={dhi}, "
330
  f"dry_bulb={outdoor_temp} for {component_name}")
331
+
332
  year = 2025
333
  n = TFMCalculations.day_of_year(month, day, year)
334
  EOT = TFMCalculations.equation_of_time(n)
335
  lambda_std = 15 * timezone
336
  standard_time = hour - 1 + 0.5
337
  LST = standard_time + (4 * (lambda_std - longitude) + EOT) / 60
338
+
339
  delta = 23.45 * math.sin(math.radians(360 / 365 * (284 + n)))
340
  phi = math.radians(latitude)
341
  delta_rad = math.radians(delta)
342
  hra = 15 * (LST - 12)
343
  hra_rad = math.radians(hra)
344
+
345
  sin_alpha = math.sin(phi) * math.sin(delta_rad) + math.cos(phi) * math.cos(delta_rad) * math.cos(hra_rad)
346
  alpha = math.degrees(math.asin(sin_alpha))
347
+
348
  if abs(math.cos(math.radians(alpha))) < 0.01:
349
  azimuth = 0
350
  else:
 
353
  azimuth = math.degrees(math.atan2(sin_az, cos_az))
354
  if hra > 0:
355
  azimuth = 360 - azimuth if azimuth > 0 else -azimuth
356
+
357
  logger.info(f"Solar angles for {month}/{day}/{hour}: declination={delta:.2f}, LST={LST:.2f}, "
358
  f"HRA={hra:.2f}, altitude={alpha:.2f}, azimuth={azimuth:.2f} for {component_name}")
359
+
360
  building_info = {"orientation_angle": building_orientation}
361
  try:
362
  surface_tilt, surface_azimuth, h_o, emissivity, absorptivity = \
 
385
  else: # WINDOW, SKYLIGHT
386
  absorptivity = 0.0
387
  h_o = DEFAULT_WINDOW_PROPERTIES["h_o"]
388
+
389
  alpha_rad = math.radians(alpha)
390
  surface_tilt_rad = math.radians(surface_tilt)
391
  azimuth_rad = math.radians(azimuth)
 
400
  logger.info(f" Component {component_name} at {month}/{day}/{hour}: "
401
  f"surface_tilt={surface_tilt:.2f}, surface_azimuth={surface_azimuth:.2f}, "
402
  f"cos_theta={cos_theta:.4f}")
403
+
404
  view_factor = (1 - math.cos(surface_tilt_rad)) / 2
405
  ground_reflected = ground_reflectivity * ghi * view_factor
406
 
 
415
  shgc = component.get('shgc', 0.7) # Use component-stored shgc
416
  glazing_type = SolarCalculations.GLAZING_TYPE_MAPPING.get(component.get('fenestration', ''), "Single Clear")
417
  shading_coeff = component.get('shading_coefficient', 1.0) # Aligned with components.py
418
+ # Adjust shading coefficient based on shading type
419
+ shading_type = component.get('shading_type', 'No shading')
420
+ if shading_type == "External Shading":
421
+ shading_coeff *= 0.6
422
+ elif shading_type == "Internal Shading":
423
+ shading_coeff *= 0.8
424
+
425
  shgc_dynamic = shgc * SolarCalculations.calculate_dynamic_shgc(glazing_type, cos_theta)
426
 
427
  solar_heat_gain = component.get('area', 0.0) * shgc_dynamic * I_t * shading_coeff / 1000
428
 
429
  logger.info(f"Fenestration solar heat gain for {component_name} at {month}/{day}/{hour}: "
430
  f"{solar_heat_gain:.4f} kW (area={component.get('area', 0.0)}, shgc_dynamic={shgc_dynamic:.4f}, "
431
+ f"I_t={I_t:.2f}, shading_coeff={shading_coeff}, shading_type={shading_type})")
432
 
433
  elif component_type in [ComponentType.WALL, ComponentType.ROOF]:
434
  surface_resistance = 1/h_o
 
440
  f"I_t={I_t:.2f}, surface_resistance={surface_resistance:.4f})")
441
 
442
  return solar_heat_gain
443
+
444
  except Exception as e:
445
  logger.error(f"Error calculating solar load for {component_name} at hour {hour}: {str(e)}")
446
  return 0