mabuseif commited on
Commit
a8554b0
·
verified ·
1 Parent(s): 7a9e3aa

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +350 -40
app/main.py CHANGED
@@ -25,8 +25,6 @@ from data.reference_data import ReferenceData
25
  from data.climate_data import ClimateData
26
  from data.ashrae_tables import ASHRAETables
27
  from data.building_components import Wall as WallModel, Roof as RoofModel
28
- from data.cooling_load_updated import CoolingLoadCalculator
29
- from data.heating_load_updated import HeatingLoadCalculator
30
 
31
  # Import utility modules
32
  from utils.u_value_calculator import UValueCalculator
@@ -34,6 +32,8 @@ from utils.shading_system import ShadingSystem
34
  from utils.area_calculation_system import AreaCalculationSystem
35
  from utils.psychrometrics import Psychrometrics
36
  from utils.heat_transfer import HeatTransferCalculations
 
 
37
  from utils.component_visualization import ComponentVisualization
38
  from utils.scenario_comparison import ScenarioComparisonVisualization
39
  from utils.psychrometric_visualization import PsychrometricVisualization
@@ -169,42 +169,229 @@ class HVACCalculator:
169
  'summer_outdoor_rh': 50.0,
170
  'ground_temperature': 20.0,
171
  'design_month': 'Jul',
172
- 'latitude': '40N',
173
- 'daily_range_c': 11.0
174
  }
175
  st.warning("No climate data provided. Using default values.")
176
 
177
- # Format climate data for calculator
178
- climate_conditions = {
179
- 'outdoor_temp_c': climate_data.get('summer_outdoor_db', 35.0),
180
- 'indoor_temp_c': building_info.get('indoor_temp', 24.0),
181
- 'latitude': float(climate_data.get('latitude', '40N').replace('N', '')),
182
- 'daily_range_c': climate_data.get('daily_range_c', 11.0)
 
 
 
 
 
183
  }
184
 
185
- # Format internal loads (simplified for cooling_load_updated.py)
186
  formatted_internal_loads = {
187
- 'people': sum(load['num_people'] for load in internal_loads.get('people', [])) * 100, # W/person
188
- 'lighting': sum(load['power'] * load['usage_factor'] for load in internal_loads.get('lighting', [])), # W
189
- 'equipment': sum(load['power'] * load['usage_factor'] for load in internal_loads.get('equipment', [])), # W
190
- 'infiltration': building_info.get('infiltration_rate', 0.05) * building_info.get('floor_area', 100.0), # m³/s
191
- 'ventilation': building_info.get('ventilation_rate', 0.1) * building_info.get('floor_area', 100.0) # m³/s
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  }
193
 
194
- # Initialize calculator and calculate
195
- calculator = CoolingLoadCalculator()
196
- cooling_loads = calculator.calculate_cooling_load(building_info, building_components, climate_conditions, formatted_internal_loads)
 
 
 
 
 
 
 
 
 
 
197
 
198
  # Format results for results_display.py
199
  floor_area = building_info.get('floor_area', 100.0) or 100.0
200
  results = {
201
- 'total_load_watts': cooling_loads['total_load_watts'],
202
- 'total_load': cooling_loads['total_load_watts'] / 1000, # kW
203
- 'load_per_area': cooling_loads['total_load_watts'] / floor_area, # W/m²
204
- 'detailed_loads': cooling_loads['detailed_loads'],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  'building_info': building_info
206
  }
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  return True, "Cooling calculation completed.", results
209
  except Exception as e:
210
  return False, f"Cooling calculation error: {str(e)}", {}
@@ -242,36 +429,159 @@ class HVACCalculator:
242
  }
243
  st.warning("No climate data provided. Using default values.")
244
 
245
- # Format climate data
246
- climate_conditions = {
247
- 'outdoor_temp_c': climate_data.get('winter_outdoor_db', -10.0),
248
- 'indoor_temp_c': building_info.get('indoor_temp', 21.0),
249
- 'ground_temperature_c': climate_data.get('ground_temperature', 10.0)
 
 
 
 
250
  }
251
 
252
  # Format internal loads
