openstudio-standards 0.4.0 → 0.5.0.rc1

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/data/inventory/thermal_bridging.csv +90 -0
  3. data/data/standards/OpenStudio_Standards-deer-comstock.xlsx +0 -0
  4. data/data/standards/manage_OpenStudio_Standards.rb +1 -1
  5. data/data/standards/test_performance_expected_dd_results.csv +2014 -1891
  6. data/lib/openstudio-standards/btap/analysis.rb +8 -8
  7. data/lib/openstudio-standards/btap/bridging.rb +664 -645
  8. data/lib/openstudio-standards/btap/btap.model.rb +14 -14
  9. data/lib/openstudio-standards/btap/btap.rb +7 -7
  10. data/lib/openstudio-standards/btap/btap_result.rb +1 -1
  11. data/lib/openstudio-standards/btap/economics.rb +23 -23
  12. data/lib/openstudio-standards/btap/envelope.rb +8 -8
  13. data/lib/openstudio-standards/btap/equest.rb +1 -1
  14. data/lib/openstudio-standards/btap/geometry.rb +2 -2
  15. data/lib/openstudio-standards/btap/mpc.rb +7 -7
  16. data/lib/openstudio-standards/btap/schedules.rb +1 -1
  17. data/lib/openstudio-standards/btap/simmanager.rb +4 -4
  18. data/lib/openstudio-standards/btap/spaceloads.rb +26 -26
  19. data/lib/openstudio-standards/btap/utilities.rb +6 -6
  20. data/lib/openstudio-standards/btap/vintagizer.rb +1 -1
  21. data/lib/openstudio-standards/constructions/information.rb +83 -0
  22. data/lib/openstudio-standards/constructions/materials/modify.rb +72 -0
  23. data/lib/openstudio-standards/constructions/modify.rb +80 -0
  24. data/lib/openstudio-standards/create_typical/create_typical.rb +983 -0
  25. data/lib/openstudio-standards/create_typical/enumerations.rb +484 -0
  26. data/lib/openstudio-standards/create_typical/space_type_blend.rb +791 -0
  27. data/lib/openstudio-standards/create_typical/space_type_ratios.rb +494 -0
  28. data/lib/openstudio-standards/daylighting/space.rb +47 -0
  29. data/lib/openstudio-standards/geometry/create.rb +801 -0
  30. data/lib/openstudio-standards/geometry/create_bar.rb +2171 -0
  31. data/lib/openstudio-standards/geometry/information.rb +462 -0
  32. data/lib/openstudio-standards/geometry/modify.rb +48 -0
  33. data/lib/openstudio-standards/hvac/air_loop/information.rb +79 -0
  34. data/lib/openstudio-standards/hvac/cbecs_hvac.rb +616 -0
  35. data/lib/openstudio-standards/hvac/setpoint_managers/information.rb +91 -0
  36. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.AirTerminalSingleDuctVAVReheat.rb +1 -1
  37. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2007/ashrae_90_1_2007.AirTerminalSingleDuctVAVReheat.rb +1 -1
  38. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.AirTerminalSingleDuctVAVReheat.rb +1 -1
  39. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.Model.rb +1 -1
  40. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.AirTerminalSingleDuctVAVReheat.rb +1 -1
  41. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.Model.rb +2 -2
  42. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.hvac_systems.rb +1 -1
  43. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.AirTerminalSingleDuctVAVReheat.rb +1 -1
  44. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +4 -36
  45. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.hvac_systems.rb +1 -1
  46. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb +1 -1
  47. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +4 -36
  48. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb +3 -3
  49. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb +1 -1
  50. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.AirTerminalSingleDuctVAVReheat.rb +1 -1
  51. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.Model.elevators.rb +1 -1
  52. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.AirTerminalSingleDuctVAVReheat.rb +1 -1
  53. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.CoilHeatingGas.rb +1 -1
  54. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.Model.elevators.rb +1 -1
  55. data/lib/openstudio-standards/prototypes/ashrae_90_1/nrel_nze_ready_2017/nrel_zne_ready_2017.AirTerminalSingleDuctVAVReheat.rb +1 -1
  56. data/lib/openstudio-standards/prototypes/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.AirTerminalSingleDuctVAVReheat.rb +1 -1
  57. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.College.rb +7 -7
  58. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Courthouse.rb +8 -8
  59. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.FullServiceRestaurant.rb +14 -14
  60. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.HighRiseApartment.rb +9 -9
  61. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Hospital.rb +16 -16
  62. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Laboratory.rb +7 -7
  63. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeDataCenterHighITE.rb +8 -8
  64. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeDataCenterLowITE.rb +8 -8
  65. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeHotel.rb +11 -11
  66. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeOffice.rb +7 -7
  67. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeOfficeDetailed.rb +9 -9
  68. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.MediumOffice.rb +8 -8
  69. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.MediumOfficeDetailed.rb +11 -11
  70. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.MidriseApartment.rb +9 -9
  71. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Outpatient.rb +19 -19
  72. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.PrimarySchool.rb +10 -10
  73. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.QuickServiceRestaurant.rb +13 -13
  74. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.RetailStandalone.rb +6 -6
  75. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.RetailStripmall.rb +6 -6
  76. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SecondarySchool.rb +9 -9
  77. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallDataCenterHighITE.rb +8 -8
  78. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallDataCenterLowITE.rb +8 -8
  79. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallHotel.rb +8 -8
  80. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallOffice.rb +8 -8
  81. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallOfficeDetailed.rb +11 -11
  82. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperMarket.rb +10 -10
  83. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperTallBuilding.rb +19 -19
  84. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.TallBuilding.rb +18 -18
  85. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Warehouse.rb +6 -6
  86. data/lib/openstudio-standards/prototypes/common/do_not_edit_metaclasses.rb +957 -957
  87. data/lib/openstudio-standards/prototypes/common/objects/Prototype.AirConditionerVariableRefrigerantFlow.rb +1 -1
  88. data/lib/openstudio-standards/prototypes/common/objects/Prototype.AirTerminalSingleDuctVAVReheat.rb +1 -1
  89. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilCoolingWaterToAirHeatPumpEquationFit.rb +84 -16
  90. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingDXSingleSpeed.rb +1 -1
  91. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingGas.rb +1 -1
  92. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingWaterToAirHeatPumpEquationFit.rb +61 -10
  93. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ControllerWaterCoil.rb +1 -1
  94. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoolingTower.rb +1 -1
  95. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Fan.rb +1 -1
  96. data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanConstantVolume.rb +1 -1
  97. data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanOnOff.rb +1 -1
  98. data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanVariableVolume.rb +1 -1
  99. data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanZoneExhaust.rb +1 -1
  100. data/lib/openstudio-standards/prototypes/common/objects/Prototype.HeatExchangerAirToAirSensibleAndLatent.rb +2 -2
  101. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.exterior_lights.rb +4 -4
  102. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.hvac.rb +4 -4
  103. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.rb +43 -30
  104. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.swh.rb +1 -1
  105. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +18 -11
  106. data/lib/openstudio-standards/prototypes/common/objects/Prototype.SizingSystem.rb +1 -1
  107. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +774 -117
  108. data/lib/openstudio-standards/prototypes/common/objects/Prototype.radiant_system_controls.rb +340 -481
  109. data/lib/openstudio-standards/prototypes/common/objects/Prototype.refrigeration.rb +3 -3
  110. data/lib/openstudio-standards/prototypes/common/objects/Prototype.utilities.rb +3 -3
  111. data/lib/openstudio-standards/prototypes/common/prototype_metaprogramming.rb +22 -22
  112. data/lib/openstudio-standards/prototypes/deer/deer.Model.rb +1 -1
  113. data/lib/openstudio-standards/qaqc/calibration.rb +131 -0
  114. data/lib/openstudio-standards/qaqc/create_results.rb +983 -0
  115. data/lib/openstudio-standards/qaqc/envelope.rb +399 -0
  116. data/lib/openstudio-standards/qaqc/eui.rb +213 -0
  117. data/lib/openstudio-standards/qaqc/hvac.rb +1943 -0
  118. data/lib/openstudio-standards/qaqc/internal_loads.rb +568 -0
  119. data/lib/openstudio-standards/qaqc/reporting.rb +141 -0
  120. data/lib/openstudio-standards/qaqc/schedules.rb +129 -0
  121. data/lib/openstudio-standards/qaqc/service_water_heating.rb +273 -0
  122. data/lib/openstudio-standards/qaqc/weather_files.rb +497 -0
  123. data/lib/openstudio-standards/qaqc/zone_conditions.rb +278 -0
  124. data/lib/openstudio-standards/schedules/create.rb +364 -0
  125. data/lib/openstudio-standards/schedules/information.rb +169 -0
  126. data/lib/openstudio-standards/schedules/modify.rb +445 -0
  127. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +110 -71
  128. data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctParallelPIUReheat.rb +3 -3
  129. data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctVAVReheat.rb +4 -4
  130. data/lib/openstudio-standards/standards/Standards.BoilerHotWater.rb +2 -1
  131. data/lib/openstudio-standards/standards/Standards.ChillerElectricEIR.rb +16 -10
  132. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +4 -4
  133. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXTwoSpeed.rb +1 -1
  134. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +1 -1
  135. data/lib/openstudio-standards/standards/Standards.CoilDX.rb +4 -4
  136. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXMultiSpeed.rb +1 -1
  137. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +5 -5
  138. data/lib/openstudio-standards/standards/Standards.CoilHeatingGas.rb +1 -1
  139. data/lib/openstudio-standards/standards/Standards.CoilHeatingGasMultiStage.rb +1 -1
  140. data/lib/openstudio-standards/standards/Standards.CoilHeatingWaterToAirHeatPumpEquationFit.rb +1 -1
  141. data/lib/openstudio-standards/standards/Standards.Construction.rb +17 -18
  142. data/lib/openstudio-standards/standards/Standards.CoolingTower.rb +6 -6
  143. data/lib/openstudio-standards/standards/Standards.CoolingTowerSingleSpeed.rb +1 -1
  144. data/lib/openstudio-standards/standards/Standards.CoolingTowerTwoSpeed.rb +1 -1
  145. data/lib/openstudio-standards/standards/Standards.CoolingTowerVariableSpeed.rb +1 -1
  146. data/lib/openstudio-standards/standards/Standards.Fan.rb +6 -12
  147. data/lib/openstudio-standards/standards/Standards.FanVariableVolume.rb +2 -2
  148. data/lib/openstudio-standards/standards/Standards.FluidCooler.rb +1 -1
  149. data/lib/openstudio-standards/standards/Standards.HeaderedPumpsVariableSpeed.rb +1 -1
  150. data/lib/openstudio-standards/standards/Standards.HeatExchangerSensLat.rb +3 -3
  151. data/lib/openstudio-standards/standards/Standards.Model.rb +411 -261
  152. data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +2 -2
  153. data/lib/openstudio-standards/standards/Standards.PlantLoop.rb +94 -29
  154. data/lib/openstudio-standards/standards/Standards.Pump.rb +2 -2
  155. data/lib/openstudio-standards/standards/Standards.ScheduleConstant.rb +2 -2
  156. data/lib/openstudio-standards/standards/Standards.ScheduleRuleset.rb +14 -14
  157. data/lib/openstudio-standards/standards/Standards.Space.rb +37 -30
  158. data/lib/openstudio-standards/standards/Standards.SpaceType.rb +38 -29
  159. data/lib/openstudio-standards/standards/Standards.SubSurface.rb +7 -7
  160. data/lib/openstudio-standards/standards/Standards.Surface.rb +13 -13
  161. data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +109 -66
  162. data/lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb +11 -4
  163. data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +6 -6
  164. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.AirLoopHVAC.rb +1 -1
  165. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.PlantLoop.rb +1 -1
  166. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Space.rb +1 -1
  167. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/ashrae_90_1_2007.Space.rb +1 -1
  168. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.AirLoopHVAC.rb +4 -4
  169. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.AirTerminalSingleDuctVAVReheat.rb +1 -1
  170. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.Space.rb +4 -4
  171. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.AirLoopHVAC.rb +4 -4
  172. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.AirTerminalSingleDuctVAVReheat.rb +1 -1
  173. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.CoolingTowerVariableSpeed.rb +1 -1
  174. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.Model.rb +5 -21
  175. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.Space.rb +4 -4
  176. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.WaterHeaterMixed.rb +1 -1
  177. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.ZoneHVACComponent.rb +1 -1
  178. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.AirLoopHVAC.rb +36 -4
  179. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.AirTerminalSingleDuctVAVReheat.rb +1 -1
  180. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.CoolingTowerVariableSpeed.rb +1 -1
  181. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Space.rb +4 -4
  182. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.ZoneHVACComponent.rb +1 -1
  183. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/comstock_ashrae_90_1_2016/comstock_ashrae_90_1_2016.AirLoopHVAC.rb +26 -0
  184. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +53 -10
  185. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb +1 -1
  186. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoolingTowerVariableSpeed.rb +1 -1
  187. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb +6 -6
  188. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ZoneHVACComponent.rb +2 -2
  189. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/comstock_ashrae_90_1_2019/comstock_ashrae_90_1_2019.AirLoopHVAC.rb +26 -0
  190. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.curves.json +211 -211
  191. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.economizers.json +14 -14
  192. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.AirLoopHVAC.rb +4 -4
  193. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.Model.rb +1 -1
  194. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.PlantLoop.rb +1 -1
  195. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.economizers.json +14 -14
  196. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.AirLoopHVAC.rb +4 -4
  197. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.Model.rb +1 -1
  198. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.PlantLoop.rb +1 -1
  199. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.AirLoopHVAC.rb +6 -6
  200. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.AirTerminalSingleDuctVAVReheat.rb +1 -1
  201. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.CoolingTowerVariableSpeed.rb +1 -1
  202. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.PlantLoop.rb +1 -1
  203. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.Space.rb +4 -4
  204. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.AirLoopHVAC.rb +6 -6
  205. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.AirTerminalSingleDuctVAVReheat.rb +1 -1
  206. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.CoolingTowerVariableSpeed.rb +1 -1
  207. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.PlantLoop.rb +1 -1
  208. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.Space.rb +4 -4
  209. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirLoopHVAC.rb +22 -28
  210. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirTerminalSingleDuctParallelPIUReheat.rb +1 -1
  211. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirTerminalSingleDuctVAVReheat.rb +2 -2
  212. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.BoilerHotWater.rb +1 -74
  213. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ChillerElectricEIR.rb +7 -59
  214. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXSingleSpeed.rb +1 -1
  215. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXTwoSpeed.rb +1 -1
  216. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilDX.rb +1 -1
  217. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingDXSingleSpeed.rb +1 -1
  218. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingGas.rb +1 -21
  219. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.DesignSpecificationOutdoorAir.rb +101 -0
  220. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanConstantVolume.rb +1 -1
  221. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanOnOff.rb +1 -1
  222. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanVariableVolume.rb +1 -1
  223. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.HeatExchangerSensLat.rb +1 -1
  224. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Model.rb +643 -526
  225. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlanarSurface.rb +8 -2
  226. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +17 -77
  227. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Space.rb +74 -16
  228. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +96 -44
  229. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Surface.rb +6 -6
  230. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ThermalZone.rb +18 -6
  231. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ZoneHVACComponent.rb +1 -1
  232. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.rb +328 -74
  233. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +0 -118
  234. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.rb +2 -1
  235. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_rejection.json +1 -1
  236. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/baseline_outdoor_air.md +35 -0
  237. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_plug_load_measures.md +1 -1
  238. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/ashrae_90_1_prm.UserData.rb +228 -0
  239. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_enums.rb +131 -0
  240. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_space.csv +1 -1
  241. data/lib/openstudio-standards/standards/cbes/cbes.AirLoopHVAC.rb +5 -5
  242. data/lib/openstudio-standards/standards/cbes/cbes.Model.rb +1 -1
  243. data/lib/openstudio-standards/standards/cbes/cbes.PlantLoop.rb +1 -1
  244. data/lib/openstudio-standards/standards/cbes/cbes.Space.rb +1 -1
  245. data/lib/openstudio-standards/standards/cbes/cbes_t24_2005/cbes_t24_2005.Space.rb +1 -1
  246. data/lib/openstudio-standards/standards/cbes/cbes_t24_2008/cbes_t24_2008.Space.rb +1 -1
  247. data/lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb +109 -27
  248. data/lib/openstudio-standards/standards/deer/deer.Space.rb +1 -1
  249. data/lib/openstudio-standards/standards/deer/deer_1985/data/deer_1985.economizers.json +246 -4
  250. data/lib/openstudio-standards/standards/deer/deer_1996/data/deer_1996.economizers.json +246 -4
  251. data/lib/openstudio-standards/standards/deer/deer_2003/data/deer_2003.economizers.json +246 -4
  252. data/lib/openstudio-standards/standards/deer/deer_2003/deer_2003.ThermalZone.rb +18 -18
  253. data/lib/openstudio-standards/standards/deer/deer_2007/data/deer_2007.economizers.json +246 -4
  254. data/lib/openstudio-standards/standards/deer/deer_2007/deer_2007.ThermalZone.rb +18 -18
  255. data/lib/openstudio-standards/standards/deer/deer_2011/data/deer_2011.economizers.json +246 -4
  256. data/lib/openstudio-standards/standards/deer/deer_2011/deer_2011.ThermalZone.rb +18 -18
  257. data/lib/openstudio-standards/standards/deer/deer_2014/data/deer_2014.economizers.json +248 -6
  258. data/lib/openstudio-standards/standards/deer/deer_2014/deer_2014.Space.rb +3 -3
  259. data/lib/openstudio-standards/standards/deer/deer_2014/deer_2014.ThermalZone.rb +18 -18
  260. data/lib/openstudio-standards/standards/deer/deer_2015/data/deer_2015.economizers.json +248 -6
  261. data/lib/openstudio-standards/standards/deer/deer_2015/deer_2015.Space.rb +3 -3
  262. data/lib/openstudio-standards/standards/deer/deer_2015/deer_2015.ThermalZone.rb +18 -18
  263. data/lib/openstudio-standards/standards/deer/deer_2017/data/deer_2017.economizers.json +248 -6
  264. data/lib/openstudio-standards/standards/deer/deer_2017/deer_2017.Space.rb +3 -3
  265. data/lib/openstudio-standards/standards/deer/deer_2017/deer_2017.ThermalZone.rb +18 -18
  266. data/lib/openstudio-standards/standards/deer/deer_2020/data/deer_2020.economizers.json +248 -6
  267. data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.AirLoopHVAC.rb +18 -5
  268. data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.FanVariableVolume.rb +1 -1
  269. data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.Space.rb +3 -3
  270. data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.ThermalZone.rb +18 -18
  271. data/lib/openstudio-standards/standards/deer/deer_2025/data/deer_2025.economizers.json +248 -6
  272. data/lib/openstudio-standards/standards/deer/deer_2025/deer_2025.AirLoopHVAC.rb +3 -3
  273. data/lib/openstudio-standards/standards/deer/deer_2025/deer_2025.FanVariableVolume.rb +1 -1
  274. data/lib/openstudio-standards/standards/deer/deer_2025/deer_2025.Space.rb +3 -3
  275. data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.economizers.json +248 -6
  276. data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.heat_pumps.json +2 -2
  277. data/lib/openstudio-standards/standards/deer/deer_2030/deer_2030.AirLoopHVAC.rb +3 -3
  278. data/lib/openstudio-standards/standards/deer/deer_2030/deer_2030.FanVariableVolume.rb +1 -1
  279. data/lib/openstudio-standards/standards/deer/deer_2030/deer_2030.Space.rb +3 -3
  280. data/lib/openstudio-standards/standards/deer/deer_2035/data/deer_2035.economizers.json +248 -6
  281. data/lib/openstudio-standards/standards/deer/deer_2035/deer_2035.AirLoopHVAC.rb +3 -3
  282. data/lib/openstudio-standards/standards/deer/deer_2035/deer_2035.FanVariableVolume.rb +1 -1
  283. data/lib/openstudio-standards/standards/deer/deer_2035/deer_2035.Space.rb +3 -3
  284. data/lib/openstudio-standards/standards/deer/deer_2040/data/deer_2040.economizers.json +248 -6
  285. data/lib/openstudio-standards/standards/deer/deer_2040/deer_2040.AirLoopHVAC.rb +3 -3
  286. data/lib/openstudio-standards/standards/deer/deer_2040/deer_2040.FanVariableVolume.rb +1 -1
  287. data/lib/openstudio-standards/standards/deer/deer_2040/deer_2040.Space.rb +3 -3
  288. data/lib/openstudio-standards/standards/deer/deer_2045/data/deer_2045.economizers.json +260 -0
  289. data/lib/openstudio-standards/standards/deer/deer_2045/deer_2045.AirLoopHVAC.rb +3 -3
  290. data/lib/openstudio-standards/standards/deer/deer_2045/deer_2045.FanVariableVolume.rb +1 -1
  291. data/lib/openstudio-standards/standards/deer/deer_2045/deer_2045.Space.rb +3 -3
  292. data/lib/openstudio-standards/standards/deer/deer_2050/data/deer_2050.economizers.json +248 -6
  293. data/lib/openstudio-standards/standards/deer/deer_2050/deer_2050.AirLoopHVAC.rb +3 -3
  294. data/lib/openstudio-standards/standards/deer/deer_2050/deer_2050.FanVariableVolume.rb +1 -1
  295. data/lib/openstudio-standards/standards/deer/deer_2050/deer_2050.Space.rb +3 -3
  296. data/lib/openstudio-standards/standards/deer/deer_2055/data/deer_2055.economizers.json +248 -6
  297. data/lib/openstudio-standards/standards/deer/deer_2055/deer_2055.AirLoopHVAC.rb +3 -3
  298. data/lib/openstudio-standards/standards/deer/deer_2055/deer_2055.FanVariableVolume.rb +1 -1
  299. data/lib/openstudio-standards/standards/deer/deer_2055/deer_2055.Space.rb +3 -3
  300. data/lib/openstudio-standards/standards/deer/deer_2060/data/deer_2060.economizers.json +248 -6
  301. data/lib/openstudio-standards/standards/deer/deer_2060/deer_2060.AirLoopHVAC.rb +3 -3
  302. data/lib/openstudio-standards/standards/deer/deer_2060/deer_2060.FanVariableVolume.rb +1 -1
  303. data/lib/openstudio-standards/standards/deer/deer_2060/deer_2060.Space.rb +3 -3
  304. data/lib/openstudio-standards/standards/deer/deer_2065/data/deer_2065.economizers.json +248 -6
  305. data/lib/openstudio-standards/standards/deer/deer_2065/deer_2065.AirLoopHVAC.rb +3 -3
  306. data/lib/openstudio-standards/standards/deer/deer_2065/deer_2065.FanVariableVolume.rb +1 -1
  307. data/lib/openstudio-standards/standards/deer/deer_2065/deer_2065.Space.rb +3 -3
  308. data/lib/openstudio-standards/standards/deer/deer_2070/data/deer_2070.economizers.json +248 -6
  309. data/lib/openstudio-standards/standards/deer/deer_2070/deer_2070.AirLoopHVAC.rb +3 -3
  310. data/lib/openstudio-standards/standards/deer/deer_2070/deer_2070.FanVariableVolume.rb +1 -1
  311. data/lib/openstudio-standards/standards/deer/deer_2070/deer_2070.Space.rb +3 -3
  312. data/lib/openstudio-standards/standards/deer/deer_2075/data/deer_2075.economizers.json +248 -6
  313. data/lib/openstudio-standards/standards/deer/deer_2075/deer_2075.AirLoopHVAC.rb +3 -3
  314. data/lib/openstudio-standards/standards/deer/deer_2075/deer_2075.FanVariableVolume.rb +1 -1
  315. data/lib/openstudio-standards/standards/deer/deer_2075/deer_2075.Space.rb +3 -3
  316. data/lib/openstudio-standards/standards/deer/deer_pre_1975/data/deer_pre_1975.economizers.json +246 -4
  317. data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/space_types.json +447 -223
  318. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/building_envelope.rb +1 -1
  319. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/space_types.json +447 -223
  320. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_systems.rb +5 -2
  321. data/lib/openstudio-standards/standards/necb/ECMS/data/chiller_types.json +25 -0
  322. data/lib/openstudio-standards/standards/necb/ECMS/data/chillers.json +44 -0
  323. data/lib/openstudio-standards/standards/necb/ECMS/data/curves.json +225 -0
  324. data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +2 -2
  325. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +193 -73
  326. data/lib/openstudio-standards/standards/necb/ECMS/pv_ground.rb +1 -1
  327. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +10 -4
  328. data/lib/openstudio-standards/standards/necb/NECB2011/beps_compliance_path.rb +7 -7
  329. data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +4 -5
  330. data/lib/openstudio-standards/standards/necb/NECB2011/data/chiller_types.json +32 -0
  331. data/lib/openstudio-standards/standards/necb/NECB2011/data/chillers.json +1 -1
  332. data/lib/openstudio-standards/standards/necb/NECB2011/data/constants.json +36 -0
  333. data/lib/openstudio-standards/standards/necb/NECB2011/data/fuel_type_sets.json +7 -7
  334. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +47587 -0
  335. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +49764 -0
  336. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Warehouse.osm +283 -297
  337. data/lib/openstudio-standards/standards/necb/NECB2011/data/space_type_unit_definitions.txt +2 -1
  338. data/lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json +447 -223
  339. data/lib/openstudio-standards/standards/necb/NECB2011/data/standards_data.rb +3 -3
  340. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_multi_speed.rb +1 -1
  341. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +49 -27
  342. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +400 -202
  343. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +4 -4
  344. data/lib/openstudio-standards/standards/necb/NECB2015/data/space_types.json +637 -318
  345. data/lib/openstudio-standards/standards/necb/NECB2015/hvac_systems.rb +18 -1
  346. data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +3 -3
  347. data/lib/openstudio-standards/standards/necb/NECB2017/data/space_types.json +637 -318
  348. data/lib/openstudio-standards/standards/necb/NECB2017/hvac_systems.rb +1 -1
  349. data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +3 -3
  350. data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +1 -1
  351. data/lib/openstudio-standards/standards/necb/NECB2020/data/space_types.json +615 -307
  352. data/lib/openstudio-standards/standards/necb/NECB2020/service_water_heating.rb +4 -4
  353. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +10 -5
  354. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +1 -1
  355. data/lib/openstudio-standards/utilities/assertion.rb +128 -0
  356. data/lib/openstudio-standards/utilities/logging.rb +2 -3
  357. data/lib/openstudio-standards/utilities/object_info.rb +39 -18
  358. data/lib/openstudio-standards/utilities/schedule_translator.rb +8 -6
  359. data/lib/openstudio-standards/utilities/simulation.rb +24 -11
  360. data/lib/openstudio-standards/utilities/sqlfile.rb +10 -5
  361. data/lib/openstudio-standards/version.rb +1 -1
  362. data/lib/openstudio-standards/weather/Weather.Model.rb +8 -9
  363. data/lib/openstudio-standards/weather/Weather.stat_file.rb +3 -3
  364. data/lib/openstudio-standards/weather/information.rb +35 -0
  365. data/lib/openstudio-standards.rb +69 -5
  366. metadata +52 -16
  367. data/data/standards/OpenStudio_Standards-deer-ALL-comstock(space_types).xlsx +0 -0
  368. data/lib/openstudio-standards/hvac_sizing/Siz.AirLoopHVAC.rb +0 -59
  369. data/lib/openstudio-standards/hvac_sizing/Siz.CoilCoolingWater.rb +0 -13
  370. data/lib/openstudio-standards/hvac_sizing/Siz.HVACComponent.rb +0 -36
  371. data/lib/openstudio-standards/hvac_sizing/Siz.HeatingCoolingFuels.rb +0 -898
  372. data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +0 -126
  373. data/lib/openstudio-standards/hvac_sizing/Siz.ThermalZone.rb +0 -356
  374. data/lib/openstudio-standards/prototypes/ashrae_90_1/nrel_nze_ready_2017/nrel_zne_ready_2017.Model.rb +0 -35
  375. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoolingTower.rb +0 -110
  376. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoolingTowerVariableSpeed.rb +0 -5
