urbanopt-cli 0.10.0 → 0.11.0

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 (712) hide show
  1. checksums.yaml +4 -4
  2. data/.github/release.yml +24 -0
  3. data/.github/workflows/nightly_ci_build.yml +39 -29
  4. data/.gitignore +1 -0
  5. data/CHANGELOG.md +17 -0
  6. data/CMakeLists.txt +7 -7
  7. data/FindOpenStudioSDK.cmake +8 -8
  8. data/LICENSE.md +8 -35
  9. data/README.md +25 -10
  10. data/example_files/Gemfile +9 -9
  11. data/example_files/example_project_combined.json +6 -2
  12. data/example_files/example_project_with_ghe.json +859 -0
  13. data/example_files/mappers/Baseline.rb +39 -415
  14. data/example_files/mappers/ChilledWaterStorage.rb +1 -1
  15. data/example_files/mappers/CreateBar.rb +1 -1
  16. data/example_files/mappers/EvCharging.rb +1 -1
  17. data/example_files/mappers/FlexibleHotWater.rb +1 -1
  18. data/example_files/mappers/Floorspace.rb +1 -1
  19. data/example_files/mappers/HighEfficiency.rb +1 -1
  20. data/example_files/mappers/HighEfficiencyCreateBar.rb +1 -1
  21. data/example_files/mappers/HighEfficiencyFloorspace.rb +1 -1
  22. data/example_files/mappers/PeakHoursMelsShedding.rb +1 -1
  23. data/example_files/mappers/PeakHoursThermostatAdjust.rb +1 -1
  24. data/example_files/mappers/ThermalStorage.rb +1 -1
  25. data/example_files/mappers/base_workflow.osw +11 -4
  26. data/example_files/mappers/residential/template/util.rb +138 -0
  27. data/example_files/mappers/residential/util.rb +276 -0
  28. data/example_files/measures/BuildResidentialModel/measure.rb +118 -230
  29. data/example_files/measures/BuildResidentialModel/measure.xml +344 -233
  30. data/example_files/measures/BuildResidentialModel/resources/geometry.rb +7 -2
  31. data/example_files/measures/BuildResidentialModel/resources/unit_conversions.rb +5 -0
  32. data/example_files/measures/BuildResidentialModel/resources/util.rb +5 -0
  33. data/example_files/measures/BuildResidentialModel/tests/test_build_residential_model.rb +344 -0
  34. data/example_files/measures/BuildResidentialModel/tests/xml_building/17/feature1.xml +2112 -0
  35. data/example_files/measures/BuildResidentialModel/tests/xml_building/17/feature2.xml +2112 -0
  36. data/example_files/osm_building/7.osm +0 -2
  37. data/example_files/osm_building/8.osm +0 -2
  38. data/example_files/osm_building/9.osm +0 -2
  39. data/example_files/python_deps/dependencies.json +4 -3
  40. data/example_files/resources/hpxml-measures/.gitattributes +3 -0
  41. data/example_files/resources/hpxml-measures/.github/pull_request_template.md +2 -2
  42. data/example_files/resources/hpxml-measures/.github/workflows/add_to_project.yml +17 -0
  43. data/example_files/resources/hpxml-measures/.github/workflows/config.yml +37 -8
  44. data/example_files/resources/hpxml-measures/.gitignore +1 -0
  45. data/example_files/resources/hpxml-measures/.readthedocs.yml +6 -2
  46. data/example_files/resources/hpxml-measures/BuildResidentialHPXML/README.md +5596 -0
  47. data/example_files/resources/hpxml-measures/BuildResidentialHPXML/README.md.erb +41 -0
  48. data/example_files/resources/hpxml-measures/BuildResidentialHPXML/measure.rb +1324 -1035
  49. data/example_files/resources/hpxml-measures/BuildResidentialHPXML/measure.xml +325 -236
  50. data/example_files/resources/hpxml-measures/BuildResidentialHPXML/resources/geometry.rb +119 -152
  51. data/example_files/resources/hpxml-measures/BuildResidentialHPXML/tests/{build_residential_hpxml_test.rb → test_build_residential_hpxml.rb} +225 -107
  52. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/README.md +96 -0
  53. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/README.md.erb +41 -0
  54. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/measure.rb +73 -31
  55. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/measure.xml +60 -40
  56. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/resources/README.md +48 -23
  57. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/resources/constants.rb +5 -0
  58. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/resources/schedules.rb +6 -12
  59. data/example_files/resources/hpxml-measures/BuildResidentialScheduleFile/tests/{build_residential_schedule_file_test.rb → test_build_residential_schedule_file.rb} +162 -35
  60. data/example_files/resources/hpxml-measures/Changelog.md +57 -1
  61. data/example_files/resources/hpxml-measures/Gemfile +1 -1
  62. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/README.md +83 -0
  63. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/README.md.erb +41 -0
  64. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/measure.rb +1081 -878
  65. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/measure.xml +258 -204
  66. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/airflow.rb +205 -178
  67. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/battery.rb +43 -18
  68. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/constants.rb +37 -112
  69. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/constructions.rb +34 -73
  70. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/data/Xing_okstate_0664D_13659_Table_A-3.csv +4165 -0
  71. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/data/unavailable_periods.csv +2 -2
  72. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/energyplus.rb +5 -1
  73. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/generator.rb +13 -7
  74. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/geometry.rb +95 -42
  75. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hotwater_appliances.rb +132 -108
  76. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hpxml.rb +1695 -1267
  77. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hpxml_defaults.rb +668 -589
  78. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hpxml_schema/HPXML.xsd +304 -553
  79. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml +197 -112
  80. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hvac.rb +1140 -1745
  81. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/hvac_sizing.rb +412 -325
  82. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/lighting.rb +56 -48
  83. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/location.rb +49 -38
  84. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/materials.rb +5 -0
  85. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/meta_measure.rb +17 -1
  86. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/minitest_helper.rb +5 -0
  87. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/misc_loads.rb +94 -78
  88. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/output.rb +60 -2
  89. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/psychrometrics.rb +6 -1
  90. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/pv.rb +11 -5
  91. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-non-stochastic.csv +1 -1
  92. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-stochastic-30-mins.csv +52561 -0
  93. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-stochastic_2.csv +8761 -0
  94. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-stochastic_3.csv +8761 -0
  95. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-stochastic_4.csv +8761 -0
  96. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-stochastic_5.csv +8761 -0
  97. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedule_files/occupancy-stochastic_6.csv +8761 -0
  98. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/schedules.rb +129 -137
  99. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/simcontrols.rb +12 -21
  100. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/unit_conversions.rb +5 -0
  101. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/util.rb +7 -2
  102. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/utility_bills.rb +6 -1
  103. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/version.rb +7 -2
  104. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/waterheater.rb +179 -144
  105. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/weather.rb +129 -71
  106. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/xmlhelper.rb +5 -0
  107. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/resources/xmlvalidator.rb +23 -6
  108. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_airflow.rb +129 -118
  109. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_battery.rb +25 -20
  110. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_defaults.rb +2282 -2239
  111. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_enclosure.rb +395 -204
  112. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_generator.rb +12 -7
  113. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_hotwater_appliance.rb +56 -51
  114. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_hvac.rb +369 -230
  115. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_hvac_sizing.rb +371 -191
  116. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_lighting.rb +27 -20
  117. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_location.rb +55 -5
  118. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_miscloads.rb +35 -30
  119. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_pv.rb +13 -8
  120. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_schedules.rb +107 -93
  121. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_simcontrols.rb +11 -6
  122. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_validation.rb +757 -573
  123. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_water_heater.rb +77 -72
  124. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/test_weather.rb +36 -6
  125. data/example_files/resources/hpxml-measures/HPXMLtoOpenStudio/tests/util.rb +5 -0
  126. data/example_files/resources/hpxml-measures/README.md +2 -0
  127. data/example_files/resources/hpxml-measures/Rakefile +10 -3
  128. data/example_files/resources/hpxml-measures/ReportSimulationOutput/README.md +787 -0
  129. data/example_files/resources/hpxml-measures/ReportSimulationOutput/README.md.erb +41 -0
  130. data/example_files/resources/hpxml-measures/ReportSimulationOutput/measure.rb +730 -418
  131. data/example_files/resources/hpxml-measures/ReportSimulationOutput/measure.xml +1215 -9
  132. data/example_files/resources/hpxml-measures/ReportSimulationOutput/tests/{output_report_test.rb → test_report_sim_output.rb} +130 -299
  133. data/example_files/resources/hpxml-measures/ReportUtilityBills/README.md +87 -0
  134. data/example_files/resources/hpxml-measures/ReportUtilityBills/README.md.erb +41 -0
  135. data/example_files/resources/hpxml-measures/ReportUtilityBills/measure.rb +261 -89
  136. data/example_files/resources/hpxml-measures/ReportUtilityBills/measure.xml +179 -94
  137. data/example_files/resources/hpxml-measures/ReportUtilityBills/resources/simple_rates/Average_retail_price_of_electricity.csv +68 -68
  138. data/example_files/resources/hpxml-measures/ReportUtilityBills/resources/simple_rates/NG_PRI_SUM_A_EPG0_PRS_DMCF_A.csv +3 -2
  139. data/example_files/resources/hpxml-measures/ReportUtilityBills/resources/simple_rates/PET_PRI_WFR_A_EPD2F_PRS_DPGAL_W.csv +713 -685
  140. data/example_files/resources/hpxml-measures/ReportUtilityBills/resources/simple_rates/PET_PRI_WFR_A_EPLLPA_PRS_DPGAL_W.csv +716 -688
  141. data/example_files/resources/hpxml-measures/ReportUtilityBills/resources/simple_rates/README.md +5 -2
  142. data/example_files/resources/hpxml-measures/ReportUtilityBills/resources/util.rb +18 -9
  143. data/example_files/resources/hpxml-measures/ReportUtilityBills/tests/test_report_utility_bills.rb +1308 -0
  144. data/example_files/resources/hpxml-measures/docs/requirements.txt +5 -0
  145. data/example_files/resources/hpxml-measures/docs/source/conf.py +1 -2
  146. data/example_files/resources/hpxml-measures/docs/source/intro.rst +3 -20
  147. data/example_files/resources/hpxml-measures/docs/source/usage_instructions.rst +1 -1
  148. data/example_files/resources/hpxml-measures/docs/source/workflow_inputs.rst +917 -564
  149. data/example_files/resources/hpxml-measures/docs/source/workflow_outputs.rst +79 -42
  150. data/example_files/resources/hpxml-measures/tasks.rb +2305 -2055
  151. data/example_files/resources/hpxml-measures/workflow/hpxml_inputs.json +270 -587
  152. data/example_files/resources/hpxml-measures/workflow/real_homes/house001.xml +559 -557
  153. data/example_files/resources/hpxml-measures/workflow/real_homes/house002.xml +522 -520
  154. data/example_files/resources/hpxml-measures/workflow/real_homes/house003.xml +534 -532
  155. data/example_files/resources/hpxml-measures/workflow/real_homes/house004.xml +547 -545
  156. data/example_files/resources/hpxml-measures/workflow/real_homes/house005.xml +546 -544
  157. data/example_files/resources/hpxml-measures/workflow/real_homes/house006.xml +603 -623
  158. data/example_files/resources/hpxml-measures/workflow/real_homes/house007.xml +613 -633
  159. data/example_files/resources/hpxml-measures/workflow/real_homes/house008.xml +699 -721
  160. data/example_files/resources/hpxml-measures/workflow/real_homes/house009.xml +662 -661
  161. data/example_files/resources/hpxml-measures/workflow/real_homes/house010.xml +657 -677
  162. data/example_files/resources/hpxml-measures/workflow/real_homes/house011.xml +470 -467
  163. data/example_files/resources/hpxml-measures/workflow/real_homes/house012.xml +441 -438
  164. data/example_files/resources/hpxml-measures/workflow/real_homes/house013.xml +468 -465
  165. data/example_files/resources/hpxml-measures/workflow/real_homes/house014.xml +469 -466
  166. data/example_files/resources/hpxml-measures/workflow/real_homes/house015.xml +468 -465
  167. data/example_files/resources/hpxml-measures/workflow/real_homes/house016.xml +717 -714
  168. data/example_files/resources/hpxml-measures/workflow/real_homes/house017.xml +647 -645
  169. data/example_files/resources/hpxml-measures/workflow/real_homes/house018.xml +569 -566
  170. data/example_files/resources/hpxml-measures/workflow/real_homes/house019.xml +602 -599
  171. data/example_files/resources/hpxml-measures/workflow/real_homes/house020.xml +630 -627
  172. data/example_files/resources/hpxml-measures/workflow/real_homes/house021.xml +776 -774
  173. data/example_files/resources/hpxml-measures/workflow/real_homes/house022.xml +670 -667
  174. data/example_files/resources/hpxml-measures/workflow/real_homes/house023.xml +632 -629
  175. data/example_files/resources/hpxml-measures/workflow/real_homes/house024.xml +731 -729
  176. data/example_files/resources/hpxml-measures/workflow/real_homes/house025.xml +672 -669
  177. data/example_files/resources/hpxml-measures/workflow/real_homes/house026.xml +667 -644
  178. data/example_files/resources/hpxml-measures/workflow/real_homes/house027.xml +646 -644
  179. data/example_files/resources/hpxml-measures/workflow/real_homes/house028.xml +690 -688
  180. data/example_files/resources/hpxml-measures/workflow/real_homes/house029.xml +701 -699
  181. data/example_files/resources/hpxml-measures/workflow/real_homes/house030.xml +637 -615
  182. data/example_files/resources/hpxml-measures/workflow/real_homes/house031.xml +690 -688
  183. data/example_files/resources/hpxml-measures/workflow/real_homes/house032.xml +557 -554
  184. data/example_files/resources/hpxml-measures/workflow/real_homes/house033.xml +534 -531
  185. data/example_files/resources/hpxml-measures/workflow/real_homes/house034.xml +636 -635
  186. data/example_files/resources/hpxml-measures/workflow/real_homes/house035.xml +616 -613
  187. data/example_files/resources/hpxml-measures/workflow/real_homes/house036.xml +601 -598
  188. data/example_files/resources/hpxml-measures/workflow/real_homes/house037.xml +581 -578
  189. data/example_files/resources/hpxml-measures/workflow/real_homes/house038.xml +624 -622
  190. data/example_files/resources/hpxml-measures/workflow/real_homes/house039.xml +584 -582
  191. data/example_files/resources/hpxml-measures/workflow/real_homes/house040.xml +631 -629
  192. data/example_files/resources/hpxml-measures/workflow/real_homes/house041.xml +922 -921
  193. data/example_files/resources/hpxml-measures/workflow/real_homes/house042.xml +855 -853
  194. data/example_files/resources/hpxml-measures/workflow/real_homes/house043.xml +739 -737
  195. data/example_files/resources/hpxml-measures/workflow/real_homes/house044.xml +798 -796
  196. data/example_files/resources/hpxml-measures/workflow/real_homes/house045.xml +696 -694
  197. data/example_files/resources/hpxml-measures/workflow/real_homes/house046.xml +487 -483
  198. data/example_files/resources/hpxml-measures/workflow/real_homes/house047.xml +443 -440
  199. data/example_files/resources/hpxml-measures/workflow/real_homes/house048.xml +688 -686
  200. data/example_files/resources/hpxml-measures/workflow/real_homes/house049.xml +722 -720
  201. data/example_files/resources/hpxml-measures/workflow/real_homes/house050.xml +619 -617
  202. data/example_files/resources/hpxml-measures/workflow/run_simulation.rb +13 -20
  203. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-coal.xml +9 -9
  204. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-dehumidifier-ief-portable.xml +11 -12
  205. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-dehumidifier-ief-whole-home.xml +11 -12
  206. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-dehumidifier-multiple.xml +12 -13
  207. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-dehumidifier.xml +11 -12
  208. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-gas.xml +9 -9
  209. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-modified.xml +9 -9
  210. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-none.xml +4 -4
  211. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-oil.xml +9 -9
  212. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-propane.xml +9 -9
  213. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-wood.xml +9 -9
  214. data/example_files/resources/hpxml-measures/workflow/sample_files/base-atticroof-cathedral.xml +11 -11
  215. data/example_files/resources/hpxml-measures/workflow/sample_files/base-atticroof-conditioned.xml +10 -10
  216. data/example_files/resources/hpxml-measures/workflow/sample_files/base-atticroof-flat.xml +9 -9
  217. data/example_files/resources/hpxml-measures/workflow/sample_files/base-atticroof-radiant-barrier.xml +10 -11
  218. data/example_files/resources/hpxml-measures/workflow/sample_files/base-atticroof-unvented-insulated-roof.xml +9 -9
  219. data/example_files/resources/hpxml-measures/workflow/sample_files/base-atticroof-vented.xml +8 -8
  220. data/example_files/resources/hpxml-measures/workflow/sample_files/base-battery-scheduled.xml +571 -569
  221. data/example_files/resources/hpxml-measures/workflow/sample_files/base-battery.xml +9 -9
  222. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-adjacent-to-multifamily-buffer-space.xml → base-bldgtype-mf-unit-adjacent-to-multifamily-buffer-space.xml} +5 -5
  223. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-adjacent-to-multiple.xml → base-bldgtype-mf-unit-adjacent-to-multiple.xml} +17 -17
  224. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-adjacent-to-non-freezing-space.xml → base-bldgtype-mf-unit-adjacent-to-non-freezing-space.xml} +5 -5
  225. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-adjacent-to-other-heated-space.xml → base-bldgtype-mf-unit-adjacent-to-other-heated-space.xml} +5 -5
  226. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-adjacent-to-other-housing-unit.xml → base-bldgtype-mf-unit-adjacent-to-other-housing-unit.xml} +5 -5
  227. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-infil-compartmentalization-test.xml → base-bldgtype-mf-unit-infil-compartmentalization-test.xml} +461 -461
  228. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-residents-1.xml → base-bldgtype-mf-unit-residents-1.xml} +453 -453
  229. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-chiller-baseboard.xml → base-bldgtype-mf-unit-shared-boiler-chiller-baseboard.xml} +11 -11
  230. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-chiller-fan-coil-ducted.xml → base-bldgtype-mf-unit-shared-boiler-chiller-fan-coil-ducted.xml} +11 -11
  231. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-chiller-fan-coil.xml → base-bldgtype-mf-unit-shared-boiler-chiller-fan-coil.xml} +11 -11
  232. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-chiller-water-loop-heat-pump.xml → base-bldgtype-mf-unit-shared-boiler-chiller-water-loop-heat-pump.xml} +11 -11
  233. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-cooling-tower-water-loop-heat-pump.xml → base-bldgtype-mf-unit-shared-boiler-cooling-tower-water-loop-heat-pump.xml} +11 -11
  234. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-only-baseboard.xml → base-bldgtype-mf-unit-shared-boiler-only-baseboard.xml} +11 -11
  235. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-only-fan-coil-ducted.xml → base-bldgtype-mf-unit-shared-boiler-only-fan-coil-ducted.xml} +11 -11
  236. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-only-fan-coil-eae.xml → base-bldgtype-mf-unit-shared-boiler-only-fan-coil-eae.xml} +11 -11
  237. data/example_files/resources/hpxml-measures/workflow/sample_files/base-bldgtype-mf-unit-shared-boiler-only-fan-coil-fireplace-elec.xml +433 -0
  238. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-only-fan-coil.xml → base-bldgtype-mf-unit-shared-boiler-only-fan-coil.xml} +11 -11
  239. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-boiler-only-water-loop-heat-pump.xml → base-bldgtype-mf-unit-shared-boiler-only-water-loop-heat-pump.xml} +11 -11
  240. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-chiller-only-baseboard.xml → base-bldgtype-mf-unit-shared-chiller-only-baseboard.xml} +11 -11
  241. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-chiller-only-fan-coil-ducted.xml → base-bldgtype-mf-unit-shared-chiller-only-fan-coil-ducted.xml} +11 -11
  242. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-chiller-only-fan-coil.xml → base-bldgtype-mf-unit-shared-chiller-only-fan-coil.xml} +11 -11
  243. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-chiller-only-water-loop-heat-pump.xml → base-bldgtype-mf-unit-shared-chiller-only-water-loop-heat-pump.xml} +11 -11
  244. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-cooling-tower-only-water-loop-heat-pump.xml → base-bldgtype-mf-unit-shared-cooling-tower-only-water-loop-heat-pump.xml} +11 -11
  245. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-generator.xml → base-bldgtype-mf-unit-shared-generator.xml} +13 -13
  246. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-ground-loop-ground-to-air-heat-pump.xml → base-bldgtype-mf-unit-shared-ground-loop-ground-to-air-heat-pump.xml} +13 -13
  247. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-laundry-room-multiple-water-heaters.xml → base-bldgtype-mf-unit-shared-laundry-room-multiple-water-heaters.xml} +480 -480
  248. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-laundry-room.xml → base-bldgtype-mf-unit-shared-laundry-room.xml} +10 -10
  249. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-mechvent-multiple.xml → base-bldgtype-mf-unit-shared-mechvent-multiple.xml} +15 -15
  250. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-mechvent-preconditioning.xml → base-bldgtype-mf-unit-shared-mechvent-preconditioning.xml} +13 -13
  251. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-mechvent.xml → base-bldgtype-mf-unit-shared-mechvent.xml} +13 -13
  252. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-pv.xml → base-bldgtype-mf-unit-shared-pv.xml} +13 -13
  253. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-water-heater-recirc.xml → base-bldgtype-mf-unit-shared-water-heater-recirc.xml} +13 -13
  254. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily-shared-water-heater.xml → base-bldgtype-mf-unit-shared-water-heater.xml} +13 -13
  255. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-multifamily.xml → base-bldgtype-mf-unit.xml} +13 -13
  256. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-attached-2stories.xml → base-bldgtype-sfa-unit-2stories.xml} +610 -610
  257. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-attached-atticroof-cathedral.xml → base-bldgtype-sfa-unit-atticroof-cathedral.xml} +558 -558
  258. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-attached-infil-compartmentalization-test.xml → base-bldgtype-sfa-unit-infil-compartmentalization-test.xml} +610 -610
  259. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-bldgtype-attached.xml → base-bldgtype-sfa-unit.xml} +610 -610
  260. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-combi-tankless-outside.xml +8 -8
  261. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-combi-tankless.xml +9 -9
  262. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-desuperheater-2-speed.xml +9 -9
  263. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-desuperheater-gshp.xml +9 -9
  264. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-desuperheater-hpwh.xml +9 -9
  265. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-desuperheater-tankless.xml +9 -9
  266. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-desuperheater-var-speed.xml +9 -9
  267. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-desuperheater.xml +9 -9
  268. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-dwhr.xml +9 -9
  269. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-indirect-detailed-setpoints.xml +507 -505
  270. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-indirect-dse.xml +9 -9
  271. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-indirect-outside.xml +8 -8
  272. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-indirect-standbyloss.xml +9 -9
  273. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-indirect-with-solar-fraction.xml +9 -9
  274. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-indirect.xml +9 -9
  275. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-jacket-electric.xml +9 -9
  276. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-jacket-gas.xml +9 -9
  277. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-jacket-hpwh.xml +9 -9
  278. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-jacket-indirect.xml +9 -9
  279. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-low-flow-fixtures.xml +12 -10
  280. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-multiple.xml +14 -14
  281. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-none.xml +5 -5
  282. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-recirc-demand.xml +9 -9
  283. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-recirc-manual.xml +9 -9
  284. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-recirc-nocontrol.xml +9 -9
  285. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-recirc-temperature.xml +9 -9
  286. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-recirc-timer.xml +9 -9
  287. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-solar-direct-evacuated-tube.xml +9 -9
  288. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-solar-direct-flat-plate.xml +9 -9
  289. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-solar-direct-ics.xml +9 -9
  290. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-solar-fraction.xml +9 -9
  291. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-solar-indirect-flat-plate.xml +9 -9
  292. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-solar-thermosyphon-flat-plate.xml +9 -9
  293. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-coal.xml +9 -9
  294. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-detailed-setpoints.xml +12 -10
  295. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-elec-uef.xml +9 -9
  296. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-gas-outside.xml +8 -8
  297. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-gas-uef-fhr.xml +9 -9
  298. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-gas-uef.xml +9 -9
  299. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-gas.xml +9 -9
  300. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump-detailed-schedules.xml +13 -11
  301. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump-operating-mode-heat-pump-only.xml +9 -9
  302. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump-outside.xml +8 -8
  303. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump-uef.xml +9 -9
  304. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump-with-solar-fraction.xml +9 -9
  305. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump-with-solar.xml +9 -9
  306. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-heat-pump.xml +9 -9
  307. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-model-type-stratified-detailed-occupancy-stochastic.xml +12 -10
  308. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-model-type-stratified.xml +9 -9
  309. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-oil.xml +9 -9
  310. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tank-wood.xml +9 -9
  311. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-detailed-setpoints.xml +12 -10
  312. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-electric-outside.xml +8 -8
  313. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-electric-uef.xml +9 -9
  314. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-electric.xml +9 -9
  315. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-gas-uef.xml +9 -9
  316. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-gas-with-solar-fraction.xml +9 -9
  317. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-gas-with-solar.xml +9 -9
  318. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-gas.xml +9 -9
  319. data/example_files/resources/hpxml-measures/workflow/sample_files/base-dhw-tankless-propane.xml +9 -9
  320. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-2stories-garage.xml +13 -14
  321. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-2stories.xml +11 -11
  322. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-beds-1.xml +9 -9
  323. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-beds-2.xml +9 -9
  324. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-beds-4.xml +9 -9
  325. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-beds-5.xml +9 -9
  326. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-ceilingtypes.xml +576 -576
  327. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-floortypes.xml +519 -519
  328. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-garage.xml +4 -5
  329. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-ach-house-pressure.xml +9 -9
  330. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-cfm-house-pressure.xml +9 -9
  331. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-cfm50.xml +9 -9
  332. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-ela.xml +548 -548
  333. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-flue.xml +9 -9
  334. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-natural-ach.xml +9 -9
  335. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-infil-natural-cfm.xml +551 -551
  336. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-orientations.xml +9 -9
  337. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-overhangs.xml +9 -9
  338. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-rooftypes.xml +9 -9
  339. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-skylights-physical-properties.xml +9 -9
  340. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-skylights-shading.xml +9 -9
  341. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-skylights-storms.xml +9 -9
  342. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-skylights.xml +9 -9
  343. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-split-level.xml +10 -11
  344. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-thermal-mass.xml +9 -9
  345. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-walltypes.xml +19 -19
  346. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-windows-natural-ventilation-availability.xml +555 -553
  347. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-windows-none.xml +9 -9
  348. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-windows-physical-properties.xml +9 -9
  349. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-windows-shading-seasons.xml +560 -558
  350. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-windows-shading.xml +9 -9
  351. data/example_files/resources/hpxml-measures/workflow/sample_files/base-enclosure-windows-storms.xml +9 -9
  352. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-ambient.xml +10 -10
  353. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-basement-garage.xml +10 -11
  354. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-belly-wing-no-skirt.xml +496 -0
  355. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-belly-wing-skirt.xml +496 -0
  356. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-complex.xml +18 -18
  357. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-schedules-simple-power-outage-natvent-unavailable.xml → base-foundation-conditioned-basement-slab-insulation-full.xml} +552 -564
  358. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-conditioned-basement-slab-insulation.xml +9 -9
  359. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-conditioned-basement-wall-insulation.xml +555 -555
  360. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-conditioned-crawlspace.xml +8 -8
  361. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-multiple.xml +5 -5
  362. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-slab.xml +10 -11
  363. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-unconditioned-basement-above-grade.xml +4 -4
  364. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-unconditioned-basement-assembly-r.xml +4 -4
  365. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-unconditioned-basement-wall-insulation.xml +4 -4
  366. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-unconditioned-basement.xml +4 -4
  367. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-unvented-crawlspace.xml +9 -9
  368. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-vented-crawlspace-above-grade.xml +558 -0
  369. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-vented-crawlspace.xml +9 -9
  370. data/example_files/resources/hpxml-measures/workflow/sample_files/base-foundation-walkout-basement.xml +15 -15
  371. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-1-speed-cooling-only.xml +9 -9
  372. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-1-speed-heating-capacity-17f.xml +552 -552
  373. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-1-speed-heating-only.xml +9 -9
  374. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-1-speed-lockout-temperatures.xml +562 -562
  375. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-1-speed-seer2-hspf2.xml +557 -557
  376. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-1-speed.xml +9 -9
  377. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-2-speed.xml +9 -9
  378. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-var-speed-backup-boiler-hvac-seasons.xml +586 -586
  379. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-var-speed-backup-boiler-switchover-temperature.xml +9 -9
  380. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-var-speed-backup-boiler.xml +9 -9
  381. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-var-speed-backup-furnace.xml +9 -9
  382. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-air-to-air-heat-pump-var-speed-sizing-methodology-acca.xml → base-hvac-air-to-air-heat-pump-var-speed-detailed-performance-other-temperatures.xml} +89 -13
  383. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-air-to-air-heat-pump-var-speed-sizing-methodology-hers.xml → base-hvac-air-to-air-heat-pump-var-speed-detailed-performance.xml} +107 -13
  384. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-air-to-air-heat-pump-var-speed.xml +9 -9
  385. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-sizing-controls.xml +567 -565
  386. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize.xml +9 -9
  387. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-coal-only.xml +9 -9
  388. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-elec-only.xml +9 -9
  389. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-gas-central-ac-1-speed.xml +9 -9
  390. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-gas-only-pilot.xml +512 -512
  391. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-gas-only.xml +9 -9
  392. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-oil-only.xml +9 -9
  393. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-propane-only.xml +9 -9
  394. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-boiler-wood-only.xml +9 -9
  395. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-central-ac-only-1-speed-seer2.xml +536 -536
  396. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-central-ac-only-1-speed.xml +9 -9
  397. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-central-ac-only-2-speed.xml +9 -9
  398. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-central-ac-only-var-speed.xml → base-hvac-central-ac-only-var-speed-detailed-performance.xml} +49 -10
  399. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-central-ac-only-var-speed.xml +9 -9
  400. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-central-ac-plus-air-to-air-heat-pump-heating.xml +9 -9
  401. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-dse.xml +9 -9
  402. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-dual-fuel-air-to-air-heat-pump-1-speed-lockout-temperatures.xml +559 -559
  403. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-dual-fuel-air-to-air-heat-pump-1-speed.xml +9 -9
  404. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-dual-fuel-air-to-air-heat-pump-2-speed.xml +9 -9
  405. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-dual-fuel-air-to-air-heat-pump-var-speed.xml +9 -9
  406. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-dual-fuel-mini-split-heat-pump-ducted.xml +9 -9
  407. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ducts-area-fractions.xml +11 -12
  408. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ducts-area-multipliers.xml +558 -558
  409. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ducts-buried.xml +554 -554
  410. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-furnace-gas-room-ac.xml → base-hvac-ducts-defaults.xml} +28 -14
  411. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ducts-effective-rvalue.xml +552 -552
  412. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ducts-leakage-cfm50.xml +9 -9
  413. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ducts-leakage-percent.xml +9 -9
  414. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-elec-resistance-only.xml +9 -9
  415. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-evap-cooler-furnace-gas.xml +9 -9
  416. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-evap-cooler-only-ducted.xml +9 -9
  417. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-evap-cooler-only.xml +9 -9
  418. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-fireplace-wood-only.xml +9 -9
  419. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-floor-furnace-propane-only.xml +15 -10
  420. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-coal-only.xml +9 -9
  421. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-elec-central-ac-1-speed.xml +9 -9
  422. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-elec-only.xml +9 -9
  423. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-gas-central-ac-2-speed.xml +9 -9
  424. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-gas-central-ac-var-speed.xml +9 -9
  425. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-gas-only-detailed-setpoints.xml +12 -10
  426. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-gas-only-pilot.xml +541 -541
  427. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-gas-only.xml +9 -9
  428. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-gas-room-ac.xml +9 -9
  429. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-oil-only.xml +9 -9
  430. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-propane-only.xml +9 -9
  431. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-wood-only.xml +9 -9
  432. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-furnace-x3-dse.xml +9 -9
  433. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ground-to-air-heat-pump-cooling-only.xml +9 -9
  434. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ground-to-air-heat-pump-heating-only.xml +9 -9
  435. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ground-to-air-heat-pump.xml +9 -9
  436. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-air-to-air-heat-pump-1-speed.xml +9 -9
  437. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-air-to-air-heat-pump-2-speed.xml +9 -9
  438. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-air-to-air-heat-pump-1-speed-autosized-backup.xml → base-hvac-install-quality-air-to-air-heat-pump-var-speed-detailed-performance.xml} +650 -556
  439. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-air-to-air-heat-pump-var-speed.xml +9 -9
  440. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-furnace-gas-central-ac-1-speed.xml +9 -9
  441. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-furnace-gas-central-ac-2-speed.xml +9 -9
  442. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-furnace-gas-central-ac-var-speed.xml +9 -9
  443. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-furnace-gas-only.xml +9 -9
  444. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-ground-to-air-heat-pump.xml +9 -9
  445. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-mini-split-air-conditioner-only-ducted.xml +9 -9
  446. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-install-quality-mini-split-heat-pump-ducted.xml +9 -9
  447. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-air-conditioner-only-ducted.xml +9 -9
  448. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-mini-split-air-conditioner-only-ducted.xml → base-hvac-mini-split-air-conditioner-only-ductless-detailed-performance.xml} +50 -49
  449. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-air-conditioner-only-ductless.xml +9 -9
  450. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ducted-cooling-only.xml +9 -9
  451. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-mini-split-heat-pump-ducted-sizing-methodology-maxload.xml → base-hvac-mini-split-heat-pump-ducted-detailed-performance.xml} +109 -14
  452. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ducted-heating-only.xml +9 -9
  453. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ducted.xml +9 -9
  454. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ductless-backup-baseboard.xml +524 -524
  455. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-dual-fuel-mini-split-heat-pump-ducted-backup-hardsized.xml → base-hvac-mini-split-heat-pump-ductless-backup-furnace-ducts-defaults.xml} +560 -552
  456. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ductless-backup-furnace.xml +563 -563
  457. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ductless-backup-stove.xml +9 -9
  458. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-autosize-dual-fuel-mini-split-heat-pump-ducted.xml → base-hvac-mini-split-heat-pump-ductless-detailed-performance.xml} +108 -60
  459. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ductless-heating-capacity-17f.xml +505 -505
  460. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-mini-split-heat-pump-ductless.xml +9 -9
  461. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-multiple.xml +9 -9
  462. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-none.xml +10 -11
  463. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ptac-with-heating-electricity.xml +9 -9
  464. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ptac-with-heating-natural-gas.xml +504 -504
  465. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-ptac.xml +9 -9
  466. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-pthp-heating-capacity-17f.xml +512 -512
  467. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-pthp.xml +9 -9
  468. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-room-ac-only-33percent.xml +9 -9
  469. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-room-ac-only-ceer.xml +9 -9
  470. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-room-ac-only-detailed-setpoints.xml +12 -10
  471. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-room-ac-only.xml +9 -9
  472. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-room-ac-with-heating.xml +504 -504
  473. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-room-ac-with-reverse-cycle.xml +517 -517
  474. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-seasons.xml +9 -9
  475. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-setpoints-daily-schedules.xml +9 -9
  476. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-setpoints-daily-setbacks.xml +9 -9
  477. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-setpoints.xml +9 -9
  478. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-fixed-heater-gas-only.xml → base-hvac-space-heater-gas-only.xml} +10 -10
  479. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-stove-oil-only.xml +9 -9
  480. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-stove-wood-pellets-only.xml +9 -9
  481. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-undersized.xml +9 -9
  482. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-wall-furnace-elec-only.xml +9 -9
  483. data/example_files/resources/hpxml-measures/workflow/sample_files/base-lighting-ceiling-fans.xml +9 -9
  484. data/example_files/resources/hpxml-measures/workflow/sample_files/base-lighting-holiday.xml +9 -9
  485. data/example_files/resources/hpxml-measures/workflow/sample_files/base-lighting-kwh-per-year.xml +531 -528
  486. data/example_files/resources/hpxml-measures/workflow/sample_files/base-lighting-mixed.xml +536 -536
  487. data/example_files/resources/hpxml-measures/workflow/sample_files/base-lighting-none-ceiling-fans.xml +515 -515
  488. data/example_files/resources/hpxml-measures/workflow/sample_files/base-lighting-none.xml +9 -9
  489. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-AMY-2012.xml +9 -9
  490. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-baltimore-md.xml +9 -9
  491. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-capetown-zaf.xml +9 -9
  492. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-dallas-tx.xml +10 -11
  493. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-duluth-mn.xml +4 -4
  494. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-helena-mt.xml +9 -9
  495. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-honolulu-hi.xml +10 -11
  496. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-miami-fl.xml +10 -11
  497. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-phoenix-az.xml +10 -11
  498. data/example_files/resources/hpxml-measures/workflow/sample_files/base-location-portland-or.xml +9 -9
  499. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-balanced.xml +9 -9
  500. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-bath-kitchen-fans.xml +9 -9
  501. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-cfis-airflow-fraction-zero.xml +9 -9
  502. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-cfis-dse.xml +9 -9
  503. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-cfis-evap-cooler-only-ducted.xml +9 -9
  504. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-cfis-supplemental-fan-exhaust.xml +576 -576
  505. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-cfis-supplemental-fan-supply.xml +576 -576
  506. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-cfis.xml +9 -9
  507. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-erv-atre-asre.xml +9 -9
  508. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-erv.xml +9 -9
  509. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-exhaust-rated-flow-rate.xml +9 -9
  510. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-exhaust.xml +9 -9
  511. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-hrv-asre.xml +9 -9
  512. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-hrv.xml +9 -9
  513. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-multiple.xml +9 -9
  514. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-supply.xml +9 -9
  515. data/example_files/resources/hpxml-measures/workflow/sample_files/base-mechvent-whole-house-fan.xml +9 -9
  516. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-additional-properties.xml +21 -19
  517. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-bills-pv-detailed-only.xml +605 -605
  518. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-bills-pv-mixed.xml +587 -587
  519. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-bills-pv.xml +9 -9
  520. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-bills.xml +9 -9
  521. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-defaults.xml +3 -3
  522. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-emissions.xml +9 -9
  523. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-generators-battery-scheduled.xml +587 -585
  524. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-generators-battery.xml +584 -584
  525. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-generators.xml +9 -9
  526. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-ground-conductivity.xml +555 -555
  527. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-loads-large-uncommon.xml +26 -26
  528. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-loads-large-uncommon2.xml +26 -26
  529. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-loads-none.xml +9 -9
  530. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-neighbor-shading-bldgtype-multifamily.xml +508 -508
  531. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-neighbor-shading.xml +9 -9
  532. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-shielding-of-home.xml +9 -9
  533. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-schedules-simple-power-outage-natvent-available.xml → base-misc-unit-multiplier.xml} +553 -564
  534. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-usage-multiplier.xml +26 -26
  535. data/example_files/resources/hpxml-measures/workflow/sample_files/base-multiple-mf-units.xml +2755 -0
  536. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-multiple-buildings.xml → base-multiple-sfd-buildings.xml} +31 -22
  537. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-battery-ah.xml +9 -9
  538. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-battery-garage.xml +4 -5
  539. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-battery-round-trip-efficiency.xml +597 -597
  540. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-battery-scheduled.xml +599 -597
  541. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-battery.xml +9 -9
  542. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-generators-battery-scheduled.xml +615 -613
  543. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-generators-battery.xml +612 -612
  544. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv-generators.xml +596 -596
  545. data/example_files/resources/hpxml-measures/workflow/sample_files/base-pv.xml +9 -9
  546. data/example_files/resources/hpxml-measures/workflow/sample_files/base-residents-0-runperiod-1-month.xml +559 -559
  547. data/example_files/resources/hpxml-measures/workflow/sample_files/base-residents-0.xml +555 -555
  548. data/example_files/resources/hpxml-measures/workflow/sample_files/base-residents-1-misc-loads-large-uncommon.xml +616 -616
  549. data/example_files/resources/hpxml-measures/workflow/sample_files/base-residents-1-misc-loads-large-uncommon2.xml +616 -616
  550. data/example_files/resources/hpxml-measures/workflow/sample_files/base-residents-1.xml +547 -547
  551. data/example_files/resources/hpxml-measures/workflow/sample_files/base-residents-5.xml +515 -515
  552. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-all-10-mins.xml +14 -12
  553. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-hvac-crankcase-heater-40w.xml → base-schedules-detailed-mixed-timesteps-power-outage.xml} +565 -555
  554. data/example_files/resources/hpxml-measures/workflow/sample_files/{base-schedules-detailed-occupancy-stochastic-vacancy-year-round.xml → base-schedules-detailed-mixed-timesteps.xml} +554 -563
  555. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-occupancy-stochastic-10-mins.xml +12 -10
  556. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-occupancy-stochastic-power-outage.xml +566 -564
  557. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-occupancy-stochastic-vacancy.xml +12 -10
  558. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-occupancy-stochastic.xml +12 -10
  559. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-setpoints-daily-schedules.xml +12 -10
  560. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-setpoints-daily-setbacks.xml +12 -10
  561. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-detailed-setpoints.xml +12 -10
  562. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-simple-power-outage.xml +619 -619
  563. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-simple-vacancy.xml +618 -618
  564. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-simple.xml +9 -9
  565. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-calendar-year-custom.xml +9 -9
  566. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-daylight-saving-custom.xml +9 -9
  567. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-daylight-saving-disabled.xml +9 -9
  568. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-runperiod-1-month.xml +9 -9
  569. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-temperature-capacitance-multiplier.xml +553 -553
  570. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-timestep-10-mins-occupancy-stochastic-10-mins.xml +12 -10
  571. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-timestep-10-mins-occupancy-stochastic-60-mins.xml +12 -10
  572. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-timestep-10-mins.xml +9 -9
  573. data/example_files/resources/hpxml-measures/workflow/sample_files/base-simcontrol-timestep-30-mins.xml +552 -552
  574. data/example_files/resources/hpxml-measures/workflow/sample_files/base.xml +9 -9
  575. data/example_files/resources/hpxml-measures/workflow/template-build-and-run-hpxml-with-stochastic-occupancy.osw +2 -0
  576. data/example_files/resources/hpxml-measures/workflow/template-run-hpxml-with-stochastic-occupancy-subset.osw +2 -0
  577. data/example_files/resources/hpxml-measures/workflow/template-run-hpxml-with-stochastic-occupancy.osw +2 -0
  578. data/example_files/resources/hpxml-measures/workflow/template-run-hpxml.osw +4 -1
  579. data/example_files/resources/hpxml-measures/workflow/tests/ACCA_Examples/Long_Residence.xml +385 -385
  580. data/example_files/resources/hpxml-measures/workflow/tests/ACCA_Examples/Vatilo_Residence.xml +378 -380
  581. data/example_files/resources/hpxml-measures/workflow/tests/ACCA_Examples/Victor_Residence.xml +369 -369
  582. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L100AC.xml +7 -7
  583. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L100AL.xml +7 -7
  584. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L110AC.xml +7 -7
  585. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L110AL.xml +7 -7
  586. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L120AC.xml +7 -7
  587. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L120AL.xml +7 -7
  588. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L130AC.xml +7 -7
  589. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L130AL.xml +7 -7
  590. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L140AC.xml +7 -7
  591. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L140AL.xml +7 -7
  592. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L150AC.xml +7 -7
  593. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L150AL.xml +7 -7
  594. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L155AC.xml +7 -7
  595. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L155AL.xml +7 -7
  596. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L160AC.xml +7 -7
  597. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L160AL.xml +7 -7
  598. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L170AC.xml +7 -7
  599. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L170AL.xml +7 -7
  600. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L200AC.xml +7 -7
  601. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L200AL.xml +7 -7
  602. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L202AC.xml +7 -7
  603. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L202AL.xml +7 -7
  604. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L302XC.xml +7 -8
  605. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L304XC.xml +7 -8
  606. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L322XC.xml +6 -6
  607. data/example_files/resources/hpxml-measures/workflow/tests/ASHRAE_Standard_140/L324XC.xml +6 -6
  608. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results_sizing.csv +363 -0
  609. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results_workflow_simulations1.csv +281 -0
  610. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results_workflow_simulations1_bills.csv +281 -0
  611. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results_workflow_simulations2.csv +141 -0
  612. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results_workflow_simulations2_bills.csv +141 -0
  613. data/example_files/resources/hpxml-measures/workflow/tests/compare.py +12 -6
  614. data/example_files/resources/hpxml-measures/workflow/tests/util.rb +1141 -0
  615. data/example_files/weather/USA_CO_Denver.Intl.AP.725650_TMY3.ddy +536 -0
  616. data/example_files/weather/USA_CO_Denver.Intl.AP.725650_TMY3.epw +8768 -0
  617. data/example_files/weather/USA_CO_Denver.Intl.AP.725650_TMY3.stat +554 -0
  618. data/example_files/weather/USA_FL_MacDill.AFB.747880_TMY3.ddy +536 -0
  619. data/example_files/weather/USA_FL_MacDill.AFB.747880_TMY3.epw +8768 -0
  620. data/example_files/weather/USA_FL_MacDill.AFB.747880_TMY3.stat +553 -0
  621. data/example_files/weather/USA_GA_Atlanta-Hartsfield-Jackson.Intl.AP.722190_TMY3-cache.csv +35 -0
  622. data/example_files/weather/USA_GA_Atlanta-Hartsfield-Jackson.Intl.AP.722190_TMY3.ddy +536 -0
  623. data/example_files/weather/USA_GA_Atlanta-Hartsfield-Jackson.Intl.AP.722190_TMY3.epw +8768 -0
  624. data/example_files/weather/USA_GA_Atlanta-Hartsfield-Jackson.Intl.AP.722190_TMY3.stat +553 -0
  625. data/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3-cache.csv +35 -0
  626. data/example_files/xml_building/17/README.md +4 -2
  627. data/example_files/xml_building/17/feature.xml +2112 -0
  628. data/lib/uo_cli/version.rb +1 -1
  629. data/lib/uo_cli.rb +110 -17
  630. data/uo_cli.gemspec +6 -8
  631. metadata +130 -177
  632. data/Jenkinsfile +0 -10
  633. data/example_files/base_workflow_res.osw +0 -276
  634. data/example_files/resources/hpxml-measures/ReportUtilityBills/tests/utility_bills_test.rb +0 -1226
  635. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-oil-location-miami-fl.xml +0 -551
  636. data/example_files/resources/hpxml-measures/workflow/sample_files/base-appliances-propane-location-portland-or.xml +0 -551
  637. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-1-speed-cooling-only.xml +0 -544
  638. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-1-speed-heating-only.xml +0 -550
  639. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-1-speed-sizing-methodology-acca.xml +0 -552
  640. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-1-speed-sizing-methodology-hers.xml +0 -552
  641. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-1-speed-sizing-methodology-maxload-miami-fl.xml +0 -552
  642. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-1-speed-sizing-methodology-maxload.xml +0 -552
  643. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-2-speed-sizing-methodology-acca.xml +0 -552
  644. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-2-speed-sizing-methodology-hers.xml +0 -552
  645. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-2-speed-sizing-methodology-maxload.xml +0 -552
  646. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-var-speed-backup-boiler.xml +0 -569
  647. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-var-speed-backup-furnace.xml +0 -599
  648. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-air-to-air-heat-pump-var-speed-sizing-methodology-maxload.xml +0 -552
  649. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-boiler-elec-only.xml +0 -506
  650. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-boiler-gas-central-ac-1-speed.xml +0 -560
  651. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-boiler-gas-only.xml +0 -507
  652. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-central-ac-only-1-speed.xml +0 -536
  653. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-central-ac-only-2-speed.xml +0 -536
  654. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-central-ac-plus-air-to-air-heat-pump-heating.xml +0 -565
  655. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-dual-fuel-air-to-air-heat-pump-1-speed-sizing-methodology-acca.xml +0 -553
  656. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-dual-fuel-air-to-air-heat-pump-1-speed-sizing-methodology-hers.xml +0 -553
  657. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-dual-fuel-air-to-air-heat-pump-1-speed-sizing-methodology-maxload.xml +0 -553
  658. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-elec-resistance-only.xml +0 -497
  659. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-evap-cooler-furnace-gas.xml +0 -544
  660. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-floor-furnace-propane-only.xml +0 -500
  661. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-furnace-elec-only.xml +0 -536
  662. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-furnace-gas-central-ac-2-speed.xml +0 -551
  663. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-furnace-gas-central-ac-var-speed.xml +0 -551
  664. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-furnace-gas-only.xml +0 -536
  665. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ground-to-air-heat-pump-cooling-only.xml +0 -546
  666. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ground-to-air-heat-pump-heating-only.xml +0 -552
  667. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ground-to-air-heat-pump-sizing-methodology-acca.xml +0 -554
  668. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ground-to-air-heat-pump-sizing-methodology-hers.xml +0 -554
  669. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ground-to-air-heat-pump-sizing-methodology-maxload.xml +0 -554
  670. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-mini-split-heat-pump-ducted-cooling-only.xml +0 -543
  671. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-mini-split-heat-pump-ducted-heating-only.xml +0 -549
  672. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-mini-split-heat-pump-ducted-sizing-methodology-acca.xml +0 -551
  673. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-mini-split-heat-pump-ducted-sizing-methodology-hers.xml +0 -551
  674. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-mini-split-heat-pump-ductless-backup-baseboard.xml +0 -519
  675. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-mini-split-heat-pump-ductless-backup-stove.xml +0 -522
  676. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ptac-with-heating.xml +0 -503
  677. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-ptac.xml +0 -496
  678. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-pthp-sizing-methodology-acca.xml +0 -518
  679. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-pthp-sizing-methodology-hers.xml +0 -518
  680. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-pthp-sizing-methodology-maxload.xml +0 -518
  681. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-room-ac-only.xml +0 -496
  682. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-room-ac-with-heating.xml +0 -503
  683. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-room-ac-with-reverse-cycle-sizing-methodology-acca.xml +0 -518
  684. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-room-ac-with-reverse-cycle-sizing-methodology-hers.xml +0 -518
  685. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-room-ac-with-reverse-cycle-sizing-methodology-maxload.xml +0 -518
  686. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-stove-oil-only.xml +0 -500
  687. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-autosize-wall-furnace-elec-only.xml +0 -500
  688. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-floor-furnace-propane-only-pilot-light.xml +0 -506
  689. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-portable-heater-gas-only.xml +0 -501
  690. data/example_files/resources/hpxml-measures/workflow/sample_files/base-hvac-undersized-allow-increased-fixed-capacities.xml +0 -556
  691. data/example_files/resources/hpxml-measures/workflow/sample_files/base-misc-bills-none.xml +0 -548
  692. data/example_files/resources/hpxml-measures/workflow/sample_files/base-schedules-simple-vacancy-year-round.xml +0 -619
  693. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results.csv +0 -475
  694. data/example_files/resources/hpxml-measures/workflow/tests/base_results/results_bills.csv +0 -475
  695. data/example_files/resources/hpxml-measures/workflow/tests/hpxml_translator_test.rb +0 -1313
  696. data/example_files/resources/measure-info.json +0 -26
  697. data/example_files/resources/meta_measure.rb +0 -301
  698. data/example_files/xml_building/17/unit 1.xml +0 -580
  699. data/example_files/xml_building/17/unit 2.xml +0 -553
  700. data/example_files/xml_building/17/unit 3.xml +0 -553
  701. data/example_files/xml_building/17/unit 4.xml +0 -580
  702. /data/example_files/{residential → mappers/residential/template/iecc}/clothes_dryer.tsv +0 -0
  703. /data/example_files/{residential → mappers/residential/template/iecc}/clothes_washer.tsv +0 -0
  704. /data/example_files/{residential → mappers/residential/template/iecc}/cooling_system.tsv +0 -0
  705. /data/example_files/{residential → mappers/residential/template/iecc}/dishwasher.tsv +0 -0
  706. /data/example_files/{residential → mappers/residential/template/iecc}/enclosure.tsv +0 -0
  707. /data/example_files/{residential → mappers/residential/template/iecc}/heat_pump.tsv +0 -0
  708. /data/example_files/{residential → mappers/residential/template/iecc}/heating_system.tsv +0 -0
  709. /data/example_files/{residential → mappers/residential/template/iecc}/mechanical_ventilation.tsv +0 -0
  710. /data/example_files/{residential → mappers/residential/template/iecc}/refrigerator.tsv +0 -0
  711. /data/example_files/{residential → mappers/residential/template/iecc}/water_heater.tsv +0 -0
  712. /data/example_files/resources/hpxml-measures/ReportUtilityBills/tests/{JacksonElectricMemberCorp-ResidentialSeniorCitizenLowIncomeAssistance.json → Detailed Rate.json} +0 -0
