openstudio-standards 0.4.0 → 0.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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