253
  formatted_internal_loads = {
254
- 'people': sum(load['num_people'] for load in internal_loads.get('people', [])) * 70, # W/person
255
- 'lighting': sum(load['power'] * load['usage_factor'] for load in internal_loads.get('lighting', [])), # W
256
- 'equipment': sum(load['power'] * load['usage_factor'] for load in internal_loads.get('equipment', [])), # W
257
- 'infiltration': building_info.get('infiltration_rate', 0.05) * building_info.get('floor_area', 100.0), # m³/s
258
- 'ventilation': building_info.get('ventilation_rate', 0.1) * building_info.get('floor_area', 100.0) # m³/s
 
 
 
 
 
 
 
 
 
 
259
  }
260
 
261
- # Initialize calculator and calculate
262
- calculator = HeatingLoadCalculator()
263
- heating_loads = calculator.calculate_heating_load(building_info, building_components, climate_conditions, formatted_internal_loads)
 
 
 
 
 
 
 
264
 
265
  # Format results
266
  floor_area = building_info.get('floor_area', 100.0) or 100.0
267
  results = {
268
- 'total_load_watts': heating_loads['total_load_watts'],
269
- 'total_load': heating_loads['total_load_watts'] / 1000, # kW
270
- 'load_per_area': heating_loads['total_load_watts'] / floor_area, # W/m²
271
- 'detailed_loads': heating_loads['detailed_loads'],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  'building_info': building_info
273
  }
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  return True, "Heating calculation completed.", results
276
  except Exception as e:
277
  return False, f"Heating calculation error: {str(e)}", {}
 
25
  from data.climate_data import ClimateData
26
  from data.ashrae_tables import ASHRAETables
27
  from data.building_components import Wall as WallModel, Roof as RoofModel
 
 
28
 
29
  # Import utility modules
30
  from utils.u_value_calculator import UValueCalculator
 
32
  from utils.area_calculation_system import AreaCalculationSystem
33
  from utils.psychrometrics import Psychrometrics
34
  from utils.heat_transfer import HeatTransferCalculations
35
+ from utils.cooling_load import CoolingLoadCalculator
36
+ from utils.heating_load import HeatingLoadCalculator
37
  from utils.component_visualization import ComponentVisualization
38
  from utils.scenario_comparison import ScenarioComparisonVisualization
39
  from utils.psychrometric_visualization import PsychrometricVisualization
 
169
  'summer_outdoor_rh': 50.0,
170
  'ground_temperature': 20.0,
171
  'design_month': 'Jul',
172
+ 'latitude': '40N'
 
173
  }
174
  st.warning("No climate data provided. Using default values.")
175
 
176
+ # Default conditions
177
+ outdoor_conditions = {
178
+ 'temperature': climate_data.get('summer_outdoor_db', 35.0),
179
+ 'relative_humidity': climate_data.get('summer_outdoor_rh', 50.0),
180
+ 'ground_temperature': climate_data.get('ground_temperature', 20.0),
181
+ 'month': climate_data.get('design_month', 'Jul'),
182
+ 'latitude': climate_data.get('latitude', '40N')
183
+ }
184
+ indoor_conditions = {
185
+ 'temperature': building_info.get('indoor_temp', 24.0),
186
+ 'relative_humidity': building_info.get('indoor_rh', 50.0)
187
  }
188
 
189
+ # Format internal loads
190
  formatted_internal_loads = {
191
+ 'people': {
192
+ 'number': sum(load['num_people'] for load in internal_loads.get('people', [])),
193
+ 'activity_level': internal_loads.get('people', [{}])[0].get('activity_level', 'Seated/Resting'),
194
+ 'hours_occupancy': f"{internal_loads.get('people', [{}])[0].get('hours_in_operation', 8)}h"
195
+ },
196
+ 'lights': {
197
+ 'power': sum(load['power'] for load in internal_loads.get('lighting', [])),
198
+ 'use_factor': internal_loads.get('lighting', [{}])[0].get('usage_factor', 0.8),
199
+ 'special_allowance': 0.1,
200
+ 'hours_operation': f"{internal_loads.get('lighting', [{}])[0].get('hours_in_operation', 8)}h"
201
+ },
202
+ 'equipment': {
203
+ 'power': sum(load['power'] for load in internal_loads.get('equipment', [])),
204
+ 'use_factor': internal_loads.get('equipment', [{}])[0].get('usage_factor', 0.7),
205
+ 'radiation_factor': internal_loads.get('equipment', [{}])[0].get('radiation_fraction', 0.3),
206
+ 'hours_operation': f"{internal_loads.get('equipment', [{}])[0].get('hours_in_operation', 8)}h"
207
+ },
208
+ 'infiltration': {'flow_rate': building_info.get('infiltration_rate', 0.05)},
209
+ 'ventilation': {'flow_rate': building_info.get('ventilation_rate', 0.1)}
210
  }