@@ -1,3 +1,8 @@
1
+ # *********************************************************************************
2
+ # URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC.
3
+ # See also https://github.com/urbanopt/urbanopt-cli/blob/develop/LICENSE.md
4
+ # *********************************************************************************
5
+
1
6
  # frozen_string_literal: true
2
7
 
3
8
  # Require all gems up front; this is much faster than multiple resource
@@ -63,7 +68,7 @@ class HPXMLtoOpenStudio < OpenStudio::Measure::ModelMeasure
63
68
 
64
69
  arg = OpenStudio::Measure::OSArgument.makeStringArgument('building_id', false)
65
70
  arg.setDisplayName('BuildingID')
66
- arg.setDescription('The ID of the HPXML Building. Only required if there are multiple Building elements in the HPXML file.')
71
+ arg.setDescription("The ID of the HPXML Building. Only required if there are multiple Building elements in the HPXML file. Use 'ALL' to run all the HPXML Buildings (dwelling units) of a multifamily building in a single model.")
67
72
  args << arg
68
73
 
69
74
  return args
@@ -89,6 +94,7 @@ class HPXMLtoOpenStudio < OpenStudio::Measure::ModelMeasure
89
94
  debug = runner.getBoolArgumentValue('debug', user_arguments)
90
95
  skip_validation = runner.getBoolArgumentValue('skip_validation', user_arguments)
91
96
  building_id = runner.getOptionalStringArgumentValue('building_id', user_arguments)
97
+ building_id = building_id.is_initialized ? building_id.get : nil
92
98
 
93
99
  unless (Pathname.new hpxml_path).absolute?
94
100
  hpxml_path = File.expand_path(hpxml_path)
@@ -101,12 +107,6 @@ class HPXMLtoOpenStudio < OpenStudio::Measure::ModelMeasure
101
107
  output_dir = File.expand_path(output_dir)
102
108
  end
103
109
 
104
- if building_id.is_initialized
105
- building_id = building_id.get
106
- else
107
- building_id = nil
108
- end
109
-
110
110
  begin
111
111
  if skip_validation
112
112
  schema_validator = nil
@@ -117,6 +117,7 @@ class HPXMLtoOpenStudio < OpenStudio::Measure::ModelMeasure
117
117
  schematron_path = File.join(File.dirname(__FILE__), 'resources', 'hpxml_schematron', 'EPvalidator.xml')
118
118
  schematron_validator = XMLValidator.get_schematron_validator(schematron_path)
119
119
  end
120
+
120
121
  hpxml = HPXML.new(hpxml_path: hpxml_path, schema_validator: schema_validator, schematron_validator: schematron_validator, building_id: building_id)
121
122
  hpxml.errors.each do |error|
122
123
  runner.registerError(error)
@@ -126,16 +127,91 @@ class HPXMLtoOpenStudio < OpenStudio::Measure::ModelMeasure
126
127
  end
127
128
  return false unless hpxml.errors.empty?
128
129
 
