openstudio-standards 0.2.16 → 0.2.17.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/manage_OpenStudio_Standards.rb +31 -4
  3. data/lib/openstudio-standards/btap/geometry.rb +1 -1
  4. data/lib/openstudio-standards/hvac_sizing/Siz.HeatingCoolingFuels.rb +354 -2
  5. data/lib/openstudio-standards/hvac_sizing/Siz.ThermalZone.rb +79 -0
  6. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.College.rb +1 -1
  7. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Laboratory.rb +1 -1
  8. data/lib/openstudio-standards/prototypes/common/do_not_edit_metaclasses.rb +3313 -0
  9. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Fan.rb +12 -0
  10. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.rb +3 -4
  11. data/lib/openstudio-standards/prototypes/common/objects/Prototype.SizingSystem.rb +1 -1
  12. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +167 -93
  13. data/lib/openstudio-standards/prototypes/common/objects/Prototype.utilities.rb +2 -4
  14. data/lib/openstudio-standards/prototypes/common/prototype_metaprogramming.rb +1 -0
  15. data/lib/openstudio-standards/refs/references.rb +3 -0
  16. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +279 -6
  17. data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctParallelPIUReheat.rb +50 -2
  18. data/lib/openstudio-standards/standards/Standards.ChillerElectricEIR.rb +4 -0
  19. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +0 -1
  20. data/lib/openstudio-standards/standards/Standards.Construction.rb +185 -3
  21. data/lib/openstudio-standards/standards/Standards.Fan.rb +14 -6
  22. data/lib/openstudio-standards/standards/Standards.HeatExchangerSensLat.rb +1 -0
  23. data/lib/openstudio-standards/standards/Standards.Model.rb +1751 -383
  24. data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +130 -9
  25. data/lib/openstudio-standards/standards/Standards.PlantLoop.rb +50 -3
  26. data/lib/openstudio-standards/standards/Standards.ScheduleCompact.rb +44 -0
  27. data/lib/openstudio-standards/standards/Standards.ScheduleConstant.rb +27 -0
  28. data/lib/openstudio-standards/standards/Standards.ScheduleRuleset.rb +543 -0
  29. data/lib/openstudio-standards/standards/Standards.Space.rb +665 -15
  30. data/lib/openstudio-standards/standards/Standards.SpaceType.rb +141 -4
  31. data/lib/openstudio-standards/standards/Standards.SubSurface.rb +2 -1
  32. data/lib/openstudio-standards/standards/Standards.Surface.rb +117 -0
  33. data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +197 -49
  34. data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +41 -0
  35. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Model.rb +6 -8
  36. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/comstock_ashrae_90_1_2004/data/ashrae_90_1.schedules.json +45 -45
  37. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/comstock_ashrae_90_1_2004/data/comstock_ashrae_90_1_2004.spc_typ.json +7 -7
  38. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/comstock_ashrae_90_1_2007/data/ashrae_90_1.schedules.json +45 -45
  39. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/comstock_ashrae_90_1_2007/data/comstock_ashrae_90_1_2007.spc_typ.json +7 -7
  40. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/comstock_ashrae_90_1_2010/data/ashrae_90_1.schedules.json +45 -45
  41. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/comstock_ashrae_90_1_2010/data/comstock_ashrae_90_1_2010.spc_typ.json +9 -9
  42. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/comstock_ashrae_90_1_2013/data/ashrae_90_1.schedules.json +45 -45
  43. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/comstock_ashrae_90_1_2013/data/comstock_ashrae_90_1_2013.spc_typ.json +4 -4
  44. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/comstock_ashrae_90_1_2016/data/ashrae_90_1.schedules.json +45 -45
  45. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/comstock_ashrae_90_1_2016/data/comstock_ashrae_90_1_2016.spc_typ.json +5 -5
  46. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +5 -5
  47. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/comstock_ashrae_90_1_2019/data/ashrae_90_1.schedules.json +45 -45
  48. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/comstock_ashrae_90_1_2019/data/comstock_ashrae_90_1_2019.spc_typ.json +5 -5
  49. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.constructions.json +2 -2
  50. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.fans.json +12 -0
  51. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/comstock_doe_ref_1980_2004/data/ashrae_90_1.schedules.json +45 -45
  52. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/comstock_doe_ref_1980_2004/data/comstock_doe_ref_1980_2004.spc_typ.json +10 -10
  53. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/comstock_doe_ref_pre_1980/data/ashrae_90_1.schedules.json +45 -45
  54. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/comstock_doe_ref_pre_1980/data/comstock_doe_ref_pre_1980.spc_typ.json +10 -10
  55. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.AirLoopHVAC.rb +1 -0
  56. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirLoopHVAC.rb +792 -0
  57. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirTerminalSingleDuctParallelPIUReheat.rb +10 -0
  58. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirTerminalSingleDuctVAVReheat.rb +31 -0
  59. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.BoilerHotWater.rb +91 -0
  60. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ChillerElectricEIR.rb +84 -0
  61. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXSingleSpeed.rb +145 -0
  62. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXTwoSpeed.rb +106 -0
  63. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilDX.rb +71 -0
  64. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingDXSingleSpeed.rb +194 -0
  65. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingGas.rb +120 -0
  66. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoolingTower.rb +110 -0
  67. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoolingTowerVariableSpeed.rb +5 -0
  68. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Fan.rb +73 -0
  69. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanConstantVolume.rb +5 -0
  70. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanOnOff.rb +5 -0
  71. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanVariableVolume.rb +24 -0
  72. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanZoneExhaust.rb +5 -0
  73. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.HeatExchangerSensLat.rb +55 -0
  74. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Model.rb +3045 -0
  75. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlanarSurface.rb +187 -0
  76. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +450 -0
  77. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Space.rb +106 -0
  78. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +666 -0
  79. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Surface.rb +54 -0
  80. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ThermalZone.rb +168 -0
  81. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ZoneHVACComponent.rb +132 -0
  82. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.rb +239 -0
  83. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +176 -0
  84. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.rb +25 -0
  85. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +52 -0
  86. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.chillers.json +112 -0
  87. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.climate_zone_sets.json +210 -0
  88. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +10384 -0
  89. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_sets.json +133 -0
  90. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.furnaces.json +43 -0
  91. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_pumps.json +119 -0
  92. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_pumps_heating.json +130 -0
  93. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_rejection.json +13 -0
  94. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.lpd_space_type.json +568 -0
  95. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.motors.json +264 -0
  96. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_baseline_hvac.json +439 -0
  97. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_constructions.json +685 -0
  98. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_economizers.json +213 -0
  99. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_ext_ltg.json +32 -0
  100. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_heat_type.json +136 -0
  101. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_hvac_bldg_type.json +32 -0
  102. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_interior_lighting.json +1837 -0
  103. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_swh_bldg_type.json +184 -0
  104. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_wwr_bldg_type.json +84 -0
  105. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.unitary_acs.json +148 -0
  106. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.water_heaters.json +157 -0
  107. data/lib/openstudio-standards/standards/ashrae_90_1_prm/data/ashrae_90_1_prm.climate_zone_sets.json +210 -0
  108. data/lib/openstudio-standards/standards/ashrae_90_1_prm/data/ashrae_90_1_prm.curves.json +18329 -0
  109. data/lib/openstudio-standards/standards/ashrae_90_1_prm/data/ashrae_90_1_prm.fans.json +340 -0
  110. data/lib/openstudio-standards/standards/ashrae_90_1_prm/data/ashrae_90_1_prm.materials.json +49924 -0
  111. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/baseline_building_rotation_exception.md +44 -0
  112. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/check_pump_power_and_control.md +71 -0
  113. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/dcv.md +68 -0
  114. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/dcv_implementation.png +0 -0
  115. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/elevators.md +14 -0
  116. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/exhaust_air_energy_recovery.md +36 -0
  117. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/f_c_factors.md +19 -0
  118. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/fan_power_credits.md +15 -0
  119. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/preheat_coil.md +59 -0
  120. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/pump_power_control.md +46 -0
  121. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/return_air_type.md +31 -0
  122. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_baseline_wwr.md +191 -0
  123. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_hw_and_chw_supply_water_temp_reset_control.md +24 -0
  124. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_num_boilers_chillers_towers.md +49 -0
  125. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_plug_load_measures.md +80 -0
  126. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_space_lpd.md +73 -0
  127. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/unenclosed_and_unconditioned_spaces.md +11 -0
  128. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/unmet_load_hours.md +20 -0
  129. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/vav_parallel_piu_terminals_fan_control.md +23 -0
  130. data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/vav_terminals_min_flow_setpoint.md +21 -0
  131. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_airloop_hvac.csv +1 -0
  132. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_airloop_hvac_doas.csv +1 -0
  133. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_building.csv +1 -0
  134. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_design_specification_outdoor_air.csv +1 -0
  135. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_electric_equipment.csv +1 -0
  136. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_exterior_lights.csv +1 -0
  137. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_gas_equipment.csv +1 -0
  138. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_lights.csv +1 -0
  139. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_space.csv +1 -0
  140. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_spacetype.csv +1 -0
  141. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_thermal_zone.csv +1 -0
  142. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_wateruse_connections.csv +1 -0
  143. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_wateruse_equipment.csv +1 -0
  144. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_wateruse_equipment_definition.csv +1 -0
  145. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_zone_hvac.csv +1 -0
  146. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_zone_infiltration.csv +1 -0
  147. data/lib/openstudio-standards/standards/cbes/data/cbes.fans.json +12 -0
  148. data/lib/openstudio-standards/standards/deer/data/deer.fans.json +12 -0
  149. data/lib/openstudio-standards/standards/necb/ECMS/data/heat_pumps.json +1 -1
  150. data/lib/openstudio-standards/standards/necb/ECMS/data/heat_pumps_heating.json +1 -1
  151. data/lib/openstudio-standards/standards/necb/ECMS/data/unitary_acs.json +24 -11
  152. data/lib/openstudio-standards/standards/necb/ECMS/erv.rb +13 -15
  153. data/lib/openstudio-standards/standards/necb/NECB2011/data/province_map.json +17 -0
  154. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb +1 -1
  155. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb +1 -1
  156. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb +2 -2
  157. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb +6 -5
  158. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +3 -2
  159. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +2 -3
  160. data/lib/openstudio-standards/standards/necb/NECB2020/data/chillers.json +2 -2
  161. data/lib/openstudio-standards/standards/necb/NECB2020/data/space_types.json +33 -924
  162. data/lib/openstudio-standards/standards/necb/NECB2020/data/unitary_acs.json +15 -15
  163. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +135 -29
  164. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +16 -4
  165. data/lib/openstudio-standards/standards/necb/common/neb_end_use_prices.csv +40 -42
  166. data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +1 -1
  167. data/lib/openstudio-standards/standards/necb/common/space_type_upgrade_map.json +89 -89
  168. data/lib/openstudio-standards/utilities/array.rb +11 -0
  169. data/lib/openstudio-standards/utilities/logging.rb +48 -0
  170. data/lib/openstudio-standards/utilities/object_info.rb +20 -0
  171. data/lib/openstudio-standards/utilities/schedule_translator.rb +348 -0
  172. data/lib/openstudio-standards/utilities/sqlfile.rb +68 -0
  173. data/lib/openstudio-standards/version.rb +2 -2
  174. data/lib/openstudio-standards/weather/Weather.Model.rb +42 -55
  175. data/lib/openstudio-standards/weather/Weather.stat_file.rb +1 -1
  176. data/lib/openstudio-standards.rb +35 -1
  177. metadata +111 -6
  178. data/data/standards/OpenStudio_Standards-ashrae_90_1.xlsx +0 -0
  179. data/data/standards/OpenStudio_Standards-ashrae_90_1_28Jan2022.xlsx +0 -0
  180. data/data/standards/OpenStudio_Standards-ashrae_90_1_28_Jan2022_2.xlsx +0 -0
  181. data/data/standards/openstudio_standards_duplicates_log.csv +0 -143