211
 
212
+ # Calculate hourly loads
213
+ hourly_loads = self.cooling_calculator.calculate_hourly_cooling_loads(
214
+ building_components=building_components,
215
+ outdoor_conditions=outdoor_conditions,
216
+ indoor_conditions=indoor_conditions,
217
+ internal_loads=formatted_internal_loads
218
+ )
219
+
220
+ # Get design loads
221
+ design_loads = self.cooling_calculator.calculate_design_cooling_load(hourly_loads)
222
+
223
+ # Get summary
224
+ summary = self.cooling_calculator.calculate_cooling_load_summary(design_loads)
225
 
226
  # Format results for results_display.py
227
  floor_area = building_info.get('floor_area', 100.0) or 100.0
228
  results = {
229
+ 'total_load': summary['total'] / 1000, # kW
230
+ 'sensible_load': summary['total_sensible'] / 1000, # kW
231
+ 'latent_load': summary['total_latent'] / 1000, # kW
232
+ 'load_per_area': summary['total'] / floor_area, # W/m²
233
+ 'component_loads': {
234
+ 'walls': design_loads['walls'] / 1000,
235
+ 'roof': design_loads['roofs'] / 1000,
236
+ 'windows': (design_loads['windows_conduction'] + design_loads['windows_solar']) / 1000,
237
+ 'doors': design_loads['doors'] / 1000,
238
+ 'people': (design_loads['people_sensible'] + design_loads['people_latent']) / 1000,
239
+ 'lighting': design_loads['lights'] / 1000,
240
+ 'equipment': (design_loads['equipment_sensible'] + design_loads['equipment_latent']) / 1000,
241
+ 'infiltration': (design_loads['infiltration_sensible'] + design_loads['infiltration_latent']) / 1000,
242
+ 'ventilation': (design_loads['ventilation_sensible'] + design_loads['ventilation_latent']) / 1000
243
+ },
244
+ 'detailed_loads': {
245
+ 'walls': [],
246
+ 'roofs': [],
247
+ 'windows': [],
248
+ 'doors': [],
249
+ 'internal': [],
250
+ 'infiltration': {
251
+ 'air_flow': formatted_internal_loads['infiltration']['flow_rate'],
252
+ 'sensible_load': design_loads['infiltration_sensible'] / 1000,
253
+ 'latent_load': design_loads['infiltration_latent'] / 1000,
254
+ 'total_load': (design_loads['infiltration_sensible'] + design_loads['infiltration_latent']) / 1000
255
+ },
256
+ 'ventilation': {
257
+ 'air_flow': formatted_internal_loads['ventilation']['flow_rate'],
258
+ 'sensible_load': design_loads['ventilation_sensible'] / 1000,
259
+ 'latent_load': design_loads['ventilation_latent'] / 1000,
260
+ 'total_load': (design_loads['ventilation_sensible'] + design_loads['ventilation_latent']) / 1000
261
+ }
262
+ },
263
  'building_info': building_info
264
  }
265
 