129
- epw_path = Location.get_epw_path(hpxml, hpxml_path)
130
- weather = WeatherProcess.new(epw_path: epw_path, runner: runner)
130
+ eri_version = hpxml.header.eri_calculation_version # Hidden feature
131
+ eri_version = 'latest' if eri_version.nil?
132
+ eri_version = Constants.ERIVersions[-1] if eri_version == 'latest'
133
+
134
+ # Process weather once upfront
135
+ epw_path = Location.get_epw_path(hpxml.buildings[0], hpxml_path)
136
+ weather = WeatherProcess.new(epw_path: epw_path, runner: runner, hpxml: hpxml)
137
+ epw_file = OpenStudio::EpwFile.new(epw_path)
138
+ hpxml.buildings.each_with_index do |hpxml_bldg, i|
139
+ next if i == 0
140
+ next if Location.get_epw_path(hpxml_bldg, hpxml_path) == epw_path
141
+
142
+ fail 'Weather station EPW filepath has different values across dwelling units.'
143
+ end
144
+
145
+ if (building_id == 'ALL') && (hpxml.buildings.size > 1)
146
+ if hpxml.buildings.map { |hpxml_bldg| hpxml_bldg.batteries.size }.sum > 0
147
+ # FUTURE: Figure out how to allow this. If we allow it, update docs and hpxml_translator_test.rb too.
148
+ # Batteries use "TrackFacilityElectricDemandStoreExcessOnSite"; to support modeling of batteries in whole
149
+ # SFA/MF building simulations, we'd need to create custom meters with electricity usage *for each unit*
150
+ # and switch to "TrackMeterDemandStoreExcessOnSite".
151
+ # https://github.com/NREL/OpenStudio-HPXML/issues/1499
152
+ fail 'Modeling batteries for whole SFA/MF buildings is not currently supported.'
153
+ end
154
+ end
155
+
156
+ # Apply HPXML defaults upfront; process schedules & emissions
157
+ hpxml_sch_map = {}
158
+ check_emissions_references(hpxml.header, hpxml_path)
159
+ hpxml.buildings.each_with_index do |hpxml_bldg, i|
160
+ check_schedule_references(hpxml_bldg.header, hpxml_path)
161
+ in_schedules_csv = 'in.schedules.csv'
162
+ in_schedules_csv = "in.schedules#{i + 1}.csv" if i > 0
163
+ schedules_file = SchedulesFile.new(runner: runner,
164
+ schedules_paths: hpxml_bldg.header.schedules_filepaths,
165
+ year: Location.get_sim_calendar_year(hpxml.header.sim_calendar_year, epw_file),
166
+ unavailable_periods: hpxml.header.unavailable_periods,
167
+ output_path: File.join(output_dir, in_schedules_csv))
168
+ HPXMLDefaults.apply(runner, hpxml, hpxml_bldg, eri_version, weather, epw_file: epw_file, schedules_file: schedules_file)
169
+ hpxml_sch_map[hpxml_bldg] = schedules_file
170
+ end
171
+ validate_emissions_files(hpxml.header)
172
+
173
+ # Write updated HPXML object (w/ defaults) to file for inspection
174
+ hpxml_defaults_path = File.join(output_dir, 'in.xml')
175
+ XMLHelper.write_file(hpxml.to_doc, hpxml_defaults_path)
176
+
177
+ # Create OpenStudio model
178
+ hpxml_osm_map = {}
179
+ hpxml.buildings.each_with_index do |hpxml_bldg, i|
180
+ schedules_file = hpxml_sch_map[hpxml_bldg]
181
+ if hpxml.buildings.size > 1
182
+ # Create the model for this single unit
183
+ unit_model = OpenStudio::Model::Model.new
184
+ create_unit_model(hpxml, hpxml_bldg, runner, unit_model, epw_path, epw_file, weather, debug, schedules_file, eri_version, i + 1)
185
+ hpxml_osm_map[hpxml_bldg] = unit_model
186
+ else
187
+ create_unit_model(hpxml, hpxml_bldg, runner, model, epw_path, epw_file, weather, debug, schedules_file, eri_version, i + 1)
188
+ hpxml_osm_map[hpxml_bldg] = model
189
+ end
190
+ end
191
+
192
+ # Merge unit models into final model
193
+ if hpxml.buildings.size > 1
194
+ add_unit_model_to_model(model, hpxml_osm_map)
195
+ end
196
+
197
+ # Output
198
+ add_unmet_hours_output(model, hpxml_osm_map)
199
+ add_loads_output(model, add_component_loads, hpxml_osm_map)
200
+ set_output_files(model)
201
+ add_additional_properties(model, hpxml, hpxml_osm_map, hpxml_path, building_id, epw_file, hpxml_defaults_path)
202
+ # Uncomment to debug EMS
203
+ # add_ems_debug_output(model)
131
204
 
132
205
  if debug
206
+ # Write OSM file to run dir
207
+ osm_output_path = File.join(output_dir, 'in.osm')
208
+ File.write(osm_output_path, model.to_s)
209
+ runner.registerInfo("Wrote file: #{osm_output_path}")
210
+
211
+ # Copy EPW file to run dir
133
212
  epw_output_path = File.join(output_dir, 'in.epw')
134
213
  FileUtils.cp(epw_path, epw_output_path)
135
214
  end
136
-
137
- OSModel.create(hpxml, runner, model, hpxml_path, epw_path, weather, output_dir,
138
- add_component_loads, building_id, debug)
139
215
  rescue Exception => e
140
216
  runner.registerError("#{e.message}\n#{e.backtrace.join("\n")}")
141
217
  return false
@@ -143,19 +219,198 @@ class HPXMLtoOpenStudio < OpenStudio::Measure::ModelMeasure
143
219
 
144
220
  return true
145
221
  end
146
- end
147
222
 
148
- class OSModel
149
- def self.create(hpxml, runner, model, hpxml_path, epw_path, weather, output_dir,
150
- add_component_loads, building_id, debug)
151
- @hpxml = hpxml
152
- @debug = debug
223
+ def add_unit_model_to_model(model, hpxml_osm_map)
224
+ unique_objects = { 'OS:ConvergenceLimits' => 'ConvergenceLimits',
225
+ 'OS:Foundation:Kiva:Settings' => 'FoundationKivaSettings',
226
+ 'OS:OutputControl:Files' => 'OutputControlFiles',
227
+ 'OS:Output:Diagnostics' => 'OutputDiagnostics',
228
+ 'OS:Output:JSON' => 'OutputJSON',
229
+ 'OS:PerformancePrecisionTradeoffs' => 'PerformancePrecisionTradeoffs',
230
+ 'OS:RunPeriod' => 'RunPeriod',
231
+ 'OS:RunPeriodControl:DaylightSavingTime' => 'RunPeriodControlDaylightSavingTime',
232
+ 'OS:ShadowCalculation' => 'ShadowCalculation',
233
+ 'OS:SimulationControl' => 'SimulationControl',
234
+ 'OS:Site' => 'Site',
235
+ 'OS:Site:GroundTemperature:Deep' => 'SiteGroundTemperatureDeep',
236
+ 'OS:Site:GroundTemperature:Shallow' => 'SiteGroundTemperatureShallow',
237
+ 'OS:Site:WaterMainsTemperature' => 'SiteWaterMainsTemperature',
238
+ 'OS:SurfaceConvectionAlgorithm:Inside' => 'InsideSurfaceConvectionAlgorithm',
239
+ 'OS:SurfaceConvectionAlgorithm:Outside' => 'OutsideSurfaceConvectionAlgorithm',
240
+ 'OS:Timestep' => 'Timestep' }
241
+
242
+ # Handle unique objects first: Grab one from the first model we find the
243
+ # object on (may not be the first unit).
244
+ unit_model_objects = []
245
+ unique_handles_to_skip = []
246
+ uuid_regex = /\{(.*?)\}/
247
+ unique_objects.each do |idd_obj, osm_class|
248
+ first_model_object_by_type = nil
249
+ hpxml_osm_map.values.each do |unit_model|
250
+ next if unit_model.getObjectsByType(idd_obj.to_IddObjectType).empty?
251
+
252
+ model_object = unit_model.send("get#{osm_class}")
253
+
254
+ if first_model_object_by_type.nil?
255
+ # Retain object for model
256
+ unit_model_objects << model_object
257
+ first_model_object_by_type = model_object
258
+ if idd_obj == 'OS:Site:WaterMainsTemperature' # Handle referenced child object too
259
+ unit_model_objects << unit_model.getObjectsByName(model_object.temperatureSchedule.get.name.to_s)[0]
260
+ end
261
+ else
262
+ # Throw error if different values between this model_object and first_model_object_by_type
263
+ if model_object.to_s.gsub(uuid_regex, '') != first_model_object_by_type.to_s.gsub(uuid_regex, '')
264
+ fail "Unique object (#{idd_obj}) has different values across dwelling units."
265
+ end
266
+
267
+ if idd_obj == 'OS:Site:WaterMainsTemperature' # Handle referenced child object too
268
+ if model_object.temperatureSchedule.get.to_s.gsub(uuid_regex, '') != first_model_object_by_type.temperatureSchedule.get.to_s.gsub(uuid_regex, '')
269
+ fail "Unique object (#{idd_obj}) has different values across dwelling units."
270
+ end
271
+ end
272
+ end
273
+
274
+ unique_handles_to_skip << model_object.handle.to_s
275
+ if idd_obj == 'OS:Site:WaterMainsTemperature' # Handle referenced child object too
276
+ unique_handles_to_skip << model_object.temperatureSchedule.get.handle.to_s
277
+ end
278
+ end
279
+ end
280
+
281
+ hpxml_osm_map.values.each_with_index do |unit_model, unit_number|
282
+ shift_geometry(unit_model, unit_number)
283
+ prefix_all_unit_model_objects(unit_model, unit_number)
284
+
285
+ # Handle remaining (non-unique) objects now
286
+ unit_model.objects.each do |obj|
287
+ next if unit_number > 0 && obj.to_Building.is_initialized
288
+ next if unique_handles_to_skip.include? obj.handle.to_s
153
289
 
154
- @eri_version = @hpxml.header.eri_calculation_version # Hidden feature
155
- @eri_version = 'latest' if @eri_version.nil?
156
- @eri_version = Constants.ERIVersions[-1] if @eri_version == 'latest'
290
+ unit_model_objects << obj
291
+ end
292
+ end
157
293
 
158
- @apply_ashrae140_assumptions = @hpxml.header.apply_ashrae140_assumptions # Hidden feature
294
+ model.addObjects(unit_model_objects, true)
295
+ end
296
+
297
+ def shift_geometry(unit_model, unit_number)
298
+ # Shift units so they aren't right on top and shade each other
299
+ y_shift = 200.0 * unit_number # meters
300
+
301
+ # shift the unit so it's not right on top of the previous one
302
+ unit_model.getSpaces.sort.each do |space|
303
+ space.setYOrigin(y_shift)
304
+ end
305
+
306
+ # shift shading surfaces
307
+ m = OpenStudio::Matrix.new(4, 4, 0)
308
+ m[0, 0] = 1
309
+ m[1, 1] = 1
310
+ m[2, 2] = 1
311
+ m[3, 3] = 1
312
+ m[1, 3] = y_shift
313
+ t = OpenStudio::Transformation.new(m)
314
+
315
+ unit_model.getShadingSurfaceGroups.each do |shading_surface_group|
316
+ next if shading_surface_group.space.is_initialized # already got shifted
317
+
318
+ shading_surface_group.shadingSurfaces.each do |shading_surface|
319
+ shading_surface.setVertices(t * shading_surface.vertices)
320
+ end
321
+ end
322
+ end
323
+
324
+ def prefix_all_unit_model_objects(unit_model, unit_number)
325
+ # Prefix all objects with name using unit number
326
+ # FUTURE: Create objects with unique names up front so we don't have to do this
327
+
328
+ # EMS objects
329
+ ems_map = {}
330
+
331
+ unit_model.getEnergyManagementSystemSensors.each do |sensor|
332
+ ems_map[sensor.name.to_s] = make_variable_name(sensor.name, unit_number)
333
+ sensor.setKeyName(make_variable_name(sensor.keyName, unit_number)) unless sensor.keyName.empty? || sensor.keyName.downcase == 'environment'
334
+ end
335
+
336
+ unit_model.getEnergyManagementSystemActuators.each do |actuator|
337
+ ems_map[actuator.name.to_s] = make_variable_name(actuator.name, unit_number)
338
+ end
339
+
340
+ unit_model.getEnergyManagementSystemInternalVariables.each do |internal_variable|
341
+ ems_map[internal_variable.name.to_s] = make_variable_name(internal_variable.name, unit_number)
342
+ internal_variable.setInternalDataIndexKeyName(make_variable_name(internal_variable.internalDataIndexKeyName, unit_number)) unless internal_variable.internalDataIndexKeyName.empty?
343
+ end
344
+
345
+ unit_model.getEnergyManagementSystemGlobalVariables.each do |global_variable|
346
+ ems_map[global_variable.name.to_s] = make_variable_name(global_variable.name, unit_number)
347
+ end
348
+
349
+ unit_model.getEnergyManagementSystemOutputVariables.each do |output_variable|
350
+ next if output_variable.emsVariableObject.is_initialized
351
+
352
+ new_ems_variable_name = make_variable_name(output_variable.emsVariableName, unit_number)
353
+ ems_map[output_variable.emsVariableName.to_s] = new_ems_variable_name
354
+ output_variable.setEMSVariableName(new_ems_variable_name)
355
+ end
356
+
357
+ unit_model.getEnergyManagementSystemSubroutines.each do |subroutine|
358
+ ems_map[subroutine.name.to_s] = make_variable_name(subroutine.name, unit_number)
359
+ end
360
+
361
+ # variables in program lines don't get updated automatically
362
+ lhs_characters = [' ', ',', '(', ')', '+', '-', '*', '/', ';']
363
+ rhs_characters = [''] + lhs_characters
364
+ (unit_model.getEnergyManagementSystemPrograms + unit_model.getEnergyManagementSystemSubroutines).each do |program|
365
+ new_lines = []
366
+ program.lines.each do |line|
367
+ ems_map.each do |old_name, new_name|
368
+ next unless line.include?(old_name)
369
+
370
+ # old_name between at least 1 character, with the exception of '' on left and ' ' on right
371
+ lhs_characters.each do |lhs|
372
+ next unless line.include?("#{lhs}#{old_name}")
373
+
374
+ rhs_characters.each do |rhs|
375
+ next unless line.include?("#{lhs}#{old_name}#{rhs}")
376
+ next if lhs == '' && ['', ' '].include?(rhs)
377
+
378
+ line.gsub!("#{lhs}#{old_name}#{rhs}", "#{lhs}#{new_name}#{rhs}")
379
+ end
380
+ end
381
+ end
382
+ new_lines << line
383
+ end
384
+ program.setLines(new_lines)
385
+ end
386
+
387
+ # All model objects
388
+ unit_model.objects.each do |model_object|
389
+ next if model_object.name.nil?
390
+
391
+ if unit_number == 0
392
+ # OpenStudio is unhappy if these schedules are renamed
393
+ next if model_object.name.to_s == unit_model.alwaysOnContinuousSchedule.name.to_s
394
+ next if model_object.name.to_s == unit_model.alwaysOnDiscreteSchedule.name.to_s
395
+ next if model_object.name.to_s == unit_model.alwaysOffDiscreteSchedule.name.to_s
396
+ end
397
+
398
+ model_object.setName(make_variable_name(model_object.name, unit_number))
399
+ end
400
+ end
401
+
402
+ def make_variable_name(obj_name, unit_number)
403
+ return "unit#{unit_number + 1}_#{obj_name}".gsub(' ', '_').gsub('-', '_')
404
+ end
405
+
406
+ def create_unit_model(hpxml, hpxml_bldg, runner, model, epw_path, epw_file, weather, debug, schedules_file, eri_version, unit_num)
407
+ @hpxml_header = hpxml.header
408
+ @hpxml_bldg = hpxml_bldg
409
+ @debug = debug
410
+ @schedules_file = schedules_file
411
+ @eri_version = eri_version
412
+
413
+ @apply_ashrae140_assumptions = @hpxml_header.apply_ashrae140_assumptions # Hidden feature
159
414
  @apply_ashrae140_assumptions = false if @apply_ashrae140_assumptions.nil?
160
415
 
161
416
  # Here we turn off OS error-checking so that any invalid values provided
@@ -167,21 +422,14 @@ class OSModel
167
422
  model.setStrictnessLevel('None'.to_StrictnessLevel)
168
423
 
169
424
  # Init
170
- check_file_references(hpxml_path)
171
- epw_file = Location.apply_weather_file(model, epw_path)
172
- @schedules_file = SchedulesFile.new(runner: runner, model: model,
173
- schedules_paths: @hpxml.header.schedules_filepaths,
174
- year: Location.get_sim_calendar_year(@hpxml.header.sim_calendar_year, epw_file),
175
- unavailable_periods: @hpxml.header.unavailable_periods,
176
- output_path: File.join(output_dir, 'in.schedules.csv'))
177
- set_defaults_and_globals(runner, output_dir, epw_file, weather, @schedules_file)
178
- validate_emissions_files()
179
- Location.apply(model, weather, epw_file, @hpxml)
425
+ OpenStudio::Model::WeatherFile.setWeatherFile(model, epw_file)
426
+ set_defaults_and_globals()
427
+ Location.apply(model, weather, epw_file, @hpxml_header, @hpxml_bldg)
180
428
  add_simulation_params(model)
181
429
 
182
430
  # Conditioned space/zone
183
431
  spaces = {}
184
- create_or_get_space(model, spaces, HPXML::LocationLivingSpace)
432
+ create_or_get_space(model, spaces, HPXML::LocationConditionedSpace)
185
433
  set_foundation_and_walls_top()
186
434
  set_heating_and_cooling_seasons()
187
435
  add_setpoints(runner, model, weather, spaces)
@@ -197,16 +445,16 @@ class OSModel
197
445
  add_skylights(model, spaces)
198
446
  add_conditioned_floor_area(model, spaces)
199
447
  add_thermal_mass(model, spaces)
200
- Geometry.set_zone_volumes(spaces, @hpxml, @apply_ashrae140_assumptions)
201
- Geometry.explode_surfaces(model, @hpxml, @walls_top)
448
+ Geometry.set_zone_volumes(spaces, @hpxml_bldg, @apply_ashrae140_assumptions)
449
+ Geometry.explode_surfaces(model, @hpxml_bldg, @walls_top)
202
450
  add_num_occupants(model, runner, spaces)
203
451
 
204
452
  # HVAC
205
- @hvac_unavailable_periods = Schedule.get_unavailable_periods(runner, SchedulesFile::ColumnHVAC, @hpxml.header.unavailable_periods)
453
+ @hvac_unavailable_periods = Schedule.get_unavailable_periods(runner, SchedulesFile::ColumnHVAC, @hpxml_header.unavailable_periods)
206
454
  airloop_map = {} # Map of HPXML System ID -> AirLoopHVAC (or ZoneHVACFourPipeFanCoil)
207
455
  add_ideal_system(model, spaces, epw_path)
208
- add_cooling_system(model, spaces, airloop_map)
209
- add_heating_system(runner, model, spaces, airloop_map)
456
+ add_cooling_system(model, weather, spaces, airloop_map)
457
+ add_heating_system(runner, model, weather, spaces, airloop_map)
210
458
  add_heat_pump(runner, model, weather, spaces, airloop_map)
211
459
  add_dehumidifiers(runner, model, spaces)
212
460
  add_ceiling_fans(runner, model, weather, spaces)
@@ -219,8 +467,8 @@ class OSModel
219
467
  add_mfls(runner, model, spaces)
220
468
  add_lighting(runner, model, epw_file, spaces)
221
469
 
222
- # Pools & Hot Tubs
223
- add_pools_and_hot_tubs(runner, model, spaces)
470
+ # Pools & Permanent Spas
471
+ add_pools_and_permanent_spas(runner, model, spaces)
224
472
 
225
473
  # Other
226
474
  add_cooling_season(model, weather)
@@ -228,34 +476,13 @@ class OSModel
228
476
  add_photovoltaics(model)
229
477
  add_generators(model)
230
478
  add_batteries(runner, model, spaces)
231
- add_additional_properties(model, hpxml_path, building_id, epw_file)
232
-
233
- # Output
234
- add_unmet_hours_output(model, spaces)
235
- add_loads_output(model, spaces, add_component_loads)
236
- set_output_files(model)
237
- # Uncomment to debug EMS
238
- # add_ems_debug_output(model)
239
-
240
- if debug
241
- osm_output_path = File.join(output_dir, 'in.osm')
242
- File.write(osm_output_path, model.to_s)
243
- runner.registerInfo("Wrote file: #{osm_output_path}")
244
- end
479
+ add_building_unit(model, unit_num)
245
480
  end
246
481
 
247
- private
248
-
249
- def self.check_file_references(hpxml_path)
482
+ def check_emissions_references(hpxml_header, hpxml_path)
250
483
  # Check/update file references
251
- @hpxml.header.schedules_filepaths = @hpxml.header.schedules_filepaths.collect { |sfp|
252
- FilePath.check_path(sfp,
253
- File.dirname(hpxml_path),
254
- 'Schedules')
255
- }
256
-
257
- @hpxml.header.emissions_scenarios.each do |scenario|
258
- if @hpxml.header.emissions_scenarios.select { |s| s.emissions_type == scenario.emissions_type && s.name == scenario.name }.size > 1
484
+ hpxml_header.emissions_scenarios.each do |scenario|
485
+ if hpxml_header.emissions_scenarios.select { |s| s.emissions_type == scenario.emissions_type && s.name == scenario.name }.size > 1
259
486
  fail "Found multiple Emissions Scenarios with the Scenario Name=#{scenario.name} and Emissions Type=#{scenario.emissions_type}."
260
487
  end
261
488
  next if scenario.elec_schedule_filepath.nil?
@@ -266,8 +493,17 @@ class OSModel
266
493
  end
267
494
  end
268
495
 
269
- def self.validate_emissions_files()
270
- @hpxml.header.emissions_scenarios.each do |scenario|
496
+ def check_schedule_references(hpxml_bldg_header, hpxml_path)
497
+ # Check/update file references
498
+ hpxml_bldg_header.schedules_filepaths = hpxml_bldg_header.schedules_filepaths.collect { |sfp|
499
+ FilePath.check_path(sfp,
500
+ File.dirname(hpxml_path),
501
+ 'Schedules')
502
+ }
503
+ end
504
+
505
+ def validate_emissions_files(hpxml_header)
506
+ hpxml_header.emissions_scenarios.each do |scenario|
271
507
  next if scenario.elec_schedule_filepath.nil?
272
508
 
273
509
  data = File.readlines(scenario.elec_schedule_filepath)
@@ -283,76 +519,69 @@ class OSModel
283
519
  end
284
520
  end
285
521
 
286
- def self.set_defaults_and_globals(runner, output_dir, epw_file, weather, schedules_file)
522
+ def set_defaults_and_globals()
287
523
  # Initialize
288
524
  @remaining_heat_load_frac = 1.0
289
525
  @remaining_cool_load_frac = 1.0
290
526
 
291
527
  # Set globals
292
- @cfa = @hpxml.building_construction.conditioned_floor_area
293
- @ncfl = @hpxml.building_construction.number_of_conditioned_floors
294
- @ncfl_ag = @hpxml.building_construction.number_of_conditioned_floors_above_grade
295
- @nbeds = @hpxml.building_construction.number_of_bedrooms
296
- @default_azimuths = HPXMLDefaults.get_default_azimuths(@hpxml)
297
-
298
- # Apply defaults to HPXML object
299
- HPXMLDefaults.apply(runner, @hpxml, @eri_version, weather, epw_file: epw_file, schedules_file: schedules_file)
300
-
301
- # Write updated HPXML object (w/ defaults) to file for inspection
302
- @hpxml_defaults_path = File.join(output_dir, 'in.xml')
303
- XMLHelper.write_file(@hpxml.to_oga, @hpxml_defaults_path)
304
-
305
- # Now that we've written in.xml, ensure that no capacities/airflows
306
- # are zero in order to prevent potential E+ errors.
307
- HVAC.ensure_nonzero_sizing_values(@hpxml)
308
-
309
- # Now that we've written in.xml, make adjustments for modeling purposes.
310
- @frac_windows_operable = @hpxml.fraction_of_windows_operable()
311
- @hpxml.collapse_enclosure_surfaces() # Speeds up simulation
312
- @hpxml.delete_adiabatic_subsurfaces() # EnergyPlus doesn't allow this
528
+ @cfa = @hpxml_bldg.building_construction.conditioned_floor_area
529
+ @ncfl = @hpxml_bldg.building_construction.number_of_conditioned_floors
530
+ @ncfl_ag = @hpxml_bldg.building_construction.number_of_conditioned_floors_above_grade
531
+ @nbeds = @hpxml_bldg.building_construction.number_of_bedrooms
532
+ @default_azimuths = HPXMLDefaults.get_default_azimuths(@hpxml_bldg)
533
+
534
+ # Apply unit multipliers to HVAC systems and water heaters
535
+ HVAC.apply_unit_multiplier(@hpxml_bldg)
536
+ # Ensure that no capacities/airflows are zero in order to prevent potential E+ errors.
537
+ HVAC.ensure_nonzero_sizing_values(@hpxml_bldg)
538
+ # Make adjustments for modeling purposes
539
+ @frac_windows_operable = @hpxml_bldg.fraction_of_windows_operable()
540
+ @hpxml_bldg.collapse_enclosure_surfaces() # Speeds up simulation
541
+ @hpxml_bldg.delete_adiabatic_subsurfaces() # EnergyPlus doesn't allow this
313
542
 
314
543
  # We don't want this to be written to in.xml, because then if you ran the in.xml
315
544
  # file, you would get different results (operational calculation) relative to the
316
545
  # original file (asset calculation).
317
- if @hpxml.building_occupancy.number_of_residents.nil?
318
- @hpxml.building_occupancy.number_of_residents = Geometry.get_occupancy_default_num(@nbeds)
546
+ if @hpxml_bldg.building_occupancy.number_of_residents.nil?
547
+ @hpxml_bldg.building_occupancy.number_of_residents = Geometry.get_occupancy_default_num(@nbeds)
319
548
  end
320
549
 
321
550
  # If zero occupants, ensure end uses of interest are zeroed out