@@ -1,25 +1,10 @@
1
1
  class ASHRAE901PRM < Standard
2
2
  # @!group Model
3
3
 
4
- # Determines the area of the building above which point
5
- # the non-dominant area type gets it's own HVAC system type.
6
- # @return [Double] the minimum area (m^2)
7
- def model_prm_baseline_system_group_minimum_area(model, custom)
8
- exception_min_area_ft2 = 20_000
9
- # Customization - Xcel EDA Program Manual 2014
10
- # 3.2.1 Mechanical System Selection ii
11
- if custom == 'Xcel Energy CO EDA'
12
- exception_min_area_ft2 = 5000
13
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Customization; per Xcel EDA Program Manual 2014 3.2.1 Mechanical System Selection ii, minimum area for non-predominant conditions reduced to #{exception_min_area_ft2} ft2.")
14
- end
15
- exception_min_area_m2 = OpenStudio.convert(exception_min_area_ft2, 'ft^2', 'm^2').get
16
- return exception_min_area_m2
17
- end
18
-
19
- # Determines which system number is used
20
- # for the baseline system.
21
- # @return [String] the system number: 1_or_2, 3_or_4,
22
- # 5_or_6, 7_or_8, 9_or_10
4
+ # Determines which system number is used for the baseline system.
5
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
6
+ # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
7
+ # @return [String] the system number: 1_or_2, 3_or_4, 5_or_6, 7_or_8, 9_or_10
23
8
  def model_prm_baseline_system_number(model, climate_zone, area_type, fuel_type, area_ft2, num_stories, custom)
24
9
  sys_num = nil
25
10
 
@@ -84,13 +69,12 @@ class ASHRAE901PRM < Standard
84
69
 
85
70
  # Change the fuel type based on climate zone, depending on the standard.
86
71
  # For 90.1-2013, fuel type is based on climate zone, not the proposed model.
72
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
73
+ # @param fuel_type [String] Valid choices are electric, fossil, fossilandelectric,
74
+ # purchasedheat, purchasedcooling, purchasedheatandcooling
75
+ # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
87
76
  # @return [String] the revised fuel type
88
- def model_prm_baseline_system_change_fuel_type(model, fuel_type, climate_zone, custom = nil)
89
- if custom == 'Xcel Energy CO EDA'
90
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', 'Custom; per Xcel EDA Program Manual 2014 Table 3.2.2 Baseline HVAC System Types, the 90.1-2010 rules for heating fuel type (based on proposed model) rules apply.')
91
- return fuel_type
92
- end
93
-
77
+ def model_prm_baseline_system_change_fuel_type(model, fuel_type, climate_zone)
94
78
  # For 90.1-2013 the fuel type is determined based on climate zone.
95
79
  # Don't change the fuel if it purchased heating or cooling.
96
80
  if fuel_type == 'electric' || fuel_type == 'fossil'
@@ -112,31 +96,21 @@ class ASHRAE901PRM < Standard
112
96
  end
113
97
 
114
98
  # Determines the fan type used by VAV_Reheat and VAV_PFP_Boxes systems.
115
- # Variable speed fan for 90.1-2013
99
+ # Variable speed fan for 90.1-2019
100
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
116
101
  # @return [String] the fan type: TwoSpeed Fan, Variable Speed Fan
117
102
  def model_baseline_system_vav_fan_type(model)
118
103
  fan_type = 'Variable Speed Fan'
119
104
  return fan_type
120
105
  end
121
106
 
122
- # This method creates customized infiltration objects for each
123
- # space and removes the SpaceType-level infiltration objects.
107
+ # Calculate the building envelope area according to the 90.1 definition
124
108
  #
125
- # @return [Bool] true if successful, false if not
126
- def model_baseline_apply_infiltration_standard(model, climate_zone)
127
- # Model shouldn't use SpaceInfiltrationEffectiveLeakageArea
128
- # Excerpt from the EnergyPlus Input/Output reference manual:
129
- # "This model is based on work by Sherman and Grimsrud (1980)
130
- # and is appropriate for smaller, residential-type buildings."
131
- # Return an error if the model does use this object
132
- ela = 0
133
- model.getSpaceInfiltrationEffectiveLeakageAreas.sort.each do |eff_la|
134
- ela += 1
135
- end
136
- if ela > 0
137
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', 'The current model cannot include SpaceInfiltrationEffectiveLeakageArea. These objects cannot be used to model infiltration according to the 90.1-PRM rules.')
138
- end
139
-
109
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
110
+ # @return [Double] Building envelope area in m2
111
+ def model_building_envelope_area(model)
112
+ # Get climate zone
113
+ climate_zone = model_standards_climate_zone(model)
140
114
  # Get the space building envelope area
141
115
  # According to the 90.1 definition, building envelope include:
142
116
  # - "the elements of a building that separate conditioned spaces from the exterior"
@@ -145,7 +119,7 @@ class ASHRAE901PRM < Standard
145
119
  # transferred to or from the exterior, to or from unconditioned spaces or to or
146
120
  # from conditioned spaces."
147
121
  building_envelope_area_m2 = 0
148
- model.getSpaces.sort.each do |space|
122
+ model.getSpaces.each do |space|
149
123
  building_envelope_area_m2 += space_envelope_area(space, climate_zone)
150
124
  end
151
125
  if building_envelope_area_m2 == 0.0
@@ -153,13 +127,39 @@ class ASHRAE901PRM < Standard
153
127
  return 0.0
154
128
  end
155
129
 
130
+ return building_envelope_area_m2
131
+ end
132
+
133
+ # This method creates customized infiltration objects for each
134
+ # space and removes the SpaceType-level infiltration objects.
135
+ # @param model [OpenStudio::Model::Model] openstudio model
136
+ # @param specific_space_infiltration_rate_75_pa [Double] space infiltration rate at a pressure differential of 75 Pa
137
+ # @return [Boolean] true if successful, false if not
138
+ def model_apply_standard_infiltration(model, specific_space_infiltration_rate_75_pa = nil)
139
+ # Model shouldn't use SpaceInfiltrationEffectiveLeakageArea
140
+ # Excerpt from the EnergyPlus Input/Output reference manual:
141
+ # "This model is based on work by Sherman and Grimsrud (1980)
142
+ # and is appropriate for smaller, residential-type buildings."
143
+ # Raise exception if the model does use this object
144
+ ela = 0
145
+ model.getSpaceInfiltrationEffectiveLeakageAreas.sort.each do |eff_la|
146
+ ela += 1
147
+ end
148
+ if ela > 0
149
+ OpenStudio.logFree(OpenStudio::Warn, 'prm.log', 'The current model cannot include SpaceInfiltrationEffectiveLeakageArea. These objects will be skipped in modeling infiltration according to the 90.1-PRM rules.')
150
+ end
151
+
152
+ # Get the space building envelope area
153
+ building_envelope_area_m2 = model_building_envelope_area(model)
154
+ prm_raise(building_envelope_area_m2 > 0.0, @sizing_run_dir, 'Calculated building envelope area is 0 m2, Please check model inputs.')
155
+
156
156
  # Calculate current model air leakage rate @ 75 Pa and report it
157
157
  curr_tot_infil_m3_per_s_per_envelope_area = model_current_building_envelope_infiltration_at_75pa(model, building_envelope_area_m2)
158
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The proposed model I_75Pa is estimated to be #{curr_tot_infil_m3_per_s_per_envelope_area} m3/s per m2 of total building envelope.")
158
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "The model's I_75Pa is estimated to be #{curr_tot_infil_m3_per_s_per_envelope_area} m3/s per m2 of total building envelope.")
159
159
 
160
160
  # Calculate building adjusted building envelope
161
161
  # air infiltration following the 90.1 PRM rules
