openstudio-standards 0.8.3 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openstudio-standards/btap/costing/README.md +502 -0
  3. data/lib/openstudio-standards/btap/costing/btap_costing.rb +473 -0
  4. data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +359 -0
  5. data/lib/openstudio-standards/btap/costing/btap_workflow.rb +117 -0
  6. data/lib/openstudio-standards/btap/costing/common_paths.rb +78 -0
  7. data/lib/openstudio-standards/btap/costing/common_resources/ConstructionProperties.csv +52 -0
  8. data/lib/openstudio-standards/btap/costing/common_resources/Constructions.csv +37 -0
  9. data/lib/openstudio-standards/btap/costing/common_resources/construction_sets.csv +1270 -0
  10. data/lib/openstudio-standards/btap/costing/common_resources/constructions_glazing.csv +61 -0
  11. data/lib/openstudio-standards/btap/costing/common_resources/constructions_opaque.csv +2256 -0
  12. data/lib/openstudio-standards/btap/costing/common_resources/costs.csv +1904 -0
  13. data/lib/openstudio-standards/btap/costing/common_resources/costs_local_factors.csv +2315 -0
  14. data/lib/openstudio-standards/btap/costing/common_resources/hvac_vent_ahu.csv +925 -0
  15. data/lib/openstudio-standards/btap/costing/common_resources/lighting.csv +364 -0
  16. data/lib/openstudio-standards/btap/costing/common_resources/lighting_sets.csv +2667 -0
  17. data/lib/openstudio-standards/btap/costing/common_resources/locations.csv +75 -0
  18. data/lib/openstudio-standards/btap/costing/common_resources/materials_glazing.csv +35 -0
  19. data/lib/openstudio-standards/btap/costing/common_resources/materials_hvac.csv +1699 -0
  20. data/lib/openstudio-standards/btap/costing/common_resources/materials_lighting.csv +267 -0
  21. data/lib/openstudio-standards/btap/costing/common_resources/materials_opaque.csv +164 -0
  22. data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +11 -0
  23. data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +136 -0
  24. data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +177 -0
  25. data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +353 -0
  26. data/lib/openstudio-standards/btap/costing/dcv_costing.rb +314 -0
  27. data/lib/openstudio-standards/btap/costing/dummy.epw +8768 -0
  28. data/lib/openstudio-standards/btap/costing/dummy.osm +5320 -0
  29. data/lib/openstudio-standards/btap/costing/envelope_costing.rb +284 -0
  30. data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2584 -0
  31. data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +155 -0
  32. data/lib/openstudio-standards/btap/costing/lighting_costing.rb +209 -0
  33. data/lib/openstudio-standards/btap/costing/mech_sizing.json +502 -0
  34. data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +42 -0
  35. data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +225 -0
  36. data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +28705 -0
  37. data/lib/openstudio-standards/btap/costing/nv_costing.rb +547 -0
  38. data/lib/openstudio-standards/btap/costing/parallel_tests.rb +92 -0
  39. data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +687 -0
  40. data/lib/openstudio-standards/btap/costing/shw_costing.rb +705 -0
  41. data/lib/openstudio-standards/btap/costing/test_list.txt +17 -0
  42. data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +26 -0
  43. data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +80 -0
  44. data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2616 -0
  45. data/lib/openstudio-standards/constructions/modify.rb +2 -1
  46. data/lib/openstudio-standards/standards/Standards.Model.rb +39 -9
  47. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Model.rb +2 -2
  48. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/ashrae_90_1_prm.UserData.rb +6 -1
  49. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +2 -27
  50. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_3_and_8_single_speed.rb +68 -27
  51. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_4.rb +64 -25
  52. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_6.rb +9 -14
  53. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +46 -20
  54. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +635 -248
  55. data/lib/openstudio-standards/standards/necb/NECB2011/data/constants.json +43 -7
  56. data/lib/openstudio-standards/standards/necb/NECB2011/data/fuel_type_sets.json +7 -1
  57. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/HighriseApartmentMult.osm +14272 -0
  58. data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2015_table_c1.json +1 -1
  59. data/lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json +437 -437
  60. data/lib/openstudio-standards/standards/necb/NECB2011/data/systems.json +516 -0
  61. data/lib/openstudio-standards/standards/necb/NECB2011/data/systems_including_sys5.json +588 -0
  62. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_namer.rb +489 -0
  63. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb +16 -6
  64. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_2_and_5.rb +48 -5
  65. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb +2 -2
  66. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb +35 -27
  67. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb +34 -23
  68. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb +8 -6
  69. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +42 -13
  70. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +214 -25
  71. data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +61 -1
  72. data/lib/openstudio-standards/standards/necb/NECB2015/data/space_types.json +636 -636
  73. data/lib/openstudio-standards/standards/necb/NECB2015/data/unitary_acs.json +38 -38
  74. data/lib/openstudio-standards/standards/necb/NECB2015/hvac_systems.rb +15 -6
  75. data/lib/openstudio-standards/standards/necb/NECB2017/data/space_types.json +636 -636
  76. data/lib/openstudio-standards/standards/necb/NECB2020/data/chillers.json +71 -71
  77. data/lib/openstudio-standards/standards/necb/README.md +343 -0
  78. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +190 -28
  79. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +14 -5
  80. data/lib/openstudio-standards/standards/necb/common/eccc_electric_grid_intensity_20250311.csv +14 -0
  81. data/lib/openstudio-standards/standards/necb/common/nir_gas_grid_intensity_20250311.csv +14 -0
  82. data/lib/openstudio-standards/standards/necb/common/system_types.yaml +0 -0
  83. data/lib/openstudio-standards/utilities/logging.rb +18 -14
  84. data/lib/openstudio-standards/version.rb +1 -1
  85. data/lib/openstudio-standards/weather/modify.rb +2 -2
  86. data/lib/openstudio-standards.rb +12 -0
  87. metadata +53 -2