322
- if (@hpxml.building_occupancy.number_of_residents == 0) && (not @apply_ashrae140_assumptions)
323
- @hpxml.header.unavailable_periods.add(column_name: 'Vacancy',
324
- begin_month: @hpxml.header.sim_begin_month,
325
- begin_day: @hpxml.header.sim_begin_day,
551
+ if (@hpxml_bldg.building_occupancy.number_of_residents == 0) && (not @apply_ashrae140_assumptions)
552
+ @hpxml_header.unavailable_periods.add(column_name: 'Vacancy',
553
+ begin_month: @hpxml_header.sim_begin_month,
554
+ begin_day: @hpxml_header.sim_begin_day,
326
555
  begin_hour: 0,
327
- end_month: @hpxml.header.sim_end_month,
328
- end_day: @hpxml.header.sim_end_day,
556
+ end_month: @hpxml_header.sim_end_month,
557
+ end_day: @hpxml_header.sim_end_day,
329
558
  end_hour: 24,
330
559
  natvent_availability: HPXML::ScheduleUnavailable)
331
560
  end
332
561
  end
333
562
 
334
- def self.add_simulation_params(model)
335
- SimControls.apply(model, @hpxml)
563
+ def add_simulation_params(model)
564
+ SimControls.apply(model, @hpxml_header)
336
565
  end
337
566
 
338
- def self.add_num_occupants(model, runner, spaces)
567
+ def add_num_occupants(model, runner, spaces)
339
568
  # Occupants
340
- num_occ = @hpxml.building_occupancy.number_of_residents
569
+ num_occ = @hpxml_bldg.building_occupancy.number_of_residents
341
570
  return if num_occ <= 0
342
571
 
343
- Geometry.apply_occupants(model, runner, @hpxml, num_occ, spaces[HPXML::LocationLivingSpace],
344
- @schedules_file, @hpxml.header.unavailable_periods)
572
+ Geometry.apply_occupants(model, runner, @hpxml_bldg, num_occ, spaces[HPXML::LocationConditionedSpace],
573
+ @schedules_file, @hpxml_header.unavailable_periods)
345
574
  end
346
575
 
347
- def self.create_or_get_space(model, spaces, location)
576
+ def create_or_get_space(model, spaces, location)
348
577
  if spaces[location].nil?
349
- Geometry.create_space_and_zone(model, spaces, location)
578
+ Geometry.create_space_and_zone(model, spaces, location, @hpxml_bldg.building_construction.number_of_units)
350
579
  end
351
580
  return spaces[location]
352
581
  end
353
582
 
354
- def self.add_roofs(runner, model, spaces)
355
- @hpxml.roofs.each do |roof|
583
+ def add_roofs(runner, model, spaces)
584
+ @hpxml_bldg.roofs.each do |roof|
356
585
  next if roof.net_area < 1.0 # skip modeling net surface area for surfaces comprised entirely of subsurface area
357
586
 
358
587
  if roof.azimuth.nil?
@@ -463,8 +692,8 @@ class OSModel
463
692
  end
464
693
  end
465
694
 
466
- def self.add_walls(runner, model, spaces)
467
- @hpxml.walls.each do |wall|
695
+ def add_walls(runner, model, spaces)
696
+ @hpxml_bldg.walls.each do |wall|
468
697
  next if wall.net_area < 1.0 # skip modeling net surface area for surfaces comprised entirely of subsurface area
469
698
 
470
699
  if wall.azimuth.nil?
@@ -531,8 +760,8 @@ class OSModel
531
760
  end
532
761
  end
533
762
 
534
- def self.add_rim_joists(runner, model, spaces)
535
- @hpxml.rim_joists.each do |rim_joist|
763
+ def add_rim_joists(runner, model, spaces)
764
+ @hpxml_bldg.rim_joists.each do |rim_joist|
536
765
  if rim_joist.azimuth.nil?
537
766
  if rim_joist.is_exterior
538
767
  azimuths = @default_azimuths # Model as four directions for average exterior incident solar
@@ -603,8 +832,8 @@ class OSModel
603
832
  end
604
833
  end
605
834
 
606
- def self.add_floors(runner, model, spaces)
607
- @hpxml.floors.each do |floor|
835
+ def add_floors(runner, model, spaces)
836
+ @hpxml_bldg.floors.each do |floor|
608
837
  area = floor.area
609
838
  width = Math::sqrt(area)
610
839
  length = area / width
@@ -632,6 +861,12 @@ class OSModel
632
861
  surface.setWindExposure('NoWind')
633
862
  elsif floor.is_floor
634
863
  surface.setSunExposure('NoSun')
864
+ if floor.exterior_adjacent_to == HPXML::LocationManufacturedHomeUnderBelly
865
+ foundation = @hpxml_bldg.foundations.find { |x| x.to_location == floor.exterior_adjacent_to }
866
+ if foundation.belly_wing_skirt_present
867
+ surface.setWindExposure('NoWind')
868
+ end
869
+ end
635
870
  end
636
871
 
637
872
  # Apply construction
@@ -660,7 +895,7 @@ class OSModel
660
895
  else
661
896
  outside_film = Material.AirFilmFloorReduced
662
897
  end
663
- if floor.interior_adjacent_to == HPXML::LocationLivingSpace
898
+ if floor.interior_adjacent_to == HPXML::LocationConditionedSpace
664
899
  mat_int_finish_or_covering = Material.CoveringBare
665
900
  end
666
901
  end
@@ -671,116 +906,73 @@ class OSModel
671
906
  end
672
907
  end
673
908
 
674
- def self.add_foundation_walls_slabs(runner, model, weather, spaces)
675
- foundation_types = @hpxml.slabs.map { |s| s.interior_adjacent_to }.uniq
909
+ def add_foundation_walls_slabs(runner, model, weather, spaces)
910
+ foundation_types = @hpxml_bldg.slabs.map { |s| s.interior_adjacent_to }.uniq
676
911
 
677
912
  foundation_types.each do |foundation_type|
678
- # Get attached foundation walls/slabs
679
- fnd_walls = []
913
+ # Get attached slabs/foundation walls
680
914
  slabs = []
681
- @hpxml.foundation_walls.each do |foundation_wall|
682
- next unless foundation_wall.interior_adjacent_to == foundation_type
683
- next if foundation_wall.net_area < 1.0 # skip modeling net surface area for surfaces comprised entirely of subsurface area
684
-
685
- fnd_walls << foundation_wall
686
- end
687
- @hpxml.slabs.each do |slab|
915
+ @hpxml_bldg.slabs.each do |slab|
688
916
  next unless slab.interior_adjacent_to == foundation_type
689
917
 
690
918
  slabs << slab
691
919
  slab.exposed_perimeter = [slab.exposed_perimeter, 1.0].max # minimum value to prevent error if no exposed slab
692
920
  end
693
921
 
694
- # Calculate combinations of slabs/walls for each Kiva instance
695
- kiva_instances = get_kiva_instances(fnd_walls, slabs)
696
-
697
- # Obtain some wall/slab information
698
- fnd_wall_lengths = {}
699
- fnd_walls.each do |foundation_wall|
700
- next unless foundation_wall.is_exterior
701
-
702
- fnd_wall_lengths[foundation_wall] = foundation_wall.area / foundation_wall.height
703
- end
704
- slab_exp_perims = {}
705
- slab_areas = {}
706
922
  slabs.each do |slab|
707
- slab_exp_perims[slab] = slab.exposed_perimeter
708
- slab_areas[slab] = slab.area
709
- end
710
- total_slab_exp_perim = slab_exp_perims.values.sum(0.0)
711
- total_slab_area = slab_areas.values.sum(0.0)
712
- total_fnd_wall_length = fnd_wall_lengths.values.sum(0.0)
923
+ slab_frac = slab.exposed_perimeter / slabs.map { |s| s.exposed_perimeter }.sum
924
+ ext_fnd_walls = slab.connected_foundation_walls.select { |fw| fw.net_area >= 1.0 && fw.is_exterior }
713
925
 
714
- no_wall_slab_exp_perim = {}
715
-
716
- kiva_instances.each do |foundation_wall, slab|
717
- # Apportion referenced walls/slabs for this Kiva instance
718
- slab_frac = slab_exp_perims[slab] / total_slab_exp_perim
719
- if total_fnd_wall_length > 0
720
- fnd_wall_frac = fnd_wall_lengths[foundation_wall] / total_fnd_wall_length
926
+ if ext_fnd_walls.empty?
927
+ # Slab w/o foundation walls
928
+ add_foundation_slab(model, weather, spaces, slab, -1 * slab.depth_below_grade.to_f, slab.exposed_perimeter, nil)
721
929
  else
722
- fnd_wall_frac = 1.0 # Handle slab foundation type
723
- end
724
-
725
- kiva_foundation = nil
726
- if not foundation_wall.nil?
727
- # Add exterior foundation wall surface
728
- kiva_foundation = add_foundation_wall(runner, model, spaces, foundation_wall, slab_frac,
729
- total_fnd_wall_length, total_slab_exp_perim)
730
- end
930
+ # Slab w/ foundation walls
931
+ ext_fnd_walls_length = ext_fnd_walls.map { |fw| fw.area / fw.height }.sum
932
+ remaining_exposed_length = slab.exposed_perimeter
933
+
934
+ # Since we don't know which FoundationWalls are adjacent to which Slabs, we apportion
935
+ # each FoundationWall to each slab.
936
+ ext_fnd_walls.each do |fnd_wall|
937
+ # Both the foundation wall and slab must have same exposed length to prevent Kiva errors.
938
+ # For the foundation wall, we are effectively modeling the net *exposed* area.
939
+ fnd_wall_length = fnd_wall.area / fnd_wall.height
940
+ apportioned_exposed_length = fnd_wall_length / ext_fnd_walls_length * slab.exposed_perimeter # Slab exposed perimeter apportioned to this foundation wall
941
+ apportioned_total_length = fnd_wall_length * slab_frac # Foundation wall length apportioned to this slab
942
+ exposed_length = [apportioned_exposed_length, apportioned_total_length].min
943
+ remaining_exposed_length -= exposed_length
944
+
945
+ kiva_foundation = add_foundation_wall(runner, model, spaces, fnd_wall, exposed_length, fnd_wall_length)
946
+ add_foundation_slab(model, weather, spaces, slab, -1 * fnd_wall.depth_below_grade, exposed_length, kiva_foundation)
947
+ end
731
948
 
732
- # Add single combined foundation slab surface (for similar surfaces)
733
- slab_exp_perim = slab_exp_perims[slab] * fnd_wall_frac
734
- slab_area = slab_areas[slab] * fnd_wall_frac
735
- no_wall_slab_exp_perim[slab] = 0.0 if no_wall_slab_exp_perim[slab].nil?
736
- if (not foundation_wall.nil?) && (slab_exp_perim > fnd_wall_lengths[foundation_wall] * slab_frac)
737
- # Keep track of no-wall slab exposed perimeter
738
- no_wall_slab_exp_perim[slab] += (slab_exp_perim - fnd_wall_lengths[foundation_wall] * slab_frac)
739
-
740
- # Reduce this slab's exposed perimeter so that EnergyPlus does not automatically
741
- # create a second no-wall Kiva instance for each of our Kiva instances.
742
- # Instead, we will later create our own Kiva instance to account for it.
743
- # This reduces the number of Kiva instances we end up with.
744
- exp_perim_frac = (fnd_wall_lengths[foundation_wall] * slab_frac) / slab_exp_perim
745
- slab_exp_perim *= exp_perim_frac
746
- slab_area *= exp_perim_frac
747
- end
748
- if not foundation_wall.nil?
749
- z_origin = -1 * foundation_wall.depth_below_grade # Position based on adjacent foundation walls
750
- else
751
- z_origin = -1 * slab.depth_below_grade
949
+ if remaining_exposed_length > 1 # Skip if a small length (e.g., due to rounding)
950
+ # The slab's exposed perimeter exceeds the sum of attached exterior foundation wall lengths.
951
+ # This may legitimately occur for a walkout basement, where a portion of the slab has no
952
+ # adjacent foundation wall.
953
+ add_foundation_slab(model, weather, spaces, slab, 0, remaining_exposed_length, nil)
954
+ end
752
955
  end
753
- add_foundation_slab(model, weather, spaces, slab, slab_exp_perim,
754
- slab_area, z_origin, kiva_foundation)
755
- end
756
-
757
- # For each slab, create a no-wall Kiva slab instance if needed.
758
- slabs.each do |slab|
759
- next unless no_wall_slab_exp_perim[slab] > 1.0
760
-
761
- z_origin = 0
762
- slab_area = total_slab_area * no_wall_slab_exp_perim[slab] / total_slab_exp_perim
763
- add_foundation_slab(model, weather, spaces, slab, no_wall_slab_exp_perim[slab],
764
- slab_area, z_origin, nil)
765
956
  end
766
957
 
767
958
  # Interzonal foundation wall surfaces
768
959
  # The above-grade portion of these walls are modeled as EnergyPlus surfaces with standard adjacency.
769
960
  # The below-grade portion of these walls (in contact with ground) are not modeled, as Kiva does not
770
961
  # calculate heat flow between two zones through the ground.
771
- fnd_walls.each do |foundation_wall|
772
- next unless foundation_wall.is_interior
962
+ int_fnd_walls = @hpxml_bldg.foundation_walls.select { |fw| fw.is_interior && fw.interior_adjacent_to == foundation_type }
963
+ int_fnd_walls.each do |fnd_wall|
964
+ next unless fnd_wall.is_interior
773
965
 
774
- ag_height = foundation_wall.height - foundation_wall.depth_below_grade
775
- ag_net_area = foundation_wall.net_area * ag_height / foundation_wall.height
966
+ ag_height = fnd_wall.height - fnd_wall.depth_below_grade
967
+ ag_net_area = fnd_wall.net_area * ag_height / fnd_wall.height
776
968
  next if ag_net_area < 1.0
777
969
 
778
970
  length = ag_net_area / ag_height
779
971
  z_origin = -1 * ag_height
780
- if foundation_wall.azimuth.nil?
972
+ if fnd_wall.azimuth.nil?
781
973
  azimuth = @default_azimuths[0] # Arbitrary direction, doesn't receive exterior incident solar
782
974
  else
783
- azimuth = foundation_wall.azimuth
975
+ azimuth = fnd_wall.azimuth
784
976
  end
785
977
 
786
978
  vertices = Geometry.create_wall_vertices(length, ag_height, z_origin, azimuth)
@@ -789,10 +981,10 @@ class OSModel
789
981
  surface.additionalProperties.setFeature('Azimuth', azimuth)
790
982
  surface.additionalProperties.setFeature('Tilt', 90.0)
791
983
  surface.additionalProperties.setFeature('SurfaceType', 'FoundationWall')
792
- surface.setName(foundation_wall.id)
984
+ surface.setName(fnd_wall.id)
793
985
  surface.setSurfaceType('Wall')
794
- set_surface_interior(model, spaces, surface, foundation_wall)
795
- set_surface_exterior(model, spaces, surface, foundation_wall)
986
+ set_surface_interior(model, spaces, surface, fnd_wall)
987
+ set_surface_exterior(model, spaces, surface, fnd_wall)
796
988
  surface.setSunExposure('NoSun')
797
989
  surface.setWindExposure('NoWind')
798
990
 
@@ -801,57 +993,50 @@ class OSModel
801
993
  wall_type = HPXML::WallTypeConcrete
802
994
  inside_film = Material.AirFilmVertical
803
995
  outside_film = Material.AirFilmVertical
804
- assembly_r = foundation_wall.insulation_assembly_r_value
805
- mat_int_finish = Material.InteriorFinishMaterial(foundation_wall.interior_finish_type, foundation_wall.interior_finish_thickness)
996
+ assembly_r = fnd_wall.insulation_assembly_r_value
997
+ mat_int_finish = Material.InteriorFinishMaterial(fnd_wall.interior_finish_type, fnd_wall.interior_finish_thickness)
806
998
  if assembly_r.nil?
807
- concrete_thick_in = foundation_wall.thickness
808
- int_r = foundation_wall.insulation_interior_r_value
809
- ext_r = foundation_wall.insulation_exterior_r_value
999
+ concrete_thick_in = fnd_wall.thickness
1000
+ int_r = fnd_wall.insulation_interior_r_value
1001
+ ext_r = fnd_wall.insulation_exterior_r_value
810
1002
  mat_concrete = Material.Concrete(concrete_thick_in)
811
1003
  mat_int_finish_rvalue = mat_int_finish.nil? ? 0.0 : mat_int_finish.rvalue
812
1004
  assembly_r = int_r + ext_r + mat_concrete.rvalue + mat_int_finish_rvalue + inside_film.rvalue + outside_film.rvalue
813
1005
  end
814
1006
  mat_ext_finish = nil
815
1007
 
816
- Constructions.apply_wall_construction(runner, model, [surface], foundation_wall.id, wall_type, assembly_r,
1008
+ Constructions.apply_wall_construction(runner, model, [surface], fnd_wall.id, wall_type, assembly_r,
817
1009
  mat_int_finish, inside_film, outside_film, mat_ext_finish, nil, nil)
818
1010
  end
819
1011
  end
820
1012
  end
821
1013
 
822
- def self.add_foundation_wall(runner, model, spaces, foundation_wall, slab_frac,
823
- total_fnd_wall_length, total_slab_exp_perim)
824
-
825
- net_area = foundation_wall.net_area * slab_frac
826
- gross_area = foundation_wall.area * slab_frac
1014
+ def add_foundation_wall(runner, model, spaces, foundation_wall, exposed_length, fnd_wall_length)
1015
+ exposed_fraction = exposed_length / fnd_wall_length
1016
+ net_exposed_area = foundation_wall.net_area * exposed_fraction
1017
+ gross_exposed_area = foundation_wall.area * exposed_fraction
827
1018
  height = foundation_wall.height
828
1019
  height_ag = height - foundation_wall.depth_below_grade
829
1020
  z_origin = -1 * foundation_wall.depth_below_grade
830
- length = gross_area / height
831
1021
  if foundation_wall.azimuth.nil?
832
1022
  azimuth = @default_azimuths[0] # Arbitrary; solar incidence in Kiva is applied as an orientation average (to the above grade portion of the wall)
833
1023
  else
834
1024
  azimuth = foundation_wall.azimuth
835
1025
  end
836
1026
 
837
- if total_fnd_wall_length > total_slab_exp_perim
838
- # Calculate exposed section of wall based on slab's total exposed perimeter.
839
- length *= total_slab_exp_perim / total_fnd_wall_length
840
- end
1027
+ return if exposed_length < 0.1 # Avoid Kiva error if exposed wall length is too small
841
1028
 
842
- return if length < 0.1 # Avoid Kiva error if exposed wall length is too small
843
-
844
- if gross_area > net_area
1029
+ if gross_exposed_area > net_exposed_area
845
1030
  # Create a "notch" in the wall to account for the subsurfaces. This ensures that
846
1031
  # we preserve the appropriate wall height, length, and area for Kiva.
847
- subsurface_area = gross_area - net_area
1032
+ subsurface_area = gross_exposed_area - net_exposed_area
848
1033
  else
849
1034
  subsurface_area = 0
850
1035
  end
851
1036
 
852
- vertices = Geometry.create_wall_vertices(length, height, z_origin, azimuth, subsurface_area: subsurface_area)
1037
+ vertices = Geometry.create_wall_vertices(exposed_length, height, z_origin, azimuth, subsurface_area: subsurface_area)
853
1038
  surface = OpenStudio::Model::Surface.new(vertices, model)
854
- surface.additionalProperties.setFeature('Length', length)
1039
+ surface.additionalProperties.setFeature('Length', exposed_length)
855
1040
  surface.additionalProperties.setFeature('Azimuth', azimuth)
856
1041
  surface.additionalProperties.setFeature('Tilt', 90.0)
857
1042
  surface.additionalProperties.setFeature('SurfaceType', 'FoundationWall')
@@ -893,7 +1078,7 @@ class OSModel
893
1078
  int_rigid_r = foundation_wall.insulation_interior_r_value
894
1079
  end
895
1080
 
896
- soil_k_in = UnitConversions.convert(@hpxml.site.ground_conductivity, 'ft', 'in')
1081
+ soil_k_in = UnitConversions.convert(@hpxml_bldg.site.ground_conductivity, 'ft', 'in')
897
1082
 
898
1083
  Constructions.apply_foundation_wall(model, [surface], "#{foundation_wall.id} construction",
899
1084
  ext_rigid_offset, int_rigid_offset, ext_rigid_height, int_rigid_height,
@@ -907,10 +1092,10 @@ class OSModel
907
1092
  return surface.adjacentFoundation.get
908
1093
  end
909
1094
 
910
- def self.add_foundation_slab(model, weather, spaces, slab, slab_exp_perim,
911
- slab_area, z_origin, kiva_foundation)
912
-
913
- slab_tot_perim = slab_exp_perim
1095
+ def add_foundation_slab(model, weather, spaces, slab, z_origin, exposed_length, kiva_foundation)
1096
+ exposed_fraction = exposed_length / slab.exposed_perimeter
1097
+ slab_tot_perim = exposed_length
1098
+ slab_area = slab.area * exposed_fraction
914
1099
  if slab_tot_perim**2 - 16.0 * slab_area <= 0
915
1100
  # Cannot construct rectangle with this perimeter/area. Some of the
916
1101
  # perimeter is presumably not exposed, so bump up perimeter value.
@@ -961,18 +1146,18 @@ class OSModel
961
1146
  mat_carpet = Material.CoveringBare(slab.carpet_fraction,
962
1147
  slab.carpet_r_value)
963
1148
  end
964
- soil_k_in = UnitConversions.convert(@hpxml.site.ground_conductivity, 'ft', 'in')
1149
+ soil_k_in = UnitConversions.convert(@hpxml_bldg.site.ground_conductivity, 'ft', 'in')
965
1150
 
966
1151
  Constructions.apply_foundation_slab(model, surface, "#{slab.id} construction",
967
1152
  slab_under_r, slab_under_width, slab_gap_r, slab_perim_r,
968
1153
  slab_perim_depth, slab_whole_r, slab.thickness,
969
- slab_exp_perim, mat_carpet, soil_k_in, kiva_foundation)
1154
+ exposed_length, mat_carpet, soil_k_in, kiva_foundation)
970
1155
 
971
1156
  kiva_foundation = surface.adjacentFoundation.get
972
1157
 
973
1158
  foundation_walls_insulated = false
974
1159
  foundation_ceiling_insulated = false
975
- @hpxml.foundation_walls.each do |fnd_wall|
1160
+ @hpxml_bldg.foundation_walls.each do |fnd_wall|
976
1161
  next unless fnd_wall.interior_adjacent_to == slab.interior_adjacent_to
977
1162
  next unless fnd_wall.exterior_adjacent_to == HPXML::LocationGround
978
1163
 
@@ -982,8 +1167,8 @@ class OSModel
982
1167
  foundation_walls_insulated = true
983
1168
  end
984
1169
  end
985
- @hpxml.floors.each do |floor|
986
- next unless floor.interior_adjacent_to == HPXML::LocationLivingSpace
1170
+ @hpxml_bldg.floors.each do |floor|
1171
+ next unless floor.interior_adjacent_to == HPXML::LocationConditionedSpace
987
1172
  next unless floor.exterior_adjacent_to == slab.interior_adjacent_to
988
1173
 
989
1174
  if floor.insulation_assembly_r_value > 5
@@ -992,29 +1177,29 @@ class OSModel
992
1177
  end
993
1178
 
994
1179
  Constructions.apply_kiva_initial_temp(kiva_foundation, slab, weather,
995
- spaces[HPXML::LocationLivingSpace].thermalZone.get,
996
- @hpxml.header.sim_begin_month, @hpxml.header.sim_begin_day,
997
- @hpxml.header.sim_calendar_year, @schedules_file,
1180
+ spaces[HPXML::LocationConditionedSpace].thermalZone.get,
1181
+ @hpxml_header.sim_begin_month, @hpxml_header.sim_begin_day,
1182
+ @hpxml_header.sim_calendar_year, @schedules_file,
998
1183
  foundation_walls_insulated, foundation_ceiling_insulated)
999
1184
 
1000
1185
  return kiva_foundation
1001
1186
  end
1002
1187
 
1003
- def self.add_conditioned_floor_area(model, spaces)
1188
+ def add_conditioned_floor_area(model, spaces)
1004
1189
  # Check if we need to add floors between conditioned spaces (e.g., between first
1005
1190
  # and second story or conditioned basement ceiling).
1006
1191
  # This ensures that the E+ reported Conditioned Floor Area is correct.
1007
1192
 
1008
1193
  sum_cfa = 0.0
1009
- @hpxml.floors.each do |floor|
1194
+ @hpxml_bldg.floors.each do |floor|
1010
1195
  next unless floor.is_floor
1011
- next unless [HPXML::LocationLivingSpace, HPXML::LocationBasementConditioned].include?(floor.interior_adjacent_to) ||
1012
- [HPXML::LocationLivingSpace, HPXML::LocationBasementConditioned].include?(floor.exterior_adjacent_to)
1196
+ next unless [HPXML::LocationConditionedSpace, HPXML::LocationBasementConditioned].include?(floor.interior_adjacent_to) ||
1197
+ [HPXML::LocationConditionedSpace, HPXML::LocationBasementConditioned].include?(floor.exterior_adjacent_to)
1013
1198
 
1014
1199
  sum_cfa += floor.area
1015
1200
  end
1016
- @hpxml.slabs.each do |slab|
1017
- next unless [HPXML::LocationLivingSpace, HPXML::LocationBasementConditioned].include? slab.interior_adjacent_to
1201
+ @hpxml_bldg.slabs.each do |slab|
1202
+ next unless [HPXML::LocationConditionedSpace, HPXML::LocationBasementConditioned].include? slab.interior_adjacent_to
1018
1203
 
1019
1204
  sum_cfa += slab.area
1020
1205
  end
@@ -1037,7 +1222,7 @@ class OSModel
1037
1222
  floor_surface.setWindExposure('NoWind')
1038
1223
  floor_surface.setName('inferred conditioned floor')
1039
1224
  floor_surface.setSurfaceType('Floor')
1040
- floor_surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationLivingSpace))
1225
+ floor_surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationConditionedSpace))
1041
1226
  floor_surface.setOutsideBoundaryCondition('Adiabatic')
1042
1227
  floor_surface.additionalProperties.setFeature('SurfaceType', 'InferredFloor')
1043
1228
  floor_surface.additionalProperties.setFeature('Tilt', 0.0)
@@ -1050,7 +1235,7 @@ class OSModel
1050
1235
  ceiling_surface.setWindExposure('NoWind')
1051
1236
  ceiling_surface.setName('inferred conditioned ceiling')
1052
1237
  ceiling_surface.setSurfaceType('RoofCeiling')
1053
- ceiling_surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationLivingSpace))
1238
+ ceiling_surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationConditionedSpace))
1054
1239
  ceiling_surface.setOutsideBoundaryCondition('Adiabatic')
1055
1240
  ceiling_surface.additionalProperties.setFeature('SurfaceType', 'InferredCeiling')
1056
1241
  ceiling_surface.additionalProperties.setFeature('Tilt', 0.0)
@@ -1059,22 +1244,22 @@ class OSModel
1059
1244
  apply_adiabatic_construction(model, [floor_surface, ceiling_surface], 'floor')
1060
1245
  end
1061
1246
 
1062
- def self.add_thermal_mass(model, spaces)
1247
+ def add_thermal_mass(model, spaces)
1063
1248
  if @apply_ashrae140_assumptions
1064
1249
  # 1024 ft2 of interior partition wall mass, no furniture mass
1065
1250
  mat_int_finish = Material.InteriorFinishMaterial(HPXML::InteriorFinishGypsumBoard, 0.5)
1066
1251
  partition_wall_area = 1024.0 * 2 # Exposed partition wall area (both sides)
1067
1252
  Constructions.apply_partition_walls(model, 'PartitionWallConstruction', mat_int_finish, partition_wall_area, spaces)
1068
1253
  else
1069
- mat_int_finish = Material.InteriorFinishMaterial(@hpxml.partition_wall_mass.interior_finish_type, @hpxml.partition_wall_mass.interior_finish_thickness)
1070
- partition_wall_area = @hpxml.partition_wall_mass.area_fraction * @cfa # Exposed partition wall area (both sides)
1254
+ mat_int_finish = Material.InteriorFinishMaterial(@hpxml_bldg.partition_wall_mass.interior_finish_type, @hpxml_bldg.partition_wall_mass.interior_finish_thickness)
1255
+ partition_wall_area = @hpxml_bldg.partition_wall_mass.area_fraction * @cfa # Exposed partition wall area (both sides)
1071
1256
  Constructions.apply_partition_walls(model, 'PartitionWallConstruction', mat_int_finish, partition_wall_area, spaces)
1072
1257
 
1073
- Constructions.apply_furniture(model, @hpxml.furniture_mass, spaces)
1258
+ Constructions.apply_furniture(model, @hpxml_bldg.furniture_mass, spaces)
1074
1259
  end
1075
1260
  end
1076
1261
 
1077
- def self.add_cooling_season(model, weather)
1262
+ def add_cooling_season(model, weather)
1078
1263
  # Create cooling season schedule
1079
1264
  # Applies to natural ventilation and calculation of component loads, not HVAC equipment
1080
1265
  # Uses BAHSP cooling season, not user-specified cooling season (which may be, e.g., year-round)
@@ -1086,22 +1271,20 @@ class OSModel
1086
1271
  @clg_ssn_sensor.setKeyName(clg_season_sch.schedule.name.to_s)
1087
1272
  end
1088
1273
 
1089
- def self.add_windows(model, spaces)
1274
+ def add_windows(model, spaces)
1090
1275
  # We already stored @fraction_of_windows_operable, so lets remove the
1091
1276
  # fraction_operable properties from windows and re-collapse the enclosure
1092
1277
  # so as to prevent potentially modeling multiple identical windows in E+,
1093
1278
  # which can increase simulation runtime.
1094
- @hpxml.windows.each do |window|
1279
+ @hpxml_bldg.windows.each do |window|
1095
1280
  window.fraction_operable = nil
1096
1281
  end
1097
- @hpxml.collapse_enclosure_surfaces()
1282
+ @hpxml_bldg.collapse_enclosure_surfaces()
1098
1283
 
1099
- shading_group = nil
1100
1284
  shading_schedules = {}
1101
- shading_ems = { sensors: {}, program: nil }
1102
1285
 
1103
1286
  surfaces = []
1104
- @hpxml.windows.each_with_index do |window, i|
1287
+ @hpxml_bldg.windows.each do |window|
1105
1288
  window_height = 4.0 # ft, default
1106
1289
 
1107
1290
  overhang_depth = nil
@@ -1142,16 +1325,14 @@ class OSModel
1142
1325
 
1143
1326
  if not overhang_depth.nil?
1144
1327
  overhang = sub_surface.addOverhang(UnitConversions.convert(overhang_depth, 'ft', 'm'), UnitConversions.convert(overhang_distance_to_top, 'ft', 'm'))
1145
- overhang.get.setName("#{sub_surface.name} - #{Constants.ObjectNameOverhangs}")
1328
+ overhang.get.setName("#{sub_surface.name} overhangs")
1146
1329
  end
1147
1330
 
1148
1331
  # Apply construction
1149
1332
  Constructions.apply_window(model, sub_surface, 'WindowConstruction', ufactor, shgc)