162
- tot_infil_m3_per_s = model_adjusted_building_envelope_infiltration(model, building_envelope_area_m2)
162
+ tot_infil_m3_per_s = model_adjusted_building_envelope_infiltration(building_envelope_area_m2, specific_space_infiltration_rate_75_pa)
163
163
 
164
164
  # Find infiltration method used in the model, if any.
165
165
  #
@@ -171,12 +171,12 @@ class ASHRAE901PRM < Standard
171
171
  infil_coefficients = model_get_infiltration_coefficients(model)
172
172
 
173
173
  # Set the infiltration rate at each space
174
- model.getSpaces.sort.each do |space|
174
+ model.getSpaces.each do |space|
175
175
  space_apply_infiltration_rate(space, tot_infil_m3_per_s, infil_method, infil_coefficients)
176
176
  end
177
177
 
178
178
  # Remove infiltration rates set at the space type
179
- model.getSpaceTypes.sort.each do |space_type|
179
+ model.getSpaceTypes.each do |space_type|
180
180
  space_type.spaceInfiltrationDesignFlowRates.each(&:remove)
181
181
  end
182
182
 
@@ -184,13 +184,13 @@ class ASHRAE901PRM < Standard
184
184
  end
185
185
 
186
186
  # This method retrieves the type of infiltration input
187
- # used in the model. If input is inconsitent, returns
187
+ # used in the model. If input is inconsistent, returns
188
188
  # Flow/Area
189
- #
189
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
190
190
  # @return [String] infiltration input type
191
191
  def model_get_infiltration_method(model)
192
192
  infil_method = nil
193
- model.getSpaces.sort.each do |space|
193
+ model.getSpaces.each do |space|
194
194
  # Infiltration at the space level
195
195
  unless space.spaceInfiltrationDesignFlowRates.empty?
196
196
  old_infil = space.spaceInfiltrationDesignFlowRates[0]
@@ -221,7 +221,7 @@ class ASHRAE901PRM < Standard
221
221
  # This method retrieves the infiltration coefficients
222
222
  # used in the model. If input is inconsitent, returns
223
223
  # [0, 0, 0.224, 0] as per PRM user manual
224
- #
224
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
225
225
  # @return [String] infiltration input type
226
226
  def model_get_infiltration_coefficients(model)
227
227
  cst = nil
@@ -229,7 +229,7 @@ class ASHRAE901PRM < Standard
229
229
  vel = nil
230
230
  vel_2 = nil
231
231
  infil_coeffs = [cst, temp, vel, vel_2]
232
- model.getSpaces.sort.each do |space|
232
+ model.getSpaces.each do |space|
233
233
  # Infiltration at the space level
234
234
  unless space.spaceInfiltrationDesignFlowRates.empty?
235
235
  old_infil = space.spaceInfiltrationDesignFlowRates[0]
@@ -270,52 +270,46 @@ class ASHRAE901PRM < Standard
270
270
  return infil_coeffs
271
271
  end
272
272
 
273
- # This methods calculate the current model air leakage rate @ 75 Pa.
274
- # It assumes that the model follows the PRM methods, see G3.1.1.4
275
- # in 90.1-2019 for reference.
276
- #
277
- # @param [OpenStudio::Model::Model] OpenStudio Model object
278
- # @param [Double] Building envelope area as per 90.1 in m^2
273
+ # This methods calculate the air leakage rate of a space
279
274
  #
280
- # @return [Float] building model air leakage rate
281
- def model_current_building_envelope_infiltration_at_75pa(model, building_envelope_area_m2)
282
- bldg_air_leakage_rate = 0
283
- model.getSpaces.sort.each do |space|
284
- # Infiltration at the space level
285
- unless space.spaceInfiltrationDesignFlowRates.empty?
286
- infil_obj = space.spaceInfiltrationDesignFlowRates[0]
275
+ # @param space [OpenStudio::Model::Space] OpenStudio Space object
276
+ # @return [Double] Space air leakage rate
277
+ def model_get_space_air_leakage(space)
278
+ space_air_leakage = 0
279
+ space_multipler = space.multiplier
280
+ # Infiltration at the space level
281
+ unless space.spaceInfiltrationDesignFlowRates.empty?
282
+ space.spaceInfiltrationDesignFlowRates.each do |infil_obj|
287
283
  unless infil_obj.designFlowRate.is_initialized
288
284
  if infil_obj.flowperSpaceFloorArea.is_initialized
289
- bldg_air_leakage_rate += infil_obj.flowperSpaceFloorArea.get * space.floorArea
285
+ space_air_leakage += infil_obj.flowperSpaceFloorArea.get * space.floorArea * space_multipler
290
286
  elsif infil_obj.flowperExteriorSurfaceArea.is_initialized
291
- bldg_air_leakage_rate += infil_obj.flowperExteriorSurfaceArea.get * space.exteriorArea
287
+ space_air_leakage += infil_obj.flowperExteriorSurfaceArea.get * space.exteriorArea * space_multipler
292
288
  elsif infil_obj.flowperExteriorWallArea.is_initialized
293
- bldg_air_leakage_rate += infil_obj.flowperExteriorWallArea.get * space.exteriorWallArea
289
+ space_air_leakage += infil_obj.flowperExteriorWallArea.get * space.exteriorWallArea * space_multipler
294
290
  elsif infil_obj.airChangesperHour.is_initialized
295
- bldg_air_leakage_rate += infil_obj.airChangesperHour.get * space.volume / 3600
291
+ space_air_leakage += infil_obj.airChangesperHour.get * space.volume * space_multipler / 3600
296
292
  end
297
293
  end
298
294
  end
295
+ end
299
296
 
300
- # Infiltration at the space type level
301
- if space.spaceType.is_initialized
302
- space_type = space.spaceType.get
303
- unless space_type.spaceInfiltrationDesignFlowRates.empty?
304
- infil_obj = space_type.spaceInfiltrationDesignFlowRates[0]
305
- unless infil_obj.designFlowRate.is_initialized
306
- if infil_obj.flowperSpaceFloorArea.is_initialized
307
- bldg_air_leakage_rate += infil_obj.flowperSpaceFloorArea.get * space.floorArea
308
- elsif infil_obj.flowperExteriorSurfaceArea.is_initialized
309
- bldg_air_leakage_rate += infil_obj.flowperExteriorSurfaceArea.get * space.exteriorArea
310
- elsif infil_obj.flowperExteriorWallArea.is_initialized
311
- bldg_air_leakage_rate += infil_obj.flowperExteriorWallArea.get * space.exteriorWallArea
312
- elsif infil_obj.airChangesperHour.is_initialized
313
- bldg_air_leakage_rate += infil_obj.airChangesperHour.get * space.volume / 3600
314
- end
315
- end
316
- end
317
- end
297
+ return space_air_leakage
298
+ end
299
+
300
+ # This methods calculate the current model air leakage rate @ 75 Pa.
301
+ # It assumes that the model follows the PRM methods, see G3.1.1.4
302
+ # in 90.1-2019 for reference.
303
+ #
304
+ # @param model [OpenStudio::Model::Model] OpenStudio Model object
305
+ # @param building_envelope_area_m2 [Double] Building envelope area as per 90.1 in m^2
306
+ # @return [Double] building model air leakage rate
307
+ def model_current_building_envelope_infiltration_at_75pa(model, building_envelope_area_m2)
308
+ bldg_air_leakage_rate = 0
309
+ model.getSpaces.each do |space|
310
+ bldg_air_leakage_rate += model_get_space_air_leakage(space)
318
311
  end
312
+
319
313
  # adjust_infiltration_to_prototype_building_conditions(1) corresponds
320
314
  # to the 0.112 shown in G3.1.1.4
321
315
  curr_tot_infil_m3_per_s_per_envelope_area = bldg_air_leakage_rate / adjust_infiltration_to_prototype_building_conditions(1) / building_envelope_area_m2
@@ -325,13 +319,16 @@ class ASHRAE901PRM < Standard
325
319
  # This method calculates the building envelope infiltration,
326
320
  # this approach uses the 90.1 PRM rules
327
321
  #
328
- # @return [Float] building envelope infiltration
329
- def model_adjusted_building_envelope_infiltration(model, building_envelope_area_m2)
322
+ # @param building_envelope_area_m2 [Double] Building envelope area as per 90.1 in m^2
323
+ # @param specific_space_infiltration_rate_75_pa [Double] Specific space infiltration rate at 75 pa
324
+ # @return [Double] building envelope infiltration
325
+ def model_adjusted_building_envelope_infiltration(building_envelope_area_m2, specific_space_infiltration_rate_75_pa = nil)
330
326
  # Determine the total building baseline infiltration rate in cfm per ft2 of the building envelope at 75 Pa
331
- basic_infil_rate_cfm_per_ft2 = space_infiltration_rate_75_pa
332
-
333
- # Do nothing if no infiltration
334
- return 0.0 if basic_infil_rate_cfm_per_ft2.zero?
327
+ if specific_space_infiltration_rate_75_pa.nil?
328
+ basic_infil_rate_cfm_per_ft2 = space_infiltration_rate_75_pa
329
+ else
330
+ basic_infil_rate_cfm_per_ft2 = specific_space_infiltration_rate_75_pa
331
+ end
335
332
 
336
333
  # Conversion factor
337
334
  conv_fact = OpenStudio.convert(1, 'm^3/s', 'ft^3/min').to_f / OpenStudio.convert(1, 'm^2', 'ft^2').to_f
@@ -349,10 +346,11 @@ class ASHRAE901PRM < Standard
349
346
 
350
347
  # Apply the standard construction to each surface in the model, based on the construction type currently assigned.
351
348
  #
352
- # @return [Bool] true if successful, false if not
353
349
  # @param model [OpenStudio::Model::Model] OpenStudio model object
354
350
  # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
355
- # @return [Bool] returns true if successful, false if not
351
+ # @param wwr_building_type [String] building type used for defining window to wall ratio, e.g. 'Office > 50,000 sq ft'
352
+ # @param wwr_info [Hash] A map that maps each building area type to its correspondent wwr.
353
+ # @return [Boolean] returns true if successful, false if not
356
354
  def model_apply_standard_constructions(model, climate_zone, wwr_building_type: nil, wwr_info: {})
357
355
  types_to_modify = []
358
356
 
@@ -502,10 +500,9 @@ class ASHRAE901PRM < Standard
502
500
  # Go through the default construction sets and hard-assigned constructions.
503
501
  # Clone the existing constructions and set their intended surface type and standards construction type per the PRM.
504
502
  # For some standards, this will involve making modifications. For others, it will not.
505
- #
506
- # 90.1-2007, 90.1-2010, 90.1-2013
503
+ # 90.1-2019
507
504
  # @param model [OpenStudio::Model::Model] OpenStudio model object
508
- # @return [Bool] returns true if successful, false if not
505
+ # @return [Boolean] returns true if successful, false if not
509
506
  def model_apply_prm_construction_types(model)
510
507
  types_to_modify = []
511
508
 
@@ -581,6 +578,10 @@ class ASHRAE901PRM < Standard
581
578
  types_to_modify << ['Outdoors', 'ExteriorWall', 'SteelFramed']
582
579
  types_to_modify << ['Outdoors', 'ExteriorRoof', 'IEAD']
583
580
  types_to_modify << ['Outdoors', 'ExteriorFloor', 'SteelFramed']
581
+ types_to_modify << ['Outdoors', 'ExteriorWindow', 'Any Vertical Glazing']
582
+ types_to_modify << ['Outdoors', 'GlassDoor', 'Any Vertical Glazing']
583
+ types_to_modify << ['Outdoors', 'ExteriorDoor', 'NonSwinging']
584
+ types_to_modify << ['Outdoors', 'ExteriorDoor', 'Swinging']
584
585
  types_to_modify << ['Ground', 'GroundContactFloor', 'Unheated']
585
586
  types_to_modify << ['Ground', 'GroundContactWall', 'Mass']
586
587
 
@@ -625,7 +626,8 @@ class ASHRAE901PRM < Standard
625
626
 
626
627
  # Reduces the SRR to the values specified by the PRM. SRR reduction will be done by shrinking vertices toward the centroid.
627
628
  #
628
- # @param model [OpenStudio::model::Model] OpenStudio model object
629
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
630
+ # @return [Boolean] returns true if successful, false if not
629
631
  def model_apply_prm_baseline_skylight_to_roof_ratio(model)
630
632
  # Loop through all spaces in the model, and
631
633
  # per the 90.1-2019 PRM User Manual, only
@@ -707,71 +709,18 @@ class ASHRAE901PRM < Standard
707
709
  # Apply baseline values to exterior lights objects
708
710
  # Characterization of objects must be done via user data
709
711
  #
710
- # @param model [OpenStudio::model::Model] OpenStudio model object
712
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
711
713
  def model_apply_baseline_exterior_lighting(model)
712
- user_ext_lights = @standards_data.key?('userdata_exterior_lights') ? @standards_data['userdata_exterior_lights'] : nil
713
- return false if user_ext_lights.nil?
714
-
715
- non_tradeable_cats = ['nontradeable_general', 'building_facades_area', 'building_facades_perim', 'automated_teller_machines_per_location', 'automated_teller_machines_per_machine', 'entries_and_gates', 'loading_areas_for_emergency_vehicles', 'drive_through_windows_and_doors', 'parking_near_24_hour_entrances', 'roadway_parking']
716
- search_criteria = {
717
- 'template' => template
718
- }
719
-
720
- ext_ltg_baseline_values = standards_lookup_table_first(table_name: 'prm_exterior_lighting', search_criteria: search_criteria)
721
-
722
- user_ext_lights.each do |user_data|
723
- lights_name = user_data['name']
724
-
725
- # model.getExteriorLightss.each do |exterior_lights|
726
-
727
- if model.getExteriorLightsByName(lights_name).is_initialized
728
- ext_lights_obj = model.getExteriorLightsByName(lights_name).get
729
- else
730
- # Report invalid name in user data
731
- OpenStudio.logFree(OpenStudio::Warn, 'prm.log', "ExteriorLights object named #{lights_name} from user data file not found in model")
732
- next
733
- end
734
-
735
- # Make sure none of the categories are nontradeable and not a mix of tradeable and nontradeable
736
- num_trade = 0
737
- num_notrade = 0
738
- ext_ltg_cats = {}
739
- num_cats = user_data['num_ext_lights_subcats'].to_i
740
- (1..num_cats).each do |icat|
741
- cat_key = format('end_use_subcategory_%02d', icat)
742
- subcat = user_data[cat_key]
743
- if non_tradeable_cats.include?(subcat)
744
- num_notrade += 1
745
- else
746
- num_trade += 1
747
- meas_val_key = format('end_use_measurement_value_%02d', icat)
748
- meas_val = user_data[meas_val_key]
749
- ext_ltg_cats[subcat] = meas_val.to_f
750
- end
751
- end
752
-
753
- # Skip this if all lights are non-tradeable
754
- next if num_trade == 0
755
-
756
- # Error if mix of tradeable and nontradeable
757
- if (num_trade > 0) && (num_notrade > 0)
758
- OpenStudio.logFree(OpenStudio::Warn, 'prm.log', "ExteriorLights object named #{lights_name} from user data file has mix of tradeable and non-tradeable lighting types. All will be treated as non-tradeable.")
759
- next
760
- end
761
-
762
- ext_ltg_pwr = 0
763
- ext_ltg_cats.each do |cat_key, meas_val|
764
- # Get baseline power for this type of exterior lighting
765
- baseline_value = ext_ltg_baseline_values[cat_key].to_f
766
- ext_ltg_pwr += baseline_value * meas_val
767
- end
768
-
714
+ model.getExteriorLightss.each do |ext_lights_obj|
769
715
  # Update existing exterior lights object: control, schedule, power
770
716
  ext_lights_obj.setControlOption('AstronomicalClock')
771
717
  ext_lights_obj.setSchedule(model.alwaysOnDiscreteSchedule)
772
718
  ext_lights_obj.setMultiplier(1)
773
719
  ext_lights_def = ext_lights_obj.exteriorLightsDefinition
774
- ext_lights_def.setDesignLevel(ext_ltg_pwr)
720
+ ext_ltg_pwr = get_additional_property_as_double(ext_lights_obj, 'design_level', 0.0)
721
+ if ext_ltg_pwr > 0.0
722
+ ext_lights_def.setDesignLevel(ext_ltg_pwr)
723
+ end
775
724
  end
776
725
  end
777
726
 
@@ -779,20 +728,19 @@ class ASHRAE901PRM < Standard
779
728
  # @param model [OpenStudio::Model::Model] OpenStudio model object
780
729
  def model_add_prm_elevators(model)
781
730
  # Load elevator data from userdata csv files
782
- user_elevators = @standards_data.key?('userdata_electric_equipment') ? @standards_data['userdata_electric_equipment'] : nil
783
- return false if user_elevators.nil?
784
-
785
- user_elevators.each do |user_elevator|
786
- num_lifts = user_elevator['elevator_number_of_lifts'].to_i
787
- next if num_lifts == 0
788
-
789
- equip_name = user_elevator['name']
790
- number_of_levels = user_elevator['elevator_number_of_stories'].to_i
791
-
792
- elevator_weight_of_car = user_elevator['elevator_weight_of_car'].to_f
793
- elevator_rated_load = user_elevator['elevator_rated_load'].to_f
794
- elevator_speed_of_car = user_elevator['elevator_speed_of_car'].to_f
795
- if number_of_levels < 5
731
+ equipment_array = model.getElectricEquipments + model.getExteriorFuelEquipments
732
+ equipment_array.each do |equipment|
733
+ elevator_number_of_lifts = get_additional_property_as_integer(equipment, 'elevator_number_of_lifts', 0)
734
+ next unless elevator_number_of_lifts > 0.0
735
+
736
+ elevator_name = equipment.name.get
737
+ elevator_number_of_stories = get_additional_property_as_integer(equipment, 'elevator_number_of_stories', 0)
738
+ elevator_weight_of_car = get_additional_property_as_double(equipment, 'elevator_weight_of_car', 0.0)
739
+ elevator_rated_load = get_additional_property_as_double(equipment, 'elevator_rated_load', 0.0)
740
+ elevator_speed_of_car = get_additional_property_as_double(equipment, 'elevator_speed_of_car', 0.0)
741
+ elevator_counter_weight_of_car = get_additional_property_as_double(equipment, 'elevator_counter_weight_of_car', 0.0)
742
+
743
+ if elevator_number_of_stories < 5
796
744
  # From Table G3.9.2 performance rating method baseline elevator motor
