mabuseif commited on
Commit
2c31938
verified
1 Parent(s): 1515e6f

Update utils/solar.py

Browse files
Files changed (1) hide show
  1. utils/solar.py +53 -39
utils/solar.py CHANGED
@@ -119,7 +119,22 @@ class SolarCalculations:
119
  h_o (W/m虏路K), emissivity, absorptivity.
120
  """
121
  component_id = component.get('id', 'unknown_component')
122
- component_type = component.get('component_type', ComponentType.WALL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  # Default parameters
125
  if component_type == ComponentType.ROOF:
@@ -131,31 +146,31 @@ class SolarCalculations:
131
  elif component_type == ComponentType.FLOOR:
132
  surface_tilt = component.get('surface_tilt', 180.0)
133
  h_o = 17.0
134
- else: # WALL, DOOR, WINDOW
135
  surface_tilt = component.get('surface_tilt', 90.0)
136
  h_o = 17.0
137
 
138
  surface_azimuth = component.get('surface_azimuth', 0.0)
139
- emissivity = component.get('emissivity', 0.9 if component_type in [ComponentType.WALL, ComponentType.ROOF, ComponentType.DOOR] else None)
140
- absorptivity = component.get('absorptivity', 0.6 if component_type in [ComponentType.WALL, ComponentType.ROOF, ComponentType.DOOR] else 0.0)
141
 
142
  try:
143
  # For windows and skylights, use shgc instead of absorptivity and adjust h_o
144
  if component_type in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
145
- glazing_material = component.get('fenestration', None)
146
- if not glazing_material:
147
  logger.warning(f"No fenestration defined for {component_id}. Using default SHGC=0.7, h_o={h_o}.")
148
  absorptivity = component.get('shgc', 0.7)
149
  else:
150
- glazing_material_obj = (self.project_glazing_materials.get(glazing_material) or
151
- self.material_library.library_glazing_materials.get(glazing_material))
152
  if not glazing_material_obj:
153
- logger.warning(f"Fenestration '{glazing_material}' not found for {component_id}. Using default SHGC=0.7, h_o={h_o}.")
154
  absorptivity = component.get('shgc', 0.7)
155
  else:
156
  absorptivity = glazing_material_obj.get('shgc', component.get('shgc', 0.7))
157
  h_o = glazing_material_obj.get('h_o', h_o)
158
- logger.debug(f"Using fenestration '{glazing_material}' for {component_id}: shgc={absorptivity}, h_o={h_o}")
159
  emissivity = None
160
 
161
  logger.debug(f"Surface parameters for {component_id}: tilt={surface_tilt:.2f}, azimuth={surface_azimuth:.2f}, "
@@ -178,12 +193,12 @@ class SolarCalculations:
178
  surface_tilt = 180.0
179
  h_o = 17.0
180
  surface_azimuth = 0.0
181
- else: # WALL, DOOR, WINDOW
182
  surface_tilt = 90.0
183
  h_o = 17.0
184
  surface_azimuth = 0.0
185
 
186
- if component_type in [ComponentType.WALL, ComponentType.ROOF, ComponentType.DOOR]:
187
  absorptivity = 0.6
188
  emissivity = 0.9
189
  else: # WINDOW, SKYLIGHT
@@ -235,7 +250,7 @@ class SolarCalculations:
235
  timezone (float): Timezone offset in hours.
236
  ground_reflectivity (float): Ground reflectivity (albedo, typically 0.2).
237
  components (Dict[str, List]): Dictionary of component lists (e.g., walls, windows) with id, area,
238
- component_type, facade, construction, glazing_material, or door_material.
239
 
240
  Returns:
241
  List[Dict]: List of results for each hour with GHI > 0, containing solar angles and per-component results.
@@ -268,7 +283,7 @@ class SolarCalculations:
268
 
269
  lambda_std = 15 * timezone # Standard meridian longitude (掳)
270
 
271
- # Cache facade azimuths (used only for walls, doors, windows)
272
  building_info = components.get("_building_info", {})
273
  facade_cache = {
274
  "A": building_info.get("orientation_angle", 0.0),
@@ -345,17 +360,16 @@ class SolarCalculations:
345
  surface_tilt, surface_azimuth, h_o, emissivity, absorptivity = \
346
  self.get_surface_parameters(comp, building_info)
347
 
348
- # For windows/skylights, get SHGC from material
349
- shgc = 0.7 # Default
350
- if comp.component_type in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
351
- glazing_material = getattr(comp, 'glazing_material', None)
352
- if glazing_material:
353
- glazing_material_obj = (self.project_glazing_materials.get(glazing_material.name) or
354
- self.material_library.library_glazing_materials.get(glazing_material.name))
355
- if glazing_material_obj:
356
- shgc = glazing_material_obj.shgc
357
- else:
358
- logger.warning(f"Glazing material '{glazing_material.name}' not found for {comp.id}. Using default SHGC=0.7.")
359
 
360
  # Calculate angle of incidence (胃)
361
  cos_theta = (math.sin(math.radians(alpha)) * math.cos(math.radians(surface_tilt)) +
@@ -363,9 +377,9 @@ class SolarCalculations:
363
  math.cos(math.radians(azimuth - surface_azimuth)))
364
  cos_theta = max(min(cos_theta, 1.0), 0.0) # Clamp to [0, 1]
365
 
366
- logger.info(f" Component {getattr(comp, 'id', 'unknown_component')} at {month}/{day}/{hour}: "
367
- f"surface_tilt={surface_tilt:.2f}, surface_tazimuth={surface_sazimuth:.2f}, "
368
- f"cos_theta={cos_theta:.2f}")
369
 
370
  # Calculate total incident radiation (I_t)
371
  view_factor = (1 - math.cos(math.radians(surface_tilt))) / 2
@@ -374,36 +388,36 @@ class SolarCalculations:
374
 
375
  # Initialize result
376
  comp_result = {
377
- "component_id": getattr(comp, 'id', 'unknown_component'),
378
  "total_incident_radiation": round(I_t, 2),
379
  "absorptivity": round(absorptivity, 2),
380
  "emissivity": round(emissivity, 2) if emissivity is not None else None
381
  }
382
 
383
  # Calculate sol-air temperature for opaque surfaces
384
- if comp.component_type in [ComponentType.WALL, ComponentType.ROOF, ComponentType.DOOR]:
385
- delta_R = 63.0 if comp.component_type == ComponentType.ROOF else 0.0
386
  sol_air_temp = outdoor_temp + (absorptivity * I_t - (emissivity or 0.9) *
387
- delta_R) / h_o
388
  comp_result["sol_air_temp"] = round(sol_air_temp, 2)
389
  logger.info(f"Sol-air temp for {comp_result['component_id']} at {month}/{day}/{hour}: {sol_air_temp:.2f}掳C")
390
 
391
  # Calculate solar heat gain for fenestration
392
- elif comp.component_type in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
393
- glazing_type = self.GLAZING_TYPE_MAPPING.get(comp.name, 'Single Clear')
394
- iac = getattr(comp, 'iac', 1.0) # Default internal shading
395
  shgc_dynamic = shgc * self.calculate_dynamic_shgc(glazing_type, cos_theta)
396
- solar_heat_gain = comp.area * shgc_dynamic * I_t * iac / 1000 # kW
397
  comp_result["solar_heat_gain"] = round(solar_heat_gain, 2)
398
  comp_result["shgc_dynamic"] = round(shgc_dynamic, 2)
399
  logger.info(f"Solar heat gain for {comp_result['component_id']} at {month}/{day}/{hour}: "
400
- f"{solar_heat_gain:.2f} kW (area={comp.area}, shgc_dynamic={shgc_dynamic:.2f}, "
401
- f"I_t={I_t:.2f}, iac={iac})")
402
 
403
  component_results.append(comp_result)
404
 
405
  except Exception as e:
406
- component_id = getattr(comp, 'id', 'unknown_component')
407
  logger.error(f"Error processing component {component_id} at {month}/{day}/{hour}: {str(e)}")
408
  continue
409
 
 
119
  h_o (W/m虏路K), emissivity, absorptivity.
120
  """