266
+ # Populate detailed loads
267
+ for wall in building_components.get('walls', []):
268
+ load = self.cooling_calculator.calculate_wall_cooling_load(
269
+ wall=wall,
270
+ outdoor_temp=outdoor_conditions['temperature'],
271
+ indoor_temp=indoor_conditions['temperature'],
272
+ month=outdoor_conditions['month'],
273
+ hour=design_loads['design_hour'],
274
+ latitude=outdoor_conditions['latitude']
275
+ )
276
+ results['detailed_loads']['walls'].append({
277
+ 'name': wall.name,
278
+ 'orientation': wall.orientation.value,
279
+ 'area': wall.area,
280
+ 'u_value': wall.u_value,
281
+ 'cltd': self.cooling_calculator.ashrae_tables.calculate_corrected_cltd_wall(
282
+ wall_group=wall.wall_group,
283
+ orientation=wall.orientation.value,
284
+ hour=design_loads['design_hour'],
285
+ color='Dark',
286
+ month=outdoor_conditions['month'],
287
+ latitude=outdoor_conditions['latitude'],
288
+ indoor_temp=indoor_conditions['temperature'],
289
+ outdoor_temp=outdoor_conditions['temperature']
290
+ ),
291
+ 'load': load / 1000
292
+ })
293
+
294
+ for roof in building_components.get('roofs', []):
295
+ load = self.cooling_calculator.calculate_roof_cooling_load(
296
+ roof=roof,
297
+ outdoor_temp=outdoor_conditions['temperature'],
298
+ indoor_temp=indoor_conditions['temperature'],
299
+ month=outdoor_conditions['month'],
300
+ hour=design_loads['design_hour'],
301
+ latitude=outdoor_conditions['latitude']
302
+ )
303
+ results['detailed_loads']['roofs'].append({
304
+ 'name': roof.name,
305
+ 'orientation': roof.orientation.value,
306
+ 'area': roof.area,
307
+ 'u_value': roof.u_value,
308
+ 'cltd': self.cooling_calculator.ashrae_tables.calculate_corrected_cltd_roof(
309
+ roof_group=roof.roof_group,
310
+ hour=design_loads['design_hour'],
311
+ color='Dark',
312
+ month=outdoor_conditions['month'],
313
+ latitude=outdoor_conditions['latitude'],
314
+ indoor_temp=indoor_conditions['temperature'],
315
+ outdoor_temp=outdoor_conditions['temperature']
316
+ ),
317
+ 'load': load / 1000
318
+ })
319
+
320
+ for window in building_components.get('windows', []):
321
+ load_dict = self.cooling_calculator.calculate_window_cooling_load(
322
+ window=window,
323
+ outdoor_temp=outdoor_conditions['temperature'],
324
+ indoor_temp=indoor_conditions['temperature'],
325
+ month=outdoor_conditions['month'],
326
+ hour=design_loads['design_hour'],
327
+ latitude=f"{outdoor_conditions['latitude']}_{outdoor_conditions['month'].upper()}"
328
+ )
329
+ results['detailed_loads']['windows'].append({
330
+ 'name': window.name,
331
+ 'orientation': window.orientation.value,
332
+ 'area': window.area,
333
+ 'u_value': window.u_value,
334
+ 'shgc': window.shgc,
335
+ 'scl': self.cooling_calculator.ashrae_tables.get_scl(
336
+ window.orientation.value,
337
+ design_loads['design_hour'],
338
+ f"{outdoor_conditions['latitude']}_{outdoor_conditions['month'].upper()}"
339
+ ),
340
+ 'load': load_dict['total'] / 1000
341
+ })
342
+
343
+ for door in building_components.get('doors', []):
344
+ load = self.cooling_calculator.calculate_door_cooling_load(
345
+ door=door,
346
+ outdoor_temp=outdoor_conditions['temperature'],
347
+ indoor_temp=indoor_conditions['temperature']
348
+ )
349
+ results['detailed_loads']['doors'].append({
350
+ 'name': door.name,
351
+ 'orientation': door.orientation.value,
352
+ 'area': door.area,
353
+ 'u_value': door.u_value,
354
+ 'cltd': outdoor_conditions['temperature'] - indoor_conditions['temperature'],
355
+ 'load': load / 1000
356
+ })
357
+
358
+ for load_type, key in [('people', 'people'), ('lighting', 'lights'), ('equipment', 'equipment')]:
359
+ for load in internal_loads.get(key, []):
360
+ if load_type == 'people':
361
+ load_dict = self.cooling_calculator.calculate_people_cooling_load(
362
+ num_people=load['num_people'],
363
+ activity_level=load['activity_level'],
364
+ hours_occupancy=f"{load['hours_in_operation']}h",
365
+ hour=design_loads['design_hour']
366
+ )
367
+ elif load_type == 'lighting':
368
+ load_dict = {'total': self.cooling_calculator.calculate_lights_cooling_load(
369
+ power=load['power'],
370
+ use_factor=load['usage_factor'],
371
+ special_allowance=0.1,
372
+ hours_operation=f"{load['hours_in_operation']}h",
373
+ hour=design_loads['design_hour']
374
+ )}
375
+ else:
376
+ load_dict = self.cooling_calculator.calculate_equipment_cooling_load(
377
+ power=load['power'],
378
+ use_factor=load['usage_factor'],
379
+ radiation_factor=load['radiation_fraction'],
380
+ hours_operation=f"{load['hours_in_operation']}h",
381
+ hour=design_loads['design_hour']
382
+ )
383
+ results['detailed_loads']['internal'].append({
384
+ 'type': load_type.capitalize(),
385
+ 'name': load['name'],
386
+ 'quantity': load.get('num_people', load.get('power', 1)),
387
+ 'heat_gain': load_dict.get('sensible', load_dict['total']),
388
+ 'clf': self.cooling_calculator.ashrae_tables.get_clf_people(
389
+ design_loads['design_hour'],
390
+ f"{load['hours_in_operation']}h"
391
+ ) if load_type == 'people' else 1.0,
392
+ 'load': load_dict['total'] / 1000
393
+ })
394
+
395
  return True, "Cooling calculation completed.", results