1150
1333
 
1151
1334
  # Apply interior/exterior shading (as needed)
1152
- shading_vertices = Geometry.create_wall_vertices(window_length, window_height, z_origin, window.azimuth)
1153
- shading_group = Constructions.apply_window_skylight_shading(model, window, i, shading_vertices, surface, sub_surface, shading_group,
1154
- shading_schedules, shading_ems, Constants.ObjectNameWindowShade, @hpxml)
1335
+ Constructions.apply_window_skylight_shading(model, window, sub_surface, shading_schedules, @hpxml_header, @hpxml_bldg)
1155
1336
  else
1156
1337
  # Window is on an interior surface, which E+ does not allow. Model
1157
1338
  # as a door instead so that we can get the appropriate conduction
@@ -1188,14 +1369,11 @@ class OSModel
1188
1369
  apply_adiabatic_construction(model, surfaces, 'wall')
1189
1370
  end
1190
1371
 
1191
- def self.add_skylights(model, spaces)
1372
+ def add_skylights(model, spaces)
1192
1373
  surfaces = []
1193
-
1194
- shading_group = nil
1195
1374
  shading_schedules = {}
1196
- shading_ems = { sensors: {}, program: nil }
1197
1375
 
1198
- @hpxml.skylights.each_with_index do |skylight, i|
1376
+ @hpxml_bldg.skylights.each do |skylight|
1199
1377
  tilt = skylight.roof.pitch / 12.0
1200
1378
  width = Math::sqrt(skylight.area)
1201
1379
  length = skylight.area / width
@@ -1213,7 +1391,7 @@ class OSModel
1213
1391
  surface.additionalProperties.setFeature('SurfaceType', 'Skylight')
1214
1392
  surface.setName("surface #{skylight.id}")
1215
1393
  surface.setSurfaceType('RoofCeiling')
1216
- surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationLivingSpace)) # Ensures it is included in Manual J sizing
1394
+ surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationConditionedSpace)) # Ensures it is included in Manual J sizing
1217
1395
  surface.setOutsideBoundaryCondition('Outdoors') # cannot be adiabatic because subsurfaces won't be created
1218
1396
  surfaces << surface
1219
1397
 
@@ -1227,17 +1405,15 @@ class OSModel
1227
1405
  Constructions.apply_skylight(model, sub_surface, 'SkylightConstruction', ufactor, shgc)
1228
1406
 
1229
1407
  # Apply interior/exterior shading (as needed)
1230
- shading_vertices = Geometry.create_roof_vertices(length, width, z_origin, skylight.azimuth, tilt)
1231
- shading_group = Constructions.apply_window_skylight_shading(model, skylight, i, shading_vertices, surface, sub_surface, shading_group,
1232
- shading_schedules, shading_ems, Constants.ObjectNameSkylightShade, @hpxml)
1408
+ Constructions.apply_window_skylight_shading(model, skylight, sub_surface, shading_schedules, @hpxml_header, @hpxml_bldg)
1233
1409
  end
1234
1410
 
1235
1411
  apply_adiabatic_construction(model, surfaces, 'roof')
1236
1412
  end
1237
1413
 
1238
- def self.add_doors(model, spaces)
1414
+ def add_doors(model, spaces)
1239
1415
  surfaces = []
1240
- @hpxml.doors.each do |door|
1416
+ @hpxml_bldg.doors.each do |door|
1241
1417
  door_height = 6.67 # ft
1242
1418
  door_length = door.area / door_height
1243
1419
  z_origin = @foundation_top
@@ -1277,7 +1453,7 @@ class OSModel
1277
1453
  apply_adiabatic_construction(model, surfaces, 'wall')
1278
1454
  end
1279
1455
 
1280
- def self.apply_adiabatic_construction(model, surfaces, type)
1456
+ def apply_adiabatic_construction(model, surfaces, type)
1281
1457
  # Arbitrary construction for heat capacitance.
1282
1458
  # Only applies to surfaces where outside boundary conditioned is
1283
1459
  # adiabatic or surface net area is near zero.
@@ -1302,80 +1478,81 @@ class OSModel
1302
1478
  end
1303
1479
  end
1304
1480
 
1305
- def self.add_hot_water_and_appliances(runner, model, weather, spaces)
1481
+ def add_hot_water_and_appliances(runner, model, weather, spaces)
1306
1482
  # Assign spaces
1307
- @hpxml.clothes_washers.each do |clothes_washer|
1483
+ @hpxml_bldg.clothes_washers.each do |clothes_washer|
1308
1484
  clothes_washer.additional_properties.space = get_space_from_location(clothes_washer.location, spaces)
1309
1485
  end
1310
- @hpxml.clothes_dryers.each do |clothes_dryer|
1486
+ @hpxml_bldg.clothes_dryers.each do |clothes_dryer|
1311
1487
  clothes_dryer.additional_properties.space = get_space_from_location(clothes_dryer.location, spaces)
1312
1488
  end
1313
- @hpxml.dishwashers.each do |dishwasher|
1489
+ @hpxml_bldg.dishwashers.each do |dishwasher|
1314
1490
  dishwasher.additional_properties.space = get_space_from_location(dishwasher.location, spaces)
1315
1491
  end
1316
- @hpxml.refrigerators.each do |refrigerator|
1492
+ @hpxml_bldg.refrigerators.each do |refrigerator|
1317
1493
  refrigerator.additional_properties.space = get_space_from_location(refrigerator.location, spaces)
1318
1494
  end
1319
- @hpxml.freezers.each do |freezer|
1495
+ @hpxml_bldg.freezers.each do |freezer|
1320
1496
  freezer.additional_properties.space = get_space_from_location(freezer.location, spaces)
1321
1497
  end
1322
- @hpxml.cooking_ranges.each do |cooking_range|
1498
+ @hpxml_bldg.cooking_ranges.each do |cooking_range|
1323
1499
  cooking_range.additional_properties.space = get_space_from_location(cooking_range.location, spaces)
1324
1500
  end
1325
1501
 
1326
1502
  # Distribution
1327
- if @hpxml.water_heating_systems.size > 0
1328
- hot_water_distribution = @hpxml.hot_water_distributions[0]
1503
+ if @hpxml_bldg.water_heating_systems.size > 0
1504
+ hot_water_distribution = @hpxml_bldg.hot_water_distributions[0]
1329
1505
  end
1330
1506
 
1331
1507
  # Solar thermal system
1332
1508
  solar_thermal_system = nil
1333
- if @hpxml.solar_thermal_systems.size > 0
1334
- solar_thermal_system = @hpxml.solar_thermal_systems[0]
1509
+ if @hpxml_bldg.solar_thermal_systems.size > 0
1510
+ solar_thermal_system = @hpxml_bldg.solar_thermal_systems[0]
1335
1511
  end
1336
1512
 
1337
1513
  # Water Heater
1338
- unavailable_periods = Schedule.get_unavailable_periods(runner, SchedulesFile::ColumnWaterHeater, @hpxml.header.unavailable_periods)
1339
- has_uncond_bsmnt = @hpxml.has_location(HPXML::LocationBasementUnconditioned)
1514
+ unavailable_periods = Schedule.get_unavailable_periods(runner, SchedulesFile::ColumnWaterHeater, @hpxml_header.unavailable_periods)
1515
+ unit_multiplier = @hpxml_bldg.building_construction.number_of_units
1516
+ has_uncond_bsmnt = @hpxml_bldg.has_location(HPXML::LocationBasementUnconditioned)
1340
1517
  plantloop_map = {}
1341
- @hpxml.water_heating_systems.each do |water_heating_system|
1518
+ @hpxml_bldg.water_heating_systems.each do |water_heating_system|
1342
1519
  loc_space, loc_schedule = get_space_or_schedule_from_location(water_heating_system.location, model, spaces)
1343
1520
 
1344
1521
  ec_adj = HotWaterAndAppliances.get_dist_energy_consumption_adjustment(has_uncond_bsmnt, @cfa, @ncfl, water_heating_system, hot_water_distribution)
1345
1522
 
1346
1523
  sys_id = water_heating_system.id
1347
1524
  if water_heating_system.water_heater_type == HPXML::WaterHeaterTypeStorage
1348
- plantloop_map[sys_id] = Waterheater.apply_tank(model, runner, loc_space, loc_schedule, water_heating_system, ec_adj, solar_thermal_system, @eri_version, @schedules_file, unavailable_periods)
1525
+ plantloop_map[sys_id] = Waterheater.apply_tank(model, runner, loc_space, loc_schedule, water_heating_system, ec_adj, solar_thermal_system, @eri_version, @schedules_file, unavailable_periods, unit_multiplier)
1349
1526
  elsif water_heating_system.water_heater_type == HPXML::WaterHeaterTypeTankless
1350
- plantloop_map[sys_id] = Waterheater.apply_tankless(model, runner, loc_space, loc_schedule, water_heating_system, ec_adj, solar_thermal_system, @eri_version, @schedules_file, unavailable_periods)
1527
+ plantloop_map[sys_id] = Waterheater.apply_tankless(model, runner, loc_space, loc_schedule, water_heating_system, ec_adj, solar_thermal_system, @eri_version, @schedules_file, unavailable_periods, unit_multiplier)
1351
1528
  elsif water_heating_system.water_heater_type == HPXML::WaterHeaterTypeHeatPump
1352
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1353
- plantloop_map[sys_id] = Waterheater.apply_heatpump(model, runner, loc_space, loc_schedule, weather, water_heating_system, ec_adj, solar_thermal_system, living_zone, @eri_version, @schedules_file, unavailable_periods)
1529
+ conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get
1530
+ plantloop_map[sys_id] = Waterheater.apply_heatpump(model, runner, loc_space, loc_schedule, weather, water_heating_system, ec_adj, solar_thermal_system, conditioned_zone, @eri_version, @schedules_file, unavailable_periods, unit_multiplier)
1354
1531
  elsif [HPXML::WaterHeaterTypeCombiStorage, HPXML::WaterHeaterTypeCombiTankless].include? water_heating_system.water_heater_type
1355
- plantloop_map[sys_id] = Waterheater.apply_combi(model, runner, loc_space, loc_schedule, water_heating_system, ec_adj, solar_thermal_system, @eri_version, @schedules_file, unavailable_periods)
1532
+ plantloop_map[sys_id] = Waterheater.apply_combi(model, runner, loc_space, loc_schedule, water_heating_system, ec_adj, solar_thermal_system, @eri_version, @schedules_file, unavailable_periods, unit_multiplier)
1356
1533
  else
1357
1534
  fail "Unhandled water heater (#{water_heating_system.water_heater_type})."
1358
1535
  end
1359
1536
  end
1360
1537
 
1361
1538
  # Hot water fixtures and appliances
1362
- HotWaterAndAppliances.apply(model, runner, @hpxml, weather, spaces, hot_water_distribution,
1539
+ HotWaterAndAppliances.apply(model, runner, @hpxml_header, @hpxml_bldg, weather, spaces, hot_water_distribution,
1363
1540
  solar_thermal_system, @eri_version, @schedules_file, plantloop_map,
1364
- @hpxml.header.unavailable_periods)
1541
+ @hpxml_header.unavailable_periods, @hpxml_bldg.building_construction.number_of_units)
1365
1542
 
1366
1543
  if (not solar_thermal_system.nil?) && (not solar_thermal_system.collector_area.nil?) # Detailed solar water heater
1367
1544
  loc_space, loc_schedule = get_space_or_schedule_from_location(solar_thermal_system.water_heating_system.location, model, spaces)
1368
- Waterheater.apply_solar_thermal(model, loc_space, loc_schedule, solar_thermal_system, plantloop_map)
1545
+ Waterheater.apply_solar_thermal(model, loc_space, loc_schedule, solar_thermal_system, plantloop_map, unit_multiplier)
1369
1546
  end
1370
1547
 
1371
1548
  # Add combi-system EMS program with water use equipment information
1372
- Waterheater.apply_combi_system_EMS(model, @hpxml.water_heating_systems, plantloop_map)
1549
+ Waterheater.apply_combi_system_EMS(model, @hpxml_bldg.water_heating_systems, plantloop_map)
1373
1550
  end
1374
1551
 
1375
- def self.add_cooling_system(model, spaces, airloop_map)
1376
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1552
+ def add_cooling_system(model, weather, spaces, airloop_map)
1553
+ conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get
1377
1554
 
1378
- HVAC.get_hpxml_hvac_systems(@hpxml).each do |hvac_system|
1555
+ HVAC.get_hpxml_hvac_systems(@hpxml_bldg).each do |hvac_system|
1379
1556
  next if hvac_system[:cooling].nil?
1380
1557
  next unless hvac_system[:cooling].is_a? HPXML::CoolingSystem
1381
1558
 
@@ -1405,22 +1582,23 @@ class OSModel
1405
1582
  HPXML::HVACTypeMiniSplitAirConditioner,
1406
1583
  HPXML::HVACTypePTAC].include? cooling_system.cooling_system_type
1407
1584
 
1408
- airloop_map[sys_id] = HVAC.apply_air_source_hvac_systems(model, cooling_system, heating_system,
1409
- sequential_cool_load_fracs, sequential_heat_load_fracs,
1410
- living_zone, @hvac_unavailable_periods)
1585
+ airloop_map[sys_id] = HVAC.apply_air_source_hvac_systems(model, cooling_system, heating_system, sequential_cool_load_fracs, sequential_heat_load_fracs,
1586
+ weather.data.AnnualMaxDrybulb, weather.data.AnnualMinDrybulb,
1587
+ conditioned_zone, @hvac_unavailable_periods)
1411
1588
 
1412
1589
  elsif [HPXML::HVACTypeEvaporativeCooler].include? cooling_system.cooling_system_type
1413
1590
 
1414
- airloop_map[sys_id] = HVAC.apply_evaporative_cooler(model, cooling_system,
1415
- sequential_cool_load_fracs, living_zone, @hvac_unavailable_periods)
1591
+ airloop_map[sys_id] = HVAC.apply_evaporative_cooler(model, cooling_system, sequential_cool_load_fracs,
1592
+ conditioned_zone, @hvac_unavailable_periods,
1593
+ @hpxml_bldg.building_construction.number_of_units)
1416
1594
  end
1417
1595
  end
1418
1596
  end
1419
1597
 
1420
- def self.add_heating_system(runner, model, spaces, airloop_map)
1421
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1598
+ def add_heating_system(runner, model, weather, spaces, airloop_map)
1599
+ conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get
1422
1600
 
1423
- HVAC.get_hpxml_hvac_systems(@hpxml).each do |hvac_system|
1601
+ HVAC.get_hpxml_hvac_systems(@hpxml_bldg).each do |hvac_system|
1424
1602
  next if hvac_system[:heating].nil?
1425
1603
  next unless hvac_system[:heating].is_a? HPXML::HeatingSystem
1426
1604
 
@@ -1449,43 +1627,42 @@ class OSModel
1449
1627
  sys_id = heating_system.id
1450
1628
  if [HPXML::HVACTypeFurnace].include? heating_system.heating_system_type
1451
1629
 
1452
- airloop_map[sys_id] = HVAC.apply_air_source_hvac_systems(model, nil, heating_system,
1453
- [0], sequential_heat_load_fracs,
1454
- living_zone, @hvac_unavailable_periods)
1630
+ airloop_map[sys_id] = HVAC.apply_air_source_hvac_systems(model, nil, heating_system, [0], sequential_heat_load_fracs,
1631
+ weather.data.AnnualMaxDrybulb, weather.data.AnnualMinDrybulb,
1632
+ conditioned_zone, @hvac_unavailable_periods)
1455
1633
 
1456
1634
  elsif [HPXML::HVACTypeBoiler].include? heating_system.heating_system_type
1457
1635
 
1458
- airloop_map[sys_id] = HVAC.apply_boiler(model, runner, heating_system,
1459
- sequential_heat_load_fracs, living_zone, @hvac_unavailable_periods)
1636
+ airloop_map[sys_id] = HVAC.apply_boiler(model, runner, heating_system, sequential_heat_load_fracs, conditioned_zone,
1637
+ @hvac_unavailable_periods)
1460
1638
 
1461
1639
  elsif [HPXML::HVACTypeElectricResistance].include? heating_system.heating_system_type
1462
1640
 
1463
1641
  HVAC.apply_electric_baseboard(model, heating_system,
1464
- sequential_heat_load_fracs, living_zone, @hvac_unavailable_periods)
1642
+ sequential_heat_load_fracs, conditioned_zone, @hvac_unavailable_periods)
1465
1643
 
1466
1644
  elsif [HPXML::HVACTypeStove,
1467
- HPXML::HVACTypePortableHeater,
1468
- HPXML::HVACTypeFixedHeater,
1645
+ HPXML::HVACTypeSpaceHeater,
1469
1646
  HPXML::HVACTypeWallFurnace,
1470
1647
  HPXML::HVACTypeFloorFurnace,
1471
1648
  HPXML::HVACTypeFireplace].include? heating_system.heating_system_type
1472
1649
 
1473
1650
  HVAC.apply_unit_heater(model, heating_system,
1474
- sequential_heat_load_fracs, living_zone, @hvac_unavailable_periods)
1651
+ sequential_heat_load_fracs, conditioned_zone, @hvac_unavailable_periods)
1475
1652
  end
1476
1653
 
1477
1654
  next unless heating_system.is_heat_pump_backup_system
1478
1655
 
1479
1656
  # Store OS object for later use
1480
- equipment_list = model.getZoneHVACEquipmentLists.find { |el| el.thermalZone == living_zone }
1657
+ equipment_list = model.getZoneHVACEquipmentLists.find { |el| el.thermalZone == conditioned_zone }
1481
1658
  @heat_pump_backup_system_object = equipment_list.equipment[-1]
1482
1659
  end
1483
1660
  end
1484
1661
 
1485
- def self.add_heat_pump(runner, model, weather, spaces, airloop_map)
1486
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1662
+ def add_heat_pump(runner, model, weather, spaces, airloop_map)
1663
+ conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get
1487
1664
 
1488
- HVAC.get_hpxml_hvac_systems(@hpxml).each do |hvac_system|
1665
+ HVAC.get_hpxml_hvac_systems(@hpxml_bldg).each do |hvac_system|
1489
1666
  next if hvac_system[:cooling].nil?
1490
1667
  next unless hvac_system[:cooling].is_a? HPXML::HeatPump
1491
1668
 
@@ -1506,26 +1683,27 @@ class OSModel
1506
1683
 
1507
1684
  airloop_map[sys_id] = HVAC.apply_water_loop_to_air_heat_pump(model, heat_pump,
1508
1685
  sequential_heat_load_fracs, sequential_cool_load_fracs,
1509
- living_zone, @hvac_unavailable_periods)
1686
+ conditioned_zone, @hvac_unavailable_periods)
1510
1687
 
1511
1688
  elsif [HPXML::HVACTypeHeatPumpAirToAir,
1512
1689
  HPXML::HVACTypeHeatPumpMiniSplit,
1513
1690
  HPXML::HVACTypeHeatPumpPTHP,
1514
1691
  HPXML::HVACTypeHeatPumpRoom].include? heat_pump.heat_pump_type
1515
- airloop_map[sys_id] = HVAC.apply_air_source_hvac_systems(model, heat_pump, heat_pump,
1516
- sequential_cool_load_fracs, sequential_heat_load_fracs,
1517
- living_zone, @hvac_unavailable_periods)
1692
+ airloop_map[sys_id] = HVAC.apply_air_source_hvac_systems(model, heat_pump, heat_pump, sequential_cool_load_fracs, sequential_heat_load_fracs,
1693
+ weather.data.AnnualMaxDrybulb, weather.data.AnnualMinDrybulb,
1694
+ conditioned_zone, @hvac_unavailable_periods)
1518
1695
  elsif [HPXML::HVACTypeHeatPumpGroundToAir].include? heat_pump.heat_pump_type
1519
1696
 
1520
1697
  airloop_map[sys_id] = HVAC.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump,
1521
1698
  sequential_heat_load_fracs, sequential_cool_load_fracs,
1522
- living_zone, @hpxml.site.ground_conductivity, @hvac_unavailable_periods)
1699
+ conditioned_zone, @hpxml_bldg.site.ground_conductivity, @hvac_unavailable_periods,
1700
+ @hpxml_bldg.building_construction.number_of_units)
1523
1701
 
1524
1702
  end
1525
1703
 
1526
- next unless not heat_pump.backup_system.nil?
1704
+ next if heat_pump.backup_system.nil?
1527
1705
 
1528
- equipment_list = model.getZoneHVACEquipmentLists.find { |el| el.thermalZone == living_zone }
1706
+ equipment_list = model.getZoneHVACEquipmentLists.find { |el| el.thermalZone == conditioned_zone }
1529
1707
 
1530
1708
  # Set priority to be last (i.e., after the heat pump that it is backup for)
1531
1709
  equipment_list.setHeatingPriority(@heat_pump_backup_system_object, 99)
@@ -1533,14 +1711,13 @@ class OSModel
1533
1711
  end
1534
1712
  end
1535
1713
 
1536
- def self.add_ideal_system(model, spaces, epw_path)
1714
+ def add_ideal_system(model, spaces, epw_path)
1537
1715
  # Adds an ideal air system as needed to meet the load under certain circumstances:
1538
1716
  # 1. the sum of fractions load served is less than 1, or
1539
1717
  # 2. we're using an ideal air system for e.g. ASHRAE 140 loads calculation.
1540
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1541
- obj_name = Constants.ObjectNameIdealAirSystem
1718
+ conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get
1542
1719
 
1543
- if @apply_ashrae140_assumptions && (@hpxml.total_fraction_heat_load_served + @hpxml.total_fraction_heat_load_served == 0.0)
1720
+ if @apply_ashrae140_assumptions && (@hpxml_bldg.total_fraction_heat_load_served + @hpxml_bldg.total_fraction_heat_load_served == 0.0)
1544
1721
  cooling_load_frac = 1.0
1545
1722
  heating_load_frac = 1.0
1546
1723
  if @apply_ashrae140_assumptions
@@ -1552,56 +1729,57 @@ class OSModel
1552
1729
  fail 'Unexpected weather file for ASHRAE 140 run.'
1553
1730
  end
1554
1731
  end
1555
- HVAC.apply_ideal_air_loads(model, obj_name, [cooling_load_frac], [heating_load_frac],
1556
- living_zone, @hvac_unavailable_periods)
1732
+ HVAC.apply_ideal_air_loads(model, [cooling_load_frac], [heating_load_frac],
1733
+ conditioned_zone, @hvac_unavailable_periods)
1557
1734
  return
1558
1735
  end
1559
1736
 
1560
- if (@hpxml.total_fraction_heat_load_served < 1.0) && (@hpxml.total_fraction_heat_load_served > 0.0)
1561
- sequential_heat_load_fracs = HVAC.calc_sequential_load_fractions(@remaining_heat_load_frac - @hpxml.total_fraction_heat_load_served, @remaining_heat_load_frac, @heating_days)
1562
- @remaining_heat_load_frac -= (1.0 - @hpxml.total_fraction_heat_load_served)
1737
+ if (@hpxml_bldg.total_fraction_heat_load_served < 1.0) && (@hpxml_bldg.total_fraction_heat_load_served > 0.0)
1738
+ sequential_heat_load_fracs = HVAC.calc_sequential_load_fractions(@remaining_heat_load_frac - @hpxml_bldg.total_fraction_heat_load_served, @remaining_heat_load_frac, @heating_days)
1739
+ @remaining_heat_load_frac -= (1.0 - @hpxml_bldg.total_fraction_heat_load_served)
1563
1740
  else
1564
1741
  sequential_heat_load_fracs = [0.0]
1565
1742
  end
1566
1743
 
1567
- if (@hpxml.total_fraction_cool_load_served < 1.0) && (@hpxml.total_fraction_cool_load_served > 0.0)
1568
- sequential_cool_load_fracs = HVAC.calc_sequential_load_fractions(@remaining_cool_load_frac - @hpxml.total_fraction_cool_load_served, @remaining_cool_load_frac, @cooling_days)
1569
- @remaining_cool_load_frac -= (1.0 - @hpxml.total_fraction_cool_load_served)
1744
+ if (@hpxml_bldg.total_fraction_cool_load_served < 1.0) && (@hpxml_bldg.total_fraction_cool_load_served > 0.0)
1745
+ sequential_cool_load_fracs = HVAC.calc_sequential_load_fractions(@remaining_cool_load_frac - @hpxml_bldg.total_fraction_cool_load_served, @remaining_cool_load_frac, @cooling_days)
1746
+ @remaining_cool_load_frac -= (1.0 - @hpxml_bldg.total_fraction_cool_load_served)
1570
1747
  else
1571
1748
  sequential_cool_load_fracs = [0.0]
1572
1749
  end
1573
1750
 
1574
1751
  if (sequential_heat_load_fracs.sum > 0.0) || (sequential_cool_load_fracs.sum > 0.0)
1575
- HVAC.apply_ideal_air_loads(model, obj_name, sequential_cool_load_fracs, sequential_heat_load_fracs,
1576
- living_zone, @hvac_unavailable_periods)
1752
+ HVAC.apply_ideal_air_loads(model, sequential_cool_load_fracs, sequential_heat_load_fracs,
1753
+ conditioned_zone, @hvac_unavailable_periods)
1577
1754
  end
1578
1755
  end
1579
1756
 
1580
- def self.add_setpoints(runner, model, weather, spaces)
1581
- return if @hpxml.hvac_controls.size == 0
1757
+ def add_setpoints(runner, model, weather, spaces)
1758
+ return if @hpxml_bldg.hvac_controls.size == 0
1582
1759
 
1583
- hvac_control = @hpxml.hvac_controls[0]
1584
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1585
- has_ceiling_fan = (@hpxml.ceiling_fans.size > 0)
1760
+ hvac_control = @hpxml_bldg.hvac_controls[0]
1761
+ conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get
1762
+ has_ceiling_fan = (@hpxml_bldg.ceiling_fans.size > 0)
1586
1763
 
1587
- HVAC.apply_setpoints(model, runner, weather, hvac_control, living_zone, has_ceiling_fan, @heating_days, @cooling_days, @hpxml.header.sim_calendar_year, @schedules_file)
1764
+ HVAC.apply_setpoints(model, runner, weather, hvac_control, conditioned_zone, has_ceiling_fan, @heating_days, @cooling_days, @hpxml_header.sim_calendar_year, @schedules_file)
1588
1765
  end
1589
1766
 
1590
- def self.add_ceiling_fans(runner, model, weather, spaces)
1591
- return if @hpxml.ceiling_fans.size == 0
1767
+ def add_ceiling_fans(runner, model, weather, spaces)
1768
+ return if @hpxml_bldg.ceiling_fans.size == 0
1592
1769
 
1593
- ceiling_fan = @hpxml.ceiling_fans[0]
1594
- HVAC.apply_ceiling_fans(model, runner, weather, ceiling_fan, spaces[HPXML::LocationLivingSpace],
1595
- @schedules_file, @hpxml.header.unavailable_periods)
1770
+ ceiling_fan = @hpxml_bldg.ceiling_fans[0]
1771
+ HVAC.apply_ceiling_fans(model, runner, weather, ceiling_fan, spaces[HPXML::LocationConditionedSpace],
1772
+ @schedules_file, @hpxml_header.unavailable_periods)
1596
1773
  end
1597
1774
 
1598
- def self.add_dehumidifiers(runner, model, spaces)
1599
- return if @hpxml.dehumidifiers.size == 0
1775
+ def add_dehumidifiers(runner, model, spaces)
1776
+ return if @hpxml_bldg.dehumidifiers.size == 0
1600
1777
 
1601
- HVAC.apply_dehumidifiers(runner, model, @hpxml.dehumidifiers, spaces[HPXML::LocationLivingSpace], @hpxml.header.unavailable_periods)
1778
+ HVAC.apply_dehumidifiers(runner, model, @hpxml_bldg.dehumidifiers, spaces[HPXML::LocationConditionedSpace], @hpxml_header.unavailable_periods,
1779
+ @hpxml_bldg.building_construction.number_of_units)
1602
1780
  end
1603
1781
 
1604
- def self.check_distribution_system(hvac_distribution, system_type)
1782
+ def check_distribution_system(hvac_distribution, system_type)
1605
1783
  return if hvac_distribution.nil?
1606
1784
 