@@ -0,0 +1,666 @@
1
+ class ASHRAE901PRM2019 < ASHRAE901PRM
2
+ # @!group SpaceType
3
+
4
+ # Sets the selected internal loads to standards-based or typical values.
5
+ # For each category that is selected get all load instances. Remove all
6
+ # but the first instance if multiple instances. Add a new instance/definition
7
+ # if no instance exists. Modify the definition for the remaining instance
8
+ # to have the specified values. This method does not alter any
9
+ # loads directly assigned to spaces. This method skips plenums.
10
+ #
11
+ # @param space_type [OpenStudio::Model::SpaceType] space type object
12
+ # @param set_people [Bool] if true, set the people density.
13
+ # Also, assign reasonable clothing, air velocity, and work efficiency inputs
14
+ # to allow reasonable thermal comfort metrics to be calculated.
15
+ # @param set_lights [Bool] if true, set the lighting density, lighting fraction
16
+ # to return air, fraction radiant, and fraction visible.
17
+ # @param set_electric_equipment [Bool] if true, set the electric equipment density
18
+ # @param set_gas_equipment [Bool] if true, set the gas equipment density
19
+ # @param set_ventilation [Bool] if true, set the ventilation rates (per-person and per-area)
20
+ # @param set_infiltration [Bool] if true, set the infiltration rates
21
+ # @return [Bool] returns true if successful, false if not
22
+ def space_type_apply_internal_loads(space_type, set_people, set_lights, set_electric_equipment, set_gas_equipment, set_ventilation, set_infiltration)
23
+ # Skip plenums
24
+ # Check if the space type name
25
+ # contains the word plenum.
26
+ if space_type.name.get.to_s.downcase.include?('plenum')
27
+ return false
28
+ end
29
+
30
+ if space_type.standardsSpaceType.is_initialized
31
+ if space_type.standardsSpaceType.get.downcase.include?('plenum')
32
+ return false
33
+ end
34
+ end
35
+
36
+ # Save information about lighting exceptions before removing extra lights objects
37
+ # First get list of all lights objects that are exempt
38
+ regulated_lights = []
39
+ unregulated_lights = []
40
+ user_lights = @standards_data.key?('userdata_lights') ? @standards_data['userdata_lights'] : nil
41
+ if user_lights && user_lights.length >= 1
42
+ user_lights.each do |user_data|
43
+ lights_name = user_data['name']
44
+ lights_obj = space_type.model.getLightsByName(lights_name).get
45
+
46
+ if user_data['has_retail_display_exception'].to_s.downcase == 'yes' || user_data['has_unregulated_exception'].to_s.downcase == 'yes'
47
+ # If either exception is applicable
48
+ # Put this one on the unregulated list
49
+ unregulated_lights.push(lights_name)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Get all lights objects that are not exempt
55
+ space_type.lights.sort.each do |lights_obj|
56
+ lights_name = lights_obj.name.get
57
+ if !unregulated_lights.include? lights_name
58
+ regulated_lights << lights_obj
59
+ end
60
+ end
61
+
62
+ # Pre-process the light instances in the space type
63
+ # Remove all regulated instances but leave one in the space type
64
+ if regulated_lights.size.zero?
65
+ definition = OpenStudio::Model::LightsDefinition.new(space_type.model)
66
+ definition.setName("#{space_type.name} Lights Definition")
67
+ instance = OpenStudio::Model::Lights.new(definition)
68
+ lights_name = "#{space_type.name} Lights"
69
+ instance.setName(lights_name)
70
+ instance.setSpaceType(space_type)
71
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no lights, one has been created.")
72
+ space_type.additionalProperties.setFeature('regulated_lights_name', lights_name)
73
+ regulated_lights << instance
74
+ else
75
+ regulated_lights.each_with_index do |inst, i|
76
+ if i.zero?
77
+ # Save the name of the first instance to use as the baseline lights object
78
+ lights_name = inst.name.get
79
+ space_type.additionalProperties.setFeature('regulated_lights_name', lights_name)
80
+ next
81
+ end
82
+
83
+ # Remove all other lights objects that have not been identified as unregulated
84
+ if i == 1
85
+ ref_name = space_type.additionalProperties.getFeatureAsString('regulated_lights_name').to_s
86
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Multiple lights objects found in user model for #{space_type.name}. Baseline schedule will be determined from #{ref_name}")
87
+ end
88
+
89
+ OpenStudio.logFree(OpenStudio::Info, 'prm.log', "Removed lighting object #{inst.name} from #{space_type.name}. ")
90
+ inst.remove
91
+ end
92
+ end
93
+
94
+ # Get userdata from userdata_space and userdata_spacetype
95
+ user_spaces = @standards_data.key?('userdata_space') ? @standards_data['userdata_space'] : nil
96
+ user_spacetypes = @standards_data.key?('userdata_spacetype') ? @standards_data['userdata_spacetype'] : nil
97
+ if user_spaces && user_spaces.length >= 1 && has_user_lpd_values(user_spaces)
98
+ # if space type has user data & data has lighting data for user space
99
+ # call this function to enforce space-space_type one on one relationship
100
+ new_space_array = space_to_space_type_apply_lighting(user_spaces, user_spacetypes, space_type)
101
+ # process power equipment with new spaces.
102
+ space_to_space_type_apply_power_equipment(user_spacetypes, user_spaces, new_space_array)
103
+ # remove the old space
104
+ space_type.remove
105
+ else
106
+ if user_spacetypes && user_spacetypes.length >= 1 && has_user_lpd_values(user_spacetypes)
107
+ # if space type has user data & data has lighting data for user space type
108
+ user_space_type_index = user_spacetypes.index { |user_spacetype| user_spacetype['name'] == space_type.name.get }
109
+ if user_space_type_index.nil?
110
+ # cannot find a matched user_spacetype to space_type, use space_type to set LPD
111
+ set_lpd_on_space_type(space_type, user_spaces, user_spacetypes)
112
+ space_type_apply_power_equipment(space_type)
113
+ else
114
+ user_space_type = user_spacetypes[user_space_type_index]
115
+ # If multiple LPD value exist - then enforce space-space_type one on one relationship
116
+ if has_multi_lpd_values_user_data(user_space_type, space_type)
117
+ new_space_array = space_to_space_type_apply_lighting(user_spaces, user_spacetypes, space_type)
118
+ space_to_space_type_apply_power_equipment(user_spacetypes, user_spaces, new_space_array)
119
+ space_type.remove
120
+ else
121
+ # Process the user_space type data - at this point, we are sure there is no lighting per length
122
+ # So all the LPD should be identical by space
123
+ # Loop because we need to assign the occupancy control credit to each space for
124
+ # Schedule processing.
125
+ space_type_lighting_per_area = 0.0
126
+ space_type.spaces.each do |space|
127
+ space_lighting_per_area = calculate_lpd_from_userdata(user_space_type, space)
128
+ space_type_lighting_per_area = space_lighting_per_area
129
+ end
130
+ if space_type.hasAdditionalProperties && space_type.additionalProperties.hasFeature('regulated_lights_name')
131
+ lights_name = space_type.additionalProperties.getFeatureAsString('regulated_lights_name').to_s
132
+ lights_obj = space_type.model.getLightsByName(lights_name).get
133
+ lights_obj.lightsDefinition.setWattsperSpaceFloorArea(OpenStudio.convert(space_type_lighting_per_area.to_f, 'W/ft^2', 'W/m^2').get)
134
+ end
135
+ end
136
+ # process power equipment
137
+ space_type_apply_power_equipment(space_type)
138
+ end
139
+ else
140
+ # no user data, set space_type LPD
141
+ set_lpd_on_space_type(space_type, user_spaces, user_spacetypes)
142
+ # process power equipment
143
+ space_type_apply_power_equipment(space_type)
144
+ end
145
+ end
146
+ end
147
+
148
+ # A function to calculate electric value for an electric equipment.
149
+ # The function will check whether this electric equipment is motor, refrigeration, elevator or generic electric equipment
150
+ # and decide actions based on the equipment types
151
+ #
152
+ # @param user_equip_data [Hash] user equipment data
153
+ # @param power_equipment [OpenStudio::Model::ElectricEquipment] equipment
154
+ # @param power_schedule_hash [Hash] equipment operation schedule hash
155
+ # @param space_type [OpenStudio::Model:SpaceType] space type
156
+ # @param user_space_data [Hash] user space data
157
+ def calculate_electric_value_by_userdata(user_equip_data, power_equipment, power_schedule_hash, space_type, user_space_data = nil)
158
+ # Check if the plug load represents a motor (check if motorhorsepower exist), if so, record the motor HP and efficiency.
159
+ if !user_equip_data['motor_horsepower'].nil?
160
+ # Pre-processing will ensure these three user data are added correctly (float, float, boolean)
161
+ power_equipment.additionalProperties.setFeature('motor_horsepower', user_equip_data['motor_horsepower'].to_f)
162
+ power_equipment.additionalProperties.setFeature('motor_efficiency', user_equip_data['motor_efficiency'].to_f)
163
+ power_equipment.additionalProperties.setFeature('motor_is_exempt', user_equip_data['motor_is_exempt'])
164
+ elsif !(user_equip_data['fraction_of_controlled_receptacles'].nil? && user_equip_data['receptacle_power_savings'].nil?)
165
+ # If not a motor - update.
166
+ # Update the electric equipment occupancy credit (if it has)
167
+ update_power_equipment_credits(power_equipment, user_equip_data, power_schedule_hash, space_type, user_space_data)
168
+ else
169
+ # The electric equipment is either an elevator or refrigeration
170
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ElectricEquipment', "#{power_equipment.name} is an elevator or refrigeration according to the user data provided. Skip receptacle power credit.")
171
+ end
172
+ end
173
+
174
+ def space_type_apply_power_equipment(space_type)
175
+ # save schedules in a hash in case it is needed for new electric equipment
176
+ power_schedule_hash = {}
177
+ user_electric_equipment_data = @standards_data.key?('userdata_electric_equipment') ? @standards_data['userdata_electric_equipment'] : nil
178
+ user_gas_equipment_data = @standards_data.key?('userdata_gas_equipment') ? @standards_data['userdata_gas_equipment'] : nil
179
+ if user_electric_equipment_data && user_electric_equipment_data.length >= 1
180
+ space_type_electric_equipments = space_type.electricEquipment
181
+ space_type_electric_equipments.each do |sp_electric_equipment|
182
+ electric_equipment_name = sp_electric_equipment.name.get
183
+ select_user_electric_equipment_array = user_electric_equipment_data.select { |elec| elec['name'].casecmp(electric_equipment_name) == 0 }
184
+ unless select_user_electric_equipment_array.empty?
185
+ select_user_electric_equipment = select_user_electric_equipment_array[0]
186
+ calculate_electric_value_by_userdata(select_user_electric_equipment, sp_electric_equipment, power_schedule_hash, space_type, nil)
187
+ end
188
+ end
189
+ elsif user_gas_equipment_data && user_gas_equipment_data.length >= 1
190
+ space_type_gas_equipments = space_type.gasEquipment
191
+ space_type_gas_equipments.each do |sp_gas_equipment|
192
+ gas_equipment_name = sp_gas_equipment.name.get
193
+ select_user_gas_equipment_array = user_gas_equipment_data.select { |gas| gas['name'].casecmp(gas_equipment_name) == 0 }
194
+ unless select_user_gas_equipment_array.empty?
195
+ select_user_gas_equipment = select_user_gas_equipment_array[0]
196
+ # Update the gas equipment occupancy credit (if it has)
197
+ update_power_equipment_credits(sp_gas_equipment, select_user_gas_equipment, power_schedule_hash, space_type.model, nil)
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ # Apply space to space type power equipment adjustment.
204
+ # NOTE! this function shall only be used if the space to space type is one to one relationship.
205
+ # This function can process both electric equipment and gas equipment
206
+ # and this function will process user data from electric equipment and gas equipment user data
207
+ #
208
+ # @param user_spacetypes [Hash] spacetype user data
209
+ # @param user_spaces [Hash] space user data
210
+ # @param space_array [OpenStudio::Model:Space] list of spaces need for process
211
+ def space_to_space_type_apply_power_equipment(user_spacetypes, user_spaces, space_array)
212
+ # Step 1: Set electric / gas equipment
213
+ # save schedules in a hash in case it is needed for new electric equipment
214
+ power_schedule_hash = {}
215
+ # check if electric equipment data is available.
216
+ user_electric_equipment_data = @standards_data.key?('userdata_electric_equipment') ? @standards_data['userdata_electric_equipment'] : nil
217
+ user_gas_equipment_data = @standards_data.key?('userdata_gas_equipment') ? @standards_data['userdata_gas_equipment'] : nil
218
+ if user_electric_equipment_data && user_electric_equipment_data.length >= 1
219
+ space_array.each do |space|
220
+ # Each space has a unique space type
221
+ space_type = space.spaceType.get
222
+ user_spacestypes_index = user_spacetypes.index { |user_spacetype| /#{user_spacetype['name']}/i =~ space_type.name.get }
223
+ user_space_index = user_spaces.index { |user_space| user_space['name'] == space.name.get }
224
+ # Initialize with standard space_type
225
+ user_space_data = space_type.name.get
226
+ unless user_spacestypes_index.nil?
227
+ # override with user space type if specified
228
+ user_space_data = user_spacetypes[user_spacestypes_index]
229
+ end
230
+ unless user_space_index.nil?
231
+ # override with user space if specified
232
+ user_space_data = user_spaces[user_space_index]
233
+ end
234
+ space_type_electric_equipments = space_type.electricEquipment
235
+ space_type_electric_equipments.each do |sp_electric_equipment|
236
+ electric_equipment_name = sp_electric_equipment.name.get
237
+ select_user_electric_equipment_array = user_electric_equipment_data.select { |elec| /#{elec['name']}/i =~ electric_equipment_name }
238
+ unless select_user_electric_equipment_array.empty?
239
+ select_user_electric_equipment = select_user_electric_equipment_array[0]
240
+ calculate_electric_value_by_userdata(select_user_electric_equipment, sp_electric_equipment, power_schedule_hash, space_type, user_space_data)
241
+ end
242
+ end
243
+ end
244
+ elsif user_gas_equipment_data && user_gas_equipment_data.length >= 1
245
+ space_array.each do |space|
246
+ space_type = space.spaceType.get
247
+ user_spacestypes_index = user_spacetypes.index { |user_spacetype| user_spacetype['name'] == space_type.name.get }
248
+ user_space_index = user_spaces.index { |user_space| user_space['name'] == space.name.get }
249
+ user_space_data = space_type.name.get
250
+ unless user_spacestypes_index.nil?
251
+ user_space_data = user_spacetypes[user_spacestypes_index]
252
+ end
253
+ unless user_space_index.nil?
254
+ user_space_data = user_spaces[user_space_index]
255
+ end
256
+ space_type_gas_equipments = space_type.gasEquipment
257
+ space_type_gas_equipments.each do |sp_gas_equipment|
258
+ gas_equipment_name = sp_gas_equipment.name.get
259
+ select_user_gas_equipment_array = user_gas_equipment_data.select { |gas| gas['name'].casecmp(gas_equipment_name) == 0 }
260
+ unless select_user_gas_equipment_array.empty?
261
+ select_user_gas_equipment = select_user_gas_equipment_array[0]
262
+ # Update the gas equipment occupancy credit (if it has)
263
+ update_power_equipment_credits(sp_gas_equipment, select_user_gas_equipment, power_schedule_hash, space_type.model, user_space_data)
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ # Function update a power equipment schedule based on user data.
271
+ # This function works with both electric equipment and gas equipment and applies the ruleset on power equipment
272
+ # The function process user data including the fraction of controlled receptacles and receptacle power savings.
273
+ #
274
+ # @parma power_equipment [OpenStudio::Model::ElectricEquipment] or [OpenStudio::Model:GasEquipment]
275
+ # @param user_power_equipment [Hash] user data for the power equipment
276
+ # @param schedule_hash [Hash] power equipment operation schedules in a hash
277
+ # @param space_type [OpenStudio::Model:SpaceType] space type
278
+ # @param user_data [Hash] user space data
279
+ def update_power_equipment_credits(power_equipment, user_power_equipment, schedule_hash, space_type, user_data = nil)
280
+ exception_list = ['office - enclosed <= 250 sf', 'conference/meeting/multipurpose', 'copy/print',
281
+ 'lounge/breakroom - all other', 'lounge/breakroom - healthcare facility', 'classroom/lecture/training - all other',
282
+ 'classroom/lecture/training - preschool to 12th', 'office - open']
283
+
284
+ receptacle_power_credits = 0.0
285
+ # Check fraction_of_controlled_receptacles or receptacle_power_savings exist
286
+ if user_power_equipment.key?('fraction_of_controlled_receptacles') && !user_power_equipment['fraction_of_controlled_receptacles'].nil?
287
+ rc = user_power_equipment['fraction_of_controlled_receptacles'].to_f
288
+ # receptacle power credits = percent of all controlled receptacles * 10%
289
+ receptacle_power_credits = rc * 0.1
290
+ elsif user_power_equipment.key?('receptacle_power_savings') && !user_power_equipment['receptacle_power_savings'].nil?
291
+ receptacle_power_credits = user_power_equipment['receptacle_power_savings'].to_f
292
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ElectricEquipment', "#{power_equipment.name.get} has a user specified receptacle power saving credit #{receptacle_power_credits}. The modeler needs to make sure the credit is approved by a rating authority per Table G3.1 section 12.")
293
+ end
294
+
295
+ # process user space data
296
+ if user_data.is_a?(Hash)
297
+ if user_data.key?('num_std_ltg_types') && user_data['num_std_ltg_types'].to_f > 0
298
+ adjusted_receptacle_power_credits = 0.0
299
+ num_std_space_types = user_data['num_std_ltg_types'].to_i
300
+ std_space_index = 0 # loop index
301
+ # Loop through standard lighting type in a space
302
+ while std_space_index < num_std_space_types
303
+ std_space_index += 1
304
+ # Retrieve data from user_data
305
+ type_key = format('std_ltg_type%02d', std_space_index)
306
+ frac_key = format('std_ltg_type_frac%02d', std_space_index)
307
+ sub_space_type = user_data[type_key]
308
+ next if exception_list.include?(sub_space_type)
309
+
310
+ adjusted_receptacle_power_credits += user_data[frac_key].to_f * receptacle_power_credits
311
+ # Adjust while loop condition factors
312
+ end
313
+ receptacle_power_credits = adjusted_receptacle_power_credits
314
+ end
315
+ elsif user_data.is_a?(String)
316
+ if exception_list.include?(space_type.standardsSpaceType.get)
317
+ # the space type is in the exception list, no credit to the space type
318
+ receptacle_power_credits = 0.0
319
+ end
320
+ end
321
+
322
+ # Step 2: check if need to adjust the electric equipment schedule. - apply credit if needed.
323
+ if receptacle_power_credits > 0.0
324
+ # get current schedule
325
+ power_schedule = power_equipment.schedule.get
326
+ power_schedule_name = power_schedule.name.get
327
+ new_power_schedule_name = format("#{power_schedule_name}_%.4f", receptacle_power_credits)
328
+ if schedule_hash.key?(new_power_schedule_name)
329
+ # In this case, there is a schedule created, can retrieve the schedule object and reset in this space type.
330
+ schedule_rule = schedule_hash[new_power_schedule_name]
331
+ power_equipment.setSchedule(schedule_rule)
332
+ else
333
+ # In this case, create a new schedule
334
+ # 1. Clone the existing schedule
335
+ new_rule_set_schedule = deep_copy_schedule(new_power_schedule_name, power_schedule, receptacle_power_credits, space_type.model)
336
+ if power_equipment.setSchedule(new_rule_set_schedule)
337
+ schedule_hash[new_power_schedule_name] = new_rule_set_schedule
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ # Function to tset LPD on default space type
344
+ def set_lpd_on_space_type(space_type, user_spaces, user_spacetypes)
345
+ if has_multi_lpd_values_space_type(space_type)
346
+ # If multiple LPD value exist - then enforce space-space_type one on one relationship
347
+ space_to_space_type_apply_lighting(user_spaces, user_spacetypes, space_type)
348
+ else
349
+ # use default - loop through space to assign occupancy credit to each space.
350
+ space_type_lighting_per_area = 0.0
351
+ space_type.spaces.each do |space|
352
+ space_lighting_per_area = calculate_lpd_by_space(space_type, space)
353
+ space_type_lighting_per_area = space_lighting_per_area
354
+ end
355
+ if space_type.hasAdditionalProperties && space_type.additionalProperties.hasFeature('regulated_lights_name')
356
+ lights_name = space_type.additionalProperties.getFeatureAsString('regulated_lights_name').to_s
357
+ lights_obj = space_type.model.getLightsByName(lights_name).get
358
+ lights_obj.lightsDefinition.setWattsperSpaceFloorArea(OpenStudio.convert(space_type_lighting_per_area.to_f, 'W/ft^2', 'W/m^2').get)
359
+ end
360
+ end
361
+ end
362
+
363
+ # Function that applies user LPD to each space by duplicating space types
364
+ # This function is used when there are user space data available or
365
+ # the spaces under space type has lighting per length value which may cause multiple
366
+ # lighting power densities under one space_type.
367
+ # @param user_spaces hash data contained in the user space
368
+ # @param user_spacetypes hash data contained in the user spacetypes
369
+ # @param space_type OpenStudio::Model::SpaceType object
370
+ def space_to_space_type_apply_lighting(user_spaces, user_spacetypes, space_type)
371
+ space_lighting_per_area_hash = {}
372
+ # first priority - user_space data
373
+ if user_spaces && user_spaces.length >= 1
374
+ space_type.spaces.each do |space|
375
+ user_space_index = user_spaces.index { |user_space| user_space['name'] == space.name.get }
376
+ unless user_space_index.nil?
377
+ user_space_data = user_spaces[user_space_index]
378
+ if user_space_data.key?('num_std_ltg_types') && user_space_data['num_std_ltg_types'].to_f > 0
379
+ space_lighting_per_area = calculate_lpd_from_userdata(user_space_data, space)
380
+ space_lighting_per_area_hash[space.name.get] = space_lighting_per_area
381
+ end
382
+ end
383
+ end
384
+ end
385
+ # second priority - user_spacetype
386
+ if user_spacetypes && user_spacetypes.length >= 1
387
+ # if space type has user data
388
+ user_space_type_index = user_spacetypes.index { |user_spacetype| user_spacetype['name'] == space_type.name.get }
389
+ unless user_space_type_index.nil?
390
+ user_space_type_data = user_spacetypes[user_space_type_index]
391
+ if user_space_type_data.key?('num_std_ltg_types') && user_space_type_data['num_std_ltg_types'].to_f > 0
392
+ space_type.spaces.each do |space|
393
+ # unless the space is in the hash, we will add lighting per area to the space
394
+ space_name = space.name.get
395
+ unless space_lighting_per_area_hash.key?(space_name)
396
+ space_lighting_per_area = calculate_lpd_from_userdata(user_space_type_data, space)
397
+ space_lighting_per_area_hash[space_name] = space_lighting_per_area
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
403
+ # Third priority
404
+ # set space type to every space in the space_type, third priority
405
+ # will also be assigned from the default space type
406
+ space_type.spaces.each do |space|
407
+ space_name = space.name.get
408
+ unless space_lighting_per_area_hash.key?(space_name)
409
+ space_lighting_per_area = calculate_lpd_by_space(space_type, space)
410
+ space_lighting_per_area_hash[space_name] = space_lighting_per_area
411
+ end
412
+ end
413
+ # All space is explored.
414
+ # Now rewrite the space type in each space - might need to change the logic
415
+ space_array = []
416
+ space_type.spaces.each do |space|
417
+ space_name = space.name.get
418
+ new_space_type = space_type.clone.to_SpaceType.get
419
+ space.setSpaceType(new_space_type)
420
+ lighting_per_area = space_lighting_per_area_hash[space_name]
421
+ new_space_type.lights.each do |inst|
422
+ lights_name = inst.name.get
423
+ new_space_type.additionalProperties.setFeature('regulated_lights_name', lights_name)
424
+ definition = inst.lightsDefinition
425
+ unless lighting_per_area.zero?
426
+ new_definition = definition.clone.to_LightsDefinition.get
427
+ new_definition.setWattsperSpaceFloorArea(OpenStudio.convert(lighting_per_area.to_f, 'W/ft^2', 'W/m^2').get)
428
+ inst.setLightsDefinition(new_definition)
429
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set LPD to #{lighting_per_area} W/ft^2.")
430
+ end
431
+ end
432
+ space_array.push(space)
433
+ end
434
+ return space_array
435
+ end
436
+
437
+ # Modify the lighting schedules for Appendix G PRM for 2016 and later
438
+ #
439
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
440
+ def space_type_light_sch_change(model)
441
+ # set schedule for lighting
442
+ schedule_hash = {}
443
+ model.getSpaces.each do |space|
444
+ space_type = space.spaceType.get
445
+ if space_type.hasAdditionalProperties && space_type.additionalProperties.hasFeature('regulated_lights_name')
446
+ lights_name = space_type.additionalProperties.getFeatureAsString('regulated_lights_name').to_s
447
+ ltg = space_type.model.getLightsByName(lights_name).get
448
+ if ltg.schedule.is_initialized
449
+ ltg_schedule = ltg.schedule.get
450
+ ltg_schedule_name = ltg_schedule.name
451
+ occupancy_sensor_credit = space.additionalProperties.getFeatureAsDouble('occ_control_credit')
452
+ new_ltg_schedule_name = format("#{ltg_schedule_name}_%.4f", occupancy_sensor_credit)
453
+ if schedule_hash.key?(new_ltg_schedule_name)
454
+ # In this case, there is a schedule created, can retrieve the schedule object and reset in this space type
455
+ schedule_rule = schedule_hash[new_ltg_schedule_name]
456
+ ltg.setSchedule(schedule_rule)
457
+ else
458
+ # In this case, create a new schedule
459
+ # 1. Clone the existing schedule
460
+ new_rule_set_schedule = deep_copy_schedule(new_ltg_schedule_name, ltg_schedule, occupancy_sensor_credit, model)
461
+ if ltg.setSchedule(new_rule_set_schedule)
462
+ schedule_hash[new_ltg_schedule_name] = new_rule_set_schedule
463
+ end
464
+ end
465
+ end
466
+ end
467
+ end
468
+ end
469
+
470
+ def deep_copy_schedule(new_schedule_name, schedule, adjustment_factor, model)
471
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.ScheduleRuleset', "Creating a new lighting schedule that applies occupancy sensor adjustment factor: #{adjustment_factor} based on #{schedule.name.get} schedule")
472
+ ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
473
+ ruleset.setName(new_schedule_name)
474
+
475
+ # schedule types limits and default day schedule - keep the copy
476
+ schedule_ruleset = schedule.to_ScheduleRuleset.get
477
+ schedule_type_limit = schedule_ruleset.scheduleTypeLimits.get
478
+ default_day_schedule = schedule_ruleset.defaultDaySchedule
479
+ default_winter_design_day_schedule = schedule_ruleset.winterDesignDaySchedule
480
+ default_summer_design_day_schedule = schedule_ruleset.summerDesignDaySchedule
481
+
482
+ schedule_ruleset.scheduleRules.each do |week_rule|
483
+ day_rule = week_rule.daySchedule
484
+ start_date = week_rule.startDate.get
485
+ end_date = week_rule.endDate.get
486
+
487
+ # create a new day rule - copy and apply the ajustment factor
488
+ new_day_rule = OpenStudio::Model::ScheduleDay.new(model)
489
+ new_day_rule.setName(format("#{day_rule.name.get}_%.4f", adjustment_factor))
490
+ new_day_rule.setScheduleTypeLimits(schedule_type_limit)
491
+
492
+ # process day rule
493
+ times = day_rule.times()
494
+ # remove the effect of occupancy sensors
495
+ times.each do |time|
496
+ hour_value = day_rule.getValue(time)
497
+ new_value = hour_value / (1.0 - adjustment_factor.to_f)
498
+ if new_value > 1
499
+ new_day_rule.addValue(time, 1.0)
500
+ else
501
+ new_day_rule.addValue(time, new_value)
502
+ end
503
+ end
504
+
505
+ # create week rule schedule
506
+ new_week_rule = OpenStudio::Model::ScheduleRule.new(ruleset, new_day_rule)
507
+ new_week_rule.setName(format("#{week_rule.name.get}_%.4f", adjustment_factor))
508
+ new_week_rule.setApplySunday(week_rule.applySunday)
509
+ new_week_rule.setApplyMonday(week_rule.applyMonday)
510
+ new_week_rule.setApplyTuesday(week_rule.applyTuesday)
511
+ new_week_rule.setApplyWednesday(week_rule.applyWednesday)
512
+ new_week_rule.setApplyThursday(week_rule.applyThursday)
513
+ new_week_rule.setApplyFriday(week_rule.applyFriday)
514
+ new_week_rule.setApplySaturday(week_rule.applySaturday)
515
+ new_week_rule.setStartDate(start_date)
516
+ new_week_rule.setEndDate(end_date)
517
+ end
518
+ # default day schedule
519
+ default_day = ruleset.defaultDaySchedule
520
+ default_day.clearValues
521
+ default_day.times.each_index { |counter| default_day.addValue(default_day_schedule.times[counter], default_day_schedule.values[counter]) }
522
+ # winter design day schedule
523
+ winter_design_day_schedule = ruleset.winterDesignDaySchedule
524
+ winter_design_day_schedule.clearValues
525
+ winter_design_day_schedule.times.each_index { |counter| winter_design_day_schedule.addValue(default_winter_design_day_schedule.times[counter], default_winter_design_day_schedule.values[counter]) }
526
+ summer_design_day_schedule = ruleset.summerDesignDaySchedule
527
+ summer_design_day_schedule.clearValues
528
+ summer_design_day_schedule.times.each_index { |counter| summer_design_day_schedule.addValue(default_summer_design_day_schedule.times[counter], default_summer_design_day_schedule.values[counter]) }
529
+ return ruleset
530
+ end
531
+
532
+ # calculate the lighting power density per area based on space type
533
+ # The function will calculate the LPD based on the space type (STRING)
534
+ # It considers lighting per area, lighting per length as well as occupancy factors in the database.
535
+ # @param space_type [String]
536
+ # @param space [OpenStudio::Model::Space]
537
+ def calculate_lpd_by_space(space_type, space)
538
+ # get interior lighting data
539
+ space_type_properties = interior_lighting_get_prm_data(space_type)
540
+ space_lighting_per_area = 0.0
541
+ # Assign data
542
+ lights_have_info = false
543
+ lighting_per_area = space_type_properties['w/ft^2'].to_f
544
+ lighting_per_length = space_type_properties['w/ft'].to_f
545
+ manon_or_partauto = space_type_properties['manon_or_partauto'].to_i
546
+ lights_have_info = true unless lighting_per_area.zero? && lighting_per_length.zero?
547
+ occ_control_reduction_factor = 0.0
548
+
549
+ if lights_have_info
550
+ # Space height
551
+ space_volume = space.volume
552
+ space_area = space.floorArea
553
+ space_height = OpenStudio.convert(space_volume / space_area, 'm', 'ft').get
554
+ # calculate the new lpd values
555
+ space_lighting_per_area = lighting_per_length * space_height + lighting_per_area
556
+
557
+ # Adjust the occupancy control sensor reduction factor from dataset
558
+ if manon_or_partauto == 1
559
+ occ_control_reduction_factor = space_type_properties['occup_sensor_savings'].to_f
560
+ else
561
+ occ_control_reduction_factor = space_type_properties['occup_sensor_auto_on_svgs'].to_f
562
+ end
563
+ end
564
+ # add calculated occupancy control credit for later ltg schedule adjustment
565
+ space.additionalProperties.setFeature('occ_control_credit', occ_control_reduction_factor)
566
+ return space_lighting_per_area
567
+ end
568
+
569
+ # Function checks whether the user data contains lighting data
570
+ def has_user_lpd_values(user_space_data)
571
+ user_space_data.each do |user_data|
572
+ if user_data.key?('num_std_ltg_types') && user_data['num_std_ltg_types'].to_f > 0
573
+ return true
574
+ end
575
+ end
576
+ return false
577
+ end
578
+
579
+ # Function checks whether there are multi lpd values in the space type
580
+ # multi-lpd value means there are multiple spaces and the lighting_per_length > 0
581
+ def has_multi_lpd_values_space_type(space_type)
582
+ space_type_properties = interior_lighting_get_prm_data(space_type)
583
+ lighting_per_length = space_type_properties['w/ft'].to_f
584
+ return space_type.spaces.size > 1 && lighting_per_length > 0
585
+ end
586
+
587
+ # Function checks whether there are multi lpd values in the space type from user's data
588
+ # The sum of each space fraction in the user_data is assumed to be 1.0
589
+ # multi-lpd value means lighting per area > 0 and lighting_per_length > 0
590
+ def has_multi_lpd_values_user_data(user_data, space_type)
591
+ num_std_ltg_types = user_data['num_std_ltg_types'].to_i
592
+ std_ltg_index = 0 # loop index
593
+ # Loop through standard lighting type in a space
594
+ sum_lighting_per_area = 0
595
+ sum_lighting_per_length = 0
596
+ while std_ltg_index < num_std_ltg_types
597
+ # Retrieve data from user_data
598
+ type_key = format('std_ltg_type%02d', (std_ltg_index + 1))
599
+ sub_space_type = user_data[type_key]
600
+ # Adjust while loop condition factors
601
+ std_ltg_index += 1
602
+ # get interior lighting data
603
+ sub_space_type_properties = interior_lighting_get_prm_data(sub_space_type)
604
+ # Assign data
605
+ lighting_per_length = sub_space_type_properties['w/ft'].to_f
606
+ sum_lighting_per_length += lighting_per_length
607
+ end
608
+ return space_type.spaces.size > 1 && sum_lighting_per_length > 0
609
+ end
610
+
611
+ # Calculate the lighting power density per area based on user data (space_based)
612
+ # The function will calculate the LPD based on the space type (STRING)
613
+ # It considers lighting per area, lighting per length as well as occupancy factors in the database.
614
+ # The sum of each space fraction in the user_data is assumed to be 1.0
615
+ # @param user_data [Hash] user data from the user csv
616
+ # @param space [OpenStudio::Model::Space]
617
+ def calculate_lpd_from_userdata(user_data, space)
618
+ num_std_ltg_types = user_data['num_std_ltg_types'].to_i
619
+ space_lighting_per_area = 0.0
620
+ occupancy_control_credit_sum = 0.0
621
+ std_ltg_index = 0 # loop index
622
+ # Loop through standard lighting type in a space
623
+ while std_ltg_index < num_std_ltg_types
624
+ # Retrieve data from user_data
625
+ type_key = format('std_ltg_type%02d', (std_ltg_index + 1))
626
+ frac_key = format('std_ltg_type_frac%02d', (std_ltg_index + 1))
627
+ sub_space_type = user_data[type_key]
628
+ sub_space_type_frac = user_data[frac_key].to_f
629
+ # Adjust while loop condition factors
630
+ std_ltg_index += 1
631
+ # get interior lighting data
632
+ sub_space_type_properties = interior_lighting_get_prm_data(sub_space_type)
633
+ # Assign data
634
+ lights_have_info = false
635
+ lighting_per_area = sub_space_type_properties['w/ft^2'].to_f
636
+ lighting_per_length = sub_space_type_properties['w/ft'].to_f
637
+ lights_have_info = true unless lighting_per_area.zero? && lighting_per_length.zero?
638
+ manon_or_partauto = sub_space_type_properties['manon_or_partauto'].to_i
639
+
640
+ if lights_have_info
641
+ # Space height
642
+ space_volume = space.volume
643
+ space_area = space.floorArea
644
+ space_height = OpenStudio.convert(space_volume / space_area, 'm', 'ft').get
645
+ # calculate and add new lpd values
646
+ user_space_type_lighting_per_area = (lighting_per_length * space_height +
647
+ lighting_per_area) * sub_space_type_frac
648
+ space_lighting_per_area += user_space_type_lighting_per_area
649
+
650
+ # Adjust the occupancy control sensor reduction factor from dataset
651
+ occ_control_reduction_factor = 0.0
652
+ if manon_or_partauto == 1
653
+ occ_control_reduction_factor = sub_space_type_properties['occup_sensor_savings'].to_f
654
+ else
655
+ occ_control_reduction_factor = sub_space_type_properties['occup_sensor_auto_on_svgs'].to_f
656
+ end
657
+ # Now calculate the occupancy control credit factor (weighted by frac_lpd)
658
+ occupancy_control_credit_sum += occ_control_reduction_factor * user_space_type_lighting_per_area
659
+ end
660
+ end
661
+ # add calculated occupancy control credit for later ltg schedule adjustment
662
+ # If space_lighting_per_area = 0, it means there is no lights_have_info, and subsequently, the occupancy_control_credit_sum should be 0
663
+ space.additionalProperties.setFeature('occ_control_credit', space_lighting_per_area > 0 ? occupancy_control_credit_sum / space_lighting_per_area : occupancy_control_credit_sum)
664
+ return space_lighting_per_area
665
+ end
666
+ end