797
745
  elevator_mech_eff = 0.58
798
746
  elevator_counter_weight_of_car = 0.0
@@ -804,29 +752,23 @@ class ASHRAE901PRM < Standard
804
752
  # From Table G3.9.2 performance rating method baseline elevator motor
805
753
  elevator_mech_eff = 0.64
806
754
  # Determine the elevator counterweight
807
- if user_elevator['elevator_counter_weight_of_car'].nil?
755
+ if elevator_counter_weight_of_car == 0.0
808
756
  # When the proposed design counterweight is not specified
809
757
  # it is determined as per Table G3.9.2
810
758
  elevator_counter_weight_of_car = elevator_weight_of_car + 0.4 * elevator_rated_load
811
- else
812
- elevator_counter_weight_of_car = user_elevator['elevator_counter_weight_of_car'].to_f
813
759
  end
814
760
  search_criteria = {
815
761
  'template' => template,
816
762
  'type' => 'Any'
817
763
  }
818
764
  end
819
-
820
- elevator_motor_bhp = (elevator_weight_of_car + elevator_rated_load - elevator_counter_weight_of_car) * elevator_speed_of_car / (33000 * elevator_mech_eff)
821
-
822
- # Lookup the minimum motor efficiency
765
+ elevator_motor_bhp = (elevator_weight_of_car + elevator_rated_load - elevator_counter_weight_of_car) * elevator_speed_of_car / (33000 * elevator_mech_eff) # Lookup the minimum motor efficiency
823
766
  elevator_motor_eff = standards_data['motors']
824
767
  motor_properties = model_find_object(elevator_motor_eff, search_criteria, nil, nil, nil, nil, elevator_motor_bhp)
825
768
  if motor_properties.nil?
826
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.elevator', "For #{equip_name}, could not find motor properties using search criteria: #{search_criteria}, motor_bhp = #{motor_bhp} hp.")
769
+ OpenStudio.logFree(OpenStudio::Error, 'prm.log', "For #{elevator_name}, could not find motor properties using search criteria: #{search_criteria}, motor_bhp = #{elevator_motor_bhp} hp.")
827
770
  return false
828
771
  end
829
-
830
772
  nominal_hp = motor_properties['maximum_capacity'].to_f.round(1)
831
773
  # Round to nearest whole HP for niceness
832
774
  if nominal_hp >= 2
@@ -837,33 +779,28 @@ class ASHRAE901PRM < Standard
837
779
  # Add 0.01 hp to avoid search errors.
838
780
  motor_properties = model_find_object(elevator_motor_eff, search_criteria, nil, nil, nil, nil, nominal_hp + 0.01)
839
781
  if motor_properties.nil?
840
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.model', "For #{equip_name}, could not find nominal motor properties using search criteria: #{search_criteria}, motor_hp = #{nominal_hp} hp.")
782
+ OpenStudio.logFree(OpenStudio::Error, 'prm.log', "For #{elevator_name}, could not find nominal motor properties using search criteria: #{search_criteria}, motor_hp = #{nominal_hp} hp.")
841
783
  return false
842
784
  end
843
785
  motor_eff = motor_properties['nominal_full_load_efficiency'].to_f
844
- elevator_power = num_lifts * elevator_motor_bhp * 746 / motor_eff
786
+ elevator_power = elevator_number_of_lifts * elevator_motor_bhp * 746 / motor_eff
845
787
 
846
- # Set elevator power to either regular electric equipment object or
847
- # exterior fuel equipment
848
- if model.getElectricEquipmentByName(equip_name).is_initialized
849
- model.getElectricEquipmentByName(equip_name).get.electricEquipmentDefinition.setDesignLevel(elevator_power)
850
- elevator_space = model.getElectricEquipmentByName(equip_name).get.space.get
851
- end
852
- if model.getExteriorFuelEquipmentByName(equip_name).is_initialized
853
- model.getExteriorFuelEquipmentByName(equip_name).exteriorFuelEquipmentDefinition.setDesignLevel(elevator_power)
854
- elevator_space = model.getElectricEquipmentByName(equip_name).get.space.get
788
+ if equipment.is_a?(OpenStudio::Model::ElectricEquipment)
789
+ equipment.electricEquipmentDefinition.setDesignLevel(elevator_power)
790
+ else
791
+ equipment.exteriorFuelEquipmentDefinition.setDesignLevel(elevator_power)
855
792
  end
856
-
793
+ elevator_space = prm_get_optional_handler(equipment, @sizing_run_dir, 'space')
857
794
  # Add ventilation and lighting process loads if modeled in the proposed model
858
795
  misc_elevator_process_loads = 0.0
859
- misc_elevator_process_loads += user_elevator['elevator_ventilation_cfm'].to_f * 0.33
860
- misc_elevator_process_loads += user_elevator['elevator_area_ft2'].to_f * 3.14
796
+ misc_elevator_process_loads += get_additional_property_as_double(equipment, 'elevator_ventilation_cfm', 0.0) * 0.33
797
+ misc_elevator_process_loads += get_additional_property_as_double(equipment, 'elevator_area_ft2', 0.0) * 3.14
861
798
  if misc_elevator_process_loads > 0
862
799
  misc_elevator_process_loads_def = OpenStudio::Model::ElectricEquipmentDefinition.new(model)
863
- misc_elevator_process_loads_def.setName("#{equip_name} - Misc Process Loads - Def")
800
+ misc_elevator_process_loads_def.setName("#{elevator_name} - Misc Process Loads - Def")
864
801
  misc_elevator_process_loads_def.setDesignLevel(misc_elevator_process_loads)
865
802
  misc_elevator_process_loads = OpenStudio::Model::ElectricEquipment.new(misc_elevator_process_loads_def)
866
- misc_elevator_process_loads.setName("#{equip_name} - Misc Process Loads")
803
+ misc_elevator_process_loads.setName("#{elevator_name} - Misc Process Loads")
867
804
  misc_elevator_process_loads.setEndUseSubcategory('Elevators')
868
805
  misc_elevator_process_loads.setSchedule(model.alwaysOnDiscreteSchedule)
869
806
  misc_elevator_process_loads.setSpace(elevator_space)
@@ -873,20 +810,13 @@ class ASHRAE901PRM < Standard
873
810
 
874
811
  # Add design day schedule objects for space loads, for PRM 2019 baseline models
875
812
  # @author Xuechen (Jerry) Lei, PNNL
876
- # @param model [OpenStudio::model::Model] OpenStudio model object
877
- #
813
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
878
814
  def model_apply_prm_baseline_sizing_schedule(model)
879
815
  space_loads = model.getSpaceLoads
880
816
  loads = []
881
817
  space_loads.sort.each do |space_load|
882
- load_type = space_load.iddObjectType.valueName.sub('OS_', '').strip.sub('_', '')
883
- casting_method_name = "to_#{load_type}"
884
- if space_load.respond_to?(casting_method_name)
885
- casted_load = space_load.public_send(casting_method_name).get
886
- loads << casted_load
887
- else
888
- p 'Need Debug, casting method not found @JXL'
889
- end
818
+ casted_load = model_cast_model_object(space_load)
819
+ loads << casted_load unless casted_load.nil?
890
820
  end
891
821
 