1607
1785
  hvac_distribution_type_map = { HPXML::HVACTypeFurnace => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
@@ -1619,9 +1797,9 @@ class OSModel
1619
1797
  end
1620
1798
  end
1621
1799
 
1622
- def self.add_mels(runner, model, spaces)
1800
+ def add_mels(runner, model, spaces)
1623
1801
  # Misc
1624
- @hpxml.plug_loads.each do |plug_load|
1802
+ @hpxml_bldg.plug_loads.each do |plug_load|
1625
1803
  if plug_load.plug_load_type == HPXML::PlugLoadTypeOther
1626
1804
  obj_name = Constants.ObjectNameMiscPlugLoads
1627
1805
  elsif plug_load.plug_load_type == HPXML::PlugLoadTypeTelevision
@@ -1636,14 +1814,14 @@ class OSModel
1636
1814
  next
1637
1815
  end
1638
1816
 
1639
- MiscLoads.apply_plug(model, runner, plug_load, obj_name, spaces[HPXML::LocationLivingSpace], @apply_ashrae140_assumptions,
1640
- @schedules_file, @hpxml.header.unavailable_periods)
1817
+ MiscLoads.apply_plug(model, runner, plug_load, obj_name, spaces[HPXML::LocationConditionedSpace], @apply_ashrae140_assumptions,
1818
+ @schedules_file, @hpxml_header.unavailable_periods)
1641
1819
  end
1642
1820
  end
1643
1821
 
1644
- def self.add_mfls(runner, model, spaces)
1822
+ def add_mfls(runner, model, spaces)
1645
1823
  # Misc
1646
- @hpxml.fuel_loads.each do |fuel_load|
1824
+ @hpxml_bldg.fuel_loads.each do |fuel_load|
1647
1825
  if fuel_load.fuel_load_type == HPXML::FuelLoadTypeGrill
1648
1826
  obj_name = Constants.ObjectNameMiscGrill
1649
1827
  elsif fuel_load.fuel_load_type == HPXML::FuelLoadTypeLighting
@@ -1656,44 +1834,33 @@ class OSModel
1656
1834
  next
1657
1835
  end
1658
1836
 
1659
- MiscLoads.apply_fuel(model, runner, fuel_load, obj_name, spaces[HPXML::LocationLivingSpace],
1660
- @schedules_file, @hpxml.header.unavailable_periods)
1837
+ MiscLoads.apply_fuel(model, runner, fuel_load, obj_name, spaces[HPXML::LocationConditionedSpace],
1838
+ @schedules_file, @hpxml_header.unavailable_periods)
1661
1839
  end
1662
1840
  end
1663
1841
 
1664
- def self.add_lighting(runner, model, epw_file, spaces)
1665
- Lighting.apply(runner, model, epw_file, spaces, @hpxml.lighting_groups, @hpxml.lighting, @eri_version,
1666
- @schedules_file, @cfa, @hpxml.header.unavailable_periods)
1842
+ def add_lighting(runner, model, epw_file, spaces)
1843
+ Lighting.apply(runner, model, epw_file, spaces, @hpxml_bldg.lighting_groups, @hpxml_bldg.lighting, @eri_version,
1844
+ @schedules_file, @cfa, @hpxml_header.unavailable_periods, @hpxml_bldg.building_construction.number_of_units)
1667
1845
  end
1668
1846
 
1669
- def self.add_pools_and_hot_tubs(runner, model, spaces)
1670
- @hpxml.pools.each do |pool|
1671
- next if pool.type == HPXML::TypeNone
1847
+ def add_pools_and_permanent_spas(runner, model, spaces)
1848
+ (@hpxml_bldg.pools + @hpxml_bldg.permanent_spas).each do |pool_or_spa|
1849
+ next if pool_or_spa.type == HPXML::TypeNone
1672
1850
 
1673
- MiscLoads.apply_pool_or_hot_tub_heater(runner, model, pool, Constants.ObjectNameMiscPoolHeater, spaces[HPXML::LocationLivingSpace],
1674
- @schedules_file, @hpxml.header.unavailable_periods)
1675
- next if pool.pump_type == HPXML::TypeNone
1851
+ MiscLoads.apply_pool_or_permanent_spa_heater(runner, model, pool_or_spa, spaces[HPXML::LocationConditionedSpace],
1852
+ @schedules_file, @hpxml_header.unavailable_periods)
1853
+ next if pool_or_spa.pump_type == HPXML::TypeNone
1676
1854
 
1677
- MiscLoads.apply_pool_or_hot_tub_pump(runner, model, pool, Constants.ObjectNameMiscPoolPump, spaces[HPXML::LocationLivingSpace],
1678
- @schedules_file, @hpxml.header.unavailable_periods)
1679
- end
1680
-
1681
- @hpxml.hot_tubs.each do |hot_tub|
1682
- next if hot_tub.type == HPXML::TypeNone
1683
-
1684
- MiscLoads.apply_pool_or_hot_tub_heater(runner, model, hot_tub, Constants.ObjectNameMiscHotTubHeater, spaces[HPXML::LocationLivingSpace],
1685
- @schedules_file, @hpxml.header.unavailable_periods)
1686
- next if hot_tub.pump_type == HPXML::TypeNone
1687
-
1688
- MiscLoads.apply_pool_or_hot_tub_pump(runner, model, hot_tub, Constants.ObjectNameMiscHotTubPump, spaces[HPXML::LocationLivingSpace],
1689
- @schedules_file, @hpxml.header.unavailable_periods)
1855
+ MiscLoads.apply_pool_or_permanent_spa_pump(runner, model, pool_or_spa, spaces[HPXML::LocationConditionedSpace],
1856
+ @schedules_file, @hpxml_header.unavailable_periods)
1690
1857
  end
1691
1858
  end
1692
1859
 
1693
- def self.add_airflow(runner, model, weather, spaces, airloop_map)
1860
+ def add_airflow(runner, model, weather, spaces, airloop_map)
1694
1861
  # Ducts
1695
1862
  duct_systems = {}
1696
- @hpxml.hvac_distributions.each do |hvac_distribution|
1863
+ @hpxml_bldg.hvac_distributions.each do |hvac_distribution|
1697
1864
  next unless hvac_distribution.distribution_system_type == HPXML::HVACDistributionTypeAir
1698
1865
 
1699
1866
  air_ducts = create_ducts(model, hvac_distribution, spaces)
@@ -1721,24 +1888,50 @@ class OSModel
1721
1888
  end
1722
1889
  end
1723
1890
 
1891
+ # Duct leakage to outside warnings?
1892
+ # Need to check here instead of in schematron in case duct locations are defaulted
1893
+ @hpxml_bldg.hvac_distributions.each do |hvac_distribution|
1894
+ next unless hvac_distribution.distribution_system_type == HPXML::HVACDistributionTypeAir
1895
+ next if hvac_distribution.duct_leakage_measurements.empty?
1896
+
1897
+ # Skip if there's a duct outside conditioned space
1898
+ next if hvac_distribution.ducts.select { |d| !HPXML::conditioned_locations_this_unit.include?(d.duct_location) }.size > 0
1899
+
1900
+ # Issue warning if duct leakage to outside above a certain threshold and ducts completely in conditioned space
1901
+ issue_warning = false
1902
+ units = hvac_distribution.duct_leakage_measurements[0].duct_leakage_units
1903
+ lto_measurements = hvac_distribution.duct_leakage_measurements.select { |dlm| dlm.duct_leakage_total_or_to_outside == HPXML::DuctLeakageToOutside }
1904
+ sum_lto = lto_measurements.map { |dlm| dlm.duct_leakage_value }.sum(0.0)
1905
+ if units == HPXML::UnitsCFM25
1906
+ issue_warning = true if sum_lto > 0.04 * @cfa
1907
+ elsif units == HPXML::UnitsCFM50
1908
+ issue_warning = true if sum_lto > 0.06 * @cfa
1909
+ elsif units == HPXML::UnitsPercent
1910
+ issue_warning = true if sum_lto > 0.05
1911
+ end
1912
+ next unless issue_warning
1913
+
1914
+ runner.registerWarning('Ducts are entirely within conditioned space but there is moderate leakage to the outside. Leakage to the outside is typically zero or near-zero in these situations, consider revising leakage values. Leakage will be modeled as heat lost to the ambient environment.')
1915
+ end
1916
+
1724
1917
  # Create HVAC availability sensor
1725
- @hvac_availability_sensor = nil
1918
+ hvac_availability_sensor = nil
1726
1919
  if not @hvac_unavailable_periods.empty?
1727
1920
  avail_sch = ScheduleConstant.new(model, SchedulesFile::ColumnHVAC, 1.0, Constants.ScheduleTypeLimitsFraction, unavailable_periods: @hvac_unavailable_periods)
1728
- avail_sch = avail_sch.schedule
1729
1921
 
1730
- @hvac_availability_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
1731
- @hvac_availability_sensor.setName('availability s')
1732
- @hvac_availability_sensor.setKeyName(avail_sch.name.to_s)
1922
+ hvac_availability_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
1923
+ hvac_availability_sensor.setName('hvac availability s')
1924
+ hvac_availability_sensor.setKeyName(avail_sch.schedule.name.to_s)
1925
+ hvac_availability_sensor.additionalProperties.setFeature('ObjectType', Constants.ObjectNameHVACAvailabilitySensor)
1733
1926
  end
1734
1927
 
1735
- Airflow.apply(model, runner, weather, spaces, @hpxml, @cfa, @nbeds,
1928
+ Airflow.apply(model, runner, weather, spaces, @hpxml_header, @hpxml_bldg, @cfa, @nbeds,
1736
1929
  @ncfl_ag, duct_systems, airloop_map, @clg_ssn_sensor, @eri_version,
1737
1930
  @frac_windows_operable, @apply_ashrae140_assumptions, @schedules_file,
1738
- @hpxml.header.unavailable_periods, @hvac_availability_sensor)
1931
+ @hpxml_header.unavailable_periods, hvac_availability_sensor)
1739
1932
  end
1740
1933
 
1741
- def self.create_ducts(model, hvac_distribution, spaces)
1934
+ def create_ducts(model, hvac_distribution, spaces)
1742
1935
  air_ducts = []
1743
1936
 
1744
1937
  # Duct leakage (supply/return => [value, units])
@@ -1817,96 +2010,131 @@ class OSModel
1817
2010
  return air_ducts
1818
2011
  end
1819
2012
 
1820
- def self.add_photovoltaics(model)
1821
- @hpxml.pv_systems.each do |pv_system|
1822
- next if pv_system.inverter.inverter_efficiency == @hpxml.pv_systems[0].inverter.inverter_efficiency
2013
+ def add_photovoltaics(model)
2014
+ @hpxml_bldg.pv_systems.each do |pv_system|
2015
+ next if pv_system.inverter.inverter_efficiency == @hpxml_bldg.pv_systems[0].inverter.inverter_efficiency
1823
2016
 
1824
2017
  fail 'Expected all InverterEfficiency values to be equal.'
1825
2018
  end
1826
- @hpxml.pv_systems.each do |pv_system|
1827
- PV.apply(model, @nbeds, pv_system)
2019
+ @hpxml_bldg.pv_systems.each do |pv_system|
2020
+ PV.apply(model, @nbeds, pv_system, @hpxml_bldg.building_construction.number_of_units)
1828
2021
  end
1829
2022
  end
1830
2023
 
1831
- def self.add_generators(model)
1832
- @hpxml.generators.each do |generator|
1833
- Generator.apply(model, @nbeds, generator)
2024
+ def add_generators(model)
2025
+ @hpxml_bldg.generators.each do |generator|
2026
+ Generator.apply(model, @nbeds, generator, @hpxml_bldg.building_construction.number_of_units)
1834
2027
  end
1835
2028
  end
1836
2029
 
1837
- def self.add_batteries(runner, model, spaces)
1838
- @hpxml.batteries.each do |battery|
2030
+ def add_batteries(runner, model, spaces)
2031
+ @hpxml_bldg.batteries.each do |battery|
1839
2032
  # Assign space
1840
2033
  battery.additional_properties.space = get_space_from_location(battery.location, spaces)
1841
- Battery.apply(runner, model, @hpxml.pv_systems, battery, @schedules_file)
2034
+ Battery.apply(runner, model, @hpxml_bldg.pv_systems, battery, @schedules_file, @hpxml_bldg.building_construction.number_of_units)
1842
2035
  end
1843
2036
  end
1844
2037
 
1845
- def self.add_additional_properties(model, hpxml_path, building_id, epw_file)
2038
+ def add_building_unit(model, unit_num)
2039
+ return if unit_num.nil?
2040
+
2041
+ unit = OpenStudio::Model::BuildingUnit.new(model)
2042
+ unit.additionalProperties.setFeature('unit_num', unit_num)
2043
+ model.getSpaces.each do |s|
2044
+ s.setBuildingUnit(unit)
2045
+ end
2046
+ end
2047
+
2048
+ def add_additional_properties(model, hpxml, hpxml_osm_map, hpxml_path, building_id, epw_file, hpxml_defaults_path)
1846
2049
  # Store some data for use in reporting measure
1847
2050
  additionalProperties = model.getBuilding.additionalProperties
1848
2051
  additionalProperties.setFeature('hpxml_path', hpxml_path)
1849
- additionalProperties.setFeature('hpxml_defaults_path', @hpxml_defaults_path)
2052
+ additionalProperties.setFeature('hpxml_defaults_path', hpxml_defaults_path)
1850
2053
  additionalProperties.setFeature('building_id', building_id.to_s)
1851
- emissions_scenario_names = @hpxml.header.emissions_scenarios.map { |s| s.name }.to_s
1852
- additionalProperties.setFeature('emissions_scenario_names', emissions_scenario_names)
1853
- emissions_scenario_types = @hpxml.header.emissions_scenarios.map { |s| s.emissions_type }.to_s
1854
- additionalProperties.setFeature('emissions_scenario_types', emissions_scenario_types)
1855
- additionalProperties.setFeature('has_heating', @hpxml.total_fraction_heat_load_served > 0)
1856
- additionalProperties.setFeature('has_cooling', @hpxml.total_fraction_cool_load_served > 0)
2054
+ additionalProperties.setFeature('emissions_scenario_names', hpxml.header.emissions_scenarios.map { |s| s.name }.to_s)
2055
+ additionalProperties.setFeature('emissions_scenario_types', hpxml.header.emissions_scenarios.map { |s| s.emissions_type }.to_s)
2056
+ heated_zones, cooled_zones = [], []
2057
+ hpxml_osm_map.each do |hpxml_bldg, unit_model|
2058
+ conditioned_zone_name = unit_model.getThermalZones.find { |z| z.additionalProperties.getFeatureAsString('ObjectType').to_s == HPXML::LocationConditionedSpace }.name.to_s
2059
+
2060
+ heated_zones << conditioned_zone_name if hpxml_bldg.total_fraction_heat_load_served > 0
2061
+ cooled_zones << conditioned_zone_name if hpxml_bldg.total_fraction_cool_load_served > 0
2062
+ end
2063
+ additionalProperties.setFeature('heated_zones', heated_zones.to_s)
2064
+ additionalProperties.setFeature('cooled_zones', cooled_zones.to_s)
1857
2065
  additionalProperties.setFeature('is_southern_hemisphere', epw_file.latitude < 0)
1858
2066
  end
1859
2067
 
1860
- def self.add_unmet_hours_output(model, spaces)
2068
+ def add_unmet_hours_output(model, hpxml_osm_map)
1861
2069
  # We do our own unmet hours calculation via EMS so that we can incorporate,
1862
- # e.g., heating/cooling seasons into the logic.
1863
- hvac_control = @hpxml.hvac_controls[0]
1864
- if not hvac_control.nil?
1865
- sim_year = @hpxml.header.sim_calendar_year
1866
- htg_start_day = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_heating_begin_month, hvac_control.seasons_heating_begin_day)
1867
- htg_end_day = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_heating_end_month, hvac_control.seasons_heating_end_day)
1868
- clg_start_day = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_cooling_begin_month, hvac_control.seasons_cooling_begin_day)
1869
- clg_end_day = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_cooling_end_month, hvac_control.seasons_cooling_end_day)
1870
- end
2070
+ # e.g., heating/cooling seasons into the logic. The calculation layers on top
2071
+ # of the built-in EnergyPlus unmet hours output.
2072
+
2073
+ # Create sensors and gather data
2074
+ htg_sensors, clg_sensors = {}, {}
2075
+ total_heat_load_serveds, total_cool_load_serveds = {}, {}
2076
+ htg_start_days, htg_end_days, clg_start_days, clg_end_days = {}, {}, {}, {}
2077
+ hpxml_osm_map.each_with_index do |(hpxml_bldg, unit_model), unit|
2078
+ conditioned_zone_name = unit_model.getThermalZones.find { |z| z.additionalProperties.getFeatureAsString('ObjectType').to_s == HPXML::LocationConditionedSpace }.name.to_s
2079
+
2080
+ # EMS sensors
2081
+ htg_sensors[unit] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Heating Setpoint Not Met Time')
2082
+ htg_sensors[unit].setName('zone htg unmet s')
2083
+ htg_sensors[unit].setKeyName(conditioned_zone_name)
1871
2084
 
1872
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
2085
+ clg_sensors[unit] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Cooling Setpoint Not Met Time')
2086
+ clg_sensors[unit].setName('zone clg unmet s')
2087
+ clg_sensors[unit].setKeyName(conditioned_zone_name)
1873
2088
 
1874
- # EMS sensors
1875
- htg_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Heating Setpoint Not Met Time')
1876
- htg_sensor.setName('zone htg unmet s')
1877
- htg_sensor.setKeyName(living_zone.name.to_s)
2089
+ total_heat_load_serveds[unit] = hpxml_bldg.total_fraction_heat_load_served
2090
+ total_cool_load_serveds[unit] = hpxml_bldg.total_fraction_cool_load_served
1878
2091
 
1879
- clg_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Cooling Setpoint Not Met Time')
1880
- clg_sensor.setName('zone clg unmet s')
1881
- clg_sensor.setKeyName(living_zone.name.to_s)
2092
+ hvac_control = hpxml_bldg.hvac_controls[0]
2093
+ next unless not hvac_control.nil?
2094
+
2095
+ sim_year = @hpxml_header.sim_calendar_year
2096
+ htg_start_days[unit] = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_heating_begin_month, hvac_control.seasons_heating_begin_day)
2097
+ htg_end_days[unit] = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_heating_end_month, hvac_control.seasons_heating_end_day)
2098
+ clg_start_days[unit] = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_cooling_begin_month, hvac_control.seasons_cooling_begin_day)
2099
+ clg_end_days[unit] = Schedule.get_day_num_from_month_day(sim_year, hvac_control.seasons_cooling_end_month, hvac_control.seasons_cooling_end_day)
2100
+ end
2101
+
2102
+ hvac_availability_sensor = model.getEnergyManagementSystemSensors.find { |s| s.additionalProperties.getFeatureAsString('ObjectType').to_s == Constants.ObjectNameHVACAvailabilitySensor }
1882
2103
 
1883
2104
  # EMS program
1884
2105
  clg_hrs = 'clg_unmet_hours'
1885
2106
  htg_hrs = 'htg_unmet_hours'
1886
2107
  program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
1887
- program.setName(Constants.ObjectNameUnmetHoursProgram)
2108
+ program.setName('unmet hours program')
2109
+ program.additionalProperties.setFeature('ObjectType', Constants.ObjectNameUnmetHoursProgram)
1888
2110
  program.addLine("Set #{htg_hrs} = 0")
1889
2111
  program.addLine("Set #{clg_hrs} = 0")
1890
- if @hpxml.total_fraction_heat_load_served > 0
1891
- if htg_end_day >= htg_start_day
1892
- line = "If ((DayOfYear >= #{htg_start_day}) && (DayOfYear <= #{htg_end_day}))"
1893
- else
1894
- line = "If ((DayOfYear >= #{htg_start_day}) || (DayOfYear <= #{htg_end_day}))"
2112
+ for unit in 0..hpxml_osm_map.size - 1
2113
+ if total_heat_load_serveds[unit] > 0
2114
+ if htg_end_days[unit] >= htg_start_days[unit]
2115
+ line = "If ((DayOfYear >= #{htg_start_days[unit]}) && (DayOfYear <= #{htg_end_days[unit]}))"
2116
+ else
2117
+ line = "If ((DayOfYear >= #{htg_start_days[unit]}) || (DayOfYear <= #{htg_end_days[unit]}))"
2118
+ end
2119
+ line += " && (#{hvac_availability_sensor.name} == 1)" if not hvac_availability_sensor.nil?
2120
+ program.addLine(line)
2121
+ program.addLine(" If #{htg_sensors[unit].name} > #{htg_hrs}") # Use max hourly value across all units
2122
+ program.addLine(" Set #{htg_hrs} = #{htg_sensors[unit].name}")
2123
+ program.addLine(' EndIf')
2124
+ program.addLine('EndIf')
1895
2125
  end
1896
- line += " && (#{@hvac_availability_sensor.name} == 1)" if not @hvac_availability_sensor.nil?
1897
- program.addLine(line)
1898
- program.addLine(" Set #{htg_hrs} = #{htg_hrs} + #{htg_sensor.name}")
1899
- program.addLine('EndIf')
1900
- end
1901
- if @hpxml.total_fraction_cool_load_served > 0
1902
- if clg_end_day >= clg_start_day
1903
- line = "If ((DayOfYear >= #{clg_start_day}) && (DayOfYear <= #{clg_end_day}))"
2126
+ next unless total_cool_load_serveds[unit] > 0
2127
+
2128
+ if clg_end_days[unit] >= clg_start_days[unit]
2129
+ line = "If ((DayOfYear >= #{clg_start_days[unit]}) && (DayOfYear <= #{clg_end_days[unit]}))"
1904
2130
  else
1905
- line = "If ((DayOfYear >= #{clg_start_day}) || (DayOfYear <= #{clg_end_day}))"
2131
+ line = "If ((DayOfYear >= #{clg_start_days[unit]}) || (DayOfYear <= #{clg_end_days[unit]}))"
1906
2132
  end
1907
- line += " && (#{@hvac_availability_sensor.name} == 1)" if not @hvac_availability_sensor.nil?
2133
+ line += " && (#{hvac_availability_sensor.name} == 1)" if not hvac_availability_sensor.nil?
1908
2134
  program.addLine(line)
1909
- program.addLine(" Set #{clg_hrs} = #{clg_hrs} + #{clg_sensor.name}")
2135
+ program.addLine(" If #{clg_sensors[unit].name} > #{clg_hrs}") # Use max hourly value across all units
2136
+ program.addLine(" Set #{clg_hrs} = #{clg_sensors[unit].name}")
2137
+ program.addLine(' EndIf')
1910
2138
  program.addLine('EndIf')
1911
2139
  end
1912
2140
 
@@ -1917,68 +2145,88 @@ class OSModel
1917
2145
  program_calling_manager.addProgram(program)
1918
2146
  end
1919
2147
 
1920
- def self.add_loads_output(model, spaces, add_component_loads)
1921
- living_zone = spaces[HPXML::LocationLivingSpace].thermalZone.get
1922
-
1923
- if @apply_ashrae140_assumptions
1924
- total_heat_load_served = 1.0
1925
- total_cool_load_served = 1.0
1926
- else
1927
- total_heat_load_served = @hpxml.total_fraction_heat_load_served
1928
- total_cool_load_served = @hpxml.total_fraction_cool_load_served
1929
- end
1930
-
1931
- liv_load_sensors, intgain_dehumidifier = add_total_loads_output(model, living_zone, total_heat_load_served, total_cool_load_served)
2148
+ def add_loads_output(model, add_component_loads, hpxml_osm_map)
2149
+ loads_data = add_total_loads_output(model, hpxml_osm_map)
1932
2150
  return unless add_component_loads
1933
2151
 
1934
- add_component_loads_output(model, living_zone, liv_load_sensors, intgain_dehumidifier, total_heat_load_served, total_cool_load_served)
2152
+ add_component_loads_output(model, hpxml_osm_map, loads_data)
1935
2153
  end
1936
2154
 
1937
- def self.add_total_loads_output(model, living_zone, total_heat_load_served, total_cool_load_served)
1938
- # Energy transferred in the conditioned space, used for determining heating (winter) vs cooling (summer)
1939
- liv_load_sensors = {}
1940
- liv_load_sensors[:htg] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, "Heating:EnergyTransfer:Zone:#{living_zone.name.to_s.upcase}")
1941
- liv_load_sensors[:htg].setName('htg_load_liv')
1942
- liv_load_sensors[:clg] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, "Cooling:EnergyTransfer:Zone:#{living_zone.name.to_s.upcase}")
1943
- liv_load_sensors[:clg].setName('clg_load_liv')
1944
-
1945
- # Total energy transferred (above plus ducts)
1946
- tot_load_sensors = {}
1947
- tot_load_sensors[:htg] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Heating:EnergyTransfer')
1948
- tot_load_sensors[:htg].setName('htg_load_tot')
1949
- tot_load_sensors[:clg] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Cooling:EnergyTransfer')
1950
- tot_load_sensors[:clg].setName('clg_load_tot')
2155
+ def add_total_loads_output(model, hpxml_osm_map)
2156
+ # Create sensors and gather data
2157
+ htg_cond_load_sensors, clg_cond_load_sensors = {}, {}
2158
+ htg_duct_load_sensors, clg_duct_load_sensors = {}, {}
2159
+ total_heat_load_serveds, total_cool_load_serveds = {}, {}
2160
+ dehumidifier_sensors = {}
2161
+
2162
+ hpxml_osm_map.each_with_index do |(hpxml_bldg, unit_model), unit|
2163
+ # Retrieve objects
2164
+ conditioned_zone_name = unit_model.getThermalZones.find { |z| z.additionalProperties.getFeatureAsString('ObjectType').to_s == HPXML::LocationConditionedSpace }.name.to_s
2165
+ duct_zone_names = unit_model.getThermalZones.select { |z| z.isPlenum }.map { |z| z.name.to_s }
2166
+ dehumidifier = unit_model.getZoneHVACDehumidifierDXs
2167
+ dehumidifier_name = dehumidifier[0].name.to_s unless dehumidifier.empty?
2168
+
2169
+ # Fraction heat/cool load served
2170
+ if @hpxml_header.apply_ashrae140_assumptions
2171
+ total_heat_load_serveds[unit] = 1.0
2172
+ total_cool_load_serveds[unit] = 1.0
2173
+ else
2174
+ total_heat_load_serveds[unit] = hpxml_bldg.total_fraction_heat_load_served
2175
+ total_cool_load_serveds[unit] = hpxml_bldg.total_fraction_cool_load_served
2176
+ end
1951
2177
 
1952
- # Need to adjusted E+ EnergyTransfer meters for dehumidifiers
1953
- intgain_dehumidifier = nil
1954
- model.getZoneHVACDehumidifierDXs.each do |e|
1955
- next unless e.thermalZone.get.name.to_s == living_zone.name.to_s
2178
+ # Energy transferred in conditioned zone, used for determining heating (winter) vs cooling (summer)
2179
+ htg_cond_load_sensors[unit] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, "Heating:EnergyTransfer:Zone:#{conditioned_zone_name.upcase}")
2180
+ htg_cond_load_sensors[unit].setName('htg_load_cond')
2181
+ clg_cond_load_sensors[unit] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, "Cooling:EnergyTransfer:Zone:#{conditioned_zone_name.upcase}")
2182
+ clg_cond_load_sensors[unit].setName('clg_load_cond')
1956
2183
 
1957
- { 'Zone Dehumidifier Sensible Heating Energy' => 'ig_dehumidifier' }.each do |var, name|
1958
- intgain_dehumidifier = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
1959
- intgain_dehumidifier.setName(name)
1960
- intgain_dehumidifier.setKeyName(e.name.to_s)
2184
+ # Energy transferred in duct zone(s)
2185
+ htg_duct_load_sensors[unit] = []
2186
+ clg_duct_load_sensors[unit] = []
2187
+ duct_zone_names.each do |duct_zone_name|
2188
+ htg_duct_load_sensors[unit] << OpenStudio::Model::EnergyManagementSystemSensor.new(model, "Heating:EnergyTransfer:Zone:#{duct_zone_name.upcase}")
2189
+ htg_duct_load_sensors[unit][-1].setName('htg_load_duct')
2190
+ clg_duct_load_sensors[unit] << OpenStudio::Model::EnergyManagementSystemSensor.new(model, "Cooling:EnergyTransfer:Zone:#{duct_zone_name.upcase}")
2191
+ clg_duct_load_sensors[unit][-1].setName('clg_load_duct')
1961
2192
  end