@@ -29,9 +29,10 @@ class NECB2011 < Standard
29
29
  return default if variable.nil?
30
30
  if variable.is_a? String
31
31
  return true if variable.to_s.downcase == 'true'
32
- return false
32
+ return false if variable.to_s.downcase == 'false'
33
+ return default
33
34
  end
34
- return true if variable == true
35
+ return variable if variable.is_a?(TrueClass) || variable.is_a?(FalseClass)
35
36
  return default
36
37
  end
37
38
 
@@ -214,6 +215,11 @@ class NECB2011 < Standard
214
215
  custom_weather_folder: nil,
215
216
  debug: false,
216
217
  sizing_run_dir: Dir.pwd,
218
+ hvac_system_primary: nil,
219
+ hvac_system_dwelling_units: nil,
220
+ hvac_system_washrooms: nil,
221
+ hvac_system_corridor: nil,
222
+ hvac_system_storage: nil,
217
223
  primary_heating_fuel: 'Electricity',
218
224
  swh_fuel: nil,
219
225
  dcv_type: 'NECB_Default',
@@ -271,6 +277,7 @@ class NECB2011 < Standard
271
277
  necb_hdd: true,
272
278
  boiler_fuel: nil,
273
279
  boiler_cap_ratio: nil,
280
+ airloop_fancoils_heating: nil,
274
281
  oerd_utility_pricing: nil)
275
282
  model = load_building_type_from_library(building_type: building_type)
276
283
  return model_apply_standard(model: model,
@@ -279,6 +286,11 @@ class NECB2011 < Standard
279
286
  epw_file: epw_file,
280
287
  custom_weather_folder: custom_weather_folder,
281
288
  sizing_run_dir: sizing_run_dir,
289
+ hvac_system_primary: nil,
290
+ hvac_system_dwelling_units: nil,
291
+ hvac_system_washrooms: nil,
292
+ hvac_system_corridor: nil,
293
+ hvac_system_storage: nil,
282
294
  primary_heating_fuel: primary_heating_fuel,
283
295
  swh_fuel: swh_fuel,
284
296
  dcv_type: dcv_type, # Four options: (1) 'NECB_Default', (2) 'No_DCV', (3) 'Occupancy_based_DCV' , (4) 'CO2_based_DCV'
@@ -334,6 +346,7 @@ class NECB2011 < Standard
334
346
  necb_hdd: necb_hdd,
335
347
  boiler_fuel: boiler_fuel,
336
348
  boiler_cap_ratio: boiler_cap_ratio,
349
+ airloop_fancoils_heating: airloop_fancoils_heating,
337
350
  oerd_utility_pricing: oerd_utility_pricing
338
351
  )
339
352
  end
@@ -355,9 +368,15 @@ class NECB2011 < Standard
355
368
  tbd_interpolate: nil,
356
369
  epw_file:,
357
370
  custom_weather_folder: nil,
371
+ btap_weather: true,
358
372
  sizing_run_dir: Dir.pwd,
359
373
  necb_reference_hp: false,
360
374
  necb_reference_hp_supp_fuel: 'DefaultFuel',
375
+ hvac_system_primary: 'NECB_Default',
376
+ hvac_system_dwelling_units: 'NECB_Default',
377
+ hvac_system_washrooms: 'NECB_Default',
378
+ hvac_system_corridor: 'NECB_Default',
379
+ hvac_system_storage: 'NECB_Default',
361
380
  primary_heating_fuel: 'Electricity',
362
381
  swh_fuel: nil,
363
382
  dcv_type: 'NECB_Default',
@@ -413,9 +432,13 @@ class NECB2011 < Standard
413
432
  necb_hdd: true,
414
433
  boiler_fuel: nil,
415
434
  boiler_cap_ratio: nil,
435
+ airloop_fancoils_heating: nil,
416
436
  oerd_utility_pricing: nil)
417
437
 
418
- apply_weather_data(model: model, epw_file: epw_file, custom_weather_folder: custom_weather_folder)
438
+ apply_weather_data(model: model,
439
+ epw_file: epw_file,
440
+ custom_weather_folder: custom_weather_folder,
441
+ btap_weather: btap_weather)
419
442
  primary_heating_fuel = validate_primary_heating_fuel(primary_heating_fuel: primary_heating_fuel, model: model)
420
443
  self.fuel_type_set = SystemFuels.new()
421
444
  self.fuel_type_set.set_defaults(standards_data: @standards_data, primary_heating_fuel: primary_heating_fuel)
@@ -426,11 +449,21 @@ class NECB2011 < Standard
426
449
  boiler_fuel = convert_arg_to_string(variable: boiler_fuel, default: nil)
427
450
  boiler_cap_ratio = convert_arg_to_string(variable: boiler_cap_ratio, default: nil)