892
822
  load_schedule_name_hash = {
@@ -966,40 +896,31 @@ class ASHRAE901PRM < Standard
966
896
  # @note This is not applicable to the stable baseline; hence no action in this method
967
897
  #
968
898
  # @param model [OpenStudio::Model::Model] OpenStudio model object
969
- # @return [Bool] returns true if successful, false if not
899
+ # @return [Boolean] returns true if successful, false if not
970
900
  def model_apply_multizone_vav_outdoor_air_sizing(model)
971
901
  return true
972
902
  end
973
903
 
974
904
  # Identifies non mechanically cooled ("nmc") systems, if applicable
905
+ # and add a flag to the zone's and air loop's additional properties.
906
+ # @todo Zone-level evaporative cooler is not currently supported
907
+ # by OpenStudio, will need to be added to the method when supported.
975
908
  #
976
- # TODO: Zone-level evaporative cooler is not currently supported by
977
- # by OpenStudio, will need to be added to the method when
978
- # supported.
979
- #
980
- # @param model [OpenStudio::model::Model] OpenStudio model object
981
- # @return zone_nmc_sys_type [Hash] Zone to nmc system type mapping
909
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
910
+ # @return [Hash] Zone to nmc system type mapping
982
911
  def model_identify_non_mechanically_cooled_systems(model)
983
912
  # Iterate through zones to find out if they are served by nmc systems
984
- model.getThermalZones.sort.each do |zone|
913
+ model.getThermalZones.each do |zone|
985
914
  # Check if airloop has economizer and either:
986
915
  # - No cooling coil and/or,
987
916
  # - An evaporative cooling coil
988
- air_loop = zone.airLoopHVAC
989
-
990
- unless air_loop.empty?
991
- # Iterate through all the airloops assigned to a zone
992
- zone.airLoopHVACs.each do |airloop|
993
- air_loop = air_loop.get
994
- if (!air_loop_hvac_include_cooling_coil?(air_loop) &&
995
- air_loop_hvac_include_evaporative_cooler?(air_loop)) ||
996
- (!air_loop_hvac_include_cooling_coil?(air_loop) &&
997
- air_loop_hvac_include_economizer?(air_loop))
998
- air_loop.additionalProperties.setFeature('non_mechanically_cooled', true)
999
- air_loop.thermalZones.each do |thermal_zone|
1000
- thermal_zone.additionalProperties.setFeature('non_mechanically_cooled', true)
1001
- end
1002
- end
917
+ zone.airLoopHVACs.each do |air_loop|
918
+ if (!air_loop_hvac_include_cooling_coil?(air_loop) &&
919
+ air_loop_hvac_include_evaporative_cooler?(air_loop)) ||
920
+ (!air_loop_hvac_include_cooling_coil?(air_loop) &&
921
+ air_loop_hvac_include_economizer?(air_loop))
922
+ air_loop.additionalProperties.setFeature('non_mechanically_cooled', true)
923
+ zone.additionalProperties.setFeature('non_mechanically_cooled', true)
1003
924
  end
1004
925
  end
1005
926
  end
@@ -1008,7 +929,6 @@ class ASHRAE901PRM < Standard
1008
929
  # Specify supply air temperature setpoint for unit heaters based on 90.1 Appendix G G3.1.2.8.2
1009
930
  #
1010
931
  # @param thermal_zone [OpenStudio::Model::ThermalZone] OpenStudio ThermalZone Object
1011
- #
1012
932
  # @return [Double] for zone with unit heaters, return design supply temperature; otherwise, return nil
1013
933
  def thermal_zone_prm_unitheater_design_supply_temperature(thermal_zone)
1014
934
  thermal_zone.equipment.each do |eqt|
@@ -1022,7 +942,6 @@ class ASHRAE901PRM < Standard
1022
942
  # Specify supply to room delta for laboratory spaces based on 90.1 Appendix G Exception to G3.1.2.8.1
1023
943
  #
1024
944
  # @param thermal_zone [OpenStudio::Model::ThermalZone] OpenStudio ThermalZone Object
1025
- #
1026
945
  # @return [Double] for zone with laboratory space, return 17; otherwise, return nil
1027
946
  def thermal_zone_prm_lab_delta_t(thermal_zone)
1028
947
  # For labs, add 17 delta-T; otherwise, add 20 delta-T
@@ -1046,9 +965,10 @@ class ASHRAE901PRM < Standard
1046
965
  # Applies the HVAC parts of the template to all objects in the model using the the template specified in the model.
1047
966
  #
1048
967
  # @param model [OpenStudio::Model::Model] OpenStudio model object
1049
- # @param apply_controls [Bool] toggle whether to apply air loop and plant loop controls
968
+ # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
969
+ # @param apply_controls [Boolean] toggle whether to apply air loop and plant loop controls
1050
970
  # @param sql_db_vars_map [Hash] hash map
1051
- # @return [Bool] returns true if successful, false if not
971
+ # @return [Boolean] returns true if successful, false if not
1052
972
  def model_apply_hvac_efficiency_standard(model, climate_zone, apply_controls: true, sql_db_vars_map: nil)
1053
973
  sql_db_vars_map = {} if sql_db_vars_map.nil?
1054
974
 
@@ -1067,7 +987,7 @@ class ASHRAE901PRM < Standard
1067
987
  # Zone HVAC Controls
1068
988
  model.getZoneHVACComponents.sort.each { |obj| zone_hvac_component_apply_standard_controls(obj) }
1069
989
 
1070
- # TODO: The fan and pump efficiency will be done by another task.
990
+ # @todo The fan and pump efficiency will be done by another task.
1071
991
  # Fans
1072
992
  # model.getFanVariableVolumes.sort.each { |obj| fan_apply_standard_minimum_motor_efficiency(obj, fan_brake_horsepower(obj)) }
1073
993
  # model.getFanConstantVolumes.sort.each { |obj| fan_apply_standard_minimum_motor_efficiency(obj, fan_brake_horsepower(obj)) }
@@ -1147,6 +1067,12 @@ class ASHRAE901PRM < Standard
1147
1067
  return true
1148
1068
  end
1149
1069
 
1070
+ # This function returns the cooling dx coil efficiency and curve coefficient in a Hashmap.
1071
+ #
1072
+ # @param cooling_coil [OpenStudio::Model::ModeObject]
1073
+ # @param sql_db_vars_map [Hash] hash map
1074
+ # @param sys_type [String] baseline system type string
1075
+ # @return [Hash] sql_db_vars_map
1150
1076
  def set_coil_cooling_efficiency_and_curves(cooling_coil, sql_db_vars_map, sys_type)
1151
1077
  if cooling_coil.to_CoilCoolingDXSingleSpeed.is_initialized
1152
1078
  # single speed coil
@@ -1161,6 +1087,12 @@ class ASHRAE901PRM < Standard
1161
1087
  return sql_db_vars_map
1162
1088
  end
1163
1089
 
1090
+ # This function returns the heating dx coil efficiency and curve coefficient in a Hashmap.
1091
+ #
1092
+ # @param heating_coil [OpenStudio::Model::ModeObject]
1093
+ # @param sql_db_vars_map [Hash] hash map
1094
+ # @param sys_type [String] baseline system type string
1095
+ # @return [Hash] the hashmap contains the heating efficiency and curve coefficient for the heating_coil
1164
1096
  def set_coil_heating_efficiency_and_curves(heating_coil, sql_db_vars_map, sys_type)
1165
1097
  if heating_coil.to_CoilHeatingDXSingleSpeed.is_initialized
1166
1098
  # single speed coil
@@ -1175,13 +1107,26 @@ class ASHRAE901PRM < Standard
1175
1107
  return sql_db_vars_map
1176
1108
  end
1177
1109
 
1110
+ # Template method for evaluate DCV requirements in the user model
1111
+ #
1112
+ # @param model [OpenStudio::Model::Model] OpenStudio model
1113
+ # @return [Boolean] returns true if successful, false if not
1114
+ def model_evaluate_dcv_requirements(model)
1115
+ model_mark_zone_dcv_existence(model)
1116
+ model_add_dcv_user_exception_properties(model)
1117
+ model_add_dcv_requirement_properties(model)
1118
+ model_add_apxg_dcv_properties(model)
1119
+ model_raise_user_model_dcv_errors(model)
1120
+ return true
1121
+ end
1122
+
1178
1123
  # Template method for adding a setpoint manager for a coil control logic to a heating coil.
1179
1124
  # ASHRAE 90.1-2019 Appendix G.
1180
1125
  #
1181
1126
  # @param model [OpenStudio::Model::Model] OpenStudio model
1182
- # @param thermalZones Array([OpenStudio::Model::ThermalZone]) thermal zone array
1183
- # @param coil Heating Coils
1184
- # @return [Boolean] true
1127
+ # @param thermal_zones Array([OpenStudio::Model::ThermalZone]) thermal zone array
1128
+ # @param coil [OpenStudio::Model::StraightComponent] heating coil
1129
+ # @return [Boolean] returns true if successful, false if not
1185
1130
  def model_set_central_preheat_coil_spm(model, thermal_zones, coil)
1186
1131
  # search for the highest zone setpoint temperature
1187
1132
  max_heat_setpoint = 0.0
@@ -1231,6 +1176,7 @@ class ASHRAE901PRM < Standard
1231
1176
  #
1232
1177
  # @author Xuechen (Jerry) Lei, PNNL
1233
1178
  # @param model [OpenStudio::Model::Model] OpenStudio model
1179
+ # @return [Boolean] returns true if successful, false if not
1234
1180
  def model_mark_zone_dcv_existence(model)
1235
1181
  model.getAirLoopHVACs.each do |air_loop_hvac|
1236
1182
  next unless air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized
@@ -1255,7 +1201,7 @@ class ASHRAE901PRM < Standard
1255
1201
  end
1256
1202
  end
1257
1203
 
1258
- if zone_dcv == true
1204
+ if zone_dcv
1259
1205
  thermal_zone.additionalProperties.setFeature('zone DCV implemented in user model', true)
1260
1206
  end
1261
1207
  end
@@ -1373,8 +1319,8 @@ class ASHRAE901PRM < Standard
1373
1319
  #
1374
1320
  # @author Xuechen (Jerry) Lei, PNNL
1375
1321
  # @param model [OpenStudio::Model::Model] OpenStudio model
1322
+ # @todo JXL add log msgs to PRM logger
1376
1323
  def model_raise_user_model_dcv_errors(model)
1377
- # TODO: JXL add log msgs to PRM logger
1378
1324
  model.getThermalZones.each do |thermal_zone|
1379
1325
  if thermal_zone.additionalProperties.getFeatureAsBoolean('zone DCV implemented in user model').get &&
1380
1326
  (!thermal_zone.additionalProperties.getFeatureAsBoolean('zone dcv required by 901').get ||
@@ -1460,158 +1406,283 @@ class ASHRAE901PRM < Standard
1460
1406
  # @param [String] default_wwr_building_type
1461
1407
  # @param [String] default_swh_building_type
1462
1408
  # @param [Hash] bldg_type_hvac_zone_hash A hash maps building type for hvac to a list of thermal zones
1463
- # @return True
1464
- def handle_user_input_data(model, climate_zone, default_hvac_building_type, default_wwr_building_type, default_swh_building_type, bldg_type_hvac_zone_hash)
1409
+ # @return [Boolean] returns true if successful, false if not
1410
+ def handle_user_input_data(model, climate_zone, sizing_run_dir, default_hvac_building_type, default_wwr_building_type, default_swh_building_type, bldg_type_hvac_zone_hash)
1411
+ # Set sizing run directory
1412
+ @sizing_run_dir = sizing_run_dir
1465
1413
  # load the multiple building area types from user data
1466
1414
  handle_multi_building_area_types(model, climate_zone, default_hvac_building_type, default_wwr_building_type, default_swh_building_type, bldg_type_hvac_zone_hash)
1467
1415
  # load user data from proposed model
1468
1416
  handle_airloop_user_input_data(model)
1417
+ # exterior lighting handler
1418
+ handle_exterior_lighting_user_input_data(model)
1419
+ # load OA data from user data
1420
+ handle_outdoor_air_user_input_data(model)
1469
1421
  # load air loop DOAS user data from the proposed model
1470
1422
  handle_airloop_doas_user_input_data(model)
1471
1423
  # load zone HVAC user data from proposed model
1472
1424
  handle_zone_hvac_user_input_data(model)
1473
1425
  # load thermal zone user data from proposed model
1474
1426
  handle_thermal_zone_user_input_data(model)
1427
+ # load electric equipment user data
1428
+ handle_electric_equipment_user_input_data(model)
1429
+ return true
1430
+ end
1431
+
1432
+ # A function to load exterior lighting data from user data csv files
1433
+ # The file name is userdata_exterior_lighting.csv
1434
+ # @param [OpenStudio::Model::Model] model
1435
+ def handle_exterior_lighting_user_input_data(model)
1436
+ user_data_exterior_lighting_objects = get_userdata(UserDataFiles::EXTERIOR_LIGHTS)
1437
+
1438
+ search_criteria = {
1439
+ 'template' => template
1440
+ }
1441
+ ext_ltg_baseline_values = standards_lookup_table_first(table_name: 'prm_exterior_lighting', search_criteria: search_criteria)
1442
+
1443
+ model.getExteriorLightss.each do |exterior_light|
1444
+ if user_data_exterior_lighting_objects
1445
+ user_data_updated = false
1446
+ # get exterior lighting object.
1447
+ user_data_exterior_lighting_objects.each do |user_exterior_lighting|
1448
+ next unless UserData.compare(exterior_light.name.get, user_exterior_lighting['name'])
1449
+
1450
+ num_cats = prm_read_user_data(user_exterior_lighting, 'num_ext_lights_subcats', '0').to_i
1451
+ # Make sure none of the categories are nontradeable and not a mix of tradeable and nontradeable
1452
+ num_trade = 0
1453
+ num_notrade = 0
1454
+ ext_ltg_cats = {}
1455
+ (1..num_cats).each do |icat|
1456
+ cat_key = format('end_use_subcategory_%02d', icat)
1457
+ # validated
1458
+ subcat = user_exterior_lighting[cat_key]
1459
+ # handle the userdata missing value issue.
1460
+ if UserDataNonTradableLightsCategory.matched_any?(subcat)
1461
+ num_notrade += 1
1462
+ else
1463
+ num_trade += 1
1464
+ meas_val_key = format('end_use_measurement_value_%02d', icat)
1465
+ meas_val = prm_read_user_data(user_exterior_lighting, meas_val_key, '0.0').to_f
1466
+ unless meas_val == 0
1467
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "End use subcategory #{subcat} has either missing measurement value or invalid measurement value, set to 0.0")
1468
+ end
1469
+ ext_ltg_cats[subcat] = meas_val
1470
+ end
1471
+ end
1472
+
1473
+ # skip this if all lights are non-tradeable
1474
+ if num_trade == 0
1475
+ exterior_light.additionalProperties.setFeature('design_level', 0.0)
1476
+ next
1477
+ end
1478
+
1479
+ if (num_trade > 0) && (num_notrade > 0)
1480
+ OpenStudio.logFree(OpenStudio::Warn, 'prm.log', "ExteriorLights object named #{user_exterior_lighting['name']} from user data file has a mix of tradeable and non-tradeable lighting types. All will be treated as non-tradeable.")
1481
+ next
1482
+ end
1483
+
1484
+ ext_ltg_pwr = 0
1485
+ ext_ltg_cats.each do |subcat, meas_val|
1486
+ # Get baseline power for this type of exterior lighting
1487
+ baseline_value = ext_ltg_baseline_values[subcat].to_f
1488
+ ext_ltg_pwr += baseline_value * meas_val
1489
+ end
1490
+
1491
+ exterior_light.additionalProperties.setFeature('design_level', ext_ltg_pwr)
1492
+ user_data_updated = true
1493
+ end
1494
+ unless user_data_updated
1495
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Exterior Lights name #{exterior_light.name.get} was not found in user data file: #{UserDataFiles::EXTERIOR_LIGHTS}; No user data applied.")
1496
+ end
1497
+ end
1498
+ end
1499
+ end
1500
+
1501
+ # A function to load electric equipment csv files
1502
+ # The file name is userdata_electric_equipment.csv
1503
+ # @param [OpenStudio::Model::Model] model
1504
+ def handle_electric_equipment_user_input_data(model)
1505
+ user_data_plug_load = get_userdata(UserDataFiles::ELECTRIC_EQUIPMENT)
1506
+ model.getElectricEquipments.each do |elevator_equipment|
1507
+ if user_data_plug_load
1508
+ user_data_updated = false
1509
+ user_data_plug_load.each do |user_plug_load|
1510
+ next unless UserData.compare(elevator_equipment.name.get, user_plug_load['name'])
1511
+
1512
+ num_lifts = prm_read_user_data(user_plug_load, 'elevator_number_of_lifts', '0').to_i
1513
+ if num_lifts > 0
1514
+ elevator_equipment.additionalProperties.setFeature('elevator_number_of_lifts', num_lifts)
1515
+ number_of_levels = prm_read_user_data(user_plug_load, 'elevator_number_of_stories', '0').to_i
1516
+ elevator_equipment.additionalProperties.setFeature('elevator_number_of_stories', number_of_levels)
1517
+ elevator_weight_of_car = prm_read_user_data(user_plug_load, 'elevator_weight_of_car', '0.0').to_f
1518
+ elevator_equipment.additionalProperties.setFeature('elevator_weight_of_car', elevator_weight_of_car)
1519
+ elevator_weight_of_car = prm_read_user_data(user_plug_load, 'elevator_counter_weight_of_car', '0.0').to_f
1520
+ elevator_equipment.additionalProperties.setFeature('elevator_counter_weight_of_car', elevator_weight_of_car)
1521
+ elevator_rated_load = prm_read_user_data(user_plug_load, 'elevator_rated_load', '0.0').to_f
1522
+ elevator_equipment.additionalProperties.setFeature('elevator_rated_load', elevator_rated_load)
1523
+ elevator_speed_of_car = prm_read_user_data(user_plug_load, 'elevator_speed_of_car', '0.0').to_f
1524
+ elevator_equipment.additionalProperties.setFeature('elevator_speed_of_car', elevator_speed_of_car)
1525
+ elevator_ventilation_cfm = prm_read_user_data(user_plug_load, 'elevator_ventilation_cfm', '0.0').to_f
1526
+ elevator_equipment.additionalProperties.setFeature('elevator_ventilation_cfm', elevator_ventilation_cfm)
1527
+ elevator_area_ft2 = prm_read_user_data(user_plug_load, 'elevator_area_ft2', '0.0').to_f
1528
+ elevator_equipment.additionalProperties.setFeature('elevator_area_ft2', elevator_area_ft2)
1529
+ end
1530
+ user_data_updated = true
1531
+ end
1532
+
1533
+ unless user_data_updated
1534
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Electric equipment name #{elevator_equipment.name.get} was not found in user data file: #{UserDataFiles::ELECTRIC_EQUIPMENT}; No user data applied.")
1535
+ end
1536
+ end
1537
+ end
1538
+ end
1539
+
1540
+ # A function to load outdoor air data from user data csv files
1541
+ # The file name is userdata_design_specification_outdoor_air.csv
1542
+ # @param [OpenStudio::Model::Model] model
1543
+ def handle_outdoor_air_user_input_data(model)
1544
+ user_data_oas = get_userdata(UserDataFiles::DESIGN_SPECIFICATION_OUTDOOR_AIR)
1545
+ model.getDesignSpecificationOutdoorAirs.each do |zone_oa|
1546
+ if user_data_oas
1547
+ user_data_updated = false
1548
+ user_data_oas.each do |user_oa|
1549
+ next unless UserData.compare(zone_oa.name.get, user_oa['name'])
1550
+
1551
+ user_oa.keys.each do |info_key|
1552
+ if info_key == 'name'
1553
+ zone_oa.additionalProperties.setFeature('has_user_data', true)
1554
+ else
1555
+ # this will capture the invalid string to 0.0, need to add note
1556
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Add user provided outdoor air field: #{info_key}, value: #{user_oa[info_key].to_f} to DesignSpecification:OutdoorAir #{zone_oa.name.get} ")
1557
+ zone_oa.additionalProperties.setFeature(info_key, user_oa[info_key].to_f)
1558
+ end
1559
+ end
1560
+ user_data_updated = true
1561
+ end
1562
+
1563
+ unless user_data_updated
1564
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Zone outdoor air name #{zone_oa.name.get} was not found in user data file: #{UserDataFiles::DESIGN_SPECIFICATION_OUTDOOR_AIR}; No user data applied.")
1565
+ end
1566
+ end
1567
+ end
1475
1568
  end
1476
1569
 
1477
1570
  # A function to load airloop data from userdata csv files
1478
- # @param [OpenStudio::Model::Model] OpenStudio model object
1571
+ # The function works with validated user data only.
1572
+ #
1573
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
1479
1574
  def handle_airloop_user_input_data(model)
1480
1575
  # ============================Process airloop info ============================================
1481
- user_airloops = @standards_data.key?('userdata_airloop_hvac') ? @standards_data['userdata_airloop_hvac'] : nil
1576
+ user_airloops = get_userdata(UserDataFiles::AIRLOOP_HVAC)
1482
1577
  model.getAirLoopHVACs.each do |air_loop|
1483
- air_loop_name = air_loop.name.get
1484
- if user_airloops && user_airloops.length > 1
1578
+ if user_airloops
1579
+ user_data_updated = false
1485
1580
  user_airloops.each do |user_airloop|
1486
- if air_loop_name == user_airloop['name']
1581
+ next unless UserData.compare(air_loop.name.get, user_airloop['name'])
1582
+
1583
+ air_loop.thermalZones.each do |thermal_zone|
1487
1584
  # gas phase air cleaning is system base - add proposed hvac system name to zones
1488
- if user_airloop.key?('economizer_exception_for_gas_phase_air_cleaning') && !user_airloop['economizer_exception_for_gas_phase_air_cleaning'].nil?
1489
- if user_airloop['economizer_exception_for_gas_phase_air_cleaning'].downcase == 'yes'
1490
- air_loop.thermalZones.each do |thermal_zone|
1491
- thermal_zone.additionalProperties.setFeature('economizer_exception_for_gas_phase_air_cleaning', air_loop_name)
1492
- end
1493
- end
1494
- end
1495
- # Open refrigerated cases is zone based - add yes or no to zones
1496
- if user_airloop.key?('economizer_exception_for_open_refrigerated_cases') && !user_airloop['economizer_exception_for_open_refrigerated_cases'].nil?
1497
- if user_airloop['economizer_exception_for_open_refrigerated_cases'].downcase == 'yes'
1498
- air_loop.thermalZones.each do |thermal_zone|
1499
- thermal_zone.additionalProperties.setFeature('economizer_exception_for_open_refrigerated_cases', 'yes')
1500
- end
1501
- end
1502
- end
1503
- # Fan power credits, exhaust air energy recovery
1585
+ economizer_exception_for_gas_phase_air_cleaning = user_airloop['economizer_exception_for_gas_phase_air_cleaning']
1586
+ economizer_exception_for_open_refrigerated_cases = user_airloop['economizer_exception_for_open_refrigerated_cases']
1504
1587
  user_airloop.keys.each do |info_key|
1505
- # Fan power credits
1506
- if info_key.include?('fan_power_credit')
1507
- if !user_airloop[info_key].to_s.empty?
1508
- if info_key.include?('has_')
1509
- if user_airloop[info_key].downcase == 'yes'
1510
- air_loop.thermalZones.each do |thermal_zone|
1511
- if thermal_zone.additionalProperties.hasFeature(info_key)
1512
- current_value = thermal_zone.additionalProperties.getFeatureAsDouble(info_key).to_f
1513
- thermal_zone.additionalProperties.setFeature(info_key, current_value + 1.0)
1514
- else
1515
- thermal_zone.additionalProperties.setFeature(info_key, 1.0)
1516
- end
1517
- end
1518
- end
1519
- else
1520
- air_loop.thermalZones.each do |thermal_zones|
1521
- if thermal_zone.additionalProperties.hasFeature(info_key)
1522
- current_value = thermal_zone.additionalProperties.getFeatureAsDouble(info_key).to_f
1523
- thermal_zone.additionalProperties.setFeature(info_key, current_value + user_airloop[info_key])
1524
- else
1525
- thermal_zone.additionalProperties.setFeature(info_key, user_airloop[info_key])
1526
- end
1527
- end
1528
- end
1529
- end
1588
+ if info_key.include?('has_fan_power_credit') && UserData.compare(user_airloop[info_key], UserDataBoolean::TRUE)
1589
+ current_value = get_additional_property_as_double(thermal_zone, info_key, 0.0)
1590
+ thermal_zone.additionalProperties.setFeature(info_key, current_value + 1.0)
1591
+ elsif info_key.include?('fan_power_credit')
1592
+ # Case 2: user provided value
1593
+ fan_power_credit = prm_read_user_data(user_airloop, info_key, '0.0').to_f
1594
+ current_value = get_additional_property_as_double(thermal_zone, info_key, 0.0)
1595
+ thermal_zone.additionalProperties.setFeature(info_key, current_value + fan_power_credit)
1530
1596
  end
1597
+
1531
1598
  # Exhaust air energy recovery
1532
- if info_key.include?('exhaust_energy_recovery_exception') && !user_airloop[info_key].to_s.empty?
1533
- if user_airloop[info_key].downcase == 'yes'
1534
- air_loop.thermalZones.each do |thermal_zone|
1535
- thermal_zone.additionalProperties.setFeature(info_key, 'yes')
1536
- end
1599
+ if info_key.include?('exhaust_energy_recovery_exception')
1600
+ if UserData.compare(user_airloop[info_key], UserDataBoolean::TRUE)
1601
+ thermal_zone.additionalProperties.setFeature(info_key, true)
1602
+ else
1603
+ thermal_zone.additionalProperties.setFeature(info_key, false)
1537
1604
  end
1538
1605
  end
1539
1606
  end
1607
+ if UserData.compare(economizer_exception_for_gas_phase_air_cleaning, UserDataBoolean::TRUE)
1608
+ thermal_zone.additionalProperties.setFeature('economizer_exception_for_gas_phase_air_cleaning', true)
1609
+ else
1610
+ thermal_zone.additionalProperties.setFeature('economizer_exception_for_gas_phase_air_cleaning', false)
1611
+ end
1612
+
1613
+ if UserData.compare(economizer_exception_for_open_refrigerated_cases, UserDataBoolean::TRUE)
1614
+ thermal_zone.additionalProperties.setFeature('economizer_exception_for_open_refrigerated_cases', true)
1615
+ else
1616
+ thermal_zone.additionalProperties.setFeature('economizer_exception_for_open_refrigerated_cases', false)
1617
+ end
1540
1618
  end
1619
+ user_data_updated = true
1620
+ end
1621
+ unless user_data_updated
1622
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Air loop name #{air_loop.name.get} was not found in user data file: #{UserDataFiles::AIRLOOP_HVAC}; No user data applied.")
1541
1623
  end
1542
1624
  end
1543
1625
  end
1544
1626
  end
1545
1627
 
1546
1628
  # A function to load airloop DOAS data from userdata csv files
1547
- # @param [OpenStudio::Model::Model] OpenStudio model object
1629
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
1548
1630
  def handle_airloop_doas_user_input_data(model)
1549
1631
  # Get user data
1550
- user_airloop_doass = @standards_data.key?('userdata_airloop_hvac_doas') ? @standards_data['userdata_airloop_hvac_doas'] : nil
1551
-
1552
- # Parse user data
1553
- if user_airloop_doass && user_airloop_doass.length >= 1
1554
- user_airloop_doass.each do |user_airloop_doas|
1555
- # Get AirLoopHVACDedicatedOutdoorAirSystem
1556
- air_loop_doas = model.getAirLoopHVACDedicatedOutdoorAirSystemByName(user_airloop_doas['name'])
1557
- if !air_loop_doas.is_initialized
1558
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.ashrae_90_1_prm.Model', "The AirLoopHVACDedicatedOutdoorAirSystem named #{user_airloop_doass['name']} mentioned in the userdata_airloop_hvac_doas was not found in the model, user specified data associated with it will be ignored.")
1559
- next
1560
- else
1561
- air_loop_doas = air_loop_doas.get
1562
- end
1563
-
1564
- # Parse fan power credits data
1565
- user_airloop_doas.keys.each do |info_key|
1566
- if info_key.include?('fan_power_credit')
1567
- if !user_airloop_doas[info_key].to_s.empty?
1568
- # Case 1: Yes/no
1569
- if info_key.include?('has_')
1570
- if user_airloop_doas[info_key].downcase == 'yes'
1571
- air_loop_doas.airLoops.each do |air_loop|
1572
- air_loop.thermalZones.each do |thermal_zone|
1573
- if thermal_zone.additionalProperties.hasFeature(info_key)
1574
- current_value = thermal_zone.additionalProperties.getFeatureAsDouble(info_key).to_f
1575
- thermal_zone.additionalProperties.setFeature(info_key, current_value + 1.0)
1576
- else
1577
- thermal_zone.additionalProperties.setFeature(info_key, 1.0)
1578
- end
1579
- end
1580
- end
1632
+ user_airloop_doass = get_userdata(UserDataFiles::AIRLOOP_HVAC_DOAS)
1633
+ model.getAirLoopHVACDedicatedOutdoorAirSystems.each do |air_loop_doas|
1634
+ if user_airloop_doass
1635
+ user_data_updated = false
1636
+ user_airloop_doass.each do |user_airloop_doas|
1637
+ next unless UserData.compare(user_airloop_doas['name'], air_loop_doas.name.get)
1638
+
1639
+ # Parse fan power credits data
1640
+ user_airloop_doas.keys.each do |info_key|
1641
+ if info_key.include?('has_fan_power_credit') && UserDataBoolean.compare(user_airloop_doas[info_key], UserDataBoolean::TRUE)
1642
+ air_loop_doas.airLoops.each do |air_loop|
1643
+ air_loop.thermalZones.each do |thermal_zone|
1644
+ current_value = get_additional_property_as_double(thermal_zone, info_key, 0.0)
1645
+ thermal_zone.additionalProperties.setFeature(info_key, current_value + 1.0)
1581
1646
  end
1582
- else
1583
- # Case 2: user provided value
1584
- air_loop_doas.airLoops.each do |air_loop|
1585
- air_loop.thermalZones.each do |thermal_zones|
1586
- if thermal_zone.additionalProperties.hasFeature(info_key)
1587
- current_value = thermal_zone.additionalProperties.getFeatureAsDouble(info_key).to_f
1588
- thermal_zone.additionalProperties.setFeature(info_key, current_value + user_airloop_doas[info_key])
1589
- else
1590
- thermal_zone.additionalProperties.setFeature(info_key, user_airloop_doas[info_key])
1591
- end
1592
- end
1647
+ end
1648
+ elsif info_key.include?('fan_power_credit')
1649
+ # Case 2: user provided value
1650
+ air_loop_doas.airLoops.each do |air_loop|
1651
+ air_loop.thermalZones.each do |thermal_zone|
1652
+ current_value = get_additional_property_as_double(thermal_zone, info_key, 0.0)
1653
+ thermal_zone.additionalProperties.setFeature(info_key, current_value + prm_read_user_data(user_airloop_doas, info_key, '0.0').to_f)
1593
1654
  end
1594
1655
  end
1595
1656
  end
1596
1657
  end
1658
+ user_data_updated = true
1659
+ end
1660
+ unless user_data_updated
1661
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Air Loop DOAS name #{air_loop_doas.name.get} was not found in user data file: #{UserDataFiles::AIRLOOP_HVAC_DOAS}; No user data applied.")
1597
1662
  end
1598
1663
  end
1599
1664
  end
1600
1665
  end
1601
1666
 
1602
1667
  # A function to load thermal zone data from userdata csv files
1603
- # @param [OpenStudio::Model::Model] OpenStudio model object
1668
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
1604
1669
  def handle_thermal_zone_user_input_data(model)
1670
+ userdata_thermal_zones = get_userdata(UserDataFiles::THERMAL_ZONE)
1605
1671
  model.getThermalZones.each do |thermal_zone|
1606
1672
  nightcycle_exception = false
1607
- if standards_data.key?('userdata_thermal_zone')
1608
- standards_data['userdata_thermal_zone'].each do |row|
1609
- next unless row['name'].to_s.downcase.strip == thermal_zone.name.to_s.downcase.strip
1673
+ if userdata_thermal_zones
1674
+ user_data_updated = false
1675
+ userdata_thermal_zones.each do |row|
1676
+ next unless UserData.compare(row['name'], thermal_zone.name.get)
1610
1677
 
1611
- if row['has_health_safety_night_cycle_exception'].to_s.upcase.strip == 'TRUE'
1678
+ if UserData.compare(row['has_health_safety_night_cycle_exception'], UserDataBoolean::TRUE)
1612
1679
  nightcycle_exception = true
1613
1680
  break
1614
1681
  end
1682
+ user_data_updated = true
1683
+ end
1684
+ unless user_data_updated
1685
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Thermal Zone name #{thermal_zone.name.get} was not found in user data file: #{UserDataFiles::THERMAL_ZONE}.")
1615
1686
  end
1616
1687
  end
1617
1688
  if nightcycle_exception
@@ -1637,20 +1708,20 @@ class ASHRAE901PRM < Standard
1637
1708
  # NOTE! This function will add building types to OpenStudio objects as an additional features for hierarchy 1-3
1638
1709
  # The object additional feature is empty when the function determined it uses fourth hierarchy.
1639
1710
  #
1640
- # @param [OpenStudio::Model::Model] model
1641
- # @param [String] climate_zone
1642
- # @param [String] default_hvac_building_type (Fourth Hierarchy hvac building type)
1643
- # @param [String] default_wwr_building_type (Fourth Hierarchy wwr building type)
1644
- # @param [String] default_swh_building_type (Fourth Hierarchy swh building type)
1645
- # @param [Hash] bldg_type_zone_hash An empty hash that maps building type for hvac to a list of thermal zones
1646
- # @return True
1711
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
1712
+ # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
1713
+ # @param default_hvac_building_type [String] (Fourth Hierarchy hvac building type)
1714
+ # @param default_wwr_building_type [String] (Fourth Hierarchy wwr building type)
1715
+ # @param default_swh_building_type [String] (Fourth Hierarchy swh building type)
1716
+ # @param bldg_type_hvac_zone_hash [Hash] An empty hash that maps building type for hvac to a list of thermal zones
1717
+ # @return [Boolean] returns true if successful, false if not
1647
1718
  def handle_multi_building_area_types(model, climate_zone, default_hvac_building_type, default_wwr_building_type, default_swh_building_type, bldg_type_hvac_zone_hash)
1648
1719
  # Construct the user_building hashmap
1649
- user_buildings = @standards_data.key?('userdata_building') ? @standards_data['userdata_building'] : nil
1720
+ user_buildings = get_userdata(UserDataFiles::BUILDING)
1650
1721
 
1651
1722
  # Build up a hvac_building_type : thermal zone hash map
1652
1723
  # =============================HVAC user data process===========================================
1653
- user_thermal_zones = @standards_data.key?('userdata_thermal_zone') ? @standards_data['userdata_thermal_zone'] : nil
1724
+ user_thermal_zones = get_userdata(UserDataFiles::THERMAL_ZONE)
1654
1725
  # First construct hvac building type -> thermal Zone hash and hvac building type -> floor area
1655
1726
  bldg_type_zone_hash = {}
1656
1727
  bldg_type_zone_area_hash = {}
@@ -1663,8 +1734,8 @@ class ASHRAE901PRM < Standard
1663
1734
 
1664
1735
  # Check for Second hierarchy
1665
1736
  hvac_building_type = nil
1666
- if user_thermal_zones && user_thermal_zones.length >= 1
1667
- user_thermal_zone_index = user_thermal_zones.index { |user_thermal_zone| user_thermal_zone['name'] == thermal_zone.name.get }
1737
+ if user_thermal_zones
1738
+ user_thermal_zone_index = user_thermal_zones.index { |user_thermal_zone| UserData.compare(user_thermal_zone['name'], thermal_zone.name.get) }
1668
1739
  # make sure the thermal zone has assigned a building_type_for_hvac
1669
1740
  unless user_thermal_zone_index.nil? || user_thermal_zones[user_thermal_zone_index]['building_type_for_hvac'].nil?
1670
1741
  # Only thermal zone in the user data and have building_type_for_hvac data will be assigned.
@@ -1672,9 +1743,9 @@ class ASHRAE901PRM < Standard
1672
1743
  end
1673
1744
  end
1674
1745
  # Second hierarchy does not apply, check Third hierarchy
1675
- if hvac_building_type.nil? && user_buildings && user_buildings.length >= 1
1676
- building_name = thermal_zone.model.building.get.name.get
1677
- user_building_index = user_buildings.index { |user_building| user_building['name'] == building_name }
1746
+ if hvac_building_type.nil? && user_buildings
1747
+ building_name = prm_get_optional_handler(thermal_zone.model, @sizing_run_dir, 'building', 'name')
1748
+ user_building_index = user_buildings.index { |user_building| UserData.compare(user_building['name'], building_name) }
1678
1749
  unless user_building_index.nil? || user_buildings[user_building_index]['building_type_for_hvac'].nil?
1679
1750
  # Only thermal zone in the buildings user data and have building_type_for_hvac data will be assigned.
1680
1751
  hvac_building_type = user_buildings[user_building_index]['building_type_for_hvac']
@@ -1738,12 +1809,12 @@ class ASHRAE901PRM < Standard
1738
1809
  if OpenStudio.convert(total_floor_area, 'm^2', 'ft^2').get <= 40000
1739
1810
  # Building is smaller than 40k sqft, it could only have one hvac_building_type, reset all the thermal zones.
1740
1811
  bldg_type_hvac_zone_hash[hvac_bldg_type_with_max_floor].push(*bldg_type_zone)
1741
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "The building floor area is less than 40,000 square foot. Thermal zones under hvac building type #{bldg_type} is reset to #{hvac_bldg_type_with_max_floor}")
1812
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "The building floor area is less than 40,000 square foot. Thermal zones under hvac building type #{bldg_type} is reset to #{hvac_bldg_type_with_max_floor}")
1742
1813
  else