121
  component_id = component.get('id', 'unknown_component')
122
+ component_type = component.get('type', 'wall').lower() # Changed to dictionary access
123
+
124
+ # Map string type to ComponentType enum
125
+ type_map = {
126
+ 'walls': ComponentType.WALL,
127
+ 'roofs': ComponentType.ROOF,
128
+ 'floors': ComponentType.FLOOR,
129
+ 'windows': ComponentType.WINDOW,
130
+ 'skylights': ComponentType.SKYLIGHT,
131
+ 'wall': ComponentType.WALL,
132
+ 'roof': ComponentType.ROOF,
133
+ 'floor': ComponentType.FLOOR,
134
+ 'window': ComponentType.WINDOW,
135
+ 'skylight': ComponentType.SKYLIGHT
136
+ }
137
+ component_type = type_map.get(component_type, ComponentType.WALL)
138
 
139
  # Default parameters
140
  if component_type == ComponentType.ROOF:
 
146
  elif component_type == ComponentType.FLOOR:
147
  surface_tilt = component.get('surface_tilt', 180.0)
148
  h_o = 17.0
149
+ else: # WALL, WINDOW
150
  surface_tilt = component.get('surface_tilt', 90.0)
151
  h_o = 17.0
152
 
153
  surface_azimuth = component.get('surface_azimuth', 0.0)