2193
+
2194
+ # Need to adjusted E+ EnergyTransfer meters for dehumidifier internal gains
2195
+ next if dehumidifier_name.nil?
2196
+
2197
+ dehumidifier_sensors[unit] = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Dehumidifier Sensible Heating Energy')
2198
+ dehumidifier_sensors[unit].setName('ig_dehumidifier')
2199
+ dehumidifier_sensors[unit].setKeyName(dehumidifier_name)
1962
2200
  end
1963
2201
 
1964
2202
  # EMS program
1965
2203
  program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
1966
- program.setName(Constants.ObjectNameTotalLoadsProgram)
2204
+ program.setName('total loads program')
2205
+ program.additionalProperties.setFeature('ObjectType', Constants.ObjectNameTotalLoadsProgram)
1967
2206
  program.addLine('Set loads_htg_tot = 0')
1968
2207
  program.addLine('Set loads_clg_tot = 0')
1969
- program.addLine("If #{liv_load_sensors[:htg].name} > 0")
1970
- s = " Set loads_htg_tot = (#{tot_load_sensors[:htg].name} - #{tot_load_sensors[:clg].name}) * #{total_heat_load_served}"
1971
- if not intgain_dehumidifier.nil?
1972
- s += " - #{intgain_dehumidifier.name}"
2208
+ for unit in 0..hpxml_osm_map.size - 1
2209
+ program.addLine("If #{htg_cond_load_sensors[unit].name} > 0")
2210
+ program.addLine(" Set loads_htg_tot = loads_htg_tot + (#{htg_cond_load_sensors[unit].name} - #{clg_cond_load_sensors[unit].name}) * #{total_heat_load_serveds[unit]}")
2211
+ for i in 0..htg_duct_load_sensors[unit].size - 1
2212
+ program.addLine(" Set loads_htg_tot = loads_htg_tot + (#{htg_duct_load_sensors[unit][i].name} - #{clg_duct_load_sensors[unit][i].name}) * #{total_heat_load_serveds[unit]}")
2213
+ end
2214
+ if not dehumidifier_sensors[unit].nil?
2215
+ program.addLine(" Set loads_htg_tot = loads_htg_tot - #{dehumidifier_sensors[unit].name}")
2216
+ end
2217
+ program.addLine('EndIf')
1973
2218
  end
1974
- program.addLine(s)
1975
- program.addLine("ElseIf #{liv_load_sensors[:clg].name} > 0")
1976
- s = " Set loads_clg_tot = (#{tot_load_sensors[:clg].name} - #{tot_load_sensors[:htg].name}) * #{total_cool_load_served}"
1977
- if not intgain_dehumidifier.nil?
1978
- s += " + #{intgain_dehumidifier.name}"
2219
+ for unit in 0..hpxml_osm_map.size - 1
2220
+ program.addLine("If #{clg_cond_load_sensors[unit].name} > 0")
2221
+ program.addLine(" Set loads_clg_tot = loads_clg_tot + (#{clg_cond_load_sensors[unit].name} - #{htg_cond_load_sensors[unit].name}) * #{total_cool_load_serveds[unit]}")
2222
+ for i in 0..clg_duct_load_sensors[unit].size - 1
2223
+ program.addLine(" Set loads_clg_tot = loads_clg_tot + (#{clg_duct_load_sensors[unit][i].name} - #{htg_duct_load_sensors[unit][i].name}) * #{total_cool_load_serveds[unit]}")
2224
+ end
2225
+ if not dehumidifier_sensors[unit].nil?
2226
+ program.addLine(" Set loads_clg_tot = loads_clg_tot + #{dehumidifier_sensors[unit].name}")
2227
+ end
2228
+ program.addLine('EndIf')
1979
2229
  end
1980
- program.addLine(s)
1981
- program.addLine('EndIf')
1982
2230
 
1983
2231
  # EMS calling manager
1984
2232
  program_calling_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
@@ -1986,27 +2234,11 @@ class OSModel
1986
2234
  program_calling_manager.setCallingPoint('EndOfZoneTimestepAfterZoneReporting')
1987
2235
  program_calling_manager.addProgram(program)
1988
2236
 
1989
- return liv_load_sensors, intgain_dehumidifier
2237
+ return htg_cond_load_sensors, clg_cond_load_sensors, total_heat_load_serveds, total_cool_load_serveds, dehumidifier_sensors
1990
2238
  end
1991
2239
 
1992
- def self.add_component_loads_output(model, living_zone, liv_load_sensors, intgain_dehumidifier, total_heat_load_served, total_cool_load_served)
1993
- # Prevent certain objects (e.g., OtherEquipment) from being counted towards both, e.g., ducts and internal gains
1994
- objects_already_processed = []
1995
-
1996
- # EMS Sensors: Surfaces, SubSurfaces, InternalMass
1997
- surfaces_sensors = { walls: [],
1998
- rim_joists: [],
1999
- foundation_walls: [],
2000
- floors: [],
2001
- slabs: [],
2002
- ceilings: [],
2003
- roofs: [],
2004
- windows_conduction: [],
2005
- windows_solar: [],
2006
- doors: [],
2007
- skylights_conduction: [],
2008
- skylights_solar: [],
2009
- internal_mass: [] }
2240
+ def add_component_loads_output(model, hpxml_osm_map, loads_data)
2241
+ htg_cond_load_sensors, clg_cond_load_sensors, total_heat_load_serveds, total_cool_load_serveds, dehumidifier_sensors = loads_data
2010
2242
 
2011
2243
  # Output diagnostics needed for some output variables used below
2012
2244
  output_diagnostics = model.getOutputDiagnostics
@@ -2014,397 +2246,376 @@ class OSModel
2014
2246
 
2015
2247
  area_tolerance = UnitConversions.convert(1.0, 'ft^2', 'm^2')
2016
2248
 
2017
- model.getSurfaces.sort.each do |s|
2018
- next unless s.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2249
+ nonsurf_names = ['intgains', 'lighting', 'infil', 'mechvent', 'natvent', 'whf', 'ducts']
2250
+ surf_names = ['walls', 'rim_joists', 'foundation_walls', 'floors', 'slabs', 'ceilings',
2251
+ 'roofs', 'windows_conduction', 'windows_solar', 'doors', 'skylights_conduction',
2252
+ 'skylights_solar', 'internal_mass']
2253
+
2254
+ # EMS program
2255
+ program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
2256
+ program.setName('component loads program')
2257
+ program.additionalProperties.setFeature('ObjectType', Constants.ObjectNameComponentLoadsProgram)
2019
2258
 
2020
- surface_type = s.additionalProperties.getFeatureAsString('SurfaceType')
2021
- if not surface_type.is_initialized
2022
- fail "Could not identify surface type for surface: '#{s.name}'."
2259
+ # Initialize
2260
+ [:htg, :clg].each do |mode|
2261
+ surf_names.each do |surf_name|
2262
+ program.addLine("Set loads_#{mode}_#{surf_name} = 0")
2263
+ end
2264
+ nonsurf_names.each do |nonsurf_name|
2265
+ program.addLine("Set loads_#{mode}_#{nonsurf_name} = 0")
2023
2266
  end
2267
+ end
2024
2268
 
2025
- surface_type = surface_type.get
2269
+ hpxml_osm_map.values.each_with_index do |unit_model, unit|
2270
+ conditioned_zone = unit_model.getThermalZones.find { |z| z.additionalProperties.getFeatureAsString('ObjectType').to_s == HPXML::LocationConditionedSpace }
2026
2271
 
2027
- s.subSurfaces.each do |ss|
2028
- # Conduction (windows, skylights, doors)
2029
- key = { 'Window' => :windows_conduction,
2030
- 'Door' => :doors,
2031
- 'Skylight' => :skylights_conduction }[surface_type]
2032
- fail "Unexpected subsurface for component loads: '#{ss.name}'." if key.nil?
2272
+ # Prevent certain objects (e.g., OtherEquipment) from being counted towards both, e.g., ducts and internal gains
2273
+ objects_already_processed = []
2033
2274
 
2034
- if (surface_type == 'Window') || (surface_type == 'Skylight')
2035
- vars = { 'Surface Inside Face Convection Heat Gain Energy' => 'ss_conv',
2036
- 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 'ss_ig',
2037
- 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 'ss_surf' }
2038
- else
2039
- vars = { 'Surface Inside Face Solar Radiation Heat Gain Energy' => 'ss_sol',
2040
- 'Surface Inside Face Lights Radiation Heat Gain Energy' => 'ss_lgt',
2041
- 'Surface Inside Face Convection Heat Gain Energy' => 'ss_conv',
2042
- 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 'ss_ig',
2043
- 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 'ss_surf' }
2275
+ # EMS Sensors: Surfaces, SubSurfaces, InternalMass
2276
+ surfaces_sensors = {}
2277
+ surf_names.each do |surf_name|
2278
+ surfaces_sensors[surf_name.to_sym] = []
2279
+ end
2280
+
2281
+ unit_model.getSurfaces.sort.each do |s|
2282
+ next unless s.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2283
+
2284
+ surface_type = s.additionalProperties.getFeatureAsString('SurfaceType')
2285
+ if not surface_type.is_initialized
2286
+ fail "Could not identify surface type for surface: '#{s.name}'."
2044
2287
  end
2045
2288
 
2046
- vars.each do |var, name|
2289
+ surface_type = surface_type.get
2290
+
2291
+ s.subSurfaces.each do |ss|
2292
+ # Conduction (windows, skylights, doors)
2293
+ key = { 'Window' => :windows_conduction,
2294
+ 'Door' => :doors,
2295
+ 'Skylight' => :skylights_conduction }[surface_type]
2296
+ fail "Unexpected subsurface for component loads: '#{ss.name}'." if key.nil?
2297
+
2298
+ if (surface_type == 'Window') || (surface_type == 'Skylight')
2299
+ vars = { 'Surface Inside Face Convection Heat Gain Energy' => 'ss_conv',
2300
+ 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 'ss_ig',
2301
+ 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 'ss_surf' }
2302
+ else
2303
+ vars = { 'Surface Inside Face Solar Radiation Heat Gain Energy' => 'ss_sol',
2304
+ 'Surface Inside Face Lights Radiation Heat Gain Energy' => 'ss_lgt',
2305
+ 'Surface Inside Face Convection Heat Gain Energy' => 'ss_conv',
2306
+ 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 'ss_ig',
2307
+ 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 'ss_surf' }
2308
+ end
2309
+
2310
+ vars.each do |var, name|
2311
+ surfaces_sensors[key] << []
2312
+ sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2313
+ sensor.setName(name)
2314
+ sensor.setKeyName(ss.name.to_s)
2315
+ surfaces_sensors[key][-1] << sensor
2316
+ end
2317
+
2318
+ # Solar (windows, skylights)
2319
+ next unless (surface_type == 'Window') || (surface_type == 'Skylight')
2320
+
2321
+ key = { 'Window' => :windows_solar,
2322
+ 'Skylight' => :skylights_solar }[surface_type]
2323
+ vars = { 'Surface Window Transmitted Solar Radiation Energy' => 'ss_trans_in',
2324
+ 'Surface Window Shortwave from Zone Back Out Window Heat Transfer Rate' => 'ss_back_out',
2325
+ 'Surface Window Total Glazing Layers Absorbed Shortwave Radiation Rate' => 'ss_sw_abs',
2326
+ 'Surface Window Total Glazing Layers Absorbed Solar Radiation Energy' => 'ss_sol_abs',
2327
+ 'Surface Inside Face Initial Transmitted Diffuse Transmitted Out Window Solar Radiation Rate' => 'ss_trans_out' }
2328
+
2047
2329
  surfaces_sensors[key] << []
2048
- sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2049
- sensor.setName(name)
2050
- sensor.setKeyName(ss.name.to_s)
2051
- surfaces_sensors[key][-1] << sensor
2330
+ vars.each do |var, name|
2331
+ sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2332
+ sensor.setName(name)
2333
+ sensor.setKeyName(ss.name.to_s)
2334
+ surfaces_sensors[key][-1] << sensor
2335
+ end
2052
2336
  end
2053
2337
 
2054
- # Solar (windows, skylights)
2055
- next unless (surface_type == 'Window') || (surface_type == 'Skylight')
2338
+ next if s.netArea < area_tolerance # Skip parent surfaces (of subsurfaces) that have near zero net area
2056
2339
 
2057
- key = { 'Window' => :windows_solar,
2058
- 'Skylight' => :skylights_solar }[surface_type]
2059
- vars = { 'Surface Window Transmitted Solar Radiation Energy' => 'ss_trans_in',
2060
- 'Surface Window Shortwave from Zone Back Out Window Heat Transfer Rate' => 'ss_back_out',
2061
- 'Surface Window Total Glazing Layers Absorbed Shortwave Radiation Rate' => 'ss_sw_abs',
2062
- 'Surface Window Total Glazing Layers Absorbed Solar Radiation Energy' => 'ss_sol_abs',
2063
- 'Surface Inside Face Initial Transmitted Diffuse Transmitted Out Window Solar Radiation Rate' => 'ss_trans_out' }
2340
+ key = { 'FoundationWall' => :foundation_walls,
2341
+ 'RimJoist' => :rim_joists,
2342
+ 'Wall' => :walls,
2343
+ 'Slab' => :slabs,
2344
+ 'Floor' => :floors,
2345
+ 'Ceiling' => :ceilings,
2346
+ 'Roof' => :roofs,
2347
+ 'InferredCeiling' => :internal_mass,
2348
+ 'InferredFloor' => :internal_mass }[surface_type]
2349
+ fail "Unexpected surface for component loads: '#{s.name}'." if key.nil?
2064
2350
 
2065
2351
  surfaces_sensors[key] << []
2066
- vars.each do |var, name|
2352
+ { 'Surface Inside Face Convection Heat Gain Energy' => 's_conv',
2353
+ 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 's_ig',
2354
+ 'Surface Inside Face Solar Radiation Heat Gain Energy' => 's_sol',
2355
+ 'Surface Inside Face Lights Radiation Heat Gain Energy' => 's_lgt',
2356
+ 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 's_surf' }.each do |var, name|
2067
2357
  sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2068
2358
  sensor.setName(name)
2069
- sensor.setKeyName(ss.name.to_s)
2359
+ sensor.setKeyName(s.name.to_s)
2070
2360
  surfaces_sensors[key][-1] << sensor
2071
2361
  end
2072
2362
  end
2073
2363
 
2074
- next if s.netArea < area_tolerance # Skip parent surfaces (of subsurfaces) that have near zero net area
2075
-
2076
- key = { 'FoundationWall' => :foundation_walls,
2077
- 'RimJoist' => :rim_joists,
2078
- 'Wall' => :walls,
2079
- 'Slab' => :slabs,
2080
- 'Floor' => :floors,
2081
- 'Ceiling' => :ceilings,
2082
- 'Roof' => :roofs,
2083
- 'InferredCeiling' => :internal_mass,
2084
- 'InferredFloor' => :internal_mass }[surface_type]
2085
- fail "Unexpected surface for component loads: '#{s.name}'." if key.nil?
2086
-
2087
- surfaces_sensors[key] << []
2088
- { 'Surface Inside Face Convection Heat Gain Energy' => 's_conv',
2089
- 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 's_ig',
2090
- 'Surface Inside Face Solar Radiation Heat Gain Energy' => 's_sol',
2091
- 'Surface Inside Face Lights Radiation Heat Gain Energy' => 's_lgt',
2092
- 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 's_surf' }.each do |var, name|
2093
- sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2094
- sensor.setName(name)
2095
- sensor.setKeyName(s.name.to_s)
2096
- surfaces_sensors[key][-1] << sensor
2097
- end
2098
- end
2099
-
2100
- model.getInternalMasss.sort.each do |m|
2101
- next unless m.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2102
-
2103
- surfaces_sensors[:internal_mass] << []
2104
- { 'Surface Inside Face Convection Heat Gain Energy' => 'im_conv',
2105
- 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 'im_ig',
2106
- 'Surface Inside Face Solar Radiation Heat Gain Energy' => 'im_sol',
2107
- 'Surface Inside Face Lights Radiation Heat Gain Energy' => 'im_lgt',
2108
- 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 'im_surf' }.each do |var, name|
2109
- sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2110
- sensor.setName(name)
2111
- sensor.setKeyName(m.name.to_s)
2112
- surfaces_sensors[:internal_mass][-1] << sensor
2113
- end
2114
- end
2115
-
2116
- # EMS Sensors: Infiltration, Mechanical Ventilation, Natural Ventilation, Whole House Fan
2117
- infil_sensors = []
2118
- natvent_sensors = []
2119
- whf_sensors = []
2120
- { Constants.ObjectNameInfiltration => infil_sensors,
2121
- Constants.ObjectNameNaturalVentilation => natvent_sensors,
2122
- Constants.ObjectNameWholeHouseFan => whf_sensors }.each do |prefix, array|
2123
- model.getSpaceInfiltrationDesignFlowRates.sort.each do |i|
2124
- next unless i.name.to_s.start_with? prefix
2125
- next unless i.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2126
-
2127
- { 'Infiltration Sensible Heat Gain Energy' => prefix.gsub(' ', '_') + '_' + 'gain',
2128
- 'Infiltration Sensible Heat Loss Energy' => prefix.gsub(' ', '_') + '_' + 'loss' }.each do |var, name|
2364
+ unit_model.getInternalMasss.sort.each do |m|
2365
+ next unless m.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2366
+
2367
+ surfaces_sensors[:internal_mass] << []
2368
+ { 'Surface Inside Face Convection Heat Gain Energy' => 'im_conv',
2369
+ 'Surface Inside Face Internal Gains Radiation Heat Gain Energy' => 'im_ig',
2370
+ 'Surface Inside Face Solar Radiation Heat Gain Energy' => 'im_sol',
2371
+ 'Surface Inside Face Lights Radiation Heat Gain Energy' => 'im_lgt',
2372
+ 'Surface Inside Face Net Surface Thermal Radiation Heat Gain Energy' => 'im_surf' }.each do |var, name|
2373
+ sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2374
+ sensor.setName(name)
2375
+ sensor.setKeyName(m.name.to_s)
2376
+ surfaces_sensors[:internal_mass][-1] << sensor
2377
+ end
2378
+ end
2379
+
2380
+ # EMS Sensors: Infiltration, Natural Ventilation, Whole House Fan
2381
+ infil_sensors, natvent_sensors, whf_sensors = [], [], []
2382
+ unit_model.getSpaceInfiltrationDesignFlowRates.sort.each do |i|
2383
+ next unless i.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2384
+
2385
+ object_type = i.additionalProperties.getFeatureAsString('ObjectType').get
2386
+
2387
+ { 'Infiltration Sensible Heat Gain Energy' => 'airflow_gain',
2388
+ 'Infiltration Sensible Heat Loss Energy' => 'airflow_loss' }.each do |var, name|
2129
2389
  airflow_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2130
2390
  airflow_sensor.setName(name)
2131
2391
  airflow_sensor.setKeyName(i.name.to_s)
2132
- array << airflow_sensor
2392
+ if object_type == Constants.ObjectNameInfiltration
2393
+ infil_sensors << airflow_sensor
2394
+ elsif object_type == Constants.ObjectNameNaturalVentilation
2395
+ natvent_sensors << airflow_sensor
2396
+ elsif object_type == Constants.ObjectNameWholeHouseFan
2397
+ whf_sensors << airflow_sensor
2398
+ end
2133
2399
  end
2134
2400
  end
2135
- end
2136
2401
 
2137
- mechvents_sensors = []
2138
- model.getElectricEquipments.sort.each do |o|
2139
- next unless o.name.to_s.start_with? Constants.ObjectNameMechanicalVentilation
2402
+ # EMS Sensors: Mechanical Ventilation
2403
+ mechvents_sensors = []
2404
+ unit_model.getElectricEquipments.sort.each do |o|
2405
+ next unless o.endUseSubcategory == Constants.ObjectNameMechanicalVentilation
2140
2406
 
2141
- mechvents_sensors << []
2142
- { 'Electric Equipment Convective Heating Energy' => 'mv_conv',
2143
- 'Electric Equipment Radiant Heating Energy' => 'mv_rad' }.each do |var, name|
2144
- mechvent_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2145
- mechvent_sensor.setName(name)
2146
- mechvent_sensor.setKeyName(o.name.to_s)
2147
- mechvents_sensors[-1] << mechvent_sensor
2148
2407
  objects_already_processed << o
2408
+ { 'Electric Equipment Convective Heating Energy' => 'mv_conv',
2409
+ 'Electric Equipment Radiant Heating Energy' => 'mv_rad' }.each do |var, name|
2410
+ mechvent_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2411
+ mechvent_sensor.setName(name)
2412
+ mechvent_sensor.setKeyName(o.name.to_s)
2413
+ mechvents_sensors << mechvent_sensor
2414
+ end
2149
2415
  end
2150
- end
2151
- model.getOtherEquipments.sort.each do |o|
2152
- next unless o.name.to_s.start_with? Constants.ObjectNameMechanicalVentilationHouseFan
2416
+ unit_model.getOtherEquipments.sort.each do |o|
2417
+ next unless o.endUseSubcategory == Constants.ObjectNameMechanicalVentilationHouseFan
2153
2418
 
2154
- mechvents_sensors << []
2155
- { 'Other Equipment Convective Heating Energy' => 'mv_conv',
2156
- 'Other Equipment Radiant Heating Energy' => 'mv_rad' }.each do |var, name|
2157
- mechvent_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2158
- mechvent_sensor.setName(name)
2159
- mechvent_sensor.setKeyName(o.name.to_s)
2160
- mechvents_sensors[-1] << mechvent_sensor
2161
2419
  objects_already_processed << o
2420
+ { 'Other Equipment Convective Heating Energy' => 'mv_conv',
2421
+ 'Other Equipment Radiant Heating Energy' => 'mv_rad' }.each do |var, name|
2422
+ mechvent_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2423
+ mechvent_sensor.setName(name)
2424
+ mechvent_sensor.setKeyName(o.name.to_s)
2425
+ mechvents_sensors << mechvent_sensor
2426
+ end
2162
2427
  end
2163
- end
2164
2428
 
2165
- # EMS Sensors: Ducts
2166
- ducts_sensors = []
2167
- ducts_mix_gain_sensor = nil
2168
- ducts_mix_loss_sensor = nil
2429
+ # EMS Sensors: Ducts
2430
+ ducts_sensors = []
2431
+ ducts_mix_gain_sensor = nil
2432
+ ducts_mix_loss_sensor = nil
2433
+ conditioned_zone.zoneMixing.each do |zone_mix|
2434
+ object_type = zone_mix.additionalProperties.getFeatureAsString('ObjectType').to_s
2435
+ next unless object_type == Constants.ObjectNameDuctLoad
2169
2436
 
2170
- has_duct_zone_mixing = false
2171
- living_zone.airLoopHVACs.sort.each do |airloop|
2172
- living_zone.zoneMixing.each do |zone_mix|
2173
- next unless zone_mix.name.to_s.start_with? airloop.name.to_s.gsub(' ', '_')
2437
+ ducts_mix_gain_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mixing Sensible Heat Gain Energy')
2438
+ ducts_mix_gain_sensor.setName('duct_mix_gain')
2439
+ ducts_mix_gain_sensor.setKeyName(conditioned_zone.name.to_s)
2174
2440
 
2175
- has_duct_zone_mixing = true
2441
+ ducts_mix_loss_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mixing Sensible Heat Loss Energy')
2442
+ ducts_mix_loss_sensor.setName('duct_mix_loss')
2443
+ ducts_mix_loss_sensor.setKeyName(conditioned_zone.name.to_s)
2176
2444
  end
2177
- end
2178
-
2179
- if has_duct_zone_mixing
2180
- ducts_mix_gain_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mixing Sensible Heat Gain Energy')
2181
- ducts_mix_gain_sensor.setName('duct_mix_gain')
2182
- ducts_mix_gain_sensor.setKeyName(living_zone.name.to_s)
2183
-
2184
- ducts_mix_loss_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mixing Sensible Heat Loss Energy')
2185
- ducts_mix_loss_sensor.setName('duct_mix_loss')
2186
- ducts_mix_loss_sensor.setKeyName(living_zone.name.to_s)
2187
- end
2188
-
2189
- # Duct losses
2190
- model.getOtherEquipments.sort.each do |o|
2191
- next if objects_already_processed.include? o
2445
+ unit_model.getOtherEquipments.sort.each do |o|
2446
+ next if objects_already_processed.include? o
2447
+ next unless o.endUseSubcategory == Constants.ObjectNameDuctLoad
2192
2448
 
2193
- is_duct_load = o.additionalProperties.getFeatureAsBoolean(Constants.IsDuctLoadForReport)
2194
- next unless is_duct_load.is_initialized
2449
+ objects_already_processed << o
2450
+ { 'Other Equipment Convective Heating Energy' => 'ducts_conv',
2451
+ 'Other Equipment Radiant Heating Energy' => 'ducts_rad' }.each do |var, name|
2452
+ ducts_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2453
+ ducts_sensor.setName(name)
2454
+ ducts_sensor.setKeyName(o.name.to_s)
2455
+ ducts_sensors << ducts_sensor
2456
+ end
2457
+ end
2195
2458
 
2196
- objects_already_processed << o
2197
- next unless is_duct_load.get
2459
+ # EMS Sensors: Lighting
2460
+ lightings_sensors = []
2461
+ unit_model.getLightss.sort.each do |e|
2462
+ next unless e.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2198
2463
 
2199
- ducts_sensors << []
2200
- { 'Other Equipment Convective Heating Energy' => 'ducts_conv',
2201
- 'Other Equipment Radiant Heating Energy' => 'ducts_rad' }.each do |var, name|
2202
- ducts_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2203
- ducts_sensor.setName(name)
2204
- ducts_sensor.setKeyName(o.name.to_s)
2205
- ducts_sensors[-1] << ducts_sensor
2464
+ { 'Lights Convective Heating Energy' => 'ig_lgt_conv',
2465
+ 'Lights Radiant Heating Energy' => 'ig_lgt_rad',
2466
+ 'Lights Visible Radiation Heating Energy' => 'ig_lgt_vis' }.each do |var, name|
2467
+ intgains_lights_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2468
+ intgains_lights_sensor.setName(name)
2469
+ intgains_lights_sensor.setKeyName(e.name.to_s)
2470
+ lightings_sensors << intgains_lights_sensor
2471
+ end
2206
2472
  end
2207
- end
2208
2473
 
2209
- # EMS Sensors: Lighting
2210
- lightings_sensors = []
2211
- lightings_sensors << []
2212
- model.getLightss.sort.each do |e|
2213
- next unless e.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2474
+ # EMS Sensors: Internal Gains
2475
+ intgains_sensors = []
2476
+ unit_model.getElectricEquipments.sort.each do |o|
2477
+ next if objects_already_processed.include? o
2478
+ next unless o.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2214
2479
 
2215
- lightings_sensors << []
2216
- { 'Lights Convective Heating Energy' => 'ig_lgt_conv',
2217
- 'Lights Radiant Heating Energy' => 'ig_lgt_rad',
2218
- 'Lights Visible Radiation Heating Energy' => 'ig_lgt_vis' }.each do |var, name|
2219
- intgains_lights_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2220
- intgains_lights_sensor.setName(name)
2221
- intgains_lights_sensor.setKeyName(e.name.to_s)
2222
- lightings_sensors[-1] << intgains_lights_sensor
2480
+ { 'Electric Equipment Convective Heating Energy' => 'ig_ee_conv',
2481
+ 'Electric Equipment Radiant Heating Energy' => 'ig_ee_rad' }.each do |var, name|
2482
+ intgains_elec_equip_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2483
+ intgains_elec_equip_sensor.setName(name)
2484
+ intgains_elec_equip_sensor.setKeyName(o.name.to_s)
2485
+ intgains_sensors << intgains_elec_equip_sensor
2486
+ end
2223
2487
  end
2224
- end
2225
2488
 
2226
- # EMS Sensors: Internal Gains
2227
- intgains_sensors = []
2489
+ unit_model.getOtherEquipments.sort.each do |o|
2490
+ next if objects_already_processed.include? o
2491
+ next unless o.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2228
2492
 