1743
1814
  if OpenStudio.convert(bldg_type_zone_area_hash[bldg_type], 'm^2', 'ft^2').get < 20000
1744
1815
  # in this case, all thermal zones shall be categorized as the primary hvac_building_type
1745
1816
  bldg_type_hvac_zone_hash[hvac_bldg_type_with_max_floor].push(*bldg_type_zone)
1746
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "The floor area in hvac building type #{bldg_type} is less than 20,000 square foot. Thermal zones under this hvac building type is reset to #{hvac_bldg_type_with_max_floor}")
1817
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "The floor area in hvac building type #{bldg_type} is less than 20,000 square foot. Thermal zones under this hvac building type is reset to #{hvac_bldg_type_with_max_floor}")
1747
1818
  else
1748
1819
  bldg_type_hvac_zone_hash[bldg_type] = bldg_type_zone
1749
1820
  end
@@ -1760,14 +1831,14 @@ class ASHRAE901PRM < Standard
1760
1831
  end
1761
1832
 
1762
1833
  # =============================SPACE user data process===========================================
1763
- user_spaces = @standards_data.key?('userdata_space') ? @standards_data['userdata_space'] : nil
1834
+ user_spaces = get_userdata(UserDataFiles::SPACE)
1764
1835
  model.getSpaces.each do |space|
1765
1836
  type_for_wwr = nil
1766
1837
  # Check for 2nd level hierarchy
1767
- if user_spaces && user_spaces.length >= 1
1838
+ if user_spaces
1768
1839
  user_spaces.each do |user_space|
1769
1840
  unless user_space['building_type_for_wwr'].nil?
1770
- if space.name.get == user_space['name']
1841
+ if UserData.compare(space.name.get, user_space['name'])
1771
1842
  type_for_wwr = user_space['building_type_for_wwr']
1772
1843
  end
1773
1844
  end
@@ -1776,11 +1847,11 @@ class ASHRAE901PRM < Standard
1776
1847
 
1777
1848
  if type_for_wwr.nil?
1778
1849
  # 2nd Hierarchy does not apply, check for 3rd level hierarchy
1779
- building_name = space.model.building.get.name.get
1780
- if user_buildings && user_buildings.length >= 1
1850
+ building_name = prm_get_optional_handler(space.model, @sizing_run_dir, 'building', 'name')
1851
+ if user_buildings
1781
1852
  user_buildings.each do |user_building|
1782
1853
  unless user_building['building_type_for_wwr'].nil?
1783
- if user_building['name'] == building_name
1854
+ if UserData.compare(user_building['name'], building_name)
1784
1855
  type_for_wwr = user_building['building_type_for_wwr']
1785
1856
  end
1786
1857
  end
@@ -1796,14 +1867,14 @@ class ASHRAE901PRM < Standard
1796
1867
  space.additionalProperties.setFeature('building_type_for_wwr', type_for_wwr)
1797
1868
  end
1798
1869
  # =============================SWH user data process===========================================
1799
- user_wateruse_equipments = @standards_data.key?('userdata_wateruse_equipment') ? @standards_data['userdata_wateruse_equipment'] : nil
1870
+ user_wateruse_equipments = get_userdata(UserDataFiles::WATERUSE_EQUIPMENT)
1800
1871
  model.getWaterUseEquipments.each do |wateruse_equipment|
1801
1872
  type_for_swh = nil
1802
1873
  # Check for 2nd hierarchy
1803
- if user_wateruse_equipments && user_wateruse_equipments.length >= 1
1874
+ if user_wateruse_equipments
1804
1875
  user_wateruse_equipments.each do |user_wateruse_equipment|
1805
1876
  unless user_wateruse_equipment['building_type_for_swh'].nil?
1806
- if wateruse_equipment.name.get == user_wateruse_equipment['name']
1877
+ if UserData.compare(wateruse_equipment.name.get, user_wateruse_equipment['name'])
1807
1878
  type_for_swh = user_wateruse_equipment['building_type_for_swh']
1808
1879
  end
1809
1880
  end
@@ -1813,11 +1884,11 @@ class ASHRAE901PRM < Standard
1813
1884
  if type_for_swh.nil?
1814
1885
  # 2nd hierarchy does not apply, check for 3rd hierarchy
1815
1886
  # get space building type
1816
- building_name = wateruse_equipment.model.building.get.name.get
1817
- if user_buildings && user_buildings.length >= 1
1887
+ building_name = prm_get_optional_handler(wateruse_equipment.model, @sizing_run_dir, 'building', 'name')
1888
+ if user_buildings
1818
1889
  user_buildings.each do |user_building|
1819
1890
  unless user_building['building_type_for_swh'].nil?
1820
- if user_building['name'] == building_name
1891
+ if UserData.compare(user_building['name'], building_name)
1821
1892
  type_for_swh = user_building['building_type_for_swh']
1822
1893
  end
1823
1894
  end
@@ -1837,11 +1908,11 @@ class ASHRAE901PRM < Standard
1837
1908
 
1838
1909
  # Modify the existing service water heating loops to match the baseline required heating type.
1839
1910
  #
1840
- # @param model [OpenStudio::Model::Model] the model
1911
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
1841
1912
  # @param building_type [String] the building type
1842
- # @return [Bool] returns true if successful, false if not
1913
+ # @return [Boolean] returns true if successful, false if not
1843
1914
  def model_apply_baseline_swh_loops(model, building_type)
1844
- model.getPlantLoops.sort.each do |plant_loop|
1915
+ model.getPlantLoops.each do |plant_loop|
1845
1916
  # Skip non service water heating loops
1846
1917
  next unless plant_loop_swh_loop?(plant_loop)
1847
1918
 
@@ -1850,10 +1921,7 @@ class ASHRAE901PRM < Standard
1850
1921
 
1851
1922
  htg_fuels, combination_system, storage_capacity, total_heating_capacity = plant_loop_swh_system_type(plant_loop)
1852
1923
 
1853
- # htg_fuels.size == 0 shoudln't happen
1854
-
1855
1924
  electric = true
1856
-
1857
1925
  if htg_fuels.include?('NaturalGas') ||
1858
1926
  htg_fuels.include?('PropaneGas') ||
1859
1927
  htg_fuels.include?('FuelOilNo1') ||
@@ -1866,12 +1934,11 @@ class ASHRAE901PRM < Standard
1866
1934
 
1867
1935
  # Per Table G3.1 11.e, if the baseline system was a combination of heating and service water heating,
1868
1936
  # delete all heating equipment and recreate a WaterHeater:Mixed.
1869
-
1870
1937
  if combination_system
1871
1938
  a = plant_loop.supplyComponents
1872
1939
  b = plant_loop.demandComponents
1873
- plantloopComponents = a += b
1874
- plantloopComponents.each do |component|
1940
+ plantloop_components = a += b
1941
+ plantloop_components.each do |component|
1875
1942
  # Get the object type
1876
1943
  obj_type = component.iddObjectType.valueName.to_s
1877
1944
  next if ['OS_Node', 'OS_Pump_ConstantSpeed', 'OS_Pump_VariableSpeed', 'OS_Connector_Splitter', 'OS_Connector_Mixer', 'OS_Pipe_Adiabatic'].include?(obj_type)
@@ -1903,27 +1970,7 @@ class ASHRAE901PRM < Standard
1903
1970
  # to electric resistance if it's electric
1904
1971
  else
1905
1972
  # Per Table G3.1 11.i, piping losses was deleted
1906
-
1907
- a = plant_loop.supplyComponents
1908
- b = plant_loop.demandComponents
1909
- plantloopComponents = a += b
1910
- plantloopComponents.each do |component|
1911
- # Get the object type
1912
- obj_type = component.iddObjectType.valueName.to_s
1913
- next if !['OS_Pipe_Indoor', 'OS_Pipe_Outdoor'].include?(obj_type)
1914
-
1915
- pipe = component.to_PipeIndoor.get
1916
- node = pipe.to_StraightComponent.get.outletModelObject.get.to_Node.get
1917
-
1918
- node_name = node.name.get
1919
- pipe_name = pipe.name.get
1920
-
1921
- # Add Pipe_Adiabatic
1922
- newpipe = OpenStudio::Model::PipeAdiabatic.new(model)
1923
- newpipe.setName(pipe_name)
1924
- newpipe.addToNode(node)
1925
- component.remove
1926
- end
1973
+ plant_loop_adiabatic_pipes_only(plant_loop)
1927
1974
 