428
451
  swh_fuel = convert_arg_to_string(variable: swh_fuel, default: nil)
452
+ airloop_fancoils_heating = convert_arg_to_bool(variable: airloop_fancoils_heating, default: false)
453
+
454
+ # Check if custom systems are assigned to dwelling units, washrooms, corridors, and storage rooms. If they are, set
455
+ # them to be the same as the primary system type. If no primary system type is defined then set them to be nil.
456
+ hvac_system_dwelling_units, hvac_system_corridor, hvac_system_storage, hvac_system_washrooms = reset_hvac_system_if_required(hvac_system_primary: hvac_system_primary,
457
+ hvac_system_dwelling_units: hvac_system_dwelling_units,
458
+ hvac_system_corridor: hvac_system_corridor,
459
+ hvac_system_storage: hvac_system_storage,
460
+ hvac_system_washrooms: hvac_system_washrooms)
429
461
  oerd_utility_pricing = convert_arg_to_bool(variable: oerd_utility_pricing, default: false)
430
462
 
431
463
  boiler_cap_ratios = set_boiler_cap_ratios(boiler_cap_ratio: boiler_cap_ratio, boiler_fuel: boiler_fuel) unless boiler_cap_ratio.nil? && boiler_fuel.nil?
432
464
  self.fuel_type_set.set_boiler_fuel(standards_data: @standards_data, boiler_fuel: boiler_fuel, boiler_cap_ratios: boiler_cap_ratios) unless boiler_fuel.nil?
433
465
  self.fuel_type_set.set_swh_fuel(swh_fuel: swh_fuel) unless swh_fuel.nil? || swh_fuel.to_s.downcase == 'defaultfuel'
466
+ self.fuel_type_set.set_airloop_fancoils_heating() if airloop_fancoils_heating
434
467
 
435
468
  # Ensure the volume calculation in all spaces is done automatically
436
469
  model.getSpaces.sort.each do |space|
@@ -479,6 +512,11 @@ class NECB2011 < Standard
479
512
  apply_kiva_foundation(model)