396
  except Exception as e:
397
  return False, f"Cooling calculation error: {str(e)}", {}
 
429
  }
430
  st.warning("No climate data provided. Using default values.")
431
 
432
+ # Default conditions
433
+ outdoor_conditions = {
434
+ 'design_temperature': climate_data.get('winter_outdoor_db', -10.0),
435
+ 'design_relative_humidity': climate_data.get('winter_outdoor_rh', 80.0),
436
+ 'ground_temperature': climate_data.get('ground_temperature', 10.0)
437
+ }
438
+ indoor_conditions = {
439
+ 'temperature': building_info.get('indoor_temp', 21.0),
440
+ 'relative_humidity': building_info.get('indoor_rh', 40.0)
441
  }
442
 
443
  # Format internal loads
444
  formatted_internal_loads = {
445
+ 'people': {
446
+ 'number': sum(load['num_people'] for load in internal_loads.get('people', [])),
447
+ 'sensible_gain': 70
448
+ },
449
+ 'lights': {
450
+ 'power': sum(load['power'] for load in internal_loads.get('lighting', [])),
451
+ 'use_factor': internal_loads.get('lighting', [{}])[0].get('usage_factor', 0.8)
452
+ },
453
+ 'equipment': {
454
+ 'power': sum(load['power'] for load in internal_loads.get('equipment', [])),
455
+ 'use_factor': internal_loads.get('equipment', [{}])[0].get('usage_factor', 0.7)
456
+ },
457
+ 'infiltration': {'flow_rate': building_info.get('infiltration_rate', 0.05)},
458
+ 'ventilation': {'flow_rate': building_info.get('ventilation_rate', 0.1)},
459
+ 'usage_factor': 0.7
460
  }
461
 
462
+ # Calculate design loads
463
+ design_loads = self.heating_calculator.calculate_design_heating_load(
464
+ building_components=building_components,
465
+ outdoor_conditions=outdoor_conditions,
466
+ indoor_conditions=indoor_conditions,
467
+ internal_loads=formatted_internal_loads
468
+ )
469
+
470
+ # Get summary
471
+ summary = self.heating_calculator.calculate_heating_load_summary(design_loads)
472
 
473
  # Format results
474
  floor_area = building_info.get('floor_area', 100.0) or 100.0
475
  results = {
476
+ 'total_load': summary['total'] / 1000, # kW
477
+ 'load_per_area': summary['total'] / floor_area, # W/m²
478
+ 'design_heat_loss': summary['subtotal'] / 1000, # kW
479
+ 'safety_factor': summary['safety_factor'] * 100, # %
480
+ 'component_loads': {
481
+ 'walls': design_loads['walls'] / 1000,
482
+ 'roof': design_loads['roofs'] / 1000,
483
+ 'floor': design_loads['floors'] / 1000,
484
+ 'windows': design_loads['windows'] / 1000,
485
+ 'doors': design_loads['doors'] / 1000,
486
+ 'infiltration': (design_loads['infiltration_sensible'] + design_loads['infiltration_latent']) / 1000,
487
+ 'ventilation': (design_loads['ventilation_sensible'] + design_loads['ventilation_latent']) / 1000
488
+ },
489
+ 'detailed_loads': {
490
+ 'walls': [],
491
+ 'roofs': [],
492
+ 'floors': [],
493
+ 'windows': [],
494
+ 'doors': [],
495
+ 'infiltration': {
496
+ 'air_flow': formatted_internal_loads['infiltration']['flow_rate'],
497
+ 'delta_t': indoor_conditions['temperature'] - outdoor_conditions['design_temperature'],
498
+ 'load': (design_loads['infiltration_sensible'] + design_loads['infiltration_latent']) / 1000
499
+ },
500
+ 'ventilation': {
501
+ 'air_flow': formatted_internal_loads['ventilation']['flow_rate'],
502
+ 'delta_t': indoor_conditions['temperature'] - outdoor_conditions['design_temperature'],
503
+ 'load': (design_loads['ventilation_sensible'] + design_loads['ventilation_latent']) / 1000
504
+ }
505
+ },
506
  'building_info': building_info
507
  }