154
+ emissivity = component.get('emissivity', 0.9 if component_type in [ComponentType.WALL, ComponentType.ROOF] else None)
155
+ absorptivity = component.get('absorptivity', 0.6 if component_type in [ComponentType.WALL, ComponentType.ROOF] else 0.0)
156
 
157
  try:
158
  # For windows and skylights, use shgc instead of absorptivity and adjust h_o
159
  if component_type in [ComponentType.WINDOW, ComponentType.SKYLIGHT]:
160
+ fenestration_name = component.get('fenestration', None) # Standardized to 'fenestration'
161
+ if not fenestration_name:
162
  logger.warning(f"No fenestration defined for {component_id}. Using default SHGC=0.7, h_o={h_o}.")
163
  absorptivity = component.get('shgc', 0.7)
164
  else:
165
+ glazing_material_obj = (self.project_glazing_materials.get(fenestration_name) or
166
+ self.material_library.library_glazing_materials.get(fenestration_name))
167
  if not glazing_material_obj:
168
+ logger.warning(f"Fenestration '{fenestration_name}' not found for {component_id}. Using default SHGC=0.7, h_o={h_o}.")
169
  absorptivity = component.get('shgc', 0.7)
170
  else:
171
  absorptivity = glazing_material_obj.get('shgc', component.get('shgc', 0.7))
172
  h_o = glazing_material_obj.get('h_o', h_o)
173
+ logger.debug(f"Using fenestration '{fenestration_name}' for {component_id}: shgc={absorptivity}, h_o={h_o}")
174
  emissivity = None
175
 