480
513
  apply_systems_and_efficiencies(model: model,
481
514
  sizing_run_dir: sizing_run_dir,
515
+ hvac_system_primary: hvac_system_primary,
516
+ hvac_system_dwelling_units: hvac_system_dwelling_units,
517
+ hvac_system_washrooms: hvac_system_washrooms,
518
+ hvac_system_corridor: hvac_system_corridor,
519
+ hvac_system_storage: hvac_system_storage,
482
520
  primary_heating_fuel: primary_heating_fuel,
483
521
  dcv_type: dcv_type,
484
522
  ecm_system_name: ecm_system_name,
@@ -551,6 +589,11 @@ class NECB2011 < Standard
551
589
 
552
590
  def apply_systems_and_efficiencies(model:,
553
591
  sizing_run_dir:,
592
+ hvac_system_primary: 'NECB_Default',
593
+ hvac_system_dwelling_units: 'NECB_Default',
594
+ hvac_system_washrooms: 'NECB_Default',
595
+ hvac_system_corridor: 'NECB_Default',
596
+ hvac_system_storage: 'NECB_Default',
554
597
  primary_heating_fuel:,
555
598
  dcv_type: 'NECB_Default',
556
599
  ecm_system_name: 'NECB_Default',
@@ -582,6 +625,11 @@ class NECB2011 < Standard
582
625
 
583
626
  # Create Default Systems.
584
627
  apply_systems(model: model,
628
+ hvac_system_primary: hvac_system_primary,
629
+ hvac_system_dwelling_units: hvac_system_dwelling_units,
630
+ hvac_system_washrooms: hvac_system_washrooms,
631
+ hvac_system_corridor: hvac_system_corridor,
632
+ hvac_system_storage: hvac_system_storage,
585
633
  sizing_run_dir: sizing_run_dir,
586
634
  shw_scale: shw_scale,
587
635
  baseline_system_zones_map_option: baseline_system_zones_map_option)
@@ -620,9 +668,9 @@ class NECB2011 < Standard
620
668
  ecm.add_airloop_economizer(model: model, airloop_economizer_type: airloop_economizer_type)
621
669
  # Perform a second sizing run if needed
622
670
  if (!unitary_cop.nil? && unitary_cop != 'NECB_Default') || !model.getPlantLoops.empty?
623
- if model_run_sizing_run(model, "#{sizing_run_dir}/SR2", true) == false
624
- raise('sizing run 2 failed!')
625
- end
671
+ # Do a sizing run
672
+ try_sizing_run(model: model, sizing_run_dir: sizing_run_dir, sizing_run_subdir: 'SR2')
673
+ #end
626
674
  end
627
675
  # apply unitary cop
628
676
  ecm.modify_unitary_cop(model: model, unitary_cop: unitary_cop, sizing_done: true, sql_db_vars_map: sql_db_vars_map)
@@ -689,7 +737,7 @@ class NECB2011 < Standard
689
737
  ecm.scale_oa_loads(model: model, scale: oa_scale)
690
738
  end
691
739
 
692
- def apply_weather_data(model:, epw_file:, custom_weather_folder: nil)
740
+ def apply_weather_data(model:, epw_file:, custom_weather_folder: nil, btap_weather: true)
693
741
  # Create full path to weather file
694
742
  weather_files = File.absolute_path(File.join(__FILE__, '..', '..', '..', '..', '..' , '..', "data/weather"))
695
743
  weather_file = File.join(weather_files, epw_file)
@@ -699,7 +747,7 @@ class NECB2011 < Standard
699
747
  # Check if btap_batch transferred the weather file
700
748
  weather_transfer = check_datapoint_weather_folder(epw_file: epw_file, weather_folder: weather_files, custom_weather_folder: custom_weather_folder)
701
749
  # If btap_batch didn't transfer the weather file, download it.
702
- get_weather_file_from_repo(epw_file: epw_file) unless weather_transfer
750
+ get_weather_file_from_repo(epw_file: epw_file, btap_weather: btap_weather) unless weather_transfer
703
751
  end
704
752
 
705
753
  # Fix EMS references. Temporary workaround for OS issue #2598
@@ -1080,10 +1128,10 @@ class NECB2011 < Standard
1080
1128
 
1081
1129
  # @param necb_reference_hp [Boolean] if true, NECB reference model rules for heat pumps will be used.
1082
1130
  def apply_standard_efficiencies(model:, sizing_run_dir:, dcv_type: 'NECB_Default', necb_reference_hp: false)
1083
- raise('validation of model failed.') unless validate_initial_model(model)
1131
+ # Do a sizing run
1132
+ try_sizing_run(model: model, sizing_run_dir: sizing_run_dir, sizing_run_subdir: 'plant_loops')
1084
1133
 
1085
1134
  climate_zone = 'NECB HDD Method'
1086
- raise("sizing run 1 failed! check #{sizing_run_dir}") if model_run_sizing_run(model, "#{sizing_run_dir}/plant_loops", true) == false
1087
1135
 
1088
1136
  # This is needed for NECB2011 as a workaround for sizing the reheat boxes.
1089
1137
  model.getAirTerminalSingleDuctVAVReheats.each { |iobj| air_terminal_single_duct_vav_reheat_set_heating_cap(iobj) }
@@ -2286,28 +2334,42 @@ class NECB2011 < Standard
2286
2334
  return model
2287
2335
  end
2288
2336
 
2289
- # This method handles looking for the epw_file in the https://github.com/canmet-energy/btap_weather repository. It
2337
+ # This method handles looking for the epw_file in btap_weather or Climate.OneBuilding.Org. It
2290
2338
  # checks for the epw_file in the historical data first. If it is not there then it looks in the future weather data.
2291
2339
  # If it is not there either, it throws an error.
2292
- # epw_file (String): The name of the epw file. The different weather files all share the same name as the epw file,
2293
- # only the extension changes.
2294
- def get_weather_file_from_repo(epw_file:)
2340
+ # epw_file (String): The name of the epw file. The different weather files all share the same name as the epw file,
2341
+ # only the extension changes.
2342
+ # btap_weather (Boolean): Determines whether to download from the btap_weather repository or Climate.OneBuilding.Org.
2343
+ def get_weather_file_from_repo(epw_file:, btap_weather:)
2295
2344
  # Get just the weather file name without the extension
2296
2345
  weather_loc = epw_file[0..-5]
2297
2346
  # Get the url of the file containing the historical weather data file names in the repository and the repository
2298
2347
  # folder containing the files
2299
- historic_weather_files_loc = @standards_data['constants']['historic_weather_file_list']['value'].to_s
2300
- historic_git_folder = @standards_data['constants']['historic_weather_folder_url']['value'].to_s
2348
+ if btap_weather
2349
+ historic_weather_files_loc = @standards_data['constants']['historic_weather_file_list_btap']['value'].to_s
2350
+ historic_folder = @standards_data['constants']['historic_weather_folder_url_btap']['value'].to_s
2351
+ future_weather_files_loc = @standards_data['constants']['future_weather_file_list_btap']['value'].to_s
2352
+ future_folder = @standards_data['constants']['future_weather_folder_url_btap']['value'].to_s
2353
+ else
2354
+ historic_weather_files_loc = @standards_data['constants']['historic_weather_file_list']['value'].to_s
2355
+ historic_folder = @standards_data['constants']['historic_weather_folder_url']['value'].to_s
2356
+ future_weather_files_loc = @standards_data['constants']['future_weather_file_list']['value'].to_s
2357
+ future_folder = @standards_data['constants']['future_weather_folder_url']['value'].to_s
2358
+ end
2301
2359
  # Get the files from the repository
2302
- success_flag = download_and_save_file(weather_list_url: historic_weather_files_loc, weather_loc: weather_loc, git_folder: historic_git_folder)
2360
+ success_flag = download_and_save_file(weather_list_url: historic_weather_files_loc,
2361
+ weather_loc: weather_loc,
2362
+ download_folder: historic_folder,
2363
+ btap_weather: btap_weather)
2303
2364
  return if success_flag
2304
2365
  # If the file could not be found in the historical data look for it with the future weather data.
2305
2366
  puts "Could not find #{epw_file} in historical weather data files, looking in future weather data files."
2306
- future_weather_files_loc = @standards_data['constants']['future_weather_file_list']['value'].to_s
2307
- future_git_folder = @standards_data['constants']['future_weather_folder_url']['value'].to_s
2308
- success_flag = download_and_save_file(weather_list_url: future_weather_files_loc, weather_loc: weather_loc, git_folder: future_git_folder)
2367
+ success_flag = download_and_save_file(weather_list_url: future_weather_files_loc,
2368
+ weather_loc: weather_loc,
2369
+ download_folder: future_folder,
2370
+ btap_weather: btap_weather)
2309
2371
  return if success_flag
2310
- raise("Could not locate the following file in the canmet/btap_weather repository or could not extract the data: #{epw_file}. Please check the spelling of the file or visit https://github.com/canmet-energy/btap_weather to see if the file exists.")
2372
+ raise("Could not locate the following file in #{btap_weather ? "btap_weather" : "Climate.OneBuilding.Org"} or could not extract the data: #{epw_file} Please check the spelling of the file or visit #{btap_weather ? "https://github.com/canmet-energy/btap_weather" : "https://climate.onebuilding.org/WMO_Region_4_North_and_Central_America/"} to see if the file exists.")
2311
2373
  end
2312
2374
 
2313
2375
  # This method actually looks for and downloads the zip file from the https://github.com/canmet-energy/btap_weather
@@ -2321,7 +2383,8 @@ class NECB2011 < Standard
2321
2383
  # weather_loc (string): the name of the epw file we are looking for without the .epw extension
2322
2384
  # git_folder (string): the url of the folder containing the weather files. As of 2023-07-07 this this is either the
2323
2385
  # url of the historical weather data folder or the future weather data folder.
2324
- def download_and_save_file(weather_list_url:, weather_loc:, git_folder:)
2386
+ # btap_weather (Boolean): Determines whether to download from the btap_weather repository or Climate.OneBuilding.Org.
2387
+ def download_and_save_file(weather_list_url:, weather_loc:, download_folder:, btap_weather:)
2325
2388
  status = false
2326
2389
  attempt = 1
2327
2390
  # Try to download the list of weather files 5 times, waiting 5 seconds between each attempt.
@@ -2341,7 +2404,27 @@ class NECB2011 < Standard
2341
2404
  unless zip_name.nil?
2342
2405
  # Found the weather file on the list
2343
2406
  # Define the full url of the weather zip file we want to download
2344
- save_file_url = git_folder + zip_name
2407
+ if btap_weather
2408
+ save_file_url = download_folder + zip_name
2409
+ else
2410
+ # Used to resolve the Climate.OneBuilding.Org download link from using just the filename.
2411
+ abbreviation_map = {
2412
+ 'AB' => 'AB_Alberta/',
2413
+ 'BC' => 'BC_British_Columbia/',
2414
+ 'MB' => 'MB_Manitoba/',
2415
+ 'NB' => 'NB_New_Brunswick/',
2416
+ 'NL' => 'NL_Newfoundland_and_Labrador/',
2417
+ 'NS' => 'NS_Nova_Scotia/',
2418
+ 'NT' => 'NT_Northwest_Territories/',
2419
+ 'NU' => 'NU_Nunavut/',
2420
+ 'ON' => 'ON_Ontario/',
2421
+ 'PE' => 'PE_Prince_Edward_Island/',
2422
+ 'QC' => 'QC_Quebec/',
2423
+ 'SK' => 'SK_Saskatchewan/',
2424
+ 'YT' => 'YT_Yukon/'
2425
+ }
2426
+ save_file_url = download_folder + abbreviation_map[zip_name[4..5]] + zip_name
2427
+ end
2345
2428
  # Define the local location of where the weather zip file will be saved
2346
2429
  weather_dir = File.absolute_path(File.join(__FILE__, '..', '..', '..', '..', '..' , '..', "data/weather"))
2347
2430
  save_file = File.join(weather_dir, zip_name)
@@ -2417,6 +2500,9 @@ class NECB2011 < Standard
2417
2500
  def extract_weather_data(zipped_file:, weather_dir:)
2418
2501
  # Set a flag to check if the weather data is for future weather.
2419
2502
  future_file = false
2503
+
2504
+ # Data structure to determine if the weather file contains all necessary elements.
2505
+ checklist = {'.epw' => false, '.stat' => false, '.ddy' => false}
2420
2506
  # Start expanding the data.
2421
2507
  Zip::File.open(zipped_file) do |zip_file|
2422
2508
  puts "Expanding #{zipped_file}"
@@ -2429,7 +2515,7 @@ class NECB2011 < Standard
2429
2515
  # to just '.ddy'. This is so the rest of BTAP uses the _ASHRAE.ddy file.
2430
2516
  entry_name = entry.name.to_s
2431
2517
  if entry_name.length > 11
2432
- future_file = true if entry_name[-11..-1] == '_ASHRAE.ddy'
2518
+ future_file = true if entry_name.end_with?('_ASHRAE.ddy')
2433
2519
  end
2434
2520
  # entry.extract # This was required before but now it isn't. I'm confused so am saving this comment to
2435
2521
  # remind me if there are problems later.
@@ -2437,6 +2523,12 @@ class NECB2011 < Standard
2437
2523
  content = entry.get_input_stream.read
2438
2524
  # Save the data locally
2439
2525
  File.open(curr_save_file, 'wb') { |save_f| save_f.write(content) }
2526
+
2527
+ # Add the file extension of the current entry to the checklist.
2528
+ file_extension = entry_name[entry_name.rindex('.')..]
2529
+ if checklist.key?(file_extension)
2530
+ checklist[file_extension] = true
2531
+ end
2440
2532
  end
2441
2533
  end
2442
2534
  if future_file
@@ -2453,6 +2545,13 @@ class NECB2011 < Standard
2453
2545
  FileUtils.cp(orig_ddy_name, rev_ddy_name)
2454
2546
  FileUtils.cp(ashrae_ddy_name, orig_ddy_name)
2455
2547
  end
2548
+
2549
+ # Return false if not all files are present:
2550
+ if !checklist.values.all?()
2551
+ puts "Error: Not all files present in #{zipped_file}. Missing #{checklist.select { |k, v| v == false}.keys.join(", ") } file(s). Exiting..."
2552
+ exit(1)
2553
+ end
2554
+
2456
2555
  # Return true if everything worked out
2457
2556
  return true
2458
2557
  end
@@ -2521,6 +2620,24 @@ class NECB2011 < Standard
2521
2620
  return boiler_cap_ratios
2522
2621
  end
2523
2622
 
2623
+ # Until someone has time to allow dwelling units, washrooms, cooridors, and storage rooms can get their own custom
2624
+ # system types (beyond the default necb_system), this metod will set the system type for those rooms to either be
2625
+ # their default or to the primary system type (if one is defined and the other sytems are not set to default)
2626
+ def reset_hvac_system_if_required(hvac_system_primary: nil, hvac_system_dwelling_units: nil, hvac_system_corridor: nil, hvac_system_storage: nil, hvac_system_washrooms: nil)
2627
+ if hvac_system_primary.nil? || hvac_system_primary.to_s.downcase == "necb_default"
2628
+ hvac_system_dwelling_units = "NECB_Default"
2629
+ hvac_system_corridor = "NECB_Default"
2630
+ hvac_system_storage = "NECB_Default"
2631
+ hvac_system_washrooms = "NECB_Default"
2632
+ else
2633
+ hvac_system_dwelling_units = hvac_system_primary unless hvac_system_dwelling_units.nil? || hvac_system_dwelling_units.to_s.downcase == "necb_default"
2634
+ hvac_system_corridor = hvac_system_primary unless hvac_system_corridor.nil? || hvac_system_corridor.to_s.downcase == "necb_default"
2635
+ hvac_system_storage = hvac_system_primary unless hvac_system_storage.nil? || hvac_system_storage.to_s.downcase == "necb_default"
2636
+ hvac_system_washrooms = hvac_system_primary unless hvac_system_washrooms.nil? || hvac_system_washrooms.to_s.downcase == "necb_default"
2637
+ end
2638
+ return hvac_system_dwelling_units, hvac_system_corridor, hvac_system_storage, hvac_system_washrooms
2639
+ end
2640
+
2524
2641
  # This method checks if the output_meters argument contains a net electricity meter with 'timestep' frequency. If one
2525
2642
  # is then the method does nothing. If one is not then it is added. This is used in conjunction with the
2526
2643
  # 'oerd_utility_pricing' argument. If that argument is present then a net electricity meter with 'timestep' frequency
@@ -2531,17 +2648,89 @@ class NECB2011 < Standard
2531
2648
  {
2532
2649
  "name" => "ElectricityNet:Facility",
2533
2650
  "frequency" => "Hourly"
2651
+ },
2652
+ {
2653
+ "name" => "Heating:Electricity",
2654
+ "frequency" => "Hourly"
2655
+ },
2656
+ {
2657
+ "name" => "WaterSystems:Electricity",
2658
+ "frequency" => "Hourly"
2534
2659
  }
2535
2660
  ]
2536
2661
  else
2537
- electnet_facility = output_meters.select { |output_meter| (output_meter["name"].to_s.downcase == "electricitynet:facility") && (output_meter["frequency"].to_s.downcase == "zone timestep") }
2662
+ electnet_facility = output_meters.select { |output_meter| (output_meter["name"].to_s.downcase == "electricitynet:facility") && (output_meter["frequency"].to_s.downcase == "hourly") }
2538
2663
  if electnet_facility.empty?
2539
2664
  output_meters << {
2540
2665
  "name" => "ElectricityNet:Facility",
2541
2666
  "frequency" => "Hourly"
2542
2667
  }
2543
2668
  end
2669
+ heating_elec = output_meters.select { |output_meter| (output_meter["name"].to_s.downcase == "heating:electricity") && (output_meter["frequency"].to_s.downcase == "hourly") }
2670
+ if electnet_facility.empty?
2671
+ output_meters << {
2672
+ "name" => "Heating:Electricity",
2673
+ "frequency" => "Hourly"
2674
+ }
2675
+ end
2676
+ watersys_elec = output_meters.select { |output_meter| (output_meter["name"].to_s.downcase == "watersystems:electricity") && (output_meter["frequency"].to_s.downcase == "hourly") }
2677
+ if electnet_facility.empty?
2678
+ output_meters << {
2679
+ "name" => "WaterSystems:Electricity",
2680
+ "frequency" => "Hourly"
2681
+ }
2682
+ end
2544
2683
  end
2545
2684
  return output_meters
2546
2685
  end
2686
+
2687
+ # This method is used to do a sizing run on the model. It will return true if the sizing run was successful and
2688
+ # will generate an error if it was not. The method will also resize any DX heating coils that have a capacity less than 1.0 kW to
2689
+ # 1.0 kW and rerun the sizing run until it succeeds or the sizing run continues to fail.
2690
+ #
2691
+ # Arguments:
2692
+ # model (OpenStudio::Model::Model): The model to run the sizing run on.
2693
+ # sizing_run_dir (String): The directory where the sizing run files will be saved.
2694
+ def try_sizing_run(model:, sizing_run_dir:, sizing_run_subdir:)
2695
+ raise('validation of model failed.') unless validate_initial_model(model)
2696
+
2697
+ # Do a sizing run to determine the system capacities. If a sizing run fails, hard size any failing DX heating coils
2698
+ # to 1.0 kW and rerun the sizing run until it succeeds. If no DX heating coils are found, or none had a small
2699
+ # capacity then raise an error.
2700
+ loop do
2701
+ sizing_run_success = model_run_sizing_run(model, "#{sizing_run_dir}/#{sizing_run_subdir}", true)
2702
+ break if sizing_run_success
2703
+
2704
+ # Sizing run failed, check all DX heating coils and set their size to 1 if less than 1
2705
+ dx_coil_changed = false
2706
+
2707
+ model.getCoilHeatingDXSingleSpeeds.each do |coil|
2708
+ autosized_capacity = coil.autosizedRatedTotalHeatingCapacity
2709
+ if autosized_capacity.is_initialized && autosized_capacity.get < 1.0
2710
+ coil.setRatedTotalHeatingCapacity(1.0)
2711
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "DX Heating Coil #{coil.name.to_s} has a rated capacity less than 1.0 kW and has been resized to 1.0 kW to avoid sizing run failure.")
2712
+ puts "DX Heating Coil #{coil.name.to_s} has a rated capacity less than 1.0 kW and has been resized to 1.0 kW to avoid sizing run failure."
2713
+ dx_coil_changed = true
2714
+ end
2715
+ end
2716
+ model.getCoilHeatingDXMultiSpeeds.each do |coil|
2717
+ coil.stages.each do |stage|
2718
+ autosized_capacity = stage.autosizedGrossRatedHeatingCapacity
2719
+ if autosized_capacity.is_initialized && autosized_capacity.get < 1.0
2720
+ stage.setGrossRatedHeatingCapacity(1.0)
2721
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "A DX Heating Coil #{coil.name.to_s} stage has a rated capacity less than 1.0 kW and has been resized to 1.0 kW to avoid sizing run failure.")
2722
+ puts "A DX Heating Coil #{coil.name.to_s} stage has a rated capacity less than 1.0 kW and has been resized to 1.0 kW to avoid sizing run failure."
2723
+ dx_coil_changed = true
2724
+ end
2725
+ end
2726
+ end
2727
+
2728
+ # If no DX coil was changed, break loop and raise error
2729
+ if !dx_coil_changed
2730
+ raise("sizing run failed! check #{sizing_run_dir}/#{sizing_run_subdir} (DX coil sizes adjusted, but no further changes possible)")
2731
+ end
2732
+ puts "Rerunning sizing run after adjusting DX heating coil capacity to 1.0 kW."
2733
+ # Otherwise, loop will rerun sizing
2734
+ end
2735
+ end
2547
2736
  end