1928
1975
  if electric
1929
1976
  plant_loop.supplyComponents.each do |component|
@@ -1948,38 +1995,63 @@ class ASHRAE901PRM < Standard
1948
1995
 
1949
1996
  # Check whether the baseline model generation needs to run all four orientations
1950
1997
  # The default shall be true
1998
+ # The orientation takes priority of:
1999
+ # 1. Appx G
2000
+ # 2. Method user input.
2001
+ # 3. User data override.
1951
2002
  #
1952
- # @param [Boolean] run_all_orients: user inputs to indicate whether it is required to run all orientations
1953
- # @param [OpenStudio::Model::Model] OpenStudio model
2003
+ # @param run_all_orients [Boolean] user inputs to indicate whether it is required to run all orientations
2004
+ # @param user_model [OpenStudio::Model::Model] OpenStudio model
2005
+ # @return [Boolean] True if run all orientation is required, false otherwise
1954
2006
  def run_all_orientations(run_all_orients, user_model)
1955
- # Step 0, assign the default value
1956
- run_orients_flag = run_all_orients
1957
- # Step 1 check orientation variations - priority 2
2007
+ run_orients_flag = false
2008
+ # Step 1 check orientation variations - priority 3
1958
2009
  fenestration_area_hash = get_model_fenestration_area_by_orientation(user_model)
1959
2010
  fenestration_area_hash.each do |orientation, fenestration_area|
2011
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log',
2012
+ "#{orientation} orientation has total fenestration area of #{fenestration_area} m2")
1960
2013
  fenestration_area_hash.each do |other_orientation, other_fenestration_area|
1961
2014
  next unless orientation != other_orientation
1962
2015
 
1963
2016
  variance = (other_fenestration_area - fenestration_area) / fenestration_area
1964
2017
  if variance.abs > 0.05
1965
2018
  # if greater then 0.05
2019
+ OpenStudio.logFree(OpenStudio::Info,
2020
+ 'prm.log',
2021
+ "#{orientation} has total fenestration area of #{fenestration_area} m2, which is higher than 5% variance compare to #{other_fenestration_area} at #{other_orientation}")
1966
2022
  run_orients_flag = true
1967
2023
  end
1968
2024
  end
1969
2025
  end
1970
- # Step 2 read user data - priority 1 - user data will override the priority 2
2026
+ # Step 2, assign method user input if it is provided as false.
2027
+ unless run_all_orients
2028
+ OpenStudio.logFree(OpenStudio::Error,
2029
+ 'prm.log',
2030
+ 'The run_all_orientation flag is set to False, update the run to a single orientation PRM generation.')
2031
+ run_orients_flag = run_all_orients
2032
+ end
2033
+ # Step 3 read user data - priority 1 - user data will override the priority 2
1971
2034
  user_buildings = @standards_data.key?('userdata_building') ? @standards_data['userdata_building'] : nil
1972
2035
  if user_buildings
1973
2036
  building_name = user_model.building.get.name.get
1974
2037
  user_building_index = user_buildings.index { |user_building| building_name.include? user_building['name'] }
1975
2038
  unless user_building_index.nil? || user_buildings[user_building_index]['is_exempt_from_rotations'].nil?
1976
2039
  # user data exempt the rotation, No indicates true for running orients.
1977
- run_orients_flag = user_buildings[user_building_index]['is_exempt_from_rotations'].casecmp('No') == 0
2040
+ OpenStudio.logFree(OpenStudio::Error,
2041
+ 'prm.log',
2042
+ "User data in the userdata_building.csv indicate building #{building_name} is exempted from rotation. Update the run to a single orientation PRM generation.")
2043
+ # @todo need to use user data enums later.
2044
+ run_orients_flag = user_buildings[user_building_index]['is_exempt_from_rotations'].casecmp('False') == 0
1978
2045
  end
1979
2046
  end
1980
2047
  return run_orients_flag
1981
2048
  end
1982
2049
 
2050
+ # Function that extract the total fenestration area from a model by orientations.
2051
+ # Orientation is identified as N (North), S (South), E (East), W (West)
2052
+ #
2053
+ # @param user_model [OpenStudio::Model::Model] OpenStudio model
2054
+ # @return [Hash] Hash map that contains the total area of fenestration at each orientation (N, S, E, W)
1983
2055
  def get_model_fenestration_area_by_orientation(user_model)
1984
2056
  # First index is wall, second index is window
1985
2057
  fenestration_area_hash = {
@@ -1993,7 +2065,7 @@ class ASHRAE901PRM < Standard
1993
2065
  next if space_cond_type == 'Unconditioned'
1994
2066
 
1995
2067
  # Get zone multiplier
1996
- multiplier = space.thermalZone.get.multiplier
2068
+ multiplier = prm_get_optional_handler(space, @sizing_run_dir, 'thermalZone').multiplier
1997
2069
  space.surfaces.each do |surface|
1998
2070
  next if surface.surfaceType != 'Wall'
1999
2071
  next if surface.outsideBoundaryCondition != 'Outdoors'
@@ -2013,10 +2085,9 @@ class ASHRAE901PRM < Standard
2013
2085
 
2014
2086
  # Apply the standard construction to each surface in the model, based on the construction type currently assigned.
2015
2087
  #
2016
- # @return [Bool] true if successful, false if not
2017
2088
  # @param model [OpenStudio::Model::Model] OpenStudio model object
2018
2089
  # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
2019
- # @return [Bool] returns true if successful, false if not
2090
+ # @return [Boolean] returns true if successful, false if not
2020
2091
  def model_apply_constructions(model, climate_zone, wwr_building_type, wwr_info)
2021
2092
  model_apply_standard_constructions(model, climate_zone, wwr_building_type: wwr_building_type, wwr_info: wwr_info)
2022
2093
 
@@ -2027,7 +2098,7 @@ class ASHRAE901PRM < Standard
2027
2098
  #
2028
2099
  # @param model [OpenStudio::Model::Model] OpenStudio model object
2029
2100
  # @param climate_zone [String] ASHRAE climate zone, e.g. 'ASHRAE 169-2013-4A'
2030
- # @return [Bool] returns true if successful, false if not
2101
+ # @return [Boolean] surfaces_with_fc_factor_boundary, returns true if successful, false if not
2031
2102
  def model_update_ground_temperature_profile(model, climate_zone)
2032
2103
  # Check if the ground temperature profile is needed
2033
2104
  surfaces_with_fc_factor_boundary = false
@@ -2038,32 +2109,35 @@ class ASHRAE901PRM < Standard
2038
2109
  end
2039
2110
  end
2040
2111
 
2041
- return false unless surfaces_with_fc_factor_boundary
2042
-
2043
- # Remove existing FCFactor temperature profile
2044
- model.getSiteGroundTemperatureFCfactorMethod.remove
2112
+ if surfaces_with_fc_factor_boundary
2113
+ # Remove existing FCFactor temperature profile
2114
+ model.getSiteGroundTemperatureFCfactorMethod.remove
2045
2115
 
2046
- # Get path to weather file specified in the model
2047
- weather_file_path = model.getWeatherFile.path.get.to_s
2116
+ # Get path to weather file specified in the model
2117
+ weather_file_path = prm_get_optional_handler(model.getWeatherFile, @sizing_run_dir, 'path').to_s
2048
2118
 
2049
- # Look for stat file corresponding to the weather file
2050
- stat_file_path = weather_file_path.sub('.epw', '.stat').to_s
2051
- if !File.exist? stat_file_path
2052
- # When the stat file corresponding with the weather file in the model is missing,
2053
- # use the weather file that represent the climate zone
2054
- climate_zone_weather_file_map = model_get_climate_zone_weather_file_map
2055
- weather_file = climate_zone_weather_file_map[climate_zone]
2056
- stat_file_path = model_get_weather_file(weather_file).sub('.epw', '.stat').to_s
2057
- end
2119
+ # Look for stat file corresponding to the weather file
2120
+ stat_file_path = weather_file_path.sub('.epw', '.stat').to_s
2121
+ if !File.exist? stat_file_path
2122
+ # When the stat file corresponding with the weather file in the model is missing,
2123
+ # use the weather file that represent the climate zone
2124
+ climate_zone_weather_file_map = model_get_climate_zone_weather_file_map
2125
+ prm_raise(climate_zone_weather_file_map.key?(climate_zone),
2126
+ @sizing_run_dir,
2127
+ "Failed to find a matching climate zone #{climate_zone} from the climate zone weather files.")
2128
+ weather_file = climate_zone_weather_file_map[climate_zone]
2129
+ stat_file_path = model_get_weather_file(weather_file).sub('.epw', '.stat').to_s
2130
+ end
2058
2131
 
2059
- ground_temp = OpenStudio::Model::SiteGroundTemperatureFCfactorMethod.new(model)
2060
- ground_temperatures = model_get_monthly_ground_temps_from_stat_file(stat_file_path)
2061
- unless ground_temperatures.empty?
2062
- # set the site ground temperature building surface
2063
- ground_temp.setAllMonthlyTemperatures(ground_temperatures)
2132
+ ground_temp = OpenStudio::Model::SiteGroundTemperatureFCfactorMethod.new(model)
2133
+ ground_temperatures = model_get_monthly_ground_temps_from_stat_file(stat_file_path)
2134
+ unless ground_temperatures.empty?
2135
+ # set the site ground temperature building surface
2136
+ ground_temp.setAllMonthlyTemperatures(ground_temperatures)
2137
+ end
2064
2138
  end
2065
2139
 
2066
- return true
2140
+ return surfaces_with_fc_factor_boundary
2067
2141
  end
2068
2142
 
2069
2143
  # Generate baseline log to a specific file directory
@@ -2074,7 +2148,7 @@ class ASHRAE901PRM < Standard
2074
2148
 
2075
2149
  # Retrieve zone HVAC user specified compliance inputs from CSV file
2076
2150
  #
2077
- # @param [OpenStudio::Model::Model] OpenStudio model object
2151
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
2078
2152
  def handle_zone_hvac_user_input_data(model)
2079
2153
  user_zone_hvac = @standards_data.key?('userdata_zone_hvac') ? @standards_data['userdata_zone_hvac'] : nil
2080
2154
  return unless user_zone_hvac && !user_zone_hvac.empty?
@@ -2148,9 +2222,9 @@ class ASHRAE901PRM < Standard
2148
2222
  end
2149
2223
 
2150
2224
  # This function checks whether it is required to adjust the window to wall ratio based on the model WWR and wwr limit.
2151
- # @param wwr_limit [Float] return wwr_limit
2225
+ # @param wwr_limit [Double] window to wall ratio limit
2152
2226
  # @param wwr_list [Array] list of wwr of zone conditioning category in a building area type category - residential, nonresidential and semiheated
2153
- # @return require_adjustment [Boolean] True, require adjustment, false not require adjustment.
2227
+ # @return [Boolean] True, require adjustment, false not require adjustment
2154
2228
  def model_does_require_wwr_adjustment?(wwr_limit, wwr_list)
2155
2229
  # 90.1 PRM routine requires
2156
2230
  return true
@@ -2160,7 +2234,7 @@ class ASHRAE901PRM < Standard
2160
2234
  #
2161
2235
  # @param bat [String] building category
2162
2236
  # @param wwr_list [Array] list of zone conditioning category-based WWR - residential, nonresidential and semiheated
2163
- # @return wwr_limit [Float] return adjusted wwr_limit
2237
+ # @return [Double] return adjusted wwr_limit
2164
2238
  def model_get_bat_wwr_target(bat, wwr_list)
2165
2239
  wwr_limit = 40.0
2166
2240
  # Lookup WWR target from stable baseline table
@@ -2186,32 +2260,35 @@ class ASHRAE901PRM < Standard
2186
2260
 
2187
2261
  # Calculate the window to wall ratio reduction factor
2188
2262
  #
2189
- # @param multiplier [Float] multiplier of the wwr
2190
- # @param surface_wwr [Float] the surface window to wall ratio
2191
- # @param surface_dr [Float] the surface door to wall ratio
2263
+ # @param multiplier [Double] multiplier of the wwr
2264
+ # @param surface [OpenStudio::Model:Surface] the surface object
2192
2265
  # @param wwr_building_type[String] building type for wwr
2193
- # @param wwr_target [Float] target window to wall ratio
2194
- # @param total_wall_m2 [Float] total wall area of the category in m2.
2195
- # @param total_wall_with_fene_m2 [Float] total wall area of the category with fenestrations in m2.
2196
- # @param total_fene_m2 [Float] total fenestration area
2197
- # @return [Float] reduction factor
2198
- def model_get_wwr_reduction_ratio(multiplier,
2199
- surface_wwr: 0.0,
2200
- surface_dr: 0.0,
2201
- wwr_building_type: 'All others',
2202
- wwr_target: nil,
2203
- total_wall_m2: 0.0, # prevent 0.0 division
2204
- total_wall_with_fene_m2: 0.0,
2205
- total_fene_m2: 0.0,
2206
- total_plenum_wall_m2: 0.0)
2266
+ # @param wwr_target [Double] target window to wall ratio
2267
+ # @param total_wall_m2 [Double] total wall area of the category in m2.
2268
+ # @param total_wall_with_fene_m2 [Double] total wall area of the category with fenestrations in m2.
2269
+ # @param total_fene_m2 [Double] total fenestration area
2270
+ # @return [Double] reduction factor
2271
+ def surface_get_wwr_reduction_ratio(multiplier,
2272
+ surface,
2273
+ wwr_building_type: 'All others',
2274
+ wwr_target: nil,
2275
+ total_wall_m2: 0.0, # prevent 0.0 division
2276
+ total_wall_with_fene_m2: 0.0,
2277
+ total_fene_m2: 0.0,
2278
+ total_plenum_wall_m2: 0.0)
2279
+
2280
+ surface_name = surface.name.get
2281
+ surface_wwr = surface_get_wwr(surface)
2282
+ surface_dr = surface_get_door_ratio(surface)
2207
2283
 
2208
2284
  if multiplier < 1.0
2209
2285
  # Case when reduction is required
2210
2286
  reduction_ratio = 1.0 - multiplier
2287
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log',
2288
+ "Surface #{surface_name} WWR is #{surface_wwr}. Reduce its WWR to #{surface_wwr * reduction_ratio}%")
2211
2289
  else
2212
2290
  # Case when increase is required - takes the door area into consideration.
2213
2291
  # The target is to increase each surface to maximum 90% WWR deduct the total door area.
2214
- total_dr = 0.0
2215
2292
  exist_max_wwr = 0.0
2216
2293
  if total_wall_m2 > 0 then exist_max_wwr = total_wall_with_fene_m2 * 0.9 / total_wall_m2 end
2217
2294
  if exist_max_wwr < wwr_target
@@ -2220,6 +2297,9 @@ class ASHRAE901PRM < Standard
2220
2297
  # delta_fenestration_surface_area / delta_wall_surface_area + 1.0 = increase_ratio for a surface with no windows.
2221
2298
  # ASSUMPTION!! assume adding windows to surface with no windows will never be window_m2 + door_m2 > surface_m2.
2222
2299
  reduction_ratio = (wwr_target - exist_max_wwr) * total_wall_m2 / (total_wall_m2 - total_wall_with_fene_m2 - total_plenum_wall_m2) + 1.0
2300
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log',
2301
+ "The max window to wall ratio is #{exist_max_wwr}, smaller than the target window to wall ratio #{wwr_target}.
2302
+ Surface #{surface_name} has no fenestration subsurfaces. Adding new fenestration band with WWR of #{(reduction_ratio - 1) * 100}%")
2223
2303
  else
2224
2304
  # surface has fenestration - expand it to 90% WWR or surface area minus door area, whichever is smaller.
2225
2305
  if (1.0 - surface_dr) < 0.9
@@ -2229,6 +2309,9 @@ class ASHRAE901PRM < Standard
2229
2309
  else
2230
2310
  reduction_ratio = 0.9 / surface_wwr
2231
2311
  end
2312
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log',
2313
+ "The max window to wall ratio is #{exist_max_wwr}, smaller than the target window to wall ratio #{wwr_target}.
2314
+ Surface #{surface_name} will expand its WWR to 90%")
2232
2315
  end
2233
2316
  else
2234
2317
  # multiplier will be negative number thus resulting in > 1 reduction_ratio
@@ -2248,10 +2331,10 @@ class ASHRAE901PRM < Standard
2248
2331
  # This function shall only be called if the maximum WWR value for surfaces with fenestration is lower than 90% due to
2249
2332
  # accommodating the total door surface areas
2250
2333
  #
2251
- # @param residual_ratio: [Float] the ratio of residual surfaces among the total wall surface area with no fenestrations
2334
+ # @param residual_ratio [Double] the ratio of residual surfaces among the total wall surface area with no fenestrations
2252
2335
  # @param space [OpenStudio::Model:Space] a space
2253
2336
  # @param model [OpenStudio::Model::Model] openstudio model
2254
- # @return [Bool] return true if successful, false if not
2337
+ # @return [Boolean] returns true if successful, false if not
2255
2338
  def model_readjust_surface_wwr(residual_ratio, space, model)
2256
2339
  # In this loop, we will focus on the surfaces with newly added a fenestration.
2257
2340
  space.surfaces.sort.each do |surface|
@@ -2267,10 +2350,12 @@ class ASHRAE901PRM < Standard
2267
2350
 
2268
2351
  # Assign spaces to system groups based on building area type
2269
2352
  # Get zone groups separately for each hvac building type
2353
+ #
2354
+ # @param model [OpenStudio::Model::Model] openstudio model
2270
2355
  # @param custom [String] identifier for custom programs, not used here, but included for backwards compatibility
2271
2356
  # @param bldg_type_hvac_zone_hash [Hash of bldg_type:list of zone objects] association of zones to each hvac building type