508
 
509
+ # Populate detailed loads
510
+ delta_t = indoor_conditions['temperature'] - outdoor_conditions['design_temperature']
511
+ for wall in building_components.get('walls', []):
512
+ load = self.heating_calculator.calculate_wall_heating_load(
513
+ wall=wall,
514
+ outdoor_temp=outdoor_conditions['design_temperature'],
515
+ indoor_temp=indoor_conditions['temperature']
516
+ )
517
+ results['detailed_loads']['walls'].append({
518
+ 'name': wall.name,
519
+ 'orientation': wall.orientation.value,
520
+ 'area': wall.area,
521
+ 'u_value': wall.u_value,
522
+ 'delta_t': delta_t,
523
+ 'load': load / 1000
524
+ })
525
+
526
+ for roof in building_components.get('roofs', []):
527
+ load = self.heating_calculator.calculate_roof_heating_load(
528
+ roof=roof,
529
+ outdoor_temp=outdoor_conditions['design_temperature'],
530
+ indoor_temp=indoor_conditions['temperature']
531
+ )
532
+ results['detailed_loads']['roofs'].append({
533
+ 'name': roof.name,
534
+ 'orientation': roof.orientation.value,
535
+ 'area': roof.area,
536
+ 'u_value': roof.u_value,
537
+ 'delta_t': delta_t,
538
+ 'load': load / 1000
539
+ })
540
+
541
+ for floor in building_components.get('floors', []):
542
+ load = self.heating_calculator.calculate_floor_heating_load(
543
+ floor=floor,
544
+ ground_temp=outdoor_conditions['ground_temperature'],
545
+ indoor_temp=indoor_conditions['temperature']
546
+ )
547
+ results['detailed_loads']['floors'].append({
548
+ 'name': floor.name,
549
+ 'area': floor.area,
550
+ 'u_value': floor.u_value,
551
+ 'delta_t': indoor_conditions['temperature'] - outdoor_conditions['ground_temperature'],
552
+ 'load': load / 1000
553
+ })
554
+
555
+ for window in building_components.get('windows', []):
556
+ load = self.heating_calculator.calculate_window_heating_load(
557
+ window=window,
558
+ outdoor_temp=outdoor_conditions['design_temperature'],
559
+ indoor_temp=indoor_conditions['temperature']
560
+ )
561
+ results['detailed_loads']['windows'].append({
562
+ 'name': window.name,
563
+ 'orientation': window.orientation.value,
564
+ 'area': window.area,
565
+ 'u_value': window.u_value,
566
+ 'delta_t': delta_t,
567
+ 'load': load / 1000
568
+ })
569
+
570
+ for door in building_components.get('doors', []):
571
+ load = self.heating_calculator.calculate_door_heating_load(
572
+ door=door,
573
+ outdoor_temp=outdoor_conditions['design_temperature'],
574
+ indoor_temp=indoor_conditions['temperature']
575
+ )
576
+ results['detailed_loads']['doors'].append({
577
+ 'name': door.name,
578
+ 'orientation': door.orientation.value,
579
+ 'area': door.area,
580
+ 'u_value': door.u_value,
581
+ 'delta_t': delta_t,
582
+ 'load': load / 1000
583
+ })
584
+
585
  return True, "Heating calculation completed.", results
586
  except Exception as e:
587
  return False, f"Heating calculation error: {str(e)}", {}