@@ -9,6 +9,7 @@ class SystemFuels
9
9
  attr_accessor :mau_heating_coil_type
10
10
  attr_accessor :mau_cooling_type
11
11
  attr_accessor :chiller_type
12
+ attr_accessor :heating_coil_type_sys2
12
13
  attr_accessor :heating_coil_type_sys3
13
14
  attr_accessor :heating_coil_type_sys4
14
15
  attr_accessor :heating_coil_type_sys6
@@ -17,6 +18,8 @@ class SystemFuels
17
18
  attr_accessor :fan_type
18
19
  attr_accessor :ecm_fueltype
19
20
  attr_accessor :swh_fueltype
21
+ attr_accessor :force_boiler
22
+ attr_accessor :force_airloop_hot_water
20
23
 
21
24
  def set_defaults(standards_data:, primary_heating_fuel:)
22
25
  # Get fuelset.
@@ -33,6 +36,7 @@ class SystemFuels
33
36
  @mau_cooling_type = system_fuel_defaults['mau_cooling_type']
34
37
  @chiller_type = system_fuel_defaults['chiller_type']
35
38
  @mau_heating_coil_type = system_fuel_defaults['mau_heating_coil_type']
39
+ @heating_coil_type_sys2 = system_fuel_defaults['heating_coil_type_sys2']
36
40
  @heating_coil_type_sys3 = system_fuel_defaults['heating_coil_type_sys3']