2272
2357
  # @return [Array<Hash>] an array of hashes of area information,
2273
- # with keys area_ft2, type, fuel, and zones (an array of zones)
2358
+ # with keys area_ft2, type, fuel, and zones (an array of zones)
2274
2359
  def model_prm_baseline_system_groups(model, custom, bldg_type_hvac_zone_hash)
2275
2360
  bldg_groups = []
2276
2361
 
@@ -2293,22 +2378,19 @@ class ASHRAE901PRM < Standard
2293
2378
  # Groups may include zones from multiple floors; separating by floor is handled later
2294
2379
  # For stable baseline, heating type is based on climate, not proposed heating type
2295
2380
  # Isolate zones that have heating-only or district (purchased) heat or chilled water
2296
- # @param bldg_type_hvac_zone_hash [Hash of bldg_type:list of zone objects] association of zones to each hvac building type
2381
+ # @param hvac_building_type [String] Chosen by user via measure interface or user data files
2382
+ # @param zones_in_building_type [Array<OpenStudio::Model::ThermalZone>] array of thermal zones
2297
2383
  # @return [Array<Hash>] an array of hashes of area information,
2298
- # with keys area_ft2, type, fuel, and zones (an array of zones)
2384
+ # with keys area_ft2, type, fuel, and zones (an array of zones)
2299
2385
  def get_baseline_system_groups_for_one_building_type(model, hvac_building_type, zones_in_building_type)
2300
2386
  # Build zones hash of [zone, zone area, occupancy type, building type, fuel]
2301
2387
  zones = model_zones_with_occ_and_fuel_type(model, 'custom')
2302
2388
 
2303
2389
  # Ensure that there is at least one conditioned zone
2304
- if zones.size.zero?
2305
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', 'The building does not appear to have any conditioned zones. Make sure zones have thermostat with appropriate heating and cooling setpoint schedules.')
2306
- return []
2307
- end
2390
+ prm_raise(!zones.empty?, @sizing_run_dir, 'The building does not appear to have any conditioned zones. Make sure zones have thermostat with appropriate heating and cooling setpoint schedules.')
2308
2391
 
2309
2392
  # Consider special rules for computer rooms
2310
2393
  # need load of all
2311
-
2312
2394
  # Get cooling load of all computer rooms to establish system types
2313
2395
  comp_room_loads = {}
2314
2396
  bldg_comp_room_load = 0
@@ -2317,15 +2399,18 @@ class ASHRAE901PRM < Standard
2317
2399
  has_computer_room = false
2318
2400
  # First check if any space in zone has a computer room
2319
2401
  zn['zone'].spaces.each do |space|
2320
- if space.spaceType.get.standardsSpaceType.get == 'computer room'
2402
+ if prm_get_optional_handler(space, @sizing_run_dir, 'spaceType', 'standardsSpaceType') == 'computer room'
2321
2403
  has_computer_room = true
2322
2404
  break
2323
2405
  end
2324
2406
  end
2325
2407
  if has_computer_room
2326
2408
  # Collect load for entire zone
2327
- zone_load_w = zn['zone'].coolingDesignLoad.to_f
2328
- zone_load_w *= zn['zone'].floorArea * zn['zone'].multiplier
2409
+ if zn['zone'].model.version < OpenStudio::VersionString.new('3.6.0')
2410
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.ashrae_90_1_prm.Model', 'Required ThermalZone method .autosizedCoolingDesignLoad is not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.')
2411
+ end
2412
+ zone_load_w = zn['zone'].autosizedCoolingDesignLoad.get
2413
+ zone_load_w *= zn['zone'].multiplier
2329
2414
  zone_load = OpenStudio.convert(zone_load_w, 'W', 'Btu/hr').get
2330
2415
  end
2331
2416
  comp_room_loads[zn['zone'].name.get] = zone_load
@@ -2336,14 +2421,15 @@ class ASHRAE901PRM < Standard
2336
2421
  # Make list of zone objects that contain laboratory spaces
2337
2422
  lab_zones = []
2338
2423
  has_lab_spaces = {}
2339
- model.getThermalZones.sort.each do |zone|
2424
+ model.getThermalZones.each do |zone|
2340
2425
  # Check if this zone includes laboratory space
2341
2426
  zone.spaces.each do |space|
2342
- spacetype = space.spaceType.get.standardsSpaceType.get
2343
- has_lab_spaces[zone.name.get] = false
2344
- if space.spaceType.get.standardsSpaceType.get == 'laboratory'
2427
+ space_type = prm_get_optional_handler(space, @sizing_run_dir, 'spaceType', 'standardsSpaceType')
2428
+ zone_name = zone.name.get
2429
+ has_lab_spaces[zone_name] = false
2430
+ if space_type == 'laboratory'
2345
2431
  lab_zones << zone
2346
- has_lab_spaces[zone.name.get] = true
2432
+ has_lab_spaces[zone_name] = true
2347
2433
  break
2348
2434
  end
2349
2435
  end
@@ -2371,17 +2457,16 @@ class ASHRAE901PRM < Standard
2371
2457
  # Take from hourly reports created during sizing run
2372
2458
  node_list.each do |node_name, zone_name|
2373
2459
  sql = model.sqlFile
2374
- if sql.is_initialized
2375
- sql = sql.get
2376
- query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
2377
- val = sql.execAndReturnFirstDouble(query)
2378
- query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{val.get}'"
2379
- val = sql.execAndReturnFirstDouble(query)
2380
- if val.is_initialized
2381
- result = OpenStudio::OptionalDouble.new(val.get)
2382
- end
2383
- zone_return_flow_si[zone_name] += result.to_f
2384
- end
2460
+ prm_raise(sql.is_initialized, @sizing_run_dir, 'Model is missing SQL file. It is likely caused by: 1. unsuccessful simulation, 2. SQL is not set as one of the output file.')
2461
+ sql = sql.get
2462
+ query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
2463
+ val = sql.execAndReturnFirstDouble(query)
2464
+ prm_raise(val.is_initialized, @sizing_run_dir, "No hourly return air flow data reported for node #{node_name}")
2465
+ report_data_dict_index = val.get
2466
+ query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{report_data_dict_index}'"
2467
+ val = sql.execAndReturnFirstDouble(query)
2468
+ prm_raise(val.is_initialized, @sizing_run_dir, "No hourly return air flow data reported at report index #{report_data_dict_index}")
2469
+ zone_return_flow_si[zone_name] += OpenStudio::OptionalDouble.new(val.get).to_f
2385
2470
  end
2386
2471
 
2387
2472
  # Calc ratio of Air Loop relief to sum of zone return for each air loop
@@ -2390,24 +2475,23 @@ class ASHRAE901PRM < Standard
2390
2475
  # For each air loop, get relief air flow and calculate lab exhaust from the central air handler
2391
2476
  # Take from hourly reports created during sizing run
2392
2477
  zone_relief_flow_si = {}
2393
- model.getAirLoopHVACs.sort.each do |air_loop_hvac|
2478
+ model.getAirLoopHVACs.each do |air_loop_hvac|
2394
2479
  # First get relief air flow from sizing run sql file
2395
- relief_node = air_loop_hvac.reliefAirNode.get
2480
+ relief_node = prm_get_optional_handler(air_loop_hvac, @sizing_run_dir, 'reliefAirNode')
2396
2481
  node_name = relief_node.nameString
2397
2482
  relief_flow_si = 0
2398
2483
  relief_fraction = 0
2399
2484
  sql = model.sqlFile
2400
- if sql.is_initialized
2401
- sql = sql.get
2402
- query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
2403
- val = sql.execAndReturnFirstDouble(query)
2404
- query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{val.get}'"
2405
- val = sql.execAndReturnFirstDouble(query)
2406
- if val.is_initialized
2407
- result = OpenStudio::OptionalDouble.new(val.get)
2408
- end
2409
- relief_flow_si = result.to_f
2485
+ prm_raise(sql.is_initialized, @sizing_run_dir, 'Model is missing SQL file. It is likely caused by: 1. unsuccessful simulation, 2. SQL is not set as one of the output file.')
2486
+ sql = sql.get
2487
+ query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
2488
+ val = sql.execAndReturnFirstDouble(query)
2489
+ query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{val.get}'"
2490
+ val = sql.execAndReturnFirstDouble(query)
2491
+ if val.is_initialized
2492
+ result = OpenStudio::OptionalDouble.new(val.get)
2410
2493
  end
2494
+ relief_flow_si = result.to_f
2411
2495
 
2412
2496
  # Get total flow of zones on this air loop
2413
2497
  total_zone_return_si = 0
@@ -2652,11 +2736,11 @@ class ASHRAE901PRM < Standard
2652
2736
  # Heating fuel is based on climate zone, unless district heat is in proposed
2653
2737
  #
2654
2738
  # @note Select system type from data table base on key parameters
2655
- # @param climate_zone [string] id code for the climate
2656
- # @param sys_group [hash] Hash defining a group of zones that have the same Appendix G system type
2657
- # @param custom [string] included here for backwards compatibility (not used here)
2739
+ # @param climate_zone [String] id code for the climate
2740
+ # @param sys_group [Hash] Hash defining a group of zones that have the same Appendix G system type
2741
+ # @param custom [String] included here for backwards compatibility (not used here)
2658
2742
  # @param hvac_building_type [String] Chosen by user via measure interface or user data files
2659
- # @param district_heat_zones [hash] of zone name => true for has district heat, false for has not
2743
+ # @param district_heat_zones [Hash] of zone name => true for has district heat, false for has not
2660
2744
  # @return [String] The system type. Possibilities are PTHP, PTAC, PSZ_AC, PSZ_HP, PVAV_Reheat, PVAV_PFP_Boxes,
2661
2745
  # VAV_Reheat, VAV_PFP_Boxes, Gas_Furnace, Electric_Furnace
2662
2746
  def model_prm_baseline_system_type(model, climate_zone, sys_group, custom, hvac_building_type, district_heat_zones)
@@ -2664,26 +2748,23 @@ class ASHRAE901PRM < Standard
2664
2748
  fuel_type = sys_group['fuel']
2665
2749
  area_ft2 = sys_group['building_area_type_ft2']
2666
2750
  num_stories = sys_group['stories']
2667
- zones = sys_group['zones']
2668
2751
 
2669
2752
  # [type, central_heating_fuel, zone_heating_fuel, cooling_fuel]
2670
2753
  system_type = [nil, nil, nil, nil]
2671
2754
 
2672
2755
  # Find matching record from prm baseline hvac table
2673
2756
  # First filter by number of stories
2674
- iStoryGroup = 0
2757
+ i_story_group = 0
2675
2758
  props = {}
2676
2759
  0.upto(9) do |i|
2677
- iStoryGroup += 1
2760
+ i_story_group += 1
2678
2761
  props = model_find_object(standards_data['prm_baseline_hvac'],
2679
2762
  'template' => template,
2680
2763
  'hvac_building_type' => area_type,
2681
- 'flrs_range_group' => iStoryGroup,
2764
+ 'flrs_range_group' => i_story_group,
2682
2765
  'area_range_group' => 1)
2683
2766
 
2684
- if !props
2685
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Could not find baseline HVAC type for: #{template}-#{area_type}.")
2686
- end
2767
+ prm_raise(props, @sizing_run_dir, "Could not find baseline HVAC type for: #{template}-#{area_type}.")
2687
2768
  if num_stories <= props['bldg_flrs_max']
2688
2769
  # Story Group Is found
2689
2770
  break
@@ -2691,19 +2772,16 @@ class ASHRAE901PRM < Standard
2691
2772
  end
2692
2773
 
2693
2774
  # Next filter by floor area
2694
- iAreaGroup = 0
2695
- baseine_is_found = false
2775
+ i_area_group = 0
2696
2776
  loop do
2697
- iAreaGroup += 1
2777
+ i_area_group += 1
2698
2778
  props = model_find_object(standards_data['prm_baseline_hvac'],
2699
2779
  'template' => template,
2700
2780
  'hvac_building_type' => area_type,
2701
- 'flrs_range_group' => iStoryGroup,
2702
- 'area_range_group' => iAreaGroup)
2781
+ 'flrs_range_group' => i_story_group,
2782
+ 'area_range_group' => i_area_group)
2703
2783
 
2704
- if !props
2705
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Could not find baseline HVAC type for: #{template}-#{area_type}.")
2706
- end
2784
+ prm_raise(props && i_area_group <= 9, @sizing_run_dir, "Could not find baseline HVAC type for: #{template}-#{area_type}.")
2707
2785
  below_max = false
2708
2786
  above_min = false
2709
2787
  # check if actual building floor area is within range for this area group
@@ -2725,12 +2803,8 @@ class ASHRAE901PRM < Standard
2725
2803
  above_min = true
2726
2804
  end
2727
2805
  end
2728
- if (above_min == true) && (below_max == true)
2729
- baseline_is_found = true
2730
- break
2731
- end
2732
- if iAreaGroup > 9
2733
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Could not find baseline HVAC type for: #{template}-#{area_type}.")
2806
+ if above_min && below_max
2807
+ # break condition.
2734
2808
  break
2735
2809
  end
2736
2810
  end
@@ -2790,8 +2864,8 @@ class ASHRAE901PRM < Standard
2790
2864
 
2791
2865
  # For a multizone system, create the fan schedule based on zone occupancy/fan schedules
2792
2866
  # @author Doug Maddox, PNNL
2793
- # @param model
2794
- # @param zone_fan_scheds [Hash] of hash of zoneName:8760FanSchedPerZone
2867
+ # @param model [OpenStudio::Model::Model] openstudio model
2868
+ # @param zone_op_hrs [Hash] of hash of zoneName zone_op_hrs
2795
2869
  # @param pri_zones [Array<String>] names of zones served by the multizone system
2796
2870
  # @param system_name [String] name of air loop
2797
2871
  def model_create_multizone_fan_schedule(model, zone_op_hrs, pri_zones, system_name)
@@ -2823,7 +2897,7 @@ class ASHRAE901PRM < Standard
2823
2897
  else
2824
2898
  fan_sch_limits = fan_sch_limits.get
2825
2899
  end
2826
- sch_name = system_name + ' ' + 'fan schedule'
2900
+ sch_name = "#{system_name} fan schedule"
2827
2901
  make_ruleset_sched_from_8760(model, fan_8760, sch_name, fan_sch_limits)
2828
2902
 
2829
2903
  air_loop = model.getAirLoopHVACByName(system_name).get
@@ -2836,9 +2910,9 @@ class ASHRAE901PRM < Standard
2836
2910
  # @author Doug Maddox, PNNL
2837
2911
  # @param model
2838
2912
  # @param zones [Array<Object>]
2839
- # @param zone_fan_scheds [Hash] hash of zoneName:8760FanSchedPerZone
2913
+ # @param zone_fan_scheds [Hash] hash of zoneName 8760FanSchedPerZone
2840
2914
  # @return [Hash] A hash of two arrays of ThermalZones,
2841
- # where the keys are 'primary' and 'secondary'
2915
+ # where the keys are 'primary' and 'secondary'
2842
2916
  def model_differentiate_primary_secondary_thermal_zones(model, zones, zone_fan_scheds)
2843
2917
  pri_zones = []
2844
2918
  sec_zones = []
@@ -3015,7 +3089,7 @@ class ASHRAE901PRM < Standard
3015
3089
  # to account for recent model changes
3016
3090
  # @author Doug Maddox, PNNL
3017
3091
  # @param model
3018
- # @return [Bool] true if successful, false if not
3092
+ # @return [Boolean] returns true if successful, false if not
3019
3093
  def model_refine_size_dependent_values(model, sizing_run_dir)
3020
3094
  # Final sizing run before refining size-dependent values
3021
3095
  if model_run_sizing_run(model, "#{sizing_run_dir}/SR3") == false
@@ -3044,4 +3118,47 @@ class ASHRAE901PRM < Standard
3044
3118
  end
3045
3119
  return true
3046
3120
  end
3121
+
3122
+ # Determine whether heating type is fuel or electric
3123
+ # @param hvac_building_type [String] Key for lookup of baseline system type
3124
+ # @param climate_zone [String] full name of climate zone
3125
+ # @return [String] fuel or electric
3126
+ def find_prm_heat_type(hvac_building_type, climate_zone)
3127
+ climate_code = get_climate_zone_code(climate_zone)
3128
+ heat_type_props = model_find_object(standards_data['prm_heat_type'],
3129
+ 'template' => template,
3130
+ 'hvac_building_type' => hvac_building_type,
3131
+ 'climate_zone' => climate_code)
3132
+ if !heat_type_props
3133
+ # try again with wild card for climate
3134
+ heat_type_props = model_find_object(standards_data['prm_heat_type'],
3135
+ 'template' => template,
3136
+ 'hvac_building_type' => hvac_building_type,
3137
+ 'climate_zone' => 'any')
3138
+ end
3139
+
3140
+ if !heat_type_props
3141
+ # try again with wild card for building type
3142
+ heat_type_props = model_find_object(standards_data['prm_heat_type'],
3143
+ 'template' => template,
3144
+ 'hvac_building_type' => 'all others',
3145
+ 'climate_zone' => climate_code)
3146
+ end
3147
+ prm_raise(heat_type_props, @sizing_run_dir, "Could not find baseline heat type for: #{template}-#{hvac_building_type}-#{climate_zone}.")
3148
+ return heat_type_props['heat_type']
3149
+ end
3150
+
3151
+ private
3152
+
3153
+ # Check if the PRM process uses user data. The function returns a hash when
3154
+ # 1. There is a matching user data
3155
+ # 2. The matching user data is not nil saved in the @standards_data
3156
+ # 3. The matching user data hash is not empty
3157
+ # The function returns nil if none of the above matched.
3158
+ #
3159
+ # @param user_data_csv [String] the name of the user data csv file
3160
+ # @return [hash | nil] Returns hash or nil.
3161
+ def get_userdata(user_data_csv)
3162
+ return @standards_data.key?(user_data_csv) && @standards_data[user_data_csv].length >= 1 ? @standards_data[user_data_csv] : nil
3163
+ end
3047
3164
  end