176
  logger.debug(f"Surface parameters for {component_id}: tilt={surface_tilt:.2f}, azimuth={surface_azimuth:.2f}, "
 
193
  surface_tilt = 180.0
194
  h_o = 17.0
195
  surface_azimuth = 0.0
196
+ else: # WALL, WINDOW
197
  surface_tilt = 90.0
198
  h_o = 17.0
199
  surface_azimuth = 0.0
200
 
201
+ if component_type in [ComponentType.WALL, ComponentType.ROOF]:
202
  absorptivity = 0.6
203
  emissivity = 0.9
204
  else: # WINDOW, SKYLIGHT
 
250
  timezone (float): Timezone offset in hours.
251
  ground_reflectivity (float): Ground reflectivity (albedo, typically 0.2).
252
  components (Dict[str, List]): Dictionary of component lists (e.g., walls, windows) with id, area,
253
+ type, facade, construction, fenestration, or door_material.
254
 
255
  Returns:
256
  List[Dict]: List of results for each hour with GHI > 0, containing solar angles and per-component results.
 
283
 
284
  lambda_std = 15 * timezone # Standard meridian longitude (掳)
285
 
286
+ # Cache facade azimuths (used only for walls, windows)
287
  building_info = components.get("_building_info", {})
288
  facade_cache = {
289
  "A": building_info.get("orientation_angle", 0.0),
 
360
  surface_tilt, surface_azimuth, h_o, emissivity, absorptivity = \
361
  self.get_surface_parameters(comp, building_info)
362
 
363
+ # For windows/skylights, get SHGC from component
364
+ shgc = comp.get('shgc', 0.7) # Changed to dictionary access
365
+ fenestration_name = comp.get('fenestration', None) # Standardized to 'fenestration'
366
+ if fenestration_name:
367
+ glazing_material_obj = (self.project_glazing_materials.get(fenestration_name) or
368
+ self.material_library.library_glazing_materials.get(fenestration_name))
369
+ if glazing_material_obj:
370
+ shgc = glazing_material_obj.get('shgc', shgc)
371
+ else:
372
+ logger.warning(f"Fenestration '{fenestration_name}' not found for {comp.get('id', 'unknown_component')}. Using default SHGC={shgc}.")
 
373
 
374
  # Calculate angle of incidence (胃)
375
  cos_theta = (math.sin(math.radians(alpha)) * math.cos(math.radians(surface_tilt)) +
 
377
  math.cos(math.radians(azimuth - surface_azimuth)))
378
  cos_theta = max(min(cos_theta, 1.0), 0.0) # Clamp to [0, 1]
379
 
380
+ logger.info(f" Component {comp.get('id', 'unknown_component')} at {month}/{day}/{hour}: "
381
+ f"surface_tilt={surface_tilt:.2f}, surface_azimuth={surface_azimuth:.2f}, " # Fixed typo
382
+ f"cos_theta={cos_theta:.2f}")
383
 
384
  # Calculate total incident radiation (I_t)
385
  view_factor = (1 - math.cos(math.radians(surface_tilt))) / 2
 
388
 
389
  # Initialize result
390
  comp_result = {
391
+ "component_id": comp.get('id', 'unknown_component'),
392
  "total_incident_radiation": round(I_t, 2),
393
  "absorptivity": round(absorptivity, 2),
394
  "emissivity": round(emissivity, 2) if emissivity is not None else None
395
  }
396
 
397
  # Calculate sol-air temperature for opaque surfaces
398
+ if comp.get('type', '').lower() in ['walls', 'roofs']:
399
+ delta_R = 63.0 if comp.get('type', '').lower() == 'roofs' else 0.0
400
  sol_air_temp = outdoor_temp + (absorptivity * I_t - (emissivity or 0.9) *
401
+ delta_R) / h_o
402
  comp_result["sol_air_temp"] = round(sol_air_temp, 2)
403
  logger.info(f"Sol-air temp for {comp_result['component_id']} at {month}/{day}/{hour}: {sol_air_temp:.2f}掳C")
404
 
405
  # Calculate solar heat gain for fenestration
406
+ elif comp.get('type', '').lower() in ['windows', 'skylights']:
407
+ glazing_type = self.GLAZING_TYPE_MAPPING.get(comp.get('name', ''), 'Single Clear')
408
+ iac = comp.get('shading_coefficient', 1.0) # Changed to dictionary access, aligned with components.py
409
  shgc_dynamic = shgc * self.calculate_dynamic_shgc(glazing_type, cos_theta)
410
+ solar_heat_gain = comp.get('area', 0.0) * shgc_dynamic * I_t * iac / 1000 # kW
411
  comp_result["solar_heat_gain"] = round(solar_heat_gain, 2)
412
  comp_result["shgc_dynamic"] = round(shgc_dynamic, 2)
413
  logger.info(f"Solar heat gain for {comp_result['component_id']} at {month}/{day}/{hour}: "
414
+ f"{solar_heat_gain:.2f} kW (area={comp.get('area', 0.0)}, shgc_dynamic={shgc_dynamic:.2f}, "
415
+ f"I_t={I_t:.2f}, iac={iac})")
416
 
417
  component_results.append(comp_result)
418
 
419
  except Exception as e:
420
+ component_id = comp.get('id', 'unknown_component')
421
  logger.error(f"Error processing component {component_id} at {month}/{day}/{hour}: {str(e)}")
422
  continue
423