37
41
  @heating_coil_type_sys4 = system_fuel_defaults['heating_coil_type_sys4']
38
42
  @heating_coil_type_sys6 = system_fuel_defaults['heating_coil_type_sys6']
@@ -41,6 +45,8 @@ class SystemFuels
41
45
  @fan_type = system_fuel_defaults['fan_type']
42
46
  @swh_fueltype = system_fuel_defaults['swh_fueltype']
43
47
  @ecm_fueltype = system_fuel_defaults['ecm_fueltype']
48
+ @force_boiler = false
49
+ @force_airloop_hot_water = false
44
50
  end
45
51
 
46
52
  # Forces a boiler to be generated. It searches boiler_fuel_type_sets.json for the boiler_fuel string and sets the
@@ -52,12 +58,66 @@ class SystemFuels
52
58
  @backup_boiler_fueltype = boiler_fuel_defaults['backup_boiler_fueltype']
53
59
  @secondary_boiler_cap_frac = boiler_cap_ratios[:secondary_ratio]
54
60
  @baseboard_type = boiler_fuel_defaults['baseboard_type']
55
- @mau_heating_coil_type = boiler_fuel_defaults['mau_heating_coil_type'] unless @mau_heating_coil_type == 'DX'
56
61
  @heating_coil_type_sys6 = boiler_fuel_defaults['heating_coil_type_sys6']