2229
- model.getElectricEquipments.sort.each do |o|
2230
- next unless o.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2231
- next if objects_already_processed.include? o
2232
-
2233
- intgains_sensors << []
2234
- { 'Electric Equipment Convective Heating Energy' => 'ig_ee_conv',
2235
- 'Electric Equipment Radiant Heating Energy' => 'ig_ee_rad' }.each do |var, name|
2236
- intgains_elec_equip_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2237
- intgains_elec_equip_sensor.setName(name)
2238
- intgains_elec_equip_sensor.setKeyName(o.name.to_s)
2239
- intgains_sensors[-1] << intgains_elec_equip_sensor
2493
+ { 'Other Equipment Convective Heating Energy' => 'ig_oe_conv',
2494
+ 'Other Equipment Radiant Heating Energy' => 'ig_oe_rad' }.each do |var, name|
2495
+ intgains_other_equip_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2496
+ intgains_other_equip_sensor.setName(name)
2497
+ intgains_other_equip_sensor.setKeyName(o.name.to_s)
2498
+ intgains_sensors << intgains_other_equip_sensor
2499
+ end
2240
2500
  end
2241
- end
2242
2501
 
2243
- model.getOtherEquipments.sort.each do |o|
2244
- next unless o.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2245
- next if objects_already_processed.include? o
2502
+ unit_model.getPeoples.sort.each do |e|
2503
+ next unless e.space.get.thermalZone.get.name.to_s == conditioned_zone.name.to_s
2246
2504
 
2247
- intgains_sensors << []
2248
- { 'Other Equipment Convective Heating Energy' => 'ig_oe_conv',
2249
- 'Other Equipment Radiant Heating Energy' => 'ig_oe_rad' }.each do |var, name|
2250
- intgains_other_equip_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2251
- intgains_other_equip_sensor.setName(name)
2252
- intgains_other_equip_sensor.setKeyName(o.name.to_s)
2253
- intgains_sensors[-1] << intgains_other_equip_sensor
2505
+ { 'People Convective Heating Energy' => 'ig_ppl_conv',
2506
+ 'People Radiant Heating Energy' => 'ig_ppl_rad' }.each do |var, name|
2507
+ intgains_people = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2508
+ intgains_people.setName(name)
2509
+ intgains_people.setKeyName(e.name.to_s)
2510
+ intgains_sensors << intgains_people
2511
+ end
2254
2512
  end
2255
- end
2256
2513
 
2257
- model.getPeoples.sort.each do |e|
2258
- next unless e.space.get.thermalZone.get.name.to_s == living_zone.name.to_s
2259
-
2260
- intgains_sensors << []
2261
- { 'People Convective Heating Energy' => 'ig_ppl_conv',
2262
- 'People Radiant Heating Energy' => 'ig_ppl_rad' }.each do |var, name|
2263
- intgains_people = OpenStudio::Model::EnergyManagementSystemSensor.new(model, var)
2264
- intgains_people.setName(name)
2265
- intgains_people.setKeyName(e.name.to_s)
2266
- intgains_sensors[-1] << intgains_people
2514
+ if not dehumidifier_sensors[unit].nil?
2515
+ intgains_sensors << dehumidifier_sensors[unit]
2267
2516
  end
2268
- end
2269
2517
 
2270
- if not intgain_dehumidifier.nil?
2271
- intgains_sensors[-1] << intgain_dehumidifier
2272
- end
2518
+ intgains_dhw_sensors = {}
2273
2519
 
2274
- intgains_dhw_sensors = {}
2520
+ (unit_model.getWaterHeaterMixeds + unit_model.getWaterHeaterStratifieds).sort.each do |wh|
2521
+ next unless wh.ambientTemperatureThermalZone.is_initialized
2522
+ next unless wh.ambientTemperatureThermalZone.get.name.to_s == conditioned_zone.name.to_s
2275
2523
 
2276
- (model.getWaterHeaterMixeds + model.getWaterHeaterStratifieds).sort.each do |wh|
2277
- next unless wh.ambientTemperatureThermalZone.is_initialized
2278
- next unless wh.ambientTemperatureThermalZone.get.name.to_s == living_zone.name.to_s
2524
+ dhw_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Water Heater Heat Loss Energy')
2525
+ dhw_sensor.setName('dhw_loss')
2526
+ dhw_sensor.setKeyName(wh.name.to_s)
2279
2527
 
2280
- dhw_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Water Heater Heat Loss Energy')
2281
- dhw_sensor.setName('dhw_loss')
2282
- dhw_sensor.setKeyName(wh.name.to_s)
2528
+ if wh.is_a? OpenStudio::Model::WaterHeaterMixed
2529
+ oncycle_loss = wh.onCycleLossFractiontoThermalZone
2530
+ offcycle_loss = wh.offCycleLossFractiontoThermalZone
2531
+ else
2532
+ oncycle_loss = wh.skinLossFractiontoZone
2533
+ offcycle_loss = wh.offCycleFlueLossFractiontoZone
2534
+ end
2283
2535
 
2284
- if wh.is_a? OpenStudio::Model::WaterHeaterMixed
2285
- oncycle_loss = wh.onCycleLossFractiontoThermalZone
2286
- offcycle_loss = wh.offCycleLossFractiontoThermalZone
2287
- else
2288
- oncycle_loss = wh.skinLossFractiontoZone
2289
- offcycle_loss = wh.offCycleFlueLossFractiontoZone
2536
+ dhw_rtf_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Water Heater Runtime Fraction')
2537
+ dhw_rtf_sensor.setName('dhw_rtf')
2538
+ dhw_rtf_sensor.setKeyName(wh.name.to_s)
2539
+
2540
+ intgains_dhw_sensors[dhw_sensor] = [offcycle_loss, oncycle_loss, dhw_rtf_sensor]
2541
+ end
2542
+
2543
+ # EMS program: Surfaces
2544
+ surfaces_sensors.each do |k, surface_sensors|
2545
+ program.addLine("Set hr_#{k} = 0")
2546
+ surface_sensors.each do |sensors|
2547
+ s = "Set hr_#{k} = hr_#{k}"
2548
+ sensors.each do |sensor|
2549
+ # remove ss_net if switch
2550
+ if sensor.name.to_s.start_with?('ss_net', 'ss_sol_abs', 'ss_trans_in')
2551
+ s += " - #{sensor.name}"
2552
+ elsif sensor.name.to_s.start_with?('ss_sw_abs', 'ss_trans_out', 'ss_back_out')
2553
+ s += " + #{sensor.name} * ZoneTimestep * 3600"
2554
+ else
2555
+ s += " + #{sensor.name}"
2556
+ end
2557
+ end
2558
+ program.addLine(s) if sensors.size > 0
2559
+ end
2290
2560
  end
2291
2561
 
2292
- dhw_rtf_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Water Heater Runtime Fraction')
2293
- dhw_rtf_sensor.setName('dhw_rtf')
2294
- dhw_rtf_sensor.setKeyName(wh.name.to_s)
2562
+ # EMS program: Internal Gains, Lighting, Infiltration, Natural Ventilation, Mechanical Ventilation, Ducts
2563
+ { 'intgains' => intgains_sensors,
2564
+ 'lighting' => lightings_sensors,
2565
+ 'infil' => infil_sensors,
2566
+ 'natvent' => natvent_sensors,
2567
+ 'whf' => whf_sensors,
2568
+ 'mechvent' => mechvents_sensors,
2569
+ 'ducts' => ducts_sensors }.each do |loadtype, sensors|
2570
+ program.addLine("Set hr_#{loadtype} = 0")
2571
+ next if sensors.empty?
2295
2572
 
2296
- intgains_dhw_sensors[dhw_sensor] = [offcycle_loss, oncycle_loss, dhw_rtf_sensor]
2297
- end
2298
-
2299
- nonsurf_names = ['intgains', 'lighting', 'infil', 'mechvent', 'natvent', 'whf', 'ducts']
2300
-
2301
- # EMS program
2302
- program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
2303
- program.setName(Constants.ObjectNameComponentLoadsProgram)
2304
-
2305
- # EMS program: Surfaces
2306
- surfaces_sensors.each do |k, surface_sensors|
2307
- program.addLine("Set hr_#{k} = 0")
2308
- surface_sensors.each do |sensors|
2309
- s = "Set hr_#{k} = hr_#{k}"
2573
+ s = "Set hr_#{loadtype} = hr_#{loadtype}"
2310
2574
  sensors.each do |sensor|
2311
- # remove ss_net if switch
2312
- if sensor.name.to_s.start_with?('ss_net', 'ss_sol_abs', 'ss_trans_in')
2575
+ if ['intgains', 'lighting', 'mechvent', 'ducts'].include? loadtype
2313
2576
  s += " - #{sensor.name}"
2314
- elsif sensor.name.to_s.start_with?('ss_sw_abs', 'ss_trans_out', 'ss_back_out')
2315
- s += " + #{sensor.name} * ZoneTimestep * 3600"
2316
- else
2577
+ elsif sensor.name.to_s.include? 'gain'
2578
+ s += " - #{sensor.name}"
2579
+ elsif sensor.name.to_s.include? 'loss'
2317
2580
  s += " + #{sensor.name}"
2318
2581
  end
2319
2582
  end
2320
- program.addLine(s) if sensors.size > 0
2321
- end
2322
- end
2323
-
2324
- # EMS program: Lighting
2325
- program.addLine('Set hr_lighting = 0')
2326
- lightings_sensors.each do |lighting_sensors|
2327
- s = 'Set hr_lighting = hr_lighting'
2328
- lighting_sensors.each do |sensor|
2329
- s += " - #{sensor.name}"
2330
- end
2331
- program.addLine(s) if lighting_sensors.size > 0
2332
- end
2333
-
2334
- # EMS program: Internal gains
2335
- program.addLine('Set hr_intgains = 0')
2336
- intgains_sensors.each do |intgain_sensors|
2337
- s = 'Set hr_intgains = hr_intgains'
2338
- intgain_sensors.each do |sensor|
2339
- s += " - #{sensor.name}"
2340
- end
2341
- program.addLine(s) if intgain_sensors.size > 0
2342
- end
2343
- intgains_dhw_sensors.each do |sensor, vals|
2344
- off_loss, on_loss, rtf_sensor = vals
2345
- program.addLine("Set hr_intgains = hr_intgains + #{sensor.name} * (#{off_loss}*(1-#{rtf_sensor.name}) + #{on_loss}*#{rtf_sensor.name})") # Water heater tank losses to zone
2346
- end
2583
+ program.addLine(s)
2584
+ end
2585
+ intgains_dhw_sensors.each do |sensor, vals|
2586
+ off_loss, on_loss, rtf_sensor = vals
2587
+ program.addLine("Set hr_intgains = hr_intgains + #{sensor.name} * (#{off_loss}*(1-#{rtf_sensor.name}) + #{on_loss}*#{rtf_sensor.name})") # Water heater tank losses to zone
2588
+ end
2589
+ if (not ducts_mix_loss_sensor.nil?) && (not ducts_mix_gain_sensor.nil?)
2590
+ program.addLine("Set hr_ducts = hr_ducts + (#{ducts_mix_loss_sensor.name} - #{ducts_mix_gain_sensor.name})")
2591
+ end
2592
+
2593
+ # EMS program: Heating vs Cooling logic
2594
+ program.addLine('Set htg_mode = 0')
2595
+ program.addLine('Set clg_mode = 0')
2596
+ program.addLine("If (#{htg_cond_load_sensors[unit].name} > 0)") # Assign hour to heating if heating load
2597
+ program.addLine(" Set htg_mode = #{total_heat_load_serveds[unit]}")
2598
+ program.addLine("ElseIf (#{clg_cond_load_sensors[unit].name} > 0)") # Assign hour to cooling if cooling load
2599
+ program.addLine(" Set clg_mode = #{total_cool_load_serveds[unit]}")
2600
+ program.addLine("ElseIf (#{@clg_ssn_sensor.name} > 0)") # No load, assign hour to cooling if in cooling season definition (Note: natural ventilation & whole house fan only operate during the cooling season)
2601
+ program.addLine(" Set clg_mode = #{total_cool_load_serveds[unit]}")
2602
+ program.addLine('Else') # No load, assign hour to heating if not in cooling season definition
2603
+ program.addLine(" Set htg_mode = #{total_heat_load_serveds[unit]}")
2604
+ program.addLine('EndIf')
2347
2605
 
2348
- # EMS program: Infiltration, Natural Ventilation, Mechanical Ventilation, Ducts
2349
- { infil_sensors => 'infil',
2350
- natvent_sensors => 'natvent',
2351
- whf_sensors => 'whf' }.each do |sensors, loadtype|
2352
- program.addLine("Set hr_#{loadtype} = 0")
2353
- s = "Set hr_#{loadtype} = hr_#{loadtype}"
2354
- sensors.each do |sensor|
2355
- if sensor.name.to_s.include? 'gain'
2356
- # FIXME: Workaround for https://github.com/NREL/EnergyPlus/issues/9934
2357
- # Remove when the issue is resolved
2358
- if loadtype == 'infil'
2359
- s += " - (#{sensor.name} * 3600)"
2360
- else
2361
- s += " - #{sensor.name}"
2362
- end
2363
- elsif sensor.name.to_s.include? 'loss'
2364
- s += " + #{sensor.name}"
2606
+ unit_multiplier = @hpxml_bldg.building_construction.number_of_units
2607
+ [:htg, :clg].each do |mode|
2608
+ if mode == :htg
2609
+ sign = ''
2610
+ else
2611
+ sign = '-'
2365
2612
  end
2366
- end
2367
- program.addLine(s) if sensors.size > 0
2368
- end
2369
- { mechvents_sensors => 'mechvent',
2370
- ducts_sensors => 'ducts' }.each do |all_sensors, loadtype|
2371
- program.addLine("Set hr_#{loadtype} = 0")
2372
- all_sensors.each do |sensors|
2373
- s = "Set hr_#{loadtype} = hr_#{loadtype}"
2374
- sensors.each do |sensor|
2375
- s += " - #{sensor.name}"
2613
+ surf_names.each do |surf_name|
2614
+ program.addLine("Set loads_#{mode}_#{surf_name} = loads_#{mode}_#{surf_name} + (#{sign}hr_#{surf_name} * #{mode}_mode * #{unit_multiplier})")
2615
+ end
2616
+ nonsurf_names.each do |nonsurf_name|
2617
+ program.addLine("Set loads_#{mode}_#{nonsurf_name} = loads_#{mode}_#{nonsurf_name} + (#{sign}hr_#{nonsurf_name} * #{mode}_mode * #{unit_multiplier})")
2376
2618
  end
2377
- program.addLine(s) if sensors.size > 0
2378
- end
2379
- end
2380
- if (not ducts_mix_loss_sensor.nil?) && (not ducts_mix_gain_sensor.nil?)
2381
- program.addLine("Set hr_ducts = hr_ducts + (#{ducts_mix_loss_sensor.name} - #{ducts_mix_gain_sensor.name})")
2382
- end
2383
-
2384
- # EMS program: Heating vs Cooling logic
2385
- program.addLine('Set htg_mode = 0')
2386
- program.addLine('Set clg_mode = 0')
2387
- program.addLine("If (#{liv_load_sensors[:htg].name} > 0)") # Assign hour to heating if heating load
2388
- program.addLine(" Set htg_mode = #{total_heat_load_served}")
2389
- program.addLine("ElseIf (#{liv_load_sensors[:clg].name} > 0)") # Assign hour to cooling if cooling load
2390
- program.addLine(" Set clg_mode = #{total_cool_load_served}")
2391
- program.addLine("ElseIf (#{@clg_ssn_sensor.name} > 0)") # No load, assign hour to cooling if in cooling season definition (Note: natural ventilation & whole house fan only operate during the cooling season)
2392
- program.addLine(" Set clg_mode = #{total_cool_load_served}")
2393
- program.addLine('Else') # No load, assign hour to heating if not in cooling season definition
2394
- program.addLine(" Set htg_mode = #{total_heat_load_served}")
2395
- program.addLine('EndIf')
2396
-
2397
- [:htg, :clg].each do |mode|
2398
- if mode == :htg
2399
- sign = ''
2400
- else
2401
- sign = '-'
2402
- end
2403
- surfaces_sensors.keys.each do |k|
2404
- program.addLine("Set loads_#{mode}_#{k} = #{sign}hr_#{k} * #{mode}_mode")
2405
- end
2406
- nonsurf_names.each do |nonsurf_name|
2407
- program.addLine("Set loads_#{mode}_#{nonsurf_name} = #{sign}hr_#{nonsurf_name} * #{mode}_mode")
2408
2619
  end
2409
2620
  end
2410
2621
 
@@ -2415,7 +2626,7 @@ class OSModel
2415
2626
  program_calling_manager.addProgram(program)
2416
2627
  end
2417
2628
 
2418
- def self.set_output_files(model)
2629
+ def set_output_files(model)
2419
2630
  oj = model.getOutputJSON
2420
2631
  oj.setOptionType('TimeSeriesAndTabular')
2421
2632
  oj.setOutputJSON(false)
@@ -2423,6 +2634,7 @@ class OSModel
2423
2634
 
2424
2635
  ocf = model.getOutputControlFiles
2425
2636
  ocf.setOutputAUDIT(@debug)
2637
+ ocf.setOutputCSV(@debug)
2426
2638
  ocf.setOutputBND(@debug)
2427
2639
  ocf.setOutputEIO(@debug)
2428
2640
  ocf.setOutputESO(@debug)
@@ -2431,30 +2643,31 @@ class OSModel
2431
2643
  ocf.setOutputMTR(@debug)
2432
2644
  ocf.setOutputRDD(@debug)
2433
2645
  ocf.setOutputSHD(@debug)
2646
+ ocf.setOutputCSV(@debug)
2434
2647
  ocf.setOutputSQLite(@debug)
2435
2648
  ocf.setOutputPerfLog(@debug)
2436
2649
  end
2437
2650
 
2438
- def self.add_ems_debug_output(model)
2651
+ def add_ems_debug_output(model)
2439
2652
  oems = model.getOutputEnergyManagementSystem
2440
2653
  oems.setActuatorAvailabilityDictionaryReporting('Verbose')
2441
2654
  oems.setInternalVariableAvailabilityDictionaryReporting('Verbose')
2442
2655
  oems.setEMSRuntimeLanguageDebugOutputLevel('Verbose')
2443
2656
  end
2444
2657
 
2445
- def self.set_surface_interior(model, spaces, surface, hpxml_surface)
2658
+ def set_surface_interior(model, spaces, surface, hpxml_surface)
2446
2659
  interior_adjacent_to = hpxml_surface.interior_adjacent_to
2447
2660
  if HPXML::conditioned_below_grade_locations.include? interior_adjacent_to
2448
- surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationLivingSpace))
2661
+ surface.setSpace(create_or_get_space(model, spaces, HPXML::LocationConditionedSpace))
2449
2662
  else
2450
2663
  surface.setSpace(create_or_get_space(model, spaces, interior_adjacent_to))
2451
2664
  end
2452
2665
  end
2453
2666
 
2454
- def self.set_surface_exterior(model, spaces, surface, hpxml_surface)
2667
+ def set_surface_exterior(model, spaces, surface, hpxml_surface)
2455
2668
  exterior_adjacent_to = hpxml_surface.exterior_adjacent_to
2456
2669
  is_adiabatic = hpxml_surface.is_adiabatic
2457
- if exterior_adjacent_to == HPXML::LocationOutside
2670
+ if [HPXML::LocationOutside, HPXML::LocationManufacturedHomeUnderBelly].include? exterior_adjacent_to
2458
2671
  surface.setOutsideBoundaryCondition('Outdoors')
2459
2672
  elsif exterior_adjacent_to == HPXML::LocationGround
2460
2673
  surface.setOutsideBoundaryCondition('Foundation')
@@ -2464,7 +2677,7 @@ class OSModel
2464
2677
  HPXML::LocationOtherNonFreezingSpace, HPXML::LocationOtherHousingUnit].include? exterior_adjacent_to
2465
2678
  set_surface_otherside_coefficients(surface, exterior_adjacent_to, model, spaces)
2466
2679
  elsif HPXML::conditioned_below_grade_locations.include? exterior_adjacent_to
2467
- adjacent_surface = surface.createAdjacentSurface(create_or_get_space(model, spaces, HPXML::LocationLivingSpace)).get
2680
+ adjacent_surface = surface.createAdjacentSurface(create_or_get_space(model, spaces, HPXML::LocationConditionedSpace)).get
2468
2681
  adjacent_surface.additionalProperties.setFeature('SurfaceType', surface.additionalProperties.getFeatureAsString('SurfaceType').get)
2469
2682
  else
2470
2683
  adjacent_surface = surface.createAdjacentSurface(create_or_get_space(model, spaces, exterior_adjacent_to)).get
@@ -2472,7 +2685,7 @@ class OSModel
2472
2685
  end
2473
2686
  end
2474
2687
 
2475
- def self.set_surface_otherside_coefficients(surface, exterior_adjacent_to, model, spaces)
2688
+ def set_surface_otherside_coefficients(surface, exterior_adjacent_to, model, spaces)
2476
2689
  otherside_coeffs = nil
2477
2690
  model.getSurfacePropertyOtherSideCoefficientss.each do |c|
2478
2691
  next unless c.name.to_s == exterior_adjacent_to
@@ -2493,7 +2706,7 @@ class OSModel
2493
2706
  surface.setWindExposure('NoWind')
2494
2707
  end
2495
2708
 
2496
- def self.get_space_temperature_schedule(model, location, spaces)
2709
+ def get_space_temperature_schedule(model, location, spaces)
2497
2710
  # Create outside boundary schedules to be actuated by EMS,
2498
2711
  # can be shared by any surface, duct adjacent to / located in those spaces
2499
2712
 
@@ -2506,12 +2719,13 @@ class OSModel
2506
2719
 
2507
2720
  sch = OpenStudio::Model::ScheduleConstant.new(model)
2508
2721
  sch.setName(location)
2722
+ sch.additionalProperties.setFeature('ObjectType', location)
2509
2723
 
2510
2724
  space_values = Geometry.get_temperature_scheduled_space_values(location)
2511
2725
 
2512
2726
  if location == HPXML::LocationOtherHeatedSpace
2513
2727
  # Create a sensor to get dynamic heating setpoint
2514
- htg_sch = spaces[HPXML::LocationLivingSpace].thermalZone.get.thermostatSetpointDualSetpoint.get.heatingSetpointTemperatureSchedule.get
2728
+ htg_sch = spaces[HPXML::LocationConditionedSpace].thermalZone.get.thermostatSetpointDualSetpoint.get.heatingSetpointTemperatureSchedule.get
2515
2729
  sensor_htg_spt = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
2516
2730
  sensor_htg_spt.setName('htg_spt')
2517
2731
  sensor_htg_spt.setKeyName(htg_sch.name.to_s)
@@ -2527,7 +2741,7 @@ class OSModel
2527
2741
  if space_values[:indoor_weight] > 0
2528
2742
  sensor_ia = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Air Temperature')
2529
2743
  sensor_ia.setName('cond_zone_temp')
2530
- sensor_ia.setKeyName(spaces[HPXML::LocationLivingSpace].thermalZone.get.name.to_s)
2744
+ sensor_ia.setKeyName(spaces[HPXML::LocationConditionedSpace].thermalZone.get.name.to_s)
2531
2745
  end
2532
2746
 
2533
2747
  if space_values[:outdoor_weight] > 0
@@ -2578,7 +2792,7 @@ class OSModel
2578
2792
  # Returns an OS:Space, or temperature OS:Schedule for a MF space, or nil if outside
2579
2793
  # Should be called when the object's energy use is sensitive to ambient temperature
2580
2794
  # (e.g., water heaters and ducts).
2581
- def self.get_space_or_schedule_from_location(location, model, spaces)
2795
+ def get_space_or_schedule_from_location(location, model, spaces)
2582
2796
  return if [HPXML::LocationOtherExterior,
2583
2797
  HPXML::LocationOutside,
2584
2798
  HPXML::LocationRoofDeck].include? location
@@ -2603,7 +2817,7 @@ class OSModel
2603
2817
  # Returns an OS:Space, or nil if a MF space or outside
2604
2818
  # Should be called when the object's energy use is NOT sensitive to ambient temperature
2605
2819
  # (e.g., appliances).
2606
- def self.get_space_from_location(location, spaces)
2820
+ def get_space_from_location(location, spaces)
2607
2821
  return if [HPXML::LocationOutside,
2608
2822
  HPXML::LocationOtherHeatedSpace,
2609
2823
  HPXML::LocationOtherHousingUnit,
@@ -2611,13 +2825,13 @@ class OSModel
2611
2825
  HPXML::LocationOtherNonFreezingSpace].include? location
2612
2826
 
2613
2827
  if HPXML::conditioned_locations.include? location
2614
- location = HPXML::LocationLivingSpace
2828
+ location = HPXML::LocationConditionedSpace
2615
2829
  end
2616
2830
 
2617
2831
  return spaces[location]
2618
2832
  end
2619
2833
 
2620
- def self.set_subsurface_exterior(surface, spaces, model, hpxml_surface)
2834
+ def set_subsurface_exterior(surface, spaces, model, hpxml_surface)
2621
2835
  # Set its parent surface outside boundary condition, which will be also applied to subsurfaces through OS
2622
2836
  # The parent surface is entirely comprised of the subsurface.
2623
2837
 
@@ -2629,36 +2843,25 @@ class OSModel
2629
2843
  end
2630
2844
  end
2631
2845
 
2632
- def self.get_kiva_instances(fnd_walls, slabs)
2633
- # Identify unique Kiva foundations that are required.
2634
- kiva_fnd_walls = []
2635
- fnd_walls.each do |foundation_wall|
2636
- next unless foundation_wall.is_exterior
2637
-
2638
- kiva_fnd_walls << foundation_wall
2639
- end
2640
- if kiva_fnd_walls.empty? # Handle slab foundation type
2641
- kiva_fnd_walls << nil
2642
- end
2643
-
2644
- kiva_slabs = slabs
2645
-
2646
- return kiva_fnd_walls.product(kiva_slabs)
2647
- end
2648
-
2649
- def self.set_foundation_and_walls_top()
2846
+ def set_foundation_and_walls_top()
2650
2847
  @foundation_top = 0
2651
- @hpxml.foundation_walls.each do |foundation_wall|
2848
+ @hpxml_bldg.floors.each do |floor|
2849
+ # Keeping the floor at ground level for ASHRAE 140 tests yields the expected results
2850
+ if floor.is_floor && floor.is_exterior && !@apply_ashrae140_assumptions
2851
+ @foundation_top = 2.0
2852
+ end
2853
+ end
2854
+ @hpxml_bldg.foundation_walls.each do |foundation_wall|
2652
2855
  top = -1 * foundation_wall.depth_below_grade + foundation_wall.height
2653
2856
  @foundation_top = top if top > @foundation_top
2654
2857
  end
2655
- @walls_top = @foundation_top + 8.0 * @ncfl_ag
2858
+ @walls_top = @foundation_top + @hpxml_bldg.building_construction.average_ceiling_height * @ncfl_ag
2656
2859
  end
2657
2860
 
2658
- def self.set_heating_and_cooling_seasons()
2659
- return if @hpxml.hvac_controls.size == 0
2861
+ def set_heating_and_cooling_seasons()
2862
+ return if @hpxml_bldg.hvac_controls.size == 0
2660
2863
 
2661
- hvac_control = @hpxml.hvac_controls[0]
2864
+ hvac_control = @hpxml_bldg.hvac_controls[0]
2662
2865
 
2663
2866
  htg_start_month = hvac_control.seasons_heating_begin_month
2664
2867
  htg_start_day = hvac_control.seasons_heating_begin_day
@@ -2669,8 +2872,8 @@ class OSModel
2669
2872
  clg_end_month = hvac_control.seasons_cooling_end_month
2670
2873
  clg_end_day = hvac_control.seasons_cooling_end_day
2671
2874
 
2672
- @heating_days = Schedule.get_daily_season(@hpxml.header.sim_calendar_year, htg_start_month, htg_start_day, htg_end_month, htg_end_day)
2673
- @cooling_days = Schedule.get_daily_season(@hpxml.header.sim_calendar_year, clg_start_month, clg_start_day, clg_end_month, clg_end_day)
2875
+ @heating_days = Schedule.get_daily_season(@hpxml_header.sim_calendar_year, htg_start_month, htg_start_day, htg_end_month, htg_end_day)
2876
+ @cooling_days = Schedule.get_daily_season(@hpxml_header.sim_calendar_year, clg_start_month, clg_start_day, clg_end_month, clg_end_day)
2674
2877
  end
2675
2878
  end
2676
2879