62
+ @force_boiler = true
57
63
  end
58
64
 
59
65
  # Reset the Service Hot Water fuel.
60
66
  def set_swh_fuel(swh_fuel:)
61
67
  @swh_fueltype = swh_fuel
62
68
  end
69
+
70
+ #Forces heating_coils to be 'Hot Water' except when using HPs
71
+ def set_airloop_fancoils_heating()
72
+ @force_airloop_hot_water = true
73
+ @mau_heating_coil_type = "Hot Water" unless @mau_heating_coil_type == 'DX'
74
+ @heating_coil_type_sys2 = "Hot Water" unless @heating_coil_type_sys2 == 'DX'
75
+ @heating_coil_type_sys3 = "Hot Water" unless @heating_coil_type_sys3 == 'DX'
76
+ @heating_coil_type_sys4 = "Hot Water" unless @heating_coil_type_sys4 == 'DX'
77
+ @heating_coil_type_sys6 = "Hot Water" unless @heating_coil_type_sys6 == 'DX'
78
+ if @mau_heating_coil_type == 'DX' || @heating_coil_type_sys3 == 'DX' || @heating_coil_type_sys4 == 'DX' || @heating_coil_type_sys6 == 'DX' || @heating_coil_type_sys2 == 'DX'
79
+ @necb_reference_hp_supp_fuel = 'Hot Water'
80
+ end
81
+ end
82
+
83
+ # Reset system fuels to match parameters defined by hvac_system_primary
84
+ def set_fuel_to_hvac_system_primary(hvac_system_primary:, standards_data:)
85
+ hvac_system_data = standards_data['hvac_types'].find { |system| system['description'].to_s.downcase == hvac_system_primary.to_s.downcase }
86
+ return if hvac_system_data.nil? || hvac_system_data.empty?
87
+ @baseboard_type = hvac_system_data["baseboard_type"].to_s unless hvac_system_data["baseboard_type"].nil? || @force_boiler == true
88
+ @mau_heating_coil_type = hvac_system_data["mau_heating_type"].to_s unless hvac_system_data["mau_heating_type"].nil? || @force_airloop_hot_water == true
89
+ @mau_type = hvac_system_data["mau_type"].to_bool unless hvac_system_data["mau_type"].nil?
90
+ @necb_reference_hp = hvac_system_data["necb_reference_hp"].to_bool unless hvac_system_data["necb_reference_hp"].nil?
91
+ @necb_reference_hp_supp_fuel = hvac_system_data["necb_reference_hp_supp_fuel"] unless hvac_system_data["necb_reference_hp_supp_fuel"].nil? || @force_airloop_hot_water == true
92
+ # If applying a hvac_system_primary with an NECB reference HP, make sure that the system 4 systems (if left at
93
+ # NECB_Default) work with the NECB reference HP.
94
+ if hvac_system_data["necb_reference_hp"].to_bool
95
+ @heating_coil_type_sys4 = "DX"
96
+ end
97
+ end
98
+
99
+ # Reset to default fuel info
100
+ def reset_default_fuel_info(init_fuel_type:)
101
+ @name = init_fuel_type[:name]
102
+ @boiler_fueltype = init_fuel_type[:boiler_fueltype]
103
+ @backup_boiler_fueltype = init_fuel_type[:backup_boiler_fueltype]
104
+ @primary_boiler_cap_frac = init_fuel_type[:primary_boiler_cap_frac]
105
+ @secondary_boiler_cap_frac = init_fuel_type[:secondary_boiler_cap_frac]
106
+ @baseboard_type = init_fuel_type[:baseboard_type]
107
+ @mau_type = init_fuel_type[:mau_type]
108
+ @mau_heating_coil_type = init_fuel_type[:mau_heating_coil_type]
109
+ @mau_cooling_type = init_fuel_type[:mau_cooling_type]
110
+ @chiller_type = init_fuel_type[:chiller_type]
111
+ @heating_coil_type_sys2 = init_fuel_type[:heating_coil_type_sys2]
112
+ @heating_coil_type_sys3 = init_fuel_type[:heating_coil_type_sys3]
113
+ @heating_coil_type_sys4 = init_fuel_type[:heating_coil_type_sys4]
114
+ @heating_coil_type_sys6 = init_fuel_type[:heating_coil_type_sys6]
115
+ @necb_reference_hp = init_fuel_type[:necb_reference_hp]
116
+ @necb_reference_hp_supp_fuel = init_fuel_type[:necb_reference_hp_supp_fuel]
117
+ @fan_type = init_fuel_type[:fan_type]
118
+ @ecm_fueltype = init_fuel_type[:ecm_fueltype]
119
+ @swh_fueltype = init_fuel_type[:swh_fueltype]
120
+ @force_boiler = init_fuel_type[:force_boiler]
121
+ @force_airloop_hot_water = init_fuel_type[:force_airloop_hot_water]
122
+ end
63
123
  end