openstudio-standards 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/OpenStudio_Standards.xlsx +0 -0
  3. data/data/standards/OpenStudio_Standards_boilers.json +62 -4
  4. data/data/standards/OpenStudio_Standards_chillers.json +778 -68
  5. data/data/standards/OpenStudio_Standards_construction_sets.json +52 -93
  6. data/data/standards/OpenStudio_Standards_curve_biquadratics.json +36 -36
  7. data/data/standards/OpenStudio_Standards_curve_quadratics.json +3 -3
  8. data/data/standards/OpenStudio_Standards_heat_pumps.json +840 -0
  9. data/data/standards/OpenStudio_Standards_heat_pumps_heating.json +352 -0
  10. data/data/standards/OpenStudio_Standards_heat_rejection.json +48 -0
  11. data/data/standards/OpenStudio_Standards_motors.json +270 -0
  12. data/data/standards/OpenStudio_Standards_space_types.json +10390 -2824
  13. data/data/standards/OpenStudio_Standards_unitary_acs.json +794 -18
  14. data/data/weather/USA_CO_Boulder-Broomfield-Jefferson.County.AP.724699_TMY3.ddy +538 -0
  15. data/data/weather/USA_CO_Boulder-Broomfield-Jefferson.County.AP.724699_TMY3.epw +8768 -0
  16. data/data/weather/USA_CO_Boulder-Broomfield-Jefferson.County.AP.724699_TMY3.stat +493 -0
  17. data/data/weather/USA_CO_Denver.Intl.AP.725650_TMY3.ddy +536 -0
  18. data/data/weather/USA_CO_Denver.Intl.AP.725650_TMY3.epw +8768 -0
  19. data/data/weather/USA_CO_Denver.Intl.AP.725650_TMY3.stat +554 -0
  20. data/data/weather/USA_CO_Fort.Collins.AWOS.724769_TMY3.ddy +536 -0
  21. data/data/weather/USA_CO_Fort.Collins.AWOS.724769_TMY3.epw +8768 -0
  22. data/data/weather/USA_CO_Fort.Collins.AWOS.724769_TMY3.stat +554 -0
  23. data/data/weather/envelope_info.csv +6 -0
  24. data/lib/openstudio-standards.rb +10 -11
  25. data/lib/openstudio-standards/btap/compliance.rb +251 -969
  26. data/lib/openstudio-standards/btap/envelope.rb +1 -1
  27. data/lib/openstudio-standards/btap/fileio.rb +37 -5
  28. data/lib/openstudio-standards/btap/geometry.rb +27 -17
  29. data/lib/openstudio-standards/btap/hvac.rb +80 -27
  30. data/lib/openstudio-standards/hvac_sizing/{HVACSizing.CoilHeatingDXMultiSpeed.rb → Siz.CoilHeatingDXMultiSpeed.rb} +0 -0
  31. data/lib/openstudio-standards/hvac_sizing/Siz.ControllerOutdoorAir.rb +30 -4
  32. data/lib/openstudio-standards/hvac_sizing/Siz.CoolingTowerTwoSpeed.rb +61 -5
  33. data/lib/openstudio-standards/hvac_sizing/Siz.CoolingTowerVariableSpeed.rb +37 -7
  34. data/lib/openstudio-standards/hvac_sizing/Siz.DistrictCooling.rb +27 -0
  35. data/lib/openstudio-standards/hvac_sizing/Siz.DistrictHeating.rb +27 -0
  36. data/lib/openstudio-standards/hvac_sizing/Siz.HeaderedPumpsConstantSpeed.rb +55 -0
  37. data/lib/openstudio-standards/hvac_sizing/Siz.HeaderedPumpsVariableSpeed.rb +55 -0
  38. data/lib/openstudio-standards/hvac_sizing/Siz.HeatingCoolingFuels.rb +51 -9
  39. data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +99 -17
  40. data/lib/openstudio-standards/hvac_sizing/Siz.PumpConstantSpeed.rb +1 -1
  41. data/lib/openstudio-standards/hvac_sizing/Siz.ThermalZone.rb +29 -6
  42. data/lib/openstudio-standards/hvac_sizing/Siz.WaterHeaterMixed.rb +16 -0
  43. data/lib/openstudio-standards/prototypes/Prototype.AirTerminalSingleDuctVAVReheat.rb +43 -48
  44. data/lib/openstudio-standards/prototypes/Prototype.ControllerWaterCoil.rb +5 -9
  45. data/lib/openstudio-standards/prototypes/Prototype.Fan.rb +68 -0
  46. data/lib/openstudio-standards/prototypes/Prototype.FanConstantVolume.rb +39 -43
  47. data/lib/openstudio-standards/prototypes/Prototype.FanOnOff.rb +49 -51
  48. data/lib/openstudio-standards/prototypes/Prototype.FanVariableVolume.rb +55 -61
  49. data/lib/openstudio-standards/prototypes/Prototype.FanZoneExhaust.rb +8 -10
  50. data/lib/openstudio-standards/prototypes/Prototype.HeatExchangerAirToAirSensibleAndLatent.rb +15 -20
  51. data/lib/openstudio-standards/prototypes/Prototype.Model.hvac.rb +330 -322
  52. data/lib/openstudio-standards/prototypes/Prototype.Model.rb +501 -446
  53. data/lib/openstudio-standards/prototypes/Prototype.Model.swh.rb +221 -230
  54. data/lib/openstudio-standards/prototypes/Prototype.add_objects.rb +0 -2
  55. data/lib/openstudio-standards/prototypes/Prototype.full_service_restaurant.rb +130 -137
  56. data/lib/openstudio-standards/prototypes/Prototype.high_rise_apartment.rb +374 -291
  57. data/lib/openstudio-standards/prototypes/Prototype.hospital.rb +146 -193
  58. data/lib/openstudio-standards/prototypes/Prototype.hvac_systems.rb +1315 -1113
  59. data/lib/openstudio-standards/prototypes/Prototype.large_hotel.rb +65 -88
  60. data/lib/openstudio-standards/prototypes/Prototype.large_office.rb +101 -156
  61. data/lib/openstudio-standards/prototypes/Prototype.medium_office.rb +46 -96
  62. data/lib/openstudio-standards/prototypes/Prototype.mid_rise_apartment.rb +113 -123
  63. data/lib/openstudio-standards/prototypes/Prototype.outpatient.rb +356 -345
  64. data/lib/openstudio-standards/prototypes/Prototype.primary_school.rb +48 -103
  65. data/lib/openstudio-standards/prototypes/Prototype.quick_service_restaurant.rb +115 -123
  66. data/lib/openstudio-standards/prototypes/Prototype.retail_standalone.rb +30 -39
  67. data/lib/openstudio-standards/prototypes/Prototype.retail_stripmall.rb +32 -45
  68. data/lib/openstudio-standards/prototypes/Prototype.secondary_school.rb +98 -258
  69. data/lib/openstudio-standards/prototypes/Prototype.small_hotel.rb +429 -474
  70. data/lib/openstudio-standards/prototypes/Prototype.small_office.rb +28 -36
  71. data/lib/openstudio-standards/prototypes/Prototype.strip_model.rb +7 -7
  72. data/lib/openstudio-standards/prototypes/Prototype.utilities.rb +172 -146
  73. data/lib/openstudio-standards/prototypes/Prototype.warehouse.rb +46 -53
  74. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +885 -707
  75. data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctParallelPIUReheat.rb +48 -57
  76. data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctVAVReheat.rb +24 -31
  77. data/lib/openstudio-standards/standards/Standards.BoilerHotWater.rb +80 -93
  78. data/lib/openstudio-standards/standards/Standards.BuildingStory.rb +69 -0
  79. data/lib/openstudio-standards/standards/Standards.ChillerElectricEIR.rb +60 -72
  80. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXMultiSpeed.rb +104 -108
  81. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +190 -198
  82. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXTwoSpeed.rb +134 -146
  83. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXMultiSpeed.rb +56 -60
  84. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +151 -161
  85. data/lib/openstudio-standards/standards/Standards.CoilHeatingGasMultiStage.rb +30 -34
  86. data/lib/openstudio-standards/standards/Standards.Construction.rb +116 -132
  87. data/lib/openstudio-standards/standards/Standards.CoolingTower.rb +138 -0
  88. data/lib/openstudio-standards/standards/Standards.CoolingTowerSingleSpeed.rb +11 -0
  89. data/lib/openstudio-standards/standards/Standards.CoolingTowerTwoSpeed.rb +11 -0
  90. data/lib/openstudio-standards/standards/Standards.CoolingTowerVariableSpeed.rb +16 -0
  91. data/lib/openstudio-standards/standards/Standards.Fan.rb +190 -236
  92. data/lib/openstudio-standards/standards/Standards.FanConstantVolume.rb +0 -2
  93. data/lib/openstudio-standards/standards/Standards.FanOnOff.rb +0 -2
  94. data/lib/openstudio-standards/standards/Standards.FanVariableVolume.rb +168 -14
  95. data/lib/openstudio-standards/standards/Standards.FanZoneExhaust.rb +0 -2
  96. data/lib/openstudio-standards/standards/Standards.HeaderedPumpsConstantSpeed.rb +33 -0
  97. data/lib/openstudio-standards/standards/Standards.HeaderedPumpsVariableSpeed.rb +83 -0
  98. data/lib/openstudio-standards/standards/Standards.HeatExchangerSensLat.rb +22 -0
  99. data/lib/openstudio-standards/standards/Standards.Model.rb +2385 -1622
  100. data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +83 -35
  101. data/lib/openstudio-standards/standards/Standards.PlantLoop.rb +805 -395
  102. data/lib/openstudio-standards/standards/Standards.Pump.rb +139 -119
  103. data/lib/openstudio-standards/standards/Standards.PumpConstantSpeed.rb +0 -2
  104. data/lib/openstudio-standards/standards/Standards.PumpVariableSpeed.rb +16 -15
  105. data/lib/openstudio-standards/standards/Standards.ScheduleCompact.rb +35 -0
  106. data/lib/openstudio-standards/standards/Standards.ScheduleConstant.rb +7 -13
  107. data/lib/openstudio-standards/standards/Standards.ScheduleRuleset.rb +144 -59
  108. data/lib/openstudio-standards/standards/Standards.Space.rb +1509 -1326
  109. data/lib/openstudio-standards/standards/Standards.SpaceType.rb +254 -262
  110. data/lib/openstudio-standards/standards/Standards.SubSurface.rb +105 -105
  111. data/lib/openstudio-standards/standards/Standards.Surface.rb +27 -31
  112. data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +882 -157
  113. data/lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb +179 -69
  114. data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +75 -0
  115. data/lib/openstudio-standards/utilities/logging.rb +31 -38
  116. data/lib/openstudio-standards/utilities/simulation.rb +118 -82
  117. data/lib/openstudio-standards/version.rb +1 -1
  118. data/lib/openstudio-standards/weather/Weather.Model.rb +382 -390
  119. data/lib/openstudio-standards/weather/Weather.stat_file.rb +159 -78
  120. metadata +59 -6
@@ -1,119 +1,112 @@
1
1
 
2
2
  # Extend the class to add Medium Office specific stuff
3
3
  class OpenStudio::Model::Model
4
-
5
- def define_space_type_map(building_type, building_vintage, climate_zone)
4
+ def define_space_type_map(building_type, template, climate_zone)
6
5
  space_type_map = nil
7
-
8
- case building_vintage
9
-
6
+
7
+ space_type_map = case template
8
+
10
9
  when 'NECB 2011'
11
- space_type_map ={
12
- "Warehouse - med/blk" => ["Zone3 Bulk Storage"],
13
- "Warehouse - fine" => ["Zone2 Fine Storage"],
14
- "Office - enclosed" => ["Zone1 Office"]
10
+ # dom =A
11
+ {
12
+ 'Warehouse - med/blk' => ['Zone3 Bulk Storage'],
13
+ 'Warehouse - fine' => ['Zone2 Fine Storage'],
14
+ 'Office - enclosed' => ['Zone1 Office']
15
15
  }
16
16
  else
17
- space_type_map = {
17
+ {
18
18
  'Bulk' => ['Zone3 Bulk Storage'],
19
19
  'Fine' => ['Zone2 Fine Storage'],
20
20
  'Office' => ['Zone1 Office']
21
21
  }
22
- end
22
+ end
23
23
  return space_type_map
24
24
  end
25
25
 
26
- def define_hvac_system_map(building_type, building_vintage, climate_zone)
27
-
28
- case building_vintage
26
+ def define_hvac_system_map(building_type, template, climate_zone)
27
+ case template
29
28
  when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
30
29
  system_to_space_map = [
31
- {
30
+ {
32
31
  'type' => 'PSZ-AC',
33
32
  'name' => 'HVAC_1',
34
33
  'space_names' => ['Zone1 Office']
35
- },
36
- {
34
+ },
35
+ {
37
36
  'type' => 'PSZ-AC',
38
37
  'name' => 'HVAC_2',
39
38
  'space_names' => ['Zone2 Fine Storage']
40
- },
41
- {
39
+ },
40
+ {
42
41
  'type' => 'UnitHeater',
43
42
  'name' => 'HVAC_3',
44
43
  'space_names' => ['Zone3 Bulk Storage']
45
- },
46
- {
44
+ },
45
+ {
47
46
  'type' => 'Zone Ventilation',
48
47
  'name' => 'Bulk Storage Zone Ventilation - Intake',
49
48
  'availability_sch_name' => 'Warehouse MinOA_Sched',
50
49
  'flow_rate' => 0.00025, # in m^3/s-m^2
51
50
  'ventilation_type' => 'Intake',
52
51
  'space_names' => ['Zone3 Bulk Storage']
53
- }
54
- ]
55
- when '90.1-2004','90.1-2007','90.1-2010','90.1-2013'
56
- system_to_space_map = [
57
- {
52
+ }
53
+ ]
54
+ when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
55
+ system_to_space_map = [
56
+ {
58
57
  'type' => 'PSZ-AC',
59
58
  'name' => 'HVAC_1',
60
59
  'space_names' => ['Zone1 Office']
61
- },
62
- {
60
+ },
61
+ {
63
62
  'type' => 'PSZ-AC',
64
63
  'name' => 'HVAC_2',
65
64
  'space_names' => ['Zone2 Fine Storage']
66
- },
67
- {
65
+ },
66
+ {
68
67
  'type' => 'UnitHeater',
69
68
  'name' => 'HVAC_3',
70
69
  'space_names' => ['Zone3 Bulk Storage']
71
- },
72
- {
70
+ },
71
+ {
73
72
  'type' => 'Zone Ventilation',
74
73
  'name' => 'Bulk Storage Zone Ventilation - Exhaust',
75
74
  'availability_sch_name' => 'Always On',
76
- 'flow_rate' => OpenStudio.convert(80008.9191,'cfm','m^3/s').get,
75
+ 'flow_rate' => OpenStudio.convert(80_008.9191, 'cfm', 'm^3/s').get,
77
76
  'ventilation_type' => 'Exhaust',
78
77
  'space_names' => ['Zone3 Bulk Storage']
79
- },
80
- {
78
+ },
79
+ {
81
80
  'type' => 'Zone Ventilation',
82
81
  'name' => 'Bulk Storage Zone Ventilation - Natural',
83
82
  'availability_sch_name' => 'Always On',
84
- 'flow_rate' => OpenStudio.convert(2000,'cfm','m^3/s').get,
83
+ 'flow_rate' => OpenStudio.convert(2000, 'cfm', 'm^3/s').get,
85
84
  'ventilation_type' => 'Natural',
86
85
  'space_names' => ['Zone3 Bulk Storage']
87
- },
88
- ]
86
+ }
87
+ ]
89
88
  end
90
89
 
91
90
  return system_to_space_map
92
91
  end
93
92
 
94
- def custom_hvac_tweaks(building_type, building_vintage, climate_zone, prototype_input)
95
-
93
+ def custom_hvac_tweaks(building_type, template, climate_zone, prototype_input)
96
94
  return true
97
-
98
95
  end
99
96
 
100
- def update_waterheater_loss_coefficient(building_vintage)
101
- case building_vintage
97
+ def update_waterheater_loss_coefficient(template)
98
+ case template
102
99
  when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013', 'NECB 2011'
103
- self.getWaterHeaterMixeds.sort.each do |water_heater|
100
+ getWaterHeaterMixeds.sort.each do |water_heater|
104
101
  water_heater.setOffCycleLossCoefficienttoAmbientTemperature(0.798542707)
105
102
  water_heater.setOnCycleLossCoefficienttoAmbientTemperature(0.798542707)
106
103
  end
107
- end
108
- end
109
-
110
- def custom_swh_tweaks(building_type, building_vintage, climate_zone, prototype_input)
111
-
112
- self.update_waterheater_loss_coefficient(building_vintage)
113
-
114
- return true
115
-
104
+ end
116
105
  end
117
106
 
107
+ def custom_swh_tweaks(building_type, template, climate_zone, prototype_input)
108
+ update_waterheater_loss_coefficient(template)
118
109
 
110
+ return true
111
+ end
119
112
  end
@@ -1,7 +1,6 @@
1
1
 
2
2
  # Reopen the OpenStudio class to add methods to apply standards to this object
3
3
  class OpenStudio::Model::AirLoopHVAC
4
-
5
4
  # Apply multizone vav outdoor air method and
6
5
  # adjust multizone VAV damper positions
7
6
  # to achieve a system minimum ventilation effectiveness
@@ -10,10 +9,9 @@ class OpenStudio::Model::AirLoopHVAC
10
9
  #
11
10
  # return [Bool] returns true if successful, false if not
12
11
  def apply_multizone_vav_outdoor_air_sizing(template)
13
-
14
- # TODO enable damper position adjustment for legacy IDFS
12
+ # TODO: enable damper position adjustment for legacy IDFS
15
13
  if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004'
16
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "Damper positions not modified for DOE Ref Pre-1980 or DOE Ref 1980-2004 vintages.")
14
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', 'Damper positions not modified for DOE Ref Pre-1980 or DOE Ref 1980-2004 vintages.')
17
15
  return true
18
16
  end
19
17
 
@@ -21,119 +19,126 @@ class OpenStudio::Model::AirLoopHVAC
21
19
  # Only applies to multi-zone vav systems
22
20
  # exclusion: for Outpatient: (1) both AHU1 and AHU2 in 'DOE Ref Pre-1980' and 'DOE Ref 1980-2004'
23
21
  # (2) AHU1 in 2004-2013
24
- if self.is_multizone_vav_system && !(self.name.to_s.include? "Outpatient F1")
25
- self.adjust_minimum_vav_damper_positions
22
+ if multizone_vav_system? && !(name.to_s.include? 'Outpatient F1')
23
+ adjust_minimum_vav_damper_positions
26
24
  end
27
25
 
28
26
  # Second time adjustment:
29
27
  # Only apply to 2010 and 2013 Outpatient (both AHU1 and AHU2)
30
28
  # TODO maybe apply to hospital as well?
31
- if (self.name.to_s.include? "Outpatient") && (template == '90.1-2010' || template == '90.1-2013')
32
- self.adjust_minimum_vav_damper_positions_outpatient
29
+ if (name.to_s.include? 'Outpatient') && (template == '90.1-2010' || template == '90.1-2013')
30
+ adjust_minimum_vav_damper_positions_outpatient
33
31
  end
34
32
 
35
33
  return true
36
-
37
34
  end
38
35
 
39
36
  # Apply all standard required controls to the airloop
40
37
  #
41
- # @param (see #is_economizer_required)
38
+ # @param (see #economizer_required?)
42
39
  # @return [Bool] returns true if successful, false if not
43
40
  # @todo optimum start
44
41
  # @todo night damper shutoff
45
42
  # @todo nightcycle control
46
43
  # @todo night fan shutoff
47
44
  def apply_standard_controls(template, climate_zone)
48
-
49
45
  # Energy Recovery Ventilation
50
- if self.is_energy_recovery_ventilator_required(template, climate_zone)
51
- self.apply_energy_recovery_ventilator
46
+ if energy_recovery_ventilator_required?(template, climate_zone)
47
+ apply_energy_recovery_ventilator
52
48
  end
53
49
 
54
50
  # Economizers
55
- self.set_economizer_limits(template, climate_zone)
56
- self.set_economizer_integration(template, climate_zone)
51
+ apply_economizer_limits(template, climate_zone)
52
+ apply_economizer_integration(template, climate_zone)
57
53
 
58
54
  # Multizone VAV Systems
59
- if self.is_multizone_vav_system
55
+ if multizone_vav_system?
60
56
 
61
57
  # VAV Reheat Control
62
- self.set_vav_damper_action(template)
58
+ apply_vav_damper_action(template)
63
59
 
64
60
  # Multizone VAV Optimization
65
61
  # This rule does not apply to two hospital and one outpatient systems (TODO add hospital two systems as exception)
66
- if !(self.name.to_s.include? "Outpatient F1")
67
- if self.is_multizone_vav_optimization_required(template, climate_zone)
68
- self.enable_multizone_vav_optimization
62
+ unless name.to_s.include? 'Outpatient F1'
63
+ if multizone_vav_optimization_required?(template, climate_zone)
64
+ enable_multizone_vav_optimization
69
65
  else
70
- self.disable_multizone_vav_optimization
66
+ disable_multizone_vav_optimization
71
67
  end
72
68
  end
73
69
 
74
- # VAV Static Pressure Reset
75
- # assume all systems have DDC control of VAV terminals
76
- has_ddc = true
77
- if self.is_static_pressure_reset_required(template, has_ddc)
78
- self.supply_return_exhaust_relief_fans.each do |fan|
79
- if fan.to_FanVariableVolume.is_initialized
80
- fan.set_control_type('Multi Zone VAV with Static Pressure Reset')
70
+ # Static Pressure Reset
71
+ # assume no systems have DDC control of VAV terminals
72
+ has_ddc = false
73
+ spr_req = static_pressure_reset_required?(template, has_ddc)
74
+ supply_return_exhaust_relief_fans.each do |fan|
75
+ if fan.to_FanVariableVolume.is_initialized
76
+ plr_req = fan.part_load_fan_power_limitation?(template)
77
+ # Part Load Fan Pressure Control & Static Pressure Reset
78
+ if plr_req && spr_req
79
+ fan.set_control_type('Multi Zone VAV with VSD and Static Pressure Reset')
80
+ # Part Load Fan Pressure Control only
81
+ elsif plr_req && !spr_req
82
+ fan.set_control_type('Multi Zone VAV with VSD and Fixed SP Setpoint')
83
+ # Static Pressure Reset only
84
+ elsif !plr_req && spr_req
85
+ fan.set_control_type('Multi Zone VAV with VSD and Fixed SP Setpoint')
86
+ # No Control Required
81
87
  else
82
- OpenStudio::logFree(OpenStudio::Error, "openstudio.standards.AirLoopHVAC","For #{self.name}: there is a constant volume fan on a multizone vav system. Cannot apply static pressure reset controls.")
88
+ fan.set_control_type('Multi Zone VAV with AF or BI Riding Curve')
83
89
  end
90
+ else
91
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.AirLoopHVAC', "For #{name}: there is a constant volume fan on a multizone vav system. Cannot apply static pressure reset controls.")
84
92
  end
85
93
  end
86
94
 
87
95
  end
88
96
 
89
97
  # Single zone systems
90
- # if self.thermalZones.size == 1
91
- # self.apply_single_zone_controls(template, climate_zone)
92
- # end
98
+ if self.thermalZones.size == 1
99
+ supply_return_exhaust_relief_fans.each do |fan|
100
+ if fan.to_FanVariableVolume.is_initialized
101
+ fan.set_control_type('Single Zone VAV Fan')
102
+ end
103
+ end
104
+ # self.apply_single_zone_controls(template, climate_zone)
105
+ end
93
106
 
94
107
  # DCV
95
- if self.is_demand_control_ventilation_required(template, climate_zone)
96
- self.enable_demand_control_ventilation
97
- else
98
- # TODO Need to convert the design spec OA objects
99
- # to per-area only so that if VRP is enabled we
100
- # don't get DCV accidentally? See PNNL Achieving 30% 5.2.2.21,
101
- # not convinced that this is actually necessary with current E+
102
- # capabilities.
108
+ if demand_control_ventilation_required?(template, climate_zone)
109
+ enable_demand_control_ventilation(template, climate_zone)
103
110
  end
104
111
 
105
112
  # SAT reset
106
113
  # TODO Prototype buildings use OAT-based SAT reset,
107
114
  # but PRM RM suggests Warmest zone based SAT reset.
108
- if self.is_supply_air_temperature_reset_required(template, climate_zone)
109
- self.enable_supply_air_temperature_reset_outdoor_temperature
110
- # self.enable_supply_air_temperature_reset_warmest_zone(template)
115
+ if supply_air_temperature_reset_required?(template, climate_zone)
116
+ enable_supply_air_temperature_reset_warmest_zone(template)
111
117
  end
112
118
 
113
119
  # Unoccupied shutdown
114
- if self.is_unoccupied_fan_shutoff_required(template)
115
- self.enable_unoccupied_fan_shutoff
120
+ if unoccupied_fan_shutoff_required?(template)
121
+ enable_unoccupied_fan_shutoff
116
122
  else
117
- self.setAvailabilitySchedule(self.model.alwaysOnDiscreteSchedule)
123
+ setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
118
124
  end
119
125
 
120
126
  # Motorized OA damper
121
- if self.is_motorized_oa_damper_required(template, climate_zone)
127
+ if motorized_oa_damper_required?(template, climate_zone)
122
128
  # Assume that the availability schedule has already been
123
129
  # set to reflect occupancy and use this for the OA damper.
124
- self.add_motorized_oa_damper(0.15, self.availabilitySchedule)
130
+ add_motorized_oa_damper(0.15, availabilitySchedule)
125
131
  else
126
- self.remove_motorized_oa_damper
132
+ remove_motorized_oa_damper
127
133
  end
128
134
 
129
- # TODO Optimum Start
135
+ # TODO: Optimum Start
130
136
  # for systems exceeding 10,000 cfm
131
137
  # Don't think that OS will be able to do this.
132
138
  # OS currently only allows 1 availability manager
133
139
  # at a time on an AirLoopHVAC. If we add an
134
140
  # AvailabilityManager:OptimumStart, it
135
141
  # will replace the AvailabilityManager:NightCycle.
136
-
137
142
  end
138
143
 
139
144
  # Apply all PRM baseline required controls to the airloop.
@@ -141,33 +146,40 @@ class OpenStudio::Model::AirLoopHVAC
141
146
  # prescriptive controls, which are added via
142
147
  # AirLoopHVAC.apply_standard_controls
143
148
  #
144
- # @param (see #is_economizer_required)
149
+ # @param (see #economizer_required?)
145
150
  # @return [Bool] returns true if successful, false if not
146
- def apply_performance_rating_method_baseline_controls(template, climate_zone)
147
-
151
+ def apply_prm_baseline_controls(template, climate_zone)
148
152
  # Economizers
149
- if self.is_performance_rating_method_baseline_economizer_required(template, climate_zone)
150
- self.apply_performance_rating_method_baseline_economizer(template, climate_zone)
153
+ if prm_baseline_economizer_required?(template, climate_zone)
154
+ apply_prm_baseline_economizer(template, climate_zone)
151
155
  end
152
156
 
153
157
  # Multizone VAV Systems
154
- if self.is_multizone_vav_system
158
+ if multizone_vav_system?
159
+
160
+ # VSD no Static Pressure Reset on all VAV systems
161
+ # per G3.1.3.15
162
+ supply_return_exhaust_relief_fans.each do |fan|
163
+ if fan.to_FanVariableVolume.is_initialized
164
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Setting fan part load curve per G3.1.3.15.")
165
+ fan.set_control_type('Multi Zone VAV with VSD and Fixed SP Setpoint')
166
+ end
167
+ end
155
168
 
156
169
  # SAT Reset
157
170
  # G3.1.3.12 SAT reset required for all Multizone VAV systems,
158
171
  # even if not required by prescriptive section.
159
172
  case template
160
173
  when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
161
- self.enable_supply_air_temperature_reset_warmest_zone(template)
174
+ enable_supply_air_temperature_reset_warmest_zone(template)
162
175
  end
163
176
 
164
177
  end
165
178
 
166
179
  # Unoccupied shutdown
167
- self.enable_unoccupied_fan_shutoff
180
+ enable_unoccupied_fan_shutoff
168
181
 
169
182
  return true
170
-
171
183
  end
172
184
 
173
185
  # Calculate and apply the performance rating method
@@ -181,18 +193,17 @@ class OpenStudio::Model::AirLoopHVAC
181
193
  # @todo Figure out how to split fan power between multiple fans
182
194
  # if the proposed model had multiple fans (supply, return, exhaust, etc.)
183
195
  # return [Bool] true if successful, false if not.
184
- def set_performance_rating_method_baseline_fan_power(template)
185
-
196
+ def apply_prm_baseline_fan_power(template)
186
197
  # Main AHU fans
187
198
 
188
199
  # Calculate the allowable fan motor bhp
189
200
  # for the entire airloop.
190
- allowable_fan_bhp = self.allowable_system_brake_horsepower(template)
201
+ allowable_fan_bhp = allowable_system_brake_horsepower(template)
191
202
 
192
203
  # Divide the allowable power evenly between the fans
193
204
  # on this airloop.
194
- all_fans = self.supply_return_exhaust_relief_fans
195
- allowable_fan_bhp = allowable_fan_bhp / all_fans.size
205
+ all_fans = supply_return_exhaust_relief_fans
206
+ allowable_fan_bhp /= all_fans.size
196
207
 
197
208
  # Set the motor efficiencies
198
209
  # for all fans based on the calculated
@@ -200,7 +211,7 @@ class OpenStudio::Model::AirLoopHVAC
200
211
  # fan power for each fan and adjust
201
212
  # the fan pressure rise accordingly
202
213
  all_fans.each do |fan|
203
- fan.set_standard_minimum_motor_efficiency(template, allowable_fan_bhp)
214
+ fan.apply_standard_minimum_motor_efficiency(template, allowable_fan_bhp)
204
215
  allowable_power_w = allowable_fan_bhp * 746 / fan.motorEfficiency
205
216
  fan.adjust_pressure_rise_to_meet_fan_power(allowable_power_w)
206
217
  end
@@ -208,14 +219,13 @@ class OpenStudio::Model::AirLoopHVAC
208
219
  # Fan powered terminal fans
209
220
 
210
221
  # Adjust each terminal fan
211
- self.demandComponents.each do |dc|
222
+ demandComponents.each do |dc|
212
223
  next if dc.to_AirTerminalSingleDuctParallelPIUReheat.empty?
213
224
  pfp_term = dc.to_AirTerminalSingleDuctParallelPIUReheat.get
214
- pfp_term.set_performance_rating_method_baseline_fan_power(template)
225
+ pfp_term.apply_prm_baseline_fan_power(template)
215
226
  end
216
227
 
217
228
  return true
218
-
219
229
  end
220
230
 
221
231
  # Determine the fan power limitation pressure drop adjustment
@@ -225,22 +235,21 @@ class OpenStudio::Model::AirLoopHVAC
225
235
  # @return [Double] fan power limitation pressure drop adjustment
226
236
  # units = horsepower
227
237
  # @todo Determine the presence of MERV filters and other stuff in Table 6.5.3.1.1B. May need to extend AirLoopHVAC data model
228
- def fan_power_limitation_pressure_drop_adjustment_brake_horsepower(template = "ASHRAE 90.1-2007")
229
-
238
+ def fan_power_limitation_pressure_drop_adjustment_brake_horsepower(template = 'ASHRAE 90.1-2007')
230
239
  # Get design supply air flow rate (whether autosized or hard-sized)
231
240
  dsn_air_flow_m3_per_s = 0
232
241
  dsn_air_flow_cfm = 0
233
- if self.autosizedDesignSupplyAirFlowRate.is_initialized
234
- dsn_air_flow_m3_per_s = self.autosizedDesignSupplyAirFlowRate.get
235
- dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
236
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
242
+ if autosizedDesignSupplyAirFlowRate.is_initialized
243
+ dsn_air_flow_m3_per_s = autosizedDesignSupplyAirFlowRate.get
244
+ dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, 'm^3/s', 'cfm').get
245
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
237
246
  else
238
- dsn_air_flow_m3_per_s = self.designSupplyAirFlowRate.get
239
- dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
240
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
247
+ dsn_air_flow_m3_per_s = designSupplyAirFlowRate.get
248
+ dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, 'm^3/s', 'cfm').get
249
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
241
250
  end
242
251
 
243
- # TODO determine the presence of MERV filters and other stuff
252
+ # TODO: determine the presence of MERV filters and other stuff
244
253
  # in Table 6.5.3.1.1B
245
254
  # perhaps need to extend AirLoopHVAC data model
246
255
  has_fully_ducted_return_and_or_exhaust_air_systems = false
@@ -252,16 +261,15 @@ class OpenStudio::Model::AirLoopHVAC
252
261
  if has_fully_ducted_return_and_or_exhaust_air_systems
253
262
  adj_in_wc = 0.5
254
263
  fan_pwr_adjustment_in_wc += adj_in_wc
255
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","--Added #{adj_in_wc} in wc for Fully ducted return and/or exhaust air systems")
264
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "--Added #{adj_in_wc} in wc for Fully ducted return and/or exhaust air systems")
256
265
  end
257
266
 
258
267
  # Convert the pressure drop adjustment to brake horsepower (bhp)
259
268
  # assuming that all supply air passes through all devices
260
- fan_pwr_adjustment_bhp = fan_pwr_adjustment_in_wc*dsn_air_flow_cfm / 4131
261
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: Fan Power Limitation Pressure Drop Adjustment = #{(fan_pwr_adjustment_bhp.round(2))} bhp")
269
+ fan_pwr_adjustment_bhp = fan_pwr_adjustment_in_wc * dsn_air_flow_cfm / 4131
270
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{name}: Fan Power Limitation Pressure Drop Adjustment = #{fan_pwr_adjustment_bhp.round(2)} bhp")
262
271
 
263
272
  return fan_pwr_adjustment_bhp
264
-
265
273
  end
266
274
 
267
275
  # Determine the allowable fan system brake horsepower
@@ -270,91 +278,88 @@ class OpenStudio::Model::AirLoopHVAC
270
278
  # @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
271
279
  # @return [Double] allowable fan system brake horsepower
272
280
  # units = horsepower
273
- def allowable_system_brake_horsepower(template = "ASHRAE 90.1-2007")
274
-
281
+ def allowable_system_brake_horsepower(template = 'ASHRAE 90.1-2007')
275
282
  # Get design supply air flow rate (whether autosized or hard-sized)
276
283
  dsn_air_flow_m3_per_s = 0
277
284
  dsn_air_flow_cfm = 0
278
- if self.autosizedDesignSupplyAirFlowRate.is_initialized
279
- dsn_air_flow_m3_per_s = self.autosizedDesignSupplyAirFlowRate.get
280
- dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
281
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
285
+ if autosizedDesignSupplyAirFlowRate.is_initialized
286
+ dsn_air_flow_m3_per_s = autosizedDesignSupplyAirFlowRate.get
287
+ dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, 'm^3/s', 'cfm').get
288
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
282
289
  else
283
- dsn_air_flow_m3_per_s = self.designSupplyAirFlowRate.get
284
- dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
285
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
290
+ dsn_air_flow_m3_per_s = designSupplyAirFlowRate.get
291
+ dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, 'm^3/s', 'cfm').get
292
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
286
293
  end
287
294
 
288
295
  # Get the fan limitation pressure drop adjustment bhp
289
- fan_pwr_adjustment_bhp = self.fan_power_limitation_pressure_drop_adjustment_brake_horsepower
296
+ fan_pwr_adjustment_bhp = fan_power_limitation_pressure_drop_adjustment_brake_horsepower
290
297
 
291
298
  # Determine the number of zones the system serves
292
- num_zones_served = self.thermalZones.size
299
+ num_zones_served = thermalZones.size
293
300
 
294
301
  # Get the supply air fan and determine whether VAV or CAV system.
295
302
  # Assume that supply air fan is fan closest to the demand outlet node.
296
303
  # The fan may be inside of a piece of unitary equipment.
297
304
  fan_pwr_limit_type = nil
298
- self.supplyComponents.reverse.each do |comp|
305
+ supplyComponents.reverse.each do |comp|
299
306
  if comp.to_FanConstantVolume.is_initialized || comp.to_FanOnOff.is_initialized
300
- fan_pwr_limit_type = "constant volume"
307
+ fan_pwr_limit_type = 'constant volume'
301
308
  elsif comp.to_FanVariableVolume.is_initialized
302
- fan_pwr_limit_type = "variable volume"
309
+ fan_pwr_limit_type = 'variable volume'
303
310
  elsif comp.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.is_initialized
304
311
  fan = comp.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.get.supplyAirFan
305
312
  if fan.to_FanConstantVolume.is_initialized || comp.to_FanOnOff.is_initialized
306
- fan_pwr_limit_type = "constant volume"
313
+ fan_pwr_limit_type = 'constant volume'
307
314
  elsif fan.to_FanVariableVolume.is_initialized
308
- fan_pwr_limit_type = "variable volume"
315
+ fan_pwr_limit_type = 'variable volume'
309
316
  end
310
317
  elsif comp.to_AirLoopHVACUnitarySystem.is_initialized
311
318
  fan = comp.to_AirLoopHVACUnitarySystem.get.supplyFan
312
319
  if fan.to_FanConstantVolume.is_initialized || comp.to_FanOnOff.is_initialized
313
- fan_pwr_limit_type = "constant volume"
320
+ fan_pwr_limit_type = 'constant volume'
314
321
  elsif fan.to_FanVariableVolume.is_initialized
315
- fan_pwr_limit_type = "variable volume"
322
+ fan_pwr_limit_type = 'variable volume'
316
323
  end
317
324
  end
318
325
  end
319
326
 
320
327
  # For 90.1-2010, single-zone VAV systems use the
321
328
  # constant volume limitation per 6.5.3.1.1
322
- if template == "ASHRAE 90.1-2010" && fan_pwr_limit_type = "variable volume" && num_zones_served == 1
323
- fan_pwr_limit_type = "constant volume"
324
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","For #{self.name}: Using the constant volume limitation because single-zone VAV system.")
329
+ if template == 'ASHRAE 90.1-2010' && fan_pwr_limit_type == 'variable volume' && num_zones_served == 1
330
+ fan_pwr_limit_type = 'constant volume'
331
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Using the constant volume limitation because single-zone VAV system.")
325
332
  end
326
333
 
327
334
  # Calculate the Allowable Fan System brake horsepower per Table G3.1.2.9
328
335
  allowable_fan_bhp = 0
329
- if fan_pwr_limit_type == "constant volume"
330
- allowable_fan_bhp = dsn_air_flow_cfm*0.00094+fan_pwr_adjustment_bhp
331
- elsif fan_pwr_limit_type == "variable volume"
332
- allowable_fan_bhp = dsn_air_flow_cfm*0.0013+fan_pwr_adjustment_bhp
336
+ if fan_pwr_limit_type == 'constant volume'
337
+ allowable_fan_bhp = dsn_air_flow_cfm * 0.00094 + fan_pwr_adjustment_bhp
338
+ elsif fan_pwr_limit_type == 'variable volume'
339
+ allowable_fan_bhp = dsn_air_flow_cfm * 0.0013 + fan_pwr_adjustment_bhp
333
340
  end
334
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","For #{self.name}: Allowable brake horsepower = #{(allowable_fan_bhp).round(2)}HP based on #{dsn_air_flow_cfm.round} cfm and #{fan_pwr_adjustment_bhp.round(2)} bhp of adjustment.")
341
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Allowable brake horsepower = #{allowable_fan_bhp.round(2)}HP based on #{dsn_air_flow_cfm.round} cfm and #{fan_pwr_adjustment_bhp.round(2)} bhp of adjustment.")
335
342
 
336
343
  # Calculate and report the total area for debugging/testing
337
- floor_area_served_m2 = self.floor_area_served
344
+ floor_area_served_m2 = floor_area_served
338
345
  floor_area_served_ft2 = OpenStudio.convert(floor_area_served_m2, 'm^2', 'ft^2').get
339
346
  cfm_per_ft2 = dsn_air_flow_cfm / floor_area_served_ft2
340
347
  cfm_per_hp = dsn_air_flow_cfm / allowable_fan_bhp
341
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: area served = #{floor_area_served_ft2.round} ft^2.")
342
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: flow per area = #{cfm_per_ft2.round} cfm/ft^2.")
343
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: flow per hp = #{cfm_per_hp.round} cfm/hp.")
348
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{name}: area served = #{floor_area_served_ft2.round} ft^2.")
349
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{name}: flow per area = #{cfm_per_ft2.round} cfm/ft^2.")
350
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{name}: flow per hp = #{cfm_per_hp.round} cfm/hp.")
344
351
 
345
352
  return allowable_fan_bhp
346
-
347
353
  end
348
354
 
349
355
  # Get all of the supply, return, exhaust, and relief fans on this system
350
356
  #
351
357
  # @return [Array] an array of FanConstantVolume, FanVariableVolume, and FanOnOff objects
352
- def supply_return_exhaust_relief_fans()
353
-
358
+ def supply_return_exhaust_relief_fans
354
359
  # Fans on the supply side of the airloop directly, or inside of unitary equipment.
355
360
  fans = []
356
- sup_and_oa_comps = self.supplyComponents
357
- sup_and_oa_comps += self.oaComponents
361
+ sup_and_oa_comps = supplyComponents
362
+ sup_and_oa_comps += oaComponents
358
363
  sup_and_oa_comps.each do |comp|
359
364
  if comp.to_FanConstantVolume.is_initialized
360
365
  fans << comp.to_FanConstantVolume.get
@@ -382,7 +387,6 @@ class OpenStudio::Model::AirLoopHVAC
382
387
  end
383
388
 
384
389
  return fans
385
-
386
390
  end
387
391
 
388
392
  # Determine the total brake horsepower of the fans on the system
@@ -392,20 +396,19 @@ class OpenStudio::Model::AirLoopHVAC
392
396
  # @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
393
397
  # @return [Double] total brake horsepower of the fans on the system
394
398
  # units = horsepower
395
- def system_fan_brake_horsepower(include_terminal_fans = true, template = "ASHRAE 90.1-2007")
396
-
397
- # TODO get the template from the parent model itself?
399
+ def system_fan_brake_horsepower(include_terminal_fans = true, template = 'ASHRAE 90.1-2007')
400
+ # TODO: get the template from the parent model itself?
398
401
  # Or not because maybe you want to see the difference between two standards?
399
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","#{self.name}-Determining #{template} allowable system fan power.")
402
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{name}-Determining #{template} allowable system fan power.")
400
403
 
401
404
  # Get all fans
402
405
  fans = []
403
406
  # Supply, exhaust, relief, and return fans
404
- fans += self.supply_return_exhaust_relief_fans
407
+ fans += supply_return_exhaust_relief_fans
405
408
 
406
409
  # Fans inside of fan-powered terminals
407
410
  if include_terminal_fans
408
- self.demandComponents.each do |comp|
411
+ demandComponents.each do |comp|
409
412
  if comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
410
413
  term_fan = comp.to_AirTerminalSingleDuctSeriesPIUReheat.get.supplyAirFan
411
414
  if term_fan.to_FanConstantVolume.is_initialized
@@ -424,99 +427,95 @@ class OpenStudio::Model::AirLoopHVAC
424
427
  # sum up their brake horsepower values.
425
428
  sys_fan_bhp = 0
426
429
  fans.sort.each do |fan|
427
- sys_fan_bhp += fan.brakeHorsepower
430
+ sys_fan_bhp += fan.brake_horsepower
428
431
  end
429
432
 
430
433
  return sys_fan_bhp
431
-
432
434
  end
433
435
 
434
436
  # Set the fan pressure rises that will result in
435
437
  # the system hitting the baseline allowable fan power
436
438
  #
437
439
  # @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
438
- def set_baseline_fan_pressure_rise(template = "ASHRAE 90.1-2007")
439
-
440
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "#{self.name}-Setting #{template} baseline fan power.")
440
+ def apply_baseline_fan_pressure_rise(template = 'ASHRAE 90.1-2007')
441
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{name}-Setting #{template} baseline fan power.")
441
442
 
442
443
  # Get the total system bhp from the proposed system, including terminal fans
443
- proposed_sys_bhp = self.system_fan_brake_horsepower(true)
444
+ proposed_sys_bhp = system_fan_brake_horsepower(true)
444
445
 
445
446
  # Get the allowable fan brake horsepower
446
- allowable_fan_bhp = self.allowable_system_brake_horsepower(template)
447
+ allowable_fan_bhp = allowable_system_brake_horsepower(template)
447
448
 
448
449
  # Get the fan power limitation from proposed system
449
- fan_pwr_adjustment_bhp = self.fan_power_limitation_pressure_drop_adjustment_brake_horsepower
450
+ fan_pwr_adjustment_bhp = fan_power_limitation_pressure_drop_adjustment_brake_horsepower
450
451
 
451
452
  # Subtract the fan power adjustment
452
- allowable_fan_bhp = allowable_fan_bhp-fan_pwr_adjustment_bhp
453
+ allowable_fan_bhp -= fan_pwr_adjustment_bhp
453
454
 
454
455
  # Get all fans
455
- fans = self.supply_return_exhaust_relief_fans
456
+ fans = supply_return_exhaust_relief_fans
456
457
 
457
- # TODO improve description
458
+ # TODO: improve description
458
459
  # Loop through the fans, changing the pressure rise
459
460
  # until the fan bhp is the same percentage of the baseline allowable bhp
460
461
  # as it was on the proposed system.
461
462
  fans.each do |fan|
462
463
  # TODO: Yixing Check the model of the Fan Coil Unit
463
- next if fan.name.to_s.include?("Fan Coil fan")
464
- next if fan.name.to_s.include?("UnitHeater Fan")
464
+ next if fan.name.to_s.include?('Fan Coil fan')
465
+ next if fan.name.to_s.include?('UnitHeater Fan')
465
466
 
466
- OpenStudio::logFree(OpenStudio::Info, "#{fan.name}")
467
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', fan.name.to_s)
467
468
 
468
469
  # Get the bhp of the fan on the proposed system
469
- proposed_fan_bhp = fan.brakeHorsepower
470
+ proposed_fan_bhp = fan.brake_horsepower
470
471
 
471
472
  # Get the bhp of the fan on the proposed system
472
473
  proposed_fan_bhp_frac = proposed_fan_bhp / proposed_sys_bhp
473
474
 
474
475
  # Determine the target bhp of the fan on the baseline system
475
- baseline_fan_bhp = proposed_fan_bhp_frac*allowable_fan_bhp
476
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(baseline_fan_bhp).round(1)} bhp = Baseline fan brake horsepower.")
476
+ baseline_fan_bhp = proposed_fan_bhp_frac * allowable_fan_bhp
477
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "* #{baseline_fan_bhp.round(1)} bhp = Baseline fan brake horsepower.")
477
478
 
478
479
  # Set the baseline impeller eff of the fan,
479
480
  # preserving the proposed motor eff.
480
- baseline_impeller_eff = fan.baselineImpellerEfficiency(template)
481
- fan.changeImpellerEfficiency(baseline_impeller_eff)
482
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(baseline_impeller_eff*100).round(1)}% = Baseline fan impeller efficiency.")
481
+ baseline_impeller_eff = fan.baseline_impeller_efficiency(template)
482
+ fan.change_impeller_efficiency(baseline_impeller_eff)
483
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "* #{(baseline_impeller_eff * 100).round(1)}% = Baseline fan impeller efficiency.")
483
484
 
484
485
  # Set the baseline motor efficiency for the specified bhp
485
486
  baseline_motor_eff = fan.standardMinimumMotorEfficiency(template, standards, allowable_fan_bhp)
486
- fan.changeMotorEfficiency(baseline_motor_eff)
487
+ fan.change_motor_efficiency(baseline_motor_eff)
487
488
 
488
489
  # Get design supply air flow rate (whether autosized or hard-sized)
489
490
  dsn_air_flow_m3_per_s = 0
490
491
  if fan.autosizedDesignSupplyAirFlowRate.is_initialized
491
492
  dsn_air_flow_m3_per_s = fan.autosizedDesignSupplyAirFlowRate.get
492
- dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
493
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
493
+ dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, 'm^3/s', 'cfm').get
494
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "* #{dsn_air_flow_cfm.round} cfm = Autosized Design Supply Air Flow Rate.")
494
495
  else
495
496
  dsn_air_flow_m3_per_s = fan.designSupplyAirFlowRate.get
496
- dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
497
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = User entered Design Supply Air Flow Rate.")
497
+ dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, 'm^3/s', 'cfm').get
498
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "* #{dsn_air_flow_cfm.round} cfm = User entered Design Supply Air Flow Rate.")
498
499
  end
499
500
 
500
501
  # Determine the fan pressure rise that will result in the target bhp
501
502
  # pressure_rise_pa = fan_bhp*746 / fan_motor_eff*fan_total_eff / dsn_air_flow_m3_per_s
502
- baseline_pressure_rise_pa = baseline_fan_bhp*746 / fan.motorEfficiency*fan.fanEfficiency / dsn_air_flow_m3_per_s
503
- baseline_pressure_rise_in_wc = OpenStudio.convert(fan_pressure_rise_pa, "Pa", "inH_{2}O",).get
504
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(fan_pressure_rise_in_wc).round(2)} in w.c. = Pressure drop to achieve allowable fan power.")
503
+ baseline_pressure_rise_pa = baseline_fan_bhp * 746 / fan.motorEfficiency * fan.fanEfficiency / dsn_air_flow_m3_per_s
504
+ baseline_pressure_rise_in_wc = OpenStudio.convert(fan_pressure_rise_pa, 'Pa', 'inH_{2}O').get
505
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "* #{fan_pressure_rise_in_wc.round(2)} in w.c. = Pressure drop to achieve allowable fan power.")
505
506
 
506
507
  # Calculate the bhp of the fan to make sure it matches
507
- calc_bhp = fan.brakeHorsepower
508
- if ((calc_bhp-baseline_fan_bhp) / baseline_fan_bhp).abs > 0.02
509
- OpenStudio::logFree(OpenStudio::Error, "openstudio.standards.AirLoopHVAC", "#{fan.name} baseline fan bhp supposed to be #{baseline_fan_bhp}, but is #{calc_bhp}.")
508
+ calc_bhp = fan.brake_horsepower
509
+ if ((calc_bhp - baseline_fan_bhp) / baseline_fan_bhp).abs > 0.02
510
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.AirLoopHVAC', "#{fan.name} baseline fan bhp supposed to be #{baseline_fan_bhp}, but is #{calc_bhp}.")
510
511
  end
511
-
512
512
  end
513
513
 
514
514
  # Calculate the total bhp of the system to make sure it matches the goal
515
- calc_sys_bhp = self.system_fan_brake_horsepower(false)
516
- if ((calc_sys_bhp-allowable_fan_bhp) / allowable_fan_bhp).abs > 0.02
517
- OpenStudio::logFree(OpenStudio::Error, "openstudio.standards.AirLoopHVAC", "#{self.name} baseline system bhp supposed to be #{allowable_fan_bhp}, but is #{calc_sys_bhp}.")
515
+ calc_sys_bhp = system_fan_brake_horsepower(false)
516
+ if ((calc_sys_bhp - allowable_fan_bhp) / allowable_fan_bhp).abs > 0.02
517
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.AirLoopHVAC', "#{name} baseline system bhp supposed to be #{allowable_fan_bhp}, but is #{calc_sys_bhp}.")
518
518
  end
519
-
520
519
  end
521
520
 
522
521
  # Get the total cooling capacity for the air loop
@@ -526,11 +525,10 @@ class OpenStudio::Model::AirLoopHVAC
526
525
  # @todo Change to pull water coil nominal capacity instead of design load; not a huge difference, but water coil nominal capacity not available in sizing table.
527
526
  # @todo Handle all additional cooling coil types. Currently only handles CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, and CoilCoolingWater
528
527
  def total_cooling_capacity
529
-
530
528
  # Sum the cooling capacity for all cooling components
531
529
  # on the airloop, which may be inside of unitary systems.
532
530
  total_cooling_capacity_w = 0
533
- self.supplyComponents.each do |sc|
531
+ supplyComponents.each do |sc|
534
532
  # CoilCoolingDXSingleSpeed
535
533
  if sc.to_CoilCoolingDXSingleSpeed.is_initialized
536
534
  coil = sc.to_CoilCoolingDXSingleSpeed.get
@@ -539,7 +537,7 @@ class OpenStudio::Model::AirLoopHVAC
539
537
  elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
540
538
  total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
541
539
  else
542
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
540
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
543
541
  end
544
542
  # CoilCoolingDXTwoSpeed
545
543
  elsif sc.to_CoilCoolingDXTwoSpeed.is_initialized
@@ -549,15 +547,15 @@ class OpenStudio::Model::AirLoopHVAC
549
547
  elsif coil.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
550
548
  total_cooling_capacity_w += coil.autosizedRatedHighSpeedTotalCoolingCapacity.get
551
549
  else
552
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
550
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
553
551
  end
554
552
  # CoilCoolingWater
555
553
  elsif sc.to_CoilCoolingWater.is_initialized
556
554
  coil = sc.to_CoilCoolingWater.get
557
- if coil.autosizedDesignCoilLoad.is_initialized # TODO Change to pull water coil nominal capacity instead of design load
555
+ if coil.autosizedDesignCoilLoad.is_initialized # TODO: Change to pull water coil nominal capacity instead of design load
558
556
  total_cooling_capacity_w += coil.autosizedDesignCoilLoad.get
559
557
  else
560
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
558
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
561
559
  end
562
560
  # CoilCoolingWaterToAirHeatPumpEquationFit
563
561
  elsif sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
@@ -567,7 +565,7 @@ class OpenStudio::Model::AirLoopHVAC
567
565
  elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
568
566
  total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
569
567
  else
570
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
568
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
571
569
  end
572
570
  elsif sc.to_AirLoopHVACUnitarySystem.is_initialized
573
571
  unitary = sc.to_AirLoopHVACUnitarySystem.get
@@ -581,7 +579,7 @@ class OpenStudio::Model::AirLoopHVAC
581
579
  elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
582
580
  total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
583
581
  else
584
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
582
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
585
583
  end
586
584
  # CoilCoolingDXTwoSpeed
587
585
  elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
@@ -591,15 +589,15 @@ class OpenStudio::Model::AirLoopHVAC
591
589
  elsif coil.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
592
590
  total_cooling_capacity_w += coil.autosizedRatedHighSpeedTotalCoolingCapacity.get
593
591
  else
594
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
592
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
595
593
  end
596
594
  # CoilCoolingWater
597
595
  elsif clg_coil.to_CoilCoolingWater.is_initialized
598
596
  coil = clg_coil.to_CoilCoolingWater.get
599
- if coil.autosizedDesignCoilLoad.is_initialized # TODO Change to pull water coil nominal capacity instead of design load
597
+ if coil.autosizedDesignCoilLoad.is_initialized # TODO: Change to pull water coil nominal capacity instead of design load
600
598
  total_cooling_capacity_w += coil.autosizedDesignCoilLoad.get
601
599
  else
602
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
600
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
603
601
  end
604
602
  # CoilCoolingWaterToAirHeatPumpEquationFit
605
603
  elsif clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
@@ -609,7 +607,7 @@ class OpenStudio::Model::AirLoopHVAC
609
607
  elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
610
608
  total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
611
609
  else
612
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
610
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
613
611
  end
614
612
  end
615
613
  end
@@ -624,7 +622,7 @@ class OpenStudio::Model::AirLoopHVAC
624
622
  elsif coil.autosizedRatedTotalCoolingCapacity.is_initialized
625
623
  total_cooling_capacity_w += coil.autosizedRatedTotalCoolingCapacity.get
626
624
  else
627
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
625
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
628
626
  end
629
627
  # CoilCoolingDXTwoSpeed
630
628
  elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
@@ -634,23 +632,23 @@ class OpenStudio::Model::AirLoopHVAC
634
632
  elsif coil.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
635
633
  total_cooling_capacity_w += coil.autosizedRatedHighSpeedTotalCoolingCapacity.get
636
634
  else
637
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
635
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
638
636
  end
639
637
  # CoilCoolingWater
640
638
  elsif clg_coil.to_CoilCoolingWater.is_initialized
641
639
  coil = clg_coil.to_CoilCoolingWater.get
642
- if coil.autosizedDesignCoilLoad.is_initialized # TODO Change to pull water coil nominal capacity instead of design load
640
+ if coil.autosizedDesignCoilLoad.is_initialized # TODO: Change to pull water coil nominal capacity instead of design load
643
641
  total_cooling_capacity_w += coil.autosizedDesignCoilLoad.get
644
642
  else
645
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
643
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
646
644
  end
647
645
  end
648
646
  elsif sc.to_CoilCoolingDXMultiSpeed.is_initialized ||
649
- sc.to_CoilCoolingCooledBeam.is_initialized ||
650
- sc.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.is_initialized ||
651
- sc.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.is_initialized ||
652
- sc.to_AirLoopHVACUnitarySystem.is_initialized
653
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "#{self.name} has a cooling coil named #{sc.name}, whose type is not yet covered by economizer checks.")
647
+ sc.to_CoilCoolingCooledBeam.is_initialized ||
648
+ sc.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.is_initialized ||
649
+ sc.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.is_initialized ||
650
+ sc.to_AirLoopHVACUnitarySystem.is_initialized
651
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "#{name} has a cooling coil named #{sc.name}, whose type is not yet covered by economizer checks.")
654
652
  # CoilCoolingDXMultiSpeed
655
653
  # CoilCoolingCooledBeam
656
654
  # CoilCoolingWaterToAirHeatPumpEquationFit
@@ -662,7 +660,6 @@ class OpenStudio::Model::AirLoopHVAC
662
660
  end
663
661
 
664
662
  return total_cooling_capacity_w
665
-
666
663
  end
667
664
 
668
665
  # Determine whether or not this system
@@ -674,20 +671,19 @@ class OpenStudio::Model::AirLoopHVAC
674
671
  # 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-5B', 'ASHRAE 169-2006-5C', 'ASHRAE 169-2006-6A', 'ASHRAE 169-2006-6B', 'ASHRAE 169-2006-7A',
675
672
  # 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
676
673
  # @return [Bool] returns true if an economizer is required, false if not
677
- def is_economizer_required(template, climate_zone)
678
-
674
+ def economizer_required?(template, climate_zone)
679
675
  economizer_required = false
680
676
 
681
- return economizer_required if self.name.to_s.include? "Outpatient F1"
677
+ return economizer_required if name.to_s.include? 'Outpatient F1'
682
678
 
683
679
  # A big number of btu per hr as the minimum requirement
684
- infinity_btu_per_hr = 999999999999
680
+ infinity_btu_per_hr = 999_999_999_999
685
681
  minimum_capacity_btu_per_hr = infinity_btu_per_hr
686
682
 
687
683
  # Determine if the airloop serves any computer rooms
688
684
  # / data centers, which changes the economizer.
689
685
  is_dc = false
690
- if self.data_center_area_served > 0
686
+ if data_center_area_served > 0
691
687
  is_dc = true
692
688
  end
693
689
 
@@ -708,7 +704,7 @@ class OpenStudio::Model::AirLoopHVAC
708
704
  'ASHRAE 169-2006-7B',
709
705
  'ASHRAE 169-2006-8A',
710
706
  'ASHRAE 169-2006-8B'
711
- minimum_capacity_btu_per_hr = 35000
707
+ minimum_capacity_btu_per_hr = 35_000
712
708
  when 'ASHRAE 169-2006-3B',
713
709
  'ASHRAE 169-2006-3C',
714
710
  'ASHRAE 169-2006-4B',
@@ -716,7 +712,7 @@ class OpenStudio::Model::AirLoopHVAC
716
712
  'ASHRAE 169-2006-5B',
717
713
  'ASHRAE 169-2006-5C',
718
714
  'ASHRAE 169-2006-6B'
719
- minimum_capacity_btu_per_hr = 65000
715
+ minimum_capacity_btu_per_hr = 65_000
720
716
  end
721
717
  when '90.1-2010', '90.1-2013'
722
718
  if is_dc # data center / computer room
@@ -734,7 +730,7 @@ class OpenStudio::Model::AirLoopHVAC
734
730
  'ASHRAE 169-2006-7B',
735
731
  'ASHRAE 169-2006-8A',
736
732
  'ASHRAE 169-2006-8B'
737
- minimum_capacity_btu_per_hr = 135000
733
+ minimum_capacity_btu_per_hr = 135_000
738
734
  when 'ASHRAE 169-2006-3B',
739
735
  'ASHRAE 169-2006-3C',
740
736
  'ASHRAE 169-2006-4B',
@@ -742,7 +738,7 @@ class OpenStudio::Model::AirLoopHVAC
742
738
  'ASHRAE 169-2006-5B',
743
739
  'ASHRAE 169-2006-5C',
744
740
  'ASHRAE 169-2006-6B'
745
- minimum_capacity_btu_per_hr = 65000
741
+ minimum_capacity_btu_per_hr = 65_000
746
742
  end
747
743
  else
748
744
  case climate_zone
@@ -766,43 +762,41 @@ class OpenStudio::Model::AirLoopHVAC
766
762
  'ASHRAE 169-2006-5B',
767
763
  'ASHRAE 169-2006-5C',
768
764
  'ASHRAE 169-2006-6B'
769
- minimum_capacity_btu_per_hr = 54000
765
+ minimum_capacity_btu_per_hr = 54_000
770
766
  end
771
767
  end
772
768
  when 'NECB 2011'
773
- minimum_capacity_btu_per_hr = 68243 # NECB requires economizer for cooling cap > 20 kW
769
+ minimum_capacity_btu_per_hr = 68_243 # NECB requires economizer for cooling cap > 20 kW
774
770
  end
775
771
 
776
772
  # Check whether the system requires an economizer by comparing
777
773
  # the system capacity to the minimum capacity.
778
- total_cooling_capacity_w = self.total_cooling_capacity
779
- total_cooling_capacity_btu_per_hr = OpenStudio.convert(total_cooling_capacity_w, "W", "Btu/hr").get
774
+ total_cooling_capacity_w = total_cooling_capacity
775
+ total_cooling_capacity_btu_per_hr = OpenStudio.convert(total_cooling_capacity_w, 'W', 'Btu/hr').get
780
776
  if total_cooling_capacity_btu_per_hr >= minimum_capacity_btu_per_hr
781
777
  if is_dc
782
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.")
778
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.")
783
779
  else
784
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.")
780
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.")
785
781
  end
786
782
  economizer_required = true
787
783
  else
788
784
  if is_dc
789
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.")
785
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.")
790
786
  else
791
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.")
787
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.")
792
788
  end
793
789
  end
794
790
 
795
791
  return economizer_required
796
-
797
792
  end
798
793
 
799
794
  # Set the economizer limits per the standard. Limits are based on the economizer
800
795
  # type currently specified in the ControllerOutdoorAir object on this air loop.
801
796
  #
802
- # @param (see #is_economizer_required)
797
+ # @param (see #economizer_required?)
803
798
  # @return [Bool] returns true if successful, false if not
804
- def set_economizer_limits(template, climate_zone)
805
-
799
+ def apply_economizer_limits(template, climate_zone)
806
800
  # EnergyPlus economizer types
807
801
  # 'NoEconomizer'
808
802
  # 'FixedDryBulb'
@@ -814,7 +808,7 @@ class OpenStudio::Model::AirLoopHVAC
814
808
  # 'DifferentialDryBulbAndEnthalpy'
815
809
 
816
810
  # Get the OA system and OA controller
817
- oa_sys = self.airLoopHVACOutdoorAirSystem
811
+ oa_sys = airLoopHVACOutdoorAirSystem
818
812
  if oa_sys.is_initialized
819
813
  oa_sys = oa_sys.get
820
814
  else
@@ -896,19 +890,25 @@ class OpenStudio::Model::AirLoopHVAC
896
890
  end
897
891
  end
898
892
 
893
+ # Reset the limits
894
+ oa_control.resetEconomizerMaximumLimitDryBulbTemperature
895
+ oa_control.resetEconomizerMaximumLimitEnthalpy
896
+ oa_control.resetEconomizerMaximumLimitDewpointTemperature
897
+ oa_control.resetEconomizerMinimumLimitDryBulbTemperature
898
+
899
899
  # Set the limits
900
900
  case economizer_type
901
901
  when 'FixedDryBulb'
902
902
  if drybulb_limit_f
903
903
  drybulb_limit_c = OpenStudio.convert(drybulb_limit_f, 'F', 'C').get
904
904
  oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
905
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F")
905
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F")
906
906
  end
907
907
  when 'FixedEnthalpy'
908
908
  if enthalpy_limit_btu_per_lb
909
909
  enthalpy_limit_j_per_kg = OpenStudio.convert(enthalpy_limit_btu_per_lb, 'Btu/lb', 'J/kg').get
910
910
  oa_control.setEconomizerMaximumLimitEnthalpy(enthalpy_limit_j_per_kg)
911
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, enthalpy limit = #{enthalpy_limit_btu_per_lb}Btu/lb")
911
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer type = #{economizer_type}, enthalpy limit = #{enthalpy_limit_btu_per_lb}Btu/lb")
912
912
  end
913
913
  when 'FixedDewPointAndDryBulb'
914
914
  if drybulb_limit_f && dewpoint_limit_f
@@ -916,27 +916,25 @@ class OpenStudio::Model::AirLoopHVAC
916
916
  dewpoint_limit_c = OpenStudio.convert(dewpoint_limit_f, 'F', 'C').get
917
917
  oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
918
918
  oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
919
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
919
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
920
920
  end
921
921
  end
922
922
 
923
923
  return true
924
-
925
924
  end
926
925
 
927
926
  # For systems required to have an economizer, set the economizer
928
927
  # to integrated on non-integrated per the standard.
929
928
  #
930
929
  # @note this method assumes you previously checked that an economizer is required at all
931
- # via #is_economizer_required
932
- # @param (see #is_economizer_required)
930
+ # via #economizer_required?
931
+ # @param (see #economizer_required?)
933
932
  # @return [Bool] returns true if successful, false if not
934
- def set_economizer_integration(template, climate_zone)
935
-
933
+ def apply_economizer_integration(template, climate_zone)
936
934
  # Determine if the system is a VAV system based on the fan
937
935
  # which may be inside of a unitary system.
938
936
  is_vav = false
939
- self.supplyComponents.reverse.each do |comp|
937
+ supplyComponents.reverse.each do |comp|
940
938
  if comp.to_FanVariableVolume.is_initialized
941
939
  is_vav = true
942
940
  elsif comp.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.is_initialized
@@ -955,27 +953,27 @@ class OpenStudio::Model::AirLoopHVAC
955
953
  end
956
954
 
957
955
  # Determine the number of zones the system serves
958
- num_zones_served = self.thermalZones.size
956
+ num_zones_served = thermalZones.size
959
957
 
960
958
  # A big number of btu per hr as the minimum requirement
961
- infinity_btu_per_hr = 999999999999
959
+ infinity_btu_per_hr = 999_999_999_999
962
960
  minimum_capacity_btu_per_hr = infinity_btu_per_hr
963
961
 
964
962
  # Determine if an integrated economizer is required
965
963
  integrated_economizer_required = true
966
964
  case template
967
965
  when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
968
- minimum_capacity_btu_per_hr = 65000
969
- minimum_capacity_w = OpenStudio.convert(minimum_capacity_btu_per_hr, "Btu/hr", "W").get
966
+ minimum_capacity_btu_per_hr = 65_000
967
+ minimum_capacity_w = OpenStudio.convert(minimum_capacity_btu_per_hr, 'Btu/hr', 'W').get
970
968
  # 6.5.1.3 Integrated Economizer Control
971
969
  # Exception a, DX VAV systems
972
970
  if is_vav == true && num_zones_served > 1
973
971
  integrated_economizer_required = false
974
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: non-integrated economizer per 6.5.1.3 exception a, DX VAV system.")
972
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: non-integrated economizer per 6.5.1.3 exception a, DX VAV system.")
975
973
  # Exception b, DX units less than 65,000 Btu/hr
976
- elsif self.total_cooling_capacity < minimum_capacity_w
974
+ elsif total_cooling_capacity < minimum_capacity_w
977
975
  integrated_economizer_required = false
978
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: non-integrated economizer per 6.5.1.3 exception b, DX system less than #{minimum_capacity_btu_per_hr}Btu/hr.")
976
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: non-integrated economizer per 6.5.1.3 exception b, DX system less than #{minimum_capacity_btu_per_hr}Btu/hr.")
979
977
  else
980
978
  # Exception c, Systems in climate zones 1,2,3a,4a,5a,5b,6,7,8
981
979
  case climate_zone
@@ -994,7 +992,7 @@ class OpenStudio::Model::AirLoopHVAC
994
992
  'ASHRAE 169-2006-8A',
995
993
  'ASHRAE 169-2006-8B'
996
994
  integrated_economizer_required = false
997
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: non-integrated economizer per 6.5.1.3 exception c, climate zone #{climate_zone}.")
995
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: non-integrated economizer per 6.5.1.3 exception c, climate zone #{climate_zone}.")
998
996
  when 'ASHRAE 169-2006-3B',
999
997
  'ASHRAE 169-2006-3C',
1000
998
  'ASHRAE 169-2006-4B',
@@ -1012,7 +1010,7 @@ class OpenStudio::Model::AirLoopHVAC
1012
1010
  end
1013
1011
 
1014
1012
  # Get the OA system and OA controller
1015
- oa_sys = self.airLoopHVACOutdoorAirSystem
1013
+ oa_sys = airLoopHVACOutdoorAirSystem
1016
1014
  if oa_sys.is_initialized
1017
1015
  oa_sys = oa_sys.get
1018
1016
  else
@@ -1028,19 +1026,17 @@ class OpenStudio::Model::AirLoopHVAC
1028
1026
  end
1029
1027
 
1030
1028
  return true
1031
-
1032
1029
  end
1033
1030
 
1034
1031
  # Determine if an economizer is required per the PRM.
1035
1032
  #
1036
- # @param (see #is_economizer_required)
1033
+ # @param (see #economizer_required?)
1037
1034
  # @return [Bool] returns true if required, false if not
1038
- def is_performance_rating_method_baseline_economizer_required(template, climate_zone)
1039
-
1035
+ def prm_baseline_economizer_required?(template, climate_zone)
1040
1036
  economizer_required = false
1041
1037
 
1042
1038
  # A big number of ft2 as the minimum requirement
1043
- infinity_ft2 = 999999999999
1039
+ infinity_ft2 = 999_999_999_999
1044
1040
  min_int_area_served_ft2 = infinity_ft2
1045
1041
  min_ext_area_served_ft2 = infinity_ft2
1046
1042
 
@@ -1062,7 +1058,7 @@ class OpenStudio::Model::AirLoopHVAC
1062
1058
  'ASHRAE 169-2006-7B',
1063
1059
  'ASHRAE 169-2006-8A',
1064
1060
  'ASHRAE 169-2006-8B'
1065
- min_int_area_served_ft2 = 15000
1061
+ min_int_area_served_ft2 = 15_000
1066
1062
  min_ext_area_served_ft2 = infinity_ft2 # No requirement
1067
1063
  when 'ASHRAE 169-2006-3B',
1068
1064
  'ASHRAE 169-2006-3C',
@@ -1071,8 +1067,8 @@ class OpenStudio::Model::AirLoopHVAC
1071
1067
  'ASHRAE 169-2006-5B',
1072
1068
  'ASHRAE 169-2006-5C',
1073
1069
  'ASHRAE 169-2006-6B'
1074
- min_int_area_served_ft2 = 10000
1075
- min_ext_area_served_ft2 = 25000
1070
+ min_int_area_served_ft2 = 10_000
1071
+ min_ext_area_served_ft2 = 25_000
1076
1072
  end
1077
1073
  when '90.1-2007', '90.1-2010', '90.1-2013'
1078
1074
  case climate_zone
@@ -1091,37 +1087,35 @@ class OpenStudio::Model::AirLoopHVAC
1091
1087
 
1092
1088
  # Check whether the system requires an economizer by comparing
1093
1089
  # the system capacity to the minimum capacity.
1094
- min_int_area_served_m2 = OpenStudio.convert(min_int_area_served_ft2, "ft^2", "m^2").get
1095
- min_ext_area_served_m2 = OpenStudio.convert(min_ext_area_served_ft2, "ft^2", "m^2").get
1090
+ min_int_area_served_m2 = OpenStudio.convert(min_int_area_served_ft2, 'ft^2', 'm^2').get
1091
+ min_ext_area_served_m2 = OpenStudio.convert(min_ext_area_served_ft2, 'ft^2', 'm^2').get
1096
1092
 
1097
1093
  # Get the interior and exterior area served
1098
- int_area_served_m2 = self.floor_area_served_interior_zones
1099
- ext_area_served_m2 = self.floor_area_served_exterior_zones
1094
+ int_area_served_m2 = floor_area_served_interior_zones
1095
+ ext_area_served_m2 = floor_area_served_exterior_zones
1100
1096
 
1101
1097
  # Check the floor area exception
1102
1098
  if int_area_served_m2 < min_int_area_served_m2 && ext_area_served_m2 < min_ext_area_served_m2
1103
1099
  if min_int_area_served_ft2 == infinity_ft2 && min_ext_area_served_ft2 == infinity_ft2
1104
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer not required for climate zone #{climate_zone}.")
1100
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer not required for climate zone #{climate_zone}.")
1105
1101
  else
1106
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer not required for because the interior area served of #{int_area_served_m2} ft2 < minimum of #{min_int_area_served_m2} and the perimeter area served of #{ext_area_served_m2} ft2 < minimum of #{min_ext_area_served_m2} for climate zone #{climate_zone}.")
1102
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer not required for because the interior area served of #{int_area_served_m2} ft2 < minimum of #{min_int_area_served_m2} and the perimeter area served of #{ext_area_served_m2} ft2 < minimum of #{min_ext_area_served_m2} for climate zone #{climate_zone}.")
1107
1103
  end
1108
1104
  return economizer_required
1109
1105
  end
1110
1106
 
1111
1107
  # If here, economizer required
1112
1108
  economizer_required = true
1113
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer required for the performance rating method baseline.")
1109
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer required for the performance rating method baseline.")
1114
1110
 
1115
1111
  return economizer_required
1116
-
1117
1112
  end
1118
1113
 
1119
1114
  # Apply the PRM economizer type and set temperature limits
1120
1115
  #
1121
- # @param (see #is_economizer_required)
1116
+ # @param (see #economizer_required?)
1122
1117
  # @return [Bool] returns true if successful, false if not
1123
- def apply_performance_rating_method_baseline_economizer(template, climate_zone)
1124
-
1118
+ def apply_prm_baseline_economizer(template, climate_zone)
1125
1119
  # EnergyPlus economizer types
1126
1120
  # 'NoEconomizer'
1127
1121
  # 'FixedDryBulb'
@@ -1133,7 +1127,7 @@ class OpenStudio::Model::AirLoopHVAC
1133
1127
  # 'DifferentialDryBulbAndEnthalpy'
1134
1128
 
1135
1129
  # Determine the type and limits
1136
- economizer_type = nil
1130
+ economizer_type = 'NoEconomizer'
1137
1131
  drybulb_limit_f = nil
1138
1132
  enthalpy_limit_btu_per_lb = nil
1139
1133
  dewpoint_limit_f = nil
@@ -1163,7 +1157,7 @@ class OpenStudio::Model::AirLoopHVAC
1163
1157
  economizer_type = 'FixedDryBulb'
1164
1158
  drybulb_limit_f = 65
1165
1159
  end
1166
- when '90.1-2013'
1160
+ when '90.1-2013'
1167
1161
  case climate_zone
1168
1162
  when 'ASHRAE 169-2006-1B',
1169
1163
  'ASHRAE 169-2006-2B',
@@ -1197,7 +1191,7 @@ class OpenStudio::Model::AirLoopHVAC
1197
1191
  end
1198
1192
 
1199
1193
  # Get the OA system and OA controller
1200
- oa_sys = self.airLoopHVACOutdoorAirSystem
1194
+ oa_sys = airLoopHVACOutdoorAirSystem
1201
1195
  if oa_sys.is_initialized
1202
1196
  oa_sys = oa_sys.get
1203
1197
  else
@@ -1205,19 +1199,28 @@ class OpenStudio::Model::AirLoopHVAC
1205
1199
  end
1206
1200
  oa_control = oa_sys.getControllerOutdoorAir
1207
1201
 
1202
+ # Set the economizer type
1203
+ oa_control.setEconomizerControlType(economizer_type)
1204
+
1205
+ # Reset the limits
1206
+ oa_control.resetEconomizerMaximumLimitDryBulbTemperature
1207
+ oa_control.resetEconomizerMaximumLimitEnthalpy
1208
+ oa_control.resetEconomizerMaximumLimitDewpointTemperature
1209
+ oa_control.resetEconomizerMinimumLimitDryBulbTemperature
1210
+
1208
1211
  # Set the limits
1209
1212
  case economizer_type
1210
1213
  when 'FixedDryBulb'
1211
1214
  if drybulb_limit_f
1212
1215
  drybulb_limit_c = OpenStudio.convert(drybulb_limit_f, 'F', 'C').get
1213
1216
  oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
1214
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F")
1217
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F")
1215
1218
  end
1216
1219
  when 'FixedEnthalpy'
1217
1220
  if enthalpy_limit_btu_per_lb
1218
1221
  enthalpy_limit_j_per_kg = OpenStudio.convert(enthalpy_limit_btu_per_lb, 'Btu/lb', 'J/kg').get
1219
1222
  oa_control.setEconomizerMaximumLimitEnthalpy(enthalpy_limit_j_per_kg)
1220
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, enthalpy limit = #{enthalpy_limit_btu_per_lb}Btu/lb")
1223
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer type = #{economizer_type}, enthalpy limit = #{enthalpy_limit_btu_per_lb}Btu/lb")
1221
1224
  end
1222
1225
  when 'FixedDewPointAndDryBulb'
1223
1226
  if drybulb_limit_f && dewpoint_limit_f
@@ -1225,22 +1228,20 @@ class OpenStudio::Model::AirLoopHVAC
1225
1228
  dewpoint_limit_c = OpenStudio.convert(dewpoint_limit_f, 'F', 'C').get
1226
1229
  oa_control.setEconomizerMaximumLimitDryBulbTemperature(drybulb_limit_c)
1227
1230
  oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
1228
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
1231
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
1229
1232
  end
1230
1233
  end
1231
1234
 
1232
1235
  return true
1233
-
1234
1236
  end
1235
1237
 
1236
1238
  # Check the economizer type currently specified in the ControllerOutdoorAir object on this air loop
1237
1239
  # is acceptable per the standard.
1238
1240
  #
1239
- # @param (see #is_economizer_required)
1241
+ # @param (see #economizer_required?)
1240
1242
  # @return [Bool] Returns true if allowable, if the system has no economizer or no OA system.
1241
1243
  # Returns false if the economizer type is not allowable.
1242
- def is_economizer_type_allowable(template, climate_zone)
1243
-
1244
+ def economizer_type_allowable?(template, climate_zone)
1244
1245
  # EnergyPlus economizer types
1245
1246
  # 'NoEconomizer'
1246
1247
  # 'FixedDryBulb'
@@ -1252,7 +1253,7 @@ class OpenStudio::Model::AirLoopHVAC
1252
1253
  # 'DifferentialDryBulbAndEnthalpy'
1253
1254
 
1254
1255
  # Get the OA system and OA controller
1255
- oa_sys = self.airLoopHVACOutdoorAirSystem
1256
+ oa_sys = airLoopHVACOutdoorAirSystem
1256
1257
  if oa_sys.is_initialized
1257
1258
  oa_sys = oa_sys.get
1258
1259
  else
@@ -1295,7 +1296,7 @@ class OpenStudio::Model::AirLoopHVAC
1295
1296
  'ASHRAE 169-2006-6A',
1296
1297
  prohibited_types = []
1297
1298
  end
1298
- when '90.1-2010', '90.1-2013'
1299
+ when '90.1-2010', '90.1-2013'
1299
1300
  case climate_zone
1300
1301
  when 'ASHRAE 169-2006-1B',
1301
1302
  'ASHRAE 169-2006-2B',
@@ -1330,15 +1331,14 @@ class OpenStudio::Model::AirLoopHVAC
1330
1331
  end
1331
1332
 
1332
1333
  return economizer_type_allowed
1333
-
1334
1334
  end
1335
1335
 
1336
1336
  # Check if ERV is required on this airloop.
1337
1337
  #
1338
- # @param (see #is_economizer_required)
1338
+ # @param (see #economizer_required?)
1339
1339
  # @return [Bool] Returns true if required, false if not.
1340
1340
  # @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
1341
- def is_energy_recovery_ventilator_required(template, climate_zone)
1341
+ def energy_recovery_ventilator_required?(template, climate_zone)
1342
1342
  # ERV Not Applicable for AHUs that serve
1343
1343
  # parking garage, warehouse, or multifamily
1344
1344
  # if space_types_served_names.include?('PNNL_Asset_Rating_Apartment_Space_Type') ||
@@ -1351,28 +1351,28 @@ class OpenStudio::Model::AirLoopHVAC
1351
1351
 
1352
1352
  erv_required = nil
1353
1353
  # ERV not applicable for medical AHUs (AHU1 in Outpatient), per AIA 2001 - 7.31.D2.
1354
- if self.name.to_s.include? "Outpatient F1"
1354
+ if name.to_s.include? 'Outpatient F1'
1355
1355
  erv_required = false
1356
1356
  return erv_required
1357
1357
  end
1358
1358
 
1359
1359
  # ERV not applicable for medical AHUs, per AIA 2001 - 7.31.D2.
1360
- if self.name.to_s.include? "VAV_ER"
1360
+ if name.to_s.include? 'VAV_ER'
1361
1361
  erv_required = false
1362
1362
  return erv_required
1363
- elsif self.name.to_s.include? "VAV_OR"
1363
+ elsif name.to_s.include? 'VAV_OR'
1364
1364
  erv_required = false
1365
1365
  return erv_required
1366
1366
  end
1367
1367
  case template
1368
- when '90.1-2004', '90.1-2007'
1369
- if self.name.to_s.include? "VAV_ICU"
1370
- erv_required = false
1371
- return erv_required
1372
- elsif self.name.to_s.include? "VAV_PATRMS"
1373
- erv_required = false
1374
- return erv_required
1375
- end
1368
+ when '90.1-2004', '90.1-2007'
1369
+ if name.to_s.include? 'VAV_ICU'
1370
+ erv_required = false
1371
+ return erv_required
1372
+ elsif name.to_s.include? 'VAV_PATRMS'
1373
+ erv_required = false
1374
+ return erv_required
1375
+ end
1376
1376
  end
1377
1377
 
1378
1378
  # ERV Not Applicable for AHUs that have DCV
@@ -1380,28 +1380,27 @@ class OpenStudio::Model::AirLoopHVAC
1380
1380
  controller_oa = nil
1381
1381
  controller_mv = nil
1382
1382
  oa_system = nil
1383
- if self.airLoopHVACOutdoorAirSystem.is_initialized
1384
- oa_system = self.airLoopHVACOutdoorAirSystem.get
1383
+ if airLoopHVACOutdoorAirSystem.is_initialized
1384
+ oa_system = airLoopHVACOutdoorAirSystem.get
1385
1385
  controller_oa = oa_system.getControllerOutdoorAir
1386
1386
  controller_mv = controller_oa.controllerMechanicalVentilation
1387
1387
  if controller_mv.demandControlledVentilation == true
1388
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because DCV enabled.")
1388
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV not applicable because DCV enabled.")
1389
1389
  return false
1390
1390
  end
1391
1391
  else
1392
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because it has no OA intake.")
1392
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV not applicable because it has no OA intake.")
1393
1393
  return false
1394
1394
  end
1395
1395
 
1396
-
1397
1396
  # Get the AHU design supply air flow rate
1398
1397
  dsn_flow_m3_per_s = nil
1399
- if self.designSupplyAirFlowRate.is_initialized
1400
- dsn_flow_m3_per_s = self.designSupplyAirFlowRate.get
1401
- elsif self.autosizedDesignSupplyAirFlowRate.is_initialized
1402
- dsn_flow_m3_per_s = self.autosizedDesignSupplyAirFlowRate.get
1398
+ if designSupplyAirFlowRate.is_initialized
1399
+ dsn_flow_m3_per_s = designSupplyAirFlowRate.get
1400
+ elsif autosizedDesignSupplyAirFlowRate.is_initialized
1401
+ dsn_flow_m3_per_s = autosizedDesignSupplyAirFlowRate.get
1403
1402
  else
1404
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name} design supply air flow rate is not available, cannot apply efficiency standard.")
1403
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} design supply air flow rate is not available, cannot apply efficiency standard.")
1405
1404
  return false
1406
1405
  end
1407
1406
  dsn_flow_cfm = OpenStudio.convert(dsn_flow_m3_per_s, 'm^3/s', 'cfm').get
@@ -1413,23 +1412,24 @@ class OpenStudio::Model::AirLoopHVAC
1413
1412
  elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
1414
1413
  min_oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
1415
1414
  else
1416
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.")
1415
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.")
1417
1416
  return false
1418
1417
  end
1419
1418
  min_oa_flow_cfm = OpenStudio.convert(min_oa_flow_m3_per_s, 'm^3/s', 'cfm').get
1420
1419
 
1421
1420
  # Calculate the percent OA at design airflow
1422
- pct_oa = min_oa_flow_m3_per_s/dsn_flow_m3_per_s
1421
+ pct_oa = min_oa_flow_m3_per_s / dsn_flow_m3_per_s
1423
1422
 
1424
1423
  case template
1425
1424
  when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
1426
1425
  erv_cfm = nil # Not required
1427
1426
  when '90.1-2004', '90.1-2007'
1428
- if pct_oa < 0.7
1429
- erv_cfm = nil
1430
- else
1431
- erv_cfm = 5000
1432
- end
1427
+ erv_cfm = if pct_oa < 0.7
1428
+ nil
1429
+ else
1430
+ # @Todo: Add exceptions (eg: e. cooling systems in climate zones 3C, 4C, 5B, 5C, 6B, 7 and 8 | d. Heating systems in climate zones 1 to 3)
1431
+ 5000
1432
+ end
1433
1433
  when '90.1-2010'
1434
1434
  # Table 6.5.6.1
1435
1435
  case climate_zone
@@ -1457,9 +1457,9 @@ class OpenStudio::Model::AirLoopHVAC
1457
1457
  elsif pct_oa >= 0.4 && pct_oa < 0.5
1458
1458
  erv_cfm = nil
1459
1459
  elsif pct_oa >= 0.5 && pct_oa < 0.6
1460
- erv_cfm = 26000
1460
+ erv_cfm = 26_000
1461
1461
  elsif pct_oa >= 0.6 && pct_oa < 0.7
1462
- erv_cfm = 12000
1462
+ erv_cfm = 12_000
1463
1463
  elsif pct_oa >= 0.7 && pct_oa < 0.8
1464
1464
  erv_cfm = 5000
1465
1465
  elsif pct_oa >= 0.8
@@ -1469,7 +1469,7 @@ class OpenStudio::Model::AirLoopHVAC
1469
1469
  if pct_oa < 0.3
1470
1470
  erv_cfm = nil
1471
1471
  elsif pct_oa >= 0.3 && pct_oa < 0.4
1472
- erv_cfm = 11000
1472
+ erv_cfm = 11_000
1473
1473
  elsif pct_oa >= 0.4 && pct_oa < 0.5
1474
1474
  erv_cfm = 5500
1475
1475
  elsif pct_oa >= 0.5 && pct_oa < 0.6
@@ -1515,49 +1515,168 @@ class OpenStudio::Model::AirLoopHVAC
1515
1515
  end
1516
1516
  end
1517
1517
  when '90.1-2013'
1518
- # Table 6.5.6.1-2
1519
- case climate_zone
1520
- when 'ASHRAE 169-2006-3C'
1521
- erv_cfm = nil
1522
- when 'ASHRAE 169-2006-1B', 'ASHRAE 169-2006-2B', 'ASHRAE 169-2006-3B', 'ASHRAE 169-2006-4C', 'ASHRAE 169-2006-5C'
1523
- if pct_oa < 0.1
1524
- erv_cfm = nil
1525
- elsif pct_oa >= 0.1 && pct_oa < 0.2
1526
- erv_cfm = nil
1527
- elsif pct_oa >= 0.2 && pct_oa < 0.3
1528
- erv_cfm = 19500
1529
- elsif pct_oa >= 0.3 && pct_oa < 0.4
1530
- erv_cfm = 9000
1531
- elsif pct_oa >= 0.4 && pct_oa < 0.5
1532
- erv_cfm = 5000
1533
- elsif pct_oa >= 0.5 && pct_oa < 0.6
1534
- erv_cfm = 4000
1535
- elsif pct_oa >= 0.6 && pct_oa < 0.7
1536
- erv_cfm = 3000
1537
- elsif pct_oa >= 0.7 && pct_oa < 0.8
1538
- erv_cfm = 1500
1539
- elsif pct_oa >= 0.8
1540
- erv_cfm = 0
1541
- end
1542
- when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4B', 'ASHRAE 169-2006-5B'
1543
- if pct_oa < 0.1
1544
- erv_cfm = nil
1545
- elsif pct_oa >= 0.1 && pct_oa < 0.2
1546
- erv_cfm = 2500
1547
- elsif pct_oa >= 0.2 && pct_oa < 0.3
1548
- erv_cfm = 2000
1549
- elsif pct_oa >= 0.3 && pct_oa < 0.4
1550
- erv_cfm = 1000
1551
- elsif pct_oa >= 0.4 && pct_oa < 0.5
1552
- erv_cfm = 500
1553
- elsif pct_oa >= 0.5
1554
- erv_cfm = 0
1518
+ # Calculate the number of system operating hours
1519
+ # based on the availability schedule.
1520
+ ann_op_hrs = 0.0
1521
+ avail_sch = availabilitySchedule
1522
+ if avail_sch == model.alwaysOnDiscreteSchedule
1523
+ ann_op_hrs = 8760.0
1524
+ elsif avail_sch.to_ScheduleRuleset.is_initialized
1525
+ avail_sch = avail_sch.to_ScheduleRuleset.get
1526
+ ann_op_hrs = avail_sch.annual_hours_above_value(0.0)
1527
+ else
1528
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}: could not determine annual operating hours. Assuming less than 8,000 for ERV determination.")
1529
+ end
1530
+
1531
+ if ann_op_hrs < 8000.0
1532
+ # Table 6.5.6.1-1, less than 8000 hrs
1533
+ case climate_zone
1534
+ when 'ASHRAE 169-2006-3B', 'ASHRAE 169-2006-3C', 'ASHRAE 169-2006-4B', 'ASHRAE 169-2006-4C', 'ASHRAE 169-2006-5B'
1535
+ if pct_oa < 0.1
1536
+ erv_cfm = nil
1537
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1538
+ erv_cfm = nil
1539
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1540
+ erv_cfm = nil
1541
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1542
+ erv_cfm = nil
1543
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1544
+ erv_cfm = nil
1545
+ elsif pct_oa >= 0.5 && pct_oa < 0.6
1546
+ erv_cfm = nil
1547
+ elsif pct_oa >= 0.6 && pct_oa < 0.7
1548
+ erv_cfm = nil
1549
+ elsif pct_oa >= 0.7 && pct_oa < 0.8
1550
+ erv_cfm = nil
1551
+ elsif pct_oa >= 0.8
1552
+ erv_cfm = nil
1553
+ end
1554
+ when 'ASHRAE 169-2006-1B', 'ASHRAE 169-2006-2B', 'ASHRAE 169-2006-5C'
1555
+ if pct_oa < 0.1
1556
+ erv_cfm = nil
1557
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1558
+ erv_cfm = nil
1559
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1560
+ erv_cfm = nil
1561
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1562
+ erv_cfm = nil
1563
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1564
+ erv_cfm = nil
1565
+ elsif pct_oa >= 0.5 && pct_oa < 0.6
1566
+ erv_cfm = 26_000
1567
+ elsif pct_oa >= 0.6 && pct_oa < 0.7
1568
+ erv_cfm = 12_000
1569
+ elsif pct_oa >= 0.7 && pct_oa < 0.8
1570
+ erv_cfm = 5000
1571
+ elsif pct_oa >= 0.8
1572
+ erv_cfm = 4000
1573
+ end
1574
+ when 'ASHRAE 169-2006-6B'
1575
+ if pct_oa < 0.1
1576
+ erv_cfm = nil
1577
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1578
+ erv_cfm = 28_000
1579
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1580
+ erv_cfm = 26_500
1581
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1582
+ erv_cfm = 11_000
1583
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1584
+ erv_cfm = 5500
1585
+ elsif pct_oa >= 0.5 && pct_oa < 0.6
1586
+ erv_cfm = 4500
1587
+ elsif pct_oa >= 0.6 && pct_oa < 0.7
1588
+ erv_cfm = 3500
1589
+ elsif pct_oa >= 0.7 && pct_oa < 0.8
1590
+ erv_cfm = 2500
1591
+ elsif pct_oa >= 0.8
1592
+ erv_cfm = 1500
1593
+ end
1594
+ when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4A', 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-6A'
1595
+ if pct_oa < 0.1
1596
+ erv_cfm = nil
1597
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1598
+ erv_cfm = 26_000
1599
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1600
+ erv_cfm = 16_000
1601
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1602
+ erv_cfm = 5500
1603
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1604
+ erv_cfm = 4500
1605
+ elsif pct_oa >= 0.5 && pct_oa < 0.6
1606
+ erv_cfm = 3500
1607
+ elsif pct_oa >= 0.6 && pct_oa < 0.7
1608
+ erv_cfm = 2000
1609
+ elsif pct_oa >= 0.7 && pct_oa < 0.8
1610
+ erv_cfm = 1000
1611
+ elsif pct_oa >= 0.8
1612
+ erv_cfm = 0
1613
+ end
1614
+ when 'ASHRAE 169-2006-7A', 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
1615
+ if pct_oa < 0.1
1616
+ erv_cfm = nil
1617
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1618
+ erv_cfm = 4500
1619
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1620
+ erv_cfm = 4000
1621
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1622
+ erv_cfm = 2500
1623
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1624
+ erv_cfm = 1000
1625
+ elsif pct_oa >= 0.5 && pct_oa < 0.6
1626
+ erv_cfm = 0
1627
+ elsif pct_oa >= 0.6 && pct_oa < 0.7
1628
+ erv_cfm = 0
1629
+ elsif pct_oa >= 0.7 && pct_oa < 0.8
1630
+ erv_cfm = 0
1631
+ elsif pct_oa >= 0.8
1632
+ erv_cfm = 0
1633
+ end
1555
1634
  end
1556
- when 'ASHRAE 169-2006-4A', 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-6A', 'ASHRAE 169-2006-6B', 'ASHRAE 169-2006-7A', 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
1557
- if pct_oa < 0.1
1635
+ else
1636
+ # Table 6.5.6.1-2, above 8000 hrs
1637
+ case climate_zone
1638
+ when 'ASHRAE 169-2006-3C'
1558
1639
  erv_cfm = nil
1559
- elsif pct_oa >= 0.1
1560
- erv_cfm = 0
1640
+ when 'ASHRAE 169-2006-1B', 'ASHRAE 169-2006-2B', 'ASHRAE 169-2006-3B', 'ASHRAE 169-2006-4C', 'ASHRAE 169-2006-5C'
1641
+ if pct_oa < 0.1
1642
+ erv_cfm = nil
1643
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1644
+ erv_cfm = nil
1645
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1646
+ erv_cfm = 19_500
1647
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1648
+ erv_cfm = 9000
1649
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1650
+ erv_cfm = 5000
1651
+ elsif pct_oa >= 0.5 && pct_oa < 0.6
1652
+ erv_cfm = 4000
1653
+ elsif pct_oa >= 0.6 && pct_oa < 0.7
1654
+ erv_cfm = 3000
1655
+ elsif pct_oa >= 0.7 && pct_oa < 0.8
1656
+ erv_cfm = 1500
1657
+ elsif pct_oa >= 0.8
1658
+ erv_cfm = 0
1659
+ end
1660
+ when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4B', 'ASHRAE 169-2006-5B'
1661
+ if pct_oa < 0.1
1662
+ erv_cfm = nil
1663
+ elsif pct_oa >= 0.1 && pct_oa < 0.2
1664
+ erv_cfm = 2500
1665
+ elsif pct_oa >= 0.2 && pct_oa < 0.3
1666
+ erv_cfm = 2000
1667
+ elsif pct_oa >= 0.3 && pct_oa < 0.4
1668
+ erv_cfm = 1000
1669
+ elsif pct_oa >= 0.4 && pct_oa < 0.5
1670
+ erv_cfm = 500
1671
+ elsif pct_oa >= 0.5
1672
+ erv_cfm = 0
1673
+ end
1674
+ when 'ASHRAE 169-2006-4A', 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-6A', 'ASHRAE 169-2006-6B', 'ASHRAE 169-2006-7A', 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
1675
+ if pct_oa < 0.1
1676
+ erv_cfm = nil
1677
+ elsif pct_oa >= 0.1
1678
+ erv_cfm = 0
1679
+ end
1561
1680
  end
1562
1681
  end
1563
1682
  when 'NECB 2011'
@@ -1570,13 +1689,13 @@ class OpenStudio::Model::AirLoopHVAC
1570
1689
  # Determine if an ERV is required
1571
1690
  # erv_required = nil
1572
1691
  if erv_cfm.nil?
1573
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}.")
1692
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV not required based on #{(pct_oa * 100).round}% OA flow, design supply air flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}.")
1574
1693
  erv_required = false
1575
1694
  elsif dsn_flow_cfm < erv_cfm
1576
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Does not exceed minimum flow requirement of #{erv_cfm}cfm.")
1695
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV not required based on #{(pct_oa * 100).round}% OA flow, design supply air flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Does not exceed minimum flow requirement of #{erv_cfm}cfm.")
1577
1696
  erv_required = false
1578
1697
  else
1579
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Exceeds minimum flow requirement of #{erv_cfm}cfm.")
1698
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV required based on #{(pct_oa * 100).round}% OA flow, design supply air flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Exceeds minimum flow requirement of #{erv_cfm}cfm.")
1580
1699
  erv_required = true
1581
1700
  end
1582
1701
 
@@ -1586,18 +1705,17 @@ class OpenStudio::Model::AirLoopHVAC
1586
1705
  if template == 'NECB 2011'
1587
1706
 
1588
1707
  # get all zones in the model
1589
- zones = self.thermalZones
1708
+ zones = thermalZones
1590
1709
 
1591
1710
  # initialize counters
1592
1711
  sum_zone_oa = 0.0
1593
- sum_zoneoaTimesheatDesignT = 0.0
1712
+ sum_zone_oa_times_heat_design_t = 0.0
1594
1713
 
1595
1714
  # zone loop
1596
1715
  zones.each do |zone|
1597
-
1598
1716
  # get design heat temperature for each zone; this is equivalent to design exhaust temperature
1599
1717
  zone_sizing = zone.sizingZone
1600
- heatDesignTemp = zone_sizing.zoneHeatingDesignSupplyAirTemperature
1718
+ heat_design_t = zone_sizing.zoneHeatingDesignSupplyAirTemperature
1601
1719
 
1602
1720
  # initialize counter
1603
1721
  zone_oa = 0.0
@@ -1606,89 +1724,82 @@ class OpenStudio::Model::AirLoopHVAC
1606
1724
 
1607
1725
  # space loop
1608
1726
  spaces.each do |space|
1609
- if not space.designSpecificationOutdoorAir.empty? # if empty, don't do anything
1727
+ unless space.designSpecificationOutdoorAir.empty? # if empty, don't do anything
1610
1728
  outdoor_air = space.designSpecificationOutdoorAir.get
1611
1729
 
1612
1730
  # in bTAP, outdoor air specified as outdoor air per person (m3/s/person)
1613
1731
  oa_flow_per_person = outdoor_air.outdoorAirFlowperPerson
1614
1732
  num_people = space.peoplePerFloorArea * space.floorArea
1615
- oa_flow = oa_flow_per_person * num_people # oa flow for the space
1616
- zone_oa = zone_oa + oa_flow # add up oa flow for all spaces to get zone air flow
1733
+ oa_flow = oa_flow_per_person * num_people # oa flow for the space
1734
+ zone_oa += oa_flow # add up oa flow for all spaces to get zone air flow
1617
1735
  end
1736
+ end # space loop
1618
1737
 
1619
- end # space loop
1620
-
1621
- sum_zone_oa = sum_zone_oa + zone_oa # sum of all zone oa flows to get system oa flow
1622
- sum_zoneoaTimesheatDesignT = sum_zoneoaTimesheatDesignT + (zone_oa * heatDesignTemp) # calculated to get oa flow weighted average of design exhaust temperature
1623
-
1624
- end # zone loop
1738
+ sum_zone_oa += zone_oa # sum of all zone oa flows to get system oa flow
1739
+ sum_zone_oa_times_heat_design_t += (zone_oa * heat_design_t) # calculated to get oa flow weighted average of design exhaust temperature
1740
+ end # zone loop
1625
1741
 
1626
1742
  # Calculate average exhaust temperature (oa flow weighted average)
1627
- avg_exhaust_temp = sum_zoneoaTimesheatDesignT / sum_zone_oa
1743
+ avg_exhaust_temp = sum_zone_oa_times_heat_design_t / sum_zone_oa
1628
1744
 
1629
1745
  # for debugging/testing
1630
- # puts "average exhaust temp = #{avg_exhaust_temp}"
1631
- # puts "sum_zone_oa = #{sum_zone_oa}"
1746
+ # puts "average exhaust temp = #{avg_exhaust_temp}"
1747
+ # puts "sum_zone_oa = #{sum_zone_oa}"
1632
1748
 
1633
1749
  # Get January winter design temperature
1634
1750
  # get model weather file name
1635
- weather_file = BTAP::Environment::WeatherFile.new(self.model.weatherFile.get.path.get)
1751
+ weather_file = BTAP::Environment::WeatherFile.new(model.weatherFile.get.path.get)
1636
1752
 
1637
1753
  # get winter(heating) design temp stored in array
1638
1754
  # Note that the NECB 2011 specifies using the 2.5% january design temperature
1639
1755
  # The outdoor temperature used here is the 0.4% heating design temperature of the coldest month, available in stat file
1640
1756
  outdoor_temp = weather_file.heating_design_info[1]
1641
1757
 
1642
- # for debugging/testing
1643
- # puts "outdoor design temp = #{outdoor_temp}"
1758
+ # for debugging/testing
1759
+ # puts "outdoor design temp = #{outdoor_temp}"
1644
1760
 
1645
1761
  # Calculate exhaust heat content
1646
1762
  exhaust_heat_content = 0.00123 * sum_zone_oa * 1000.0 * (avg_exhaust_temp - outdoor_temp)
1647
1763
 
1648
1764
  # for debugging/testing
1649
- # puts "exhaust heat content = #{exhaust_heat_content}"
1650
-
1765
+ # puts "exhaust heat content = #{exhaust_heat_content}"
1651
1766
 
1652
1767
  # Modify erv_required based on exhaust heat content
1653
- if ( exhaust_heat_content > 150.0 ) then
1768
+ if exhaust_heat_content > 150.0
1654
1769
  erv_required = true
1655
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on exhaust heat content.")
1770
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV required based on exhaust heat content.")
1656
1771
  else
1657
1772
  erv_required = false
1658
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on exhaust heat content.")
1773
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV not required based on exhaust heat content.")
1659
1774
  end
1660
1775
 
1661
-
1662
-
1663
- end # of NECB 2011 condition
1776
+ end # of NECB 2011 condition
1664
1777
 
1665
1778
  # for debugging/testing
1666
- # puts "erv_required = #{erv_required}"
1779
+ # puts "erv_required = #{erv_required}"
1667
1780
 
1668
1781
  return erv_required
1669
-
1670
1782
  end
1671
1783
 
1672
1784
  # Add an ERV to this airloop.
1673
1785
  # Will be a rotary-type HX
1674
1786
  #
1675
- # @param (see #is_economizer_required)
1787
+ # @param (see #economizer_required?)
1676
1788
  # @return [Bool] Returns true if required, false if not.
1677
1789
  # @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
1678
- def apply_energy_recovery_ventilator()
1679
-
1790
+ def apply_energy_recovery_ventilator
1680
1791
  # Get the oa system
1681
1792
  oa_system = nil
1682
- if self.airLoopHVACOutdoorAirSystem.is_initialized
1683
- oa_system = self.airLoopHVACOutdoorAirSystem.get
1793
+ if airLoopHVACOutdoorAirSystem.is_initialized
1794
+ oa_system = airLoopHVACOutdoorAirSystem.get
1684
1795
  else
1685
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV cannot be added because the system has no OA intake.")
1796
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, ERV cannot be added because the system has no OA intake.")
1686
1797
  return false
1687
1798
  end
1688
1799
 
1689
1800
  # Create an ERV
1690
- erv = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(self.model)
1691
- erv.setName("#{self.name} ERV")
1801
+ erv = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model)
1802
+ erv.setName("#{name} ERV")
1692
1803
  erv.setSensibleEffectivenessat100HeatingAirFlow(0.7)
1693
1804
  erv.setLatentEffectivenessat100HeatingAirFlow(0.6)
1694
1805
  erv.setSensibleEffectivenessat75HeatingAirFlow(0.7)
@@ -1733,21 +1844,19 @@ class OpenStudio::Model::AirLoopHVAC
1733
1844
  spm_oa_pretreat.addToNode(erv_outlet)
1734
1845
 
1735
1846
  # Apply the prototype Heat Exchanger power assumptions.
1736
- erv.setPrototypeNominalElectricPower
1847
+ erv.apply_prototype_nominal_electric_power
1737
1848
 
1738
1849
  return true
1739
-
1740
1850
  end
1741
1851
 
1742
1852
  # Determine if multizone vav optimization is required.
1743
1853
  #
1744
- # @param (see #is_economizer_required)
1854
+ # @param (see #economizer_required?)
1745
1855
  # @return [Bool] Returns true if required, false if not.
1746
1856
  # @todo Add exception logic for
1747
1857
  # systems with AIA healthcare ventilation requirements
1748
1858
  # dual duct systems
1749
- def is_multizone_vav_optimization_required(template, climate_zone)
1750
-
1859
+ def multizone_vav_optimization_required?(template, climate_zone)
1751
1860
  multizone_opt_required = false
1752
1861
 
1753
1862
  case template
@@ -1760,19 +1869,19 @@ class OpenStudio::Model::AirLoopHVAC
1760
1869
 
1761
1870
  # Not required for systems with fan-powered terminals
1762
1871
  num_fan_powered_terminals = 0
1763
- self.demandComponents.each do |comp|
1872
+ demandComponents.each do |comp|
1764
1873
  if comp.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized || comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
1765
1874
  num_fan_powered_terminals += 1
1766
1875
  end
1767
1876
  end
1768
1877
  if num_fan_powered_terminals > 0
1769
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, multizone vav optimization is not required because the system has #{num_fan_powered_terminals} fan-powered terminals.")
1878
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, multizone vav optimization is not required because the system has #{num_fan_powered_terminals} fan-powered terminals.")
1770
1879
  return multizone_opt_required
1771
1880
  end
1772
1881
 
1773
1882
  # Not required for systems that require an ERV
1774
- if self.has_energy_recovery
1775
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: multizone vav optimization is not required because the system has Energy Recovery.")
1883
+ if energy_recovery?
1884
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: multizone vav optimization is not required because the system has Energy Recovery.")
1776
1885
  return multizone_opt_required
1777
1886
  end
1778
1887
 
@@ -1780,23 +1889,23 @@ class OpenStudio::Model::AirLoopHVAC
1780
1889
  controller_oa = nil
1781
1890
  controller_mv = nil
1782
1891
  oa_system = nil
1783
- if self.airLoopHVACOutdoorAirSystem.is_initialized
1784
- oa_system = self.airLoopHVACOutdoorAirSystem.get
1892
+ if airLoopHVACOutdoorAirSystem.is_initialized
1893
+ oa_system = airLoopHVACOutdoorAirSystem.get
1785
1894
  controller_oa = oa_system.getControllerOutdoorAir
1786
1895
  controller_mv = controller_oa.controllerMechanicalVentilation
1787
1896
  else
1788
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, multizone optimization is not applicable because system has no OA intake.")
1897
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, multizone optimization is not applicable because system has no OA intake.")
1789
1898
  return multizone_opt_required
1790
1899
  end
1791
1900
 
1792
1901
  # Get the AHU design supply air flow rate
1793
1902
  dsn_flow_m3_per_s = nil
1794
- if self.designSupplyAirFlowRate.is_initialized
1795
- dsn_flow_m3_per_s = self.designSupplyAirFlowRate.get
1796
- elsif self.autosizedDesignSupplyAirFlowRate.is_initialized
1797
- dsn_flow_m3_per_s = self.autosizedDesignSupplyAirFlowRate.get
1903
+ if designSupplyAirFlowRate.is_initialized
1904
+ dsn_flow_m3_per_s = designSupplyAirFlowRate.get
1905
+ elsif autosizedDesignSupplyAirFlowRate.is_initialized
1906
+ dsn_flow_m3_per_s = autosizedDesignSupplyAirFlowRate.get
1798
1907
  else
1799
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name} design supply air flow rate is not available, cannot apply efficiency standard.")
1908
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} design supply air flow rate is not available, cannot apply efficiency standard.")
1800
1909
  return multizone_opt_required
1801
1910
  end
1802
1911
  dsn_flow_cfm = OpenStudio.convert(dsn_flow_m3_per_s, 'm^3/s', 'cfm').get
@@ -1808,25 +1917,25 @@ class OpenStudio::Model::AirLoopHVAC
1808
1917
  elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
1809
1918
  min_oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
1810
1919
  else
1811
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.")
1920
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.")
1812
1921
  return multizone_opt_required
1813
1922
  end
1814
1923
  min_oa_flow_cfm = OpenStudio.convert(min_oa_flow_m3_per_s, 'm^3/s', 'cfm').get
1815
1924
 
1816
1925
  # Calculate the percent OA at design airflow
1817
- pct_oa = min_oa_flow_m3_per_s/dsn_flow_m3_per_s
1926
+ pct_oa = min_oa_flow_m3_per_s / dsn_flow_m3_per_s
1818
1927
 
1819
1928
  # Not required for systems where
1820
1929
  # exhaust is more than 70% of the total OA intake.
1821
1930
  if pct_oa > 0.7
1822
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: multizone optimization is not applicable because system is more than 70% OA.")
1931
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{controller_oa.name}: multizone optimization is not applicable because system is more than 70% OA.")
1823
1932
  return multizone_opt_required
1824
1933
  end
1825
1934
 
1826
- # TODO Not required for dual-duct systems
1935
+ # TODO: Not required for dual-duct systems
1827
1936
  # if self.isDualDuct
1828
- # OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: multizone optimization is not applicable because it is a dual duct system")
1829
- # return multizone_opt_required
1937
+ # OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: multizone optimization is not applicable because it is a dual duct system")
1938
+ # return multizone_opt_required
1830
1939
  # end
1831
1940
 
1832
1941
  # If here, multizone vav optimization is required
@@ -1835,7 +1944,6 @@ class OpenStudio::Model::AirLoopHVAC
1835
1944
  return multizone_opt_required
1836
1945
 
1837
1946
  end
1838
-
1839
1947
  end
1840
1948
 
1841
1949
  # Enable multizone vav optimization by changing the Outdoor Air Method
@@ -1843,19 +1951,17 @@ class OpenStudio::Model::AirLoopHVAC
1843
1951
  #
1844
1952
  # @return [Bool] Returns true if required, false if not.
1845
1953
  def enable_multizone_vav_optimization
1846
-
1847
1954
  # Enable multizone vav optimization
1848
1955
  # at each timestep.
1849
- if self.airLoopHVACOutdoorAirSystem.is_initialized
1850
- oa_system = self.airLoopHVACOutdoorAirSystem.get
1956
+ if airLoopHVACOutdoorAirSystem.is_initialized
1957
+ oa_system = airLoopHVACOutdoorAirSystem.get
1851
1958
  controller_oa = oa_system.getControllerOutdoorAir
1852
1959
  controller_mv = controller_oa.controllerMechanicalVentilation
1853
1960
  controller_mv.setSystemOutdoorAirMethod('VentilationRateProcedure')
1854
1961
  else
1855
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, cannot enable multizone vav optimization because the system has no OA intake.")
1962
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}, cannot enable multizone vav optimization because the system has no OA intake.")
1856
1963
  return false
1857
1964
  end
1858
-
1859
1965
  end
1860
1966
 
1861
1967
  # Disable multizone vav optimization by changing the Outdoor Air Method
@@ -1863,50 +1969,48 @@ class OpenStudio::Model::AirLoopHVAC
1863
1969
  #
1864
1970
  # @return [Bool] Returns true if required, false if not.
1865
1971
  def disable_multizone_vav_optimization
1866
-
1867
1972
  # Disable multizone vav optimization
1868
1973
  # at each timestep.
1869
- if self.airLoopHVACOutdoorAirSystem.is_initialized
1870
- oa_system = self.airLoopHVACOutdoorAirSystem.get
1974
+ if airLoopHVACOutdoorAirSystem.is_initialized
1975
+ oa_system = airLoopHVACOutdoorAirSystem.get
1871
1976
  controller_oa = oa_system.getControllerOutdoorAir
1872
1977
  controller_mv = controller_oa.controllerMechanicalVentilation
1873
1978
  controller_mv.setSystemOutdoorAirMethod('ZoneSum')
1874
1979
  else
1875
- OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, cannot disable multizone vav optimization because the system has no OA intake.")
1980
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}, cannot disable multizone vav optimization because the system has no OA intake.")
1876
1981
  return false
1877
1982
  end
1878
-
1879
1983
  end
1880
1984
 
1881
- # Set the minimum VAV damper positions
1882
- #
1985
+ # Set the minimum VAV damper positions.
1883
1986
  #
1884
- def set_minimum_vav_damper_positions(template)
1885
-
1886
- self.thermalZones.each do |zone|
1987
+ # @param template [String] the building template
1988
+ # @param has_ddc [Bool] if true, will assume that there
1989
+ # is DDC control of vav terminals. If false, assumes otherwise.
1990
+ # @return [Bool] true if successful, false if not
1991
+ def apply_minimum_vav_damper_positions(template, has_ddc = true)
1992
+ thermalZones.each do |zone|
1887
1993
  zone.equipment.each do |equip|
1888
1994
  if equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
1889
- zone_oa_per_area = zone.outdoor_airflow_rate_per_area
1995
+ zone_oa = zone.outdoor_airflow_rate
1890
1996
  vav_terminal = equip.to_AirTerminalSingleDuctVAVReheat.get
1891
- vav_terminal.set_minimum_damper_position(template, zone_oa_per_area)
1997
+ vav_terminal.apply_minimum_damper_position(template, zone_oa, has_ddc)
1892
1998
  end
1893
1999
  end
1894
2000
  end
1895
2001
 
1896
2002
  return true
1897
-
1898
2003
  end
1899
2004
 
1900
2005
  # Adjust minimum VAV damper positions to the values
1901
2006
  #
1902
- # @param (see #is_economizer_required)
2007
+ # @param (see #economizer_required?)
1903
2008
  # @return [Bool] Returns true if required, false if not.
1904
2009
  # @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
1905
2010
  def adjust_minimum_vav_damper_positions
1906
-
1907
2011
  # Total uncorrected outdoor airflow rate
1908
2012
  v_ou = 0.0
1909
- self.thermalZones.each do |zone|
2013
+ thermalZones.each do |zone|
1910
2014
  v_ou += zone.outdoor_airflow_rate
1911
2015
  end
1912
2016
 
@@ -1915,17 +2019,17 @@ class OpenStudio::Model::AirLoopHVAC
1915
2019
  # System primary airflow rate (whether autosized or hard-sized)
1916
2020
  v_ps = 0.0
1917
2021
 
1918
- if self.autosizedDesignSupplyAirFlowRate.is_initialized
1919
- v_ps = self.autosizedDesignSupplyAirFlowRate.get
1920
- else
1921
- v_ps = self.designSupplyAirFlowRate.get
1922
- end
2022
+ v_ps = if autosizedDesignSupplyAirFlowRate.is_initialized
2023
+ autosizedDesignSupplyAirFlowRate.get
2024
+ else
2025
+ designSupplyAirFlowRate.get
2026
+ end
1923
2027
  v_ps_cfm = OpenStudio.convert(v_ps, 'm^3/s', 'cfm').get
1924
2028
 
1925
2029
  # Average outdoor air fraction
1926
2030
  x_s = v_ou / v_ps
1927
2031
 
1928
- OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: v_ou = #{v_ou_cfm.round} cfm, v_ps = #{v_ps_cfm.round} cfm, x_s = #{x_s.round(2)}.")
2032
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{name}: v_ou = #{v_ou_cfm.round} cfm, v_ps = #{v_ps_cfm.round} cfm, x_s = #{x_s.round(2)}.")
1929
2033
 
1930
2034
  # Determine the zone ventilation effectiveness
1931
2035
  # for every zone on the system.
@@ -1934,8 +2038,7 @@ class OpenStudio::Model::AirLoopHVAC
1934
2038
  e_vzs = []
1935
2039
  e_vzs_adj = []
1936
2040
  num_zones_adj = 0
1937
- self.thermalZones.sort.each do |zone|
1938
-
2041
+ thermalZones.sort.each do |zone|
1939
2042
  # Breathing zone airflow rate
1940
2043
  v_bz = zone.outdoor_airflow_rate
1941
2044
 
@@ -1956,7 +2059,7 @@ class OpenStudio::Model::AirLoopHVAC
1956
2059
  v_pz = clg_dsn_flow
1957
2060
  end
1958
2061
  else
1959
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: #{zone.name} clg_dsn_flow could not be found.")
2062
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}: #{zone.name} clg_dsn_flow could not be found.")
1960
2063
  end
1961
2064
  htg_dsn_flow = zone.autosizedHeatingDesignAirFlowRate
1962
2065
  if htg_dsn_flow.is_initialized
@@ -1965,42 +2068,53 @@ class OpenStudio::Model::AirLoopHVAC
1965
2068
  v_pz = htg_dsn_flow
1966
2069
  end
1967
2070
  else
1968
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: #{zone.name} htg_dsn_flow could not be found.")
2071
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}: #{zone.name} htg_dsn_flow could not be found.")
1969
2072
  end
1970
2073
 
1971
2074
  # Get the minimum damper position
1972
- mdp = 1.0
2075
+ mdp_term = 1.0
2076
+ min_zn_flow = 0.0
1973
2077
  zone.equipment.each do |equip|
1974
2078
  if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
1975
2079
  term = equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.get
1976
- mdp = term.zoneMinimumAirFlowFraction
2080
+ mdp_term = term.zoneMinimumAirFlowFraction
1977
2081
  elsif equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized
1978
2082
  term = equip.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.get
1979
- mdp = term.zoneMinimumAirFlowFraction
2083
+ mdp_term = term.zoneMinimumAirFlowFraction
1980
2084
  elsif equip.to_AirTerminalSingleDuctVAVNoReheat.is_initialized
1981
2085
  term = equip.to_AirTerminalSingleDuctVAVNoReheat.get
1982
2086
  if term.constantMinimumAirFlowFraction.is_initialized
1983
- mdp = term.constantMinimumAirFlowFraction.get
2087
+ mdp_term = term.constantMinimumAirFlowFraction.get
1984
2088
  end
1985
2089
  elsif equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
1986
2090
  term = equip.to_AirTerminalSingleDuctVAVReheat.get
1987
- mdp = term.constantMinimumAirFlowFraction
2091
+ mdp_term = term.constantMinimumAirFlowFraction
2092
+ min_zn_flow = term.fixedMinimumAirFlowRate
1988
2093
  end
1989
2094
  end
1990
2095
 
2096
+ # For VAV Reheat terminals, min flow is greater of mdp
2097
+ # and min flow rate / design flow rate.
2098
+ mdp = mdp_term
2099
+ mdp_oa = min_zn_flow / v_ps
2100
+ if min_zn_flow > 0.0
2101
+ mdp = [mdp_term, mdp_oa].max.round(2)
2102
+ end
2103
+ # OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} mdp_term = #{mdp_term.round(2)}, mdp_oa = #{mdp_oa.round(2)}; mdp_final = #{mdp}")
2104
+
1991
2105
  # Zone minimum discharge airflow rate
1992
- v_dz = v_pz*mdp
2106
+ v_dz = v_pz * mdp
1993
2107
 
1994
2108
  # Zone discharge air fraction
1995
2109
  z_d = v_oz / v_dz
1996
2110
 
1997
2111
  # Zone ventilation effectiveness
1998
- e_vz = 1+x_s-z_d
2112
+ e_vz = 1 + x_s - z_d
1999
2113
 
2000
2114
  # Store the ventilation effectiveness
2001
2115
  e_vzs << e_vz
2002
2116
 
2003
- OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} v_oz = #{v_oz.round(2)} m^3/s, v_pz = #{v_pz.round(2)} m^3/s, v_dz = #{v_dz.round(2)}, z_d = #{z_d.round(2)}.")
2117
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{name}: Zone #{zone.name} v_oz = #{v_oz.round(2)} m^3/s, v_pz = #{v_pz.round(2)} m^3/s, v_dz = #{v_dz.round(2)}, z_d = #{z_d.round(2)}.")
2004
2118
 
2005
2119
  # Check the ventilation effectiveness against
2006
2120
  # the minimum limit per PNNL and increase
@@ -2008,7 +2122,7 @@ class OpenStudio::Model::AirLoopHVAC
2008
2122
  if e_vz < 0.6
2009
2123
 
2010
2124
  # Adjusted discharge air fraction
2011
- z_d_adj = 1+x_s-0.6
2125
+ z_d_adj = 1 + x_s - 0.6
2012
2126
 
2013
2127
  # Adjusted min discharge airflow rate
2014
2128
  v_dz_adj = v_oz / z_d_adj
@@ -2022,7 +2136,7 @@ class OpenStudio::Model::AirLoopHVAC
2022
2136
  end
2023
2137
 
2024
2138
  # Zone ventilation effectiveness
2025
- e_vz_adj = 1+x_s-z_d_adj
2139
+ e_vz_adj = 1 + x_s - z_d_adj
2026
2140
 
2027
2141
  # Store the ventilation effectiveness
2028
2142
  e_vzs_adj << e_vz_adj
@@ -2046,13 +2160,12 @@ class OpenStudio::Model::AirLoopHVAC
2046
2160
 
2047
2161
  num_zones_adj += 1
2048
2162
 
2049
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} has a ventilation effectiveness of #{e_vz.round(2)}. Increasing to #{e_vz_adj.round(2)} by increasing minimum damper position from #{mdp.round(2)} to #{mdp_adj.round(2)}.")
2163
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Zone #{zone.name} has a ventilation effectiveness of #{e_vz.round(2)}. Increasing to #{e_vz_adj.round(2)} by increasing minimum damper position from #{mdp.round(2)} to #{mdp_adj.round(2)}.")
2050
2164
 
2051
2165
  else
2052
2166
  # Store the unadjusted value
2053
2167
  e_vzs_adj << e_vz
2054
2168
  end
2055
-
2056
2169
  end
2057
2170
 
2058
2171
  # Min system zone ventilation effectiveness
@@ -2071,18 +2184,17 @@ class OpenStudio::Model::AirLoopHVAC
2071
2184
 
2072
2185
  # Report out the results of the multizone calculations
2073
2186
  if num_zones_adj > 0
2074
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: the multizone outdoor air calculation method was applied. A simple summation of the zone outdoor air requirements gives a value of #{v_ou_cfm.round} cfm. Applying the multizone method gives a value of #{v_ot_cfm.round} cfm, with an original system ventilation effectiveness of #{e_v.round(2)}. After increasing the minimum damper position in #{num_zones_adj} critical zones, the resulting requirement is #{v_ot_adj_cfm.round} cfm with a system ventilation effectiveness of #{e_v_adj.round(2)}.")
2187
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: the multizone outdoor air calculation method was applied. A simple summation of the zone outdoor air requirements gives a value of #{v_ou_cfm.round} cfm. Applying the multizone method gives a value of #{v_ot_cfm.round} cfm, with an original system ventilation effectiveness of #{e_v.round(2)}. After increasing the minimum damper position in #{num_zones_adj} critical zones, the resulting requirement is #{v_ot_adj_cfm.round} cfm with a system ventilation effectiveness of #{e_v_adj.round(2)}.")
2075
2188
  else
2076
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: the multizone outdoor air calculation method was applied. A simple summation of the zone requirements gives a value of #{v_ou_cfm.round} cfm. However, applying the multizone method requires #{v_ot_adj_cfm.round} cfm based on the ventilation effectiveness of the system.")
2189
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: the multizone outdoor air calculation method was applied. A simple summation of the zone requirements gives a value of #{v_ou_cfm.round} cfm. However, applying the multizone method requires #{v_ot_adj_cfm.round} cfm based on the ventilation effectiveness of the system.")
2077
2190
  end
2078
2191
 
2079
2192
  # Hard-size the sizing:system
2080
2193
  # object with the calculated min OA flow rate
2081
- sizing_system = self.sizingSystem
2194
+ sizing_system = sizingSystem
2082
2195
  sizing_system.setDesignOutdoorAirFlowRate(v_ot_adj)
2083
2196
 
2084
2197
  return true
2085
-
2086
2198
  end
2087
2199
 
2088
2200
  # For critical zones of Outpatient, if the minimum airflow rate required by the accreditation standard (AIA 2001) is significantly
@@ -2092,14 +2204,14 @@ class OpenStudio::Model::AirLoopHVAC
2092
2204
  # For implementation purpose, since it is time-consuming to perform autosizing in three climate zones, just use
2093
2205
  # the results of the current climate zone
2094
2206
  def adjust_minimum_vav_damper_positions_outpatient
2095
- self.model.getSpaces.each do |space|
2207
+ model.getSpaces.each do |space|
2096
2208
  zone = space.thermalZone.get
2097
- sizingZone = zone.sizingZone
2209
+ sizing_zone = zone.sizingZone
2098
2210
  space_area = space.floorArea
2099
- if sizingZone.coolingDesignAirFlowMethod == 'DesignDay'
2211
+ if sizing_zone.coolingDesignAirFlowMethod == 'DesignDay'
2100
2212
  next
2101
- elsif sizingZone.coolingDesignAirFlowMethod == 'DesignDayWithLimit'
2102
- minimum_airflow_per_zone_floor_area = sizingZone.coolingMinimumAirFlowperZoneFloorArea
2213
+ elsif sizing_zone.coolingDesignAirFlowMethod == 'DesignDayWithLimit'
2214
+ minimum_airflow_per_zone_floor_area = sizing_zone.coolingMinimumAirFlowperZoneFloorArea
2103
2215
  minimum_airflow_per_zone = minimum_airflow_per_zone_floor_area * space_area
2104
2216
  # get the autosized maximum air flow of the VAV terminal
2105
2217
  zone.equipment.each do |equip|
@@ -2123,79 +2235,44 @@ class OpenStudio::Model::AirLoopHVAC
2123
2235
  # Determine if demand control ventilation (DCV) is
2124
2236
  # required for this air loop.
2125
2237
  #
2126
- # @param (see #is_economizer_required)
2238
+ # @param (see #economizer_required?)
2127
2239
  # @return [Bool] Returns true if required, false if not.
2128
2240
  # @todo Add exception logic for
2129
2241
  # systems that serve multifamily, parking garage, warehouse
2130
- def is_demand_control_ventilation_required(template, climate_zone)
2131
-
2242
+ def demand_control_ventilation_required?(template, climate_zone)
2132
2243
  dcv_required = false
2133
2244
 
2134
2245
  # Not required by the old vintages
2135
2246
  if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004' || template == 'NECB 2011'
2136
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{template} #{climate_zone}: #{self.name}: DCV is not required for any system.")
2247
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{template} #{climate_zone}: #{name}: DCV is not required for any system.")
2137
2248
  return dcv_required
2138
2249
  end
2139
2250
 
2140
2251
  # Not required for systems that require an ERV
2141
- if self.has_energy_recovery
2142
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system has Energy Recovery.")
2252
+ if energy_recovery?
2253
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: DCV is not required since the system has Energy Recovery.")
2143
2254
  return dcv_required
2144
2255
  end
2145
2256
 
2146
- # Area, occupant density, and OA flow limits
2147
- min_area_ft2 = 0
2148
- min_occ_per_1000_ft2 = 0
2257
+ # OA flow limits
2149
2258
  min_oa_without_economizer_cfm = 0
2150
2259
  min_oa_with_economizer_cfm = 0
2151
2260
  case template
2152
2261
  when '90.1-2004'
2153
- min_area_ft2 = 0
2154
- min_occ_per_1000_ft2 = 100
2155
2262
  min_oa_without_economizer_cfm = 3000
2156
2263
  min_oa_with_economizer_cfm = 0
2157
2264
  when '90.1-2007', '90.1-2010'
2158
- min_area_ft2 = 500
2159
- min_occ_per_1000_ft2 = 40
2160
2265
  min_oa_without_economizer_cfm = 3000
2161
2266
  min_oa_with_economizer_cfm = 1200
2162
2267
  when '90.1-2013'
2163
- min_area_ft2 = 500
2164
- min_occ_per_1000_ft2 = 25
2165
2268
  min_oa_without_economizer_cfm = 3000
2166
2269
  min_oa_with_economizer_cfm = 750
2167
2270
  end
2168
2271
 
2169
- # Get the area served and the number of occupants
2170
- area_served_m2 = 0
2171
- num_people = 0
2172
- self.thermalZones.each do |zone|
2173
- zone.spaces.each do |space|
2174
- area_served_m2 += space.floorArea
2175
- num_people += space.numberOfPeople
2176
- end
2177
- end
2178
-
2179
- # Check the minimum area
2180
- area_served_ft2 = OpenStudio.convert(area_served_m2, 'm^2', 'ft^2').get
2181
- if area_served_ft2 < min_area_ft2
2182
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system serves #{area_served_ft2.round} ft2, but the minimum size is #{min_area_ft2.round} ft2.")
2183
- return dcv_required
2184
- end
2185
-
2186
- # Check the minimum occupancy density
2187
- occ_per_ft2 = num_people / area_served_ft2
2188
- occ_per_1000_ft2 = occ_per_ft2*1000
2189
-
2190
- if occ_per_1000_ft2 < min_occ_per_1000_ft2
2191
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system occupant density is #{occ_per_1000_ft2.round} people/1000 ft2, but the minimum occupant density is #{min_occ_per_1000_ft2.round} people/1000 ft2.")
2192
- return dcv_required
2193
- end
2194
-
2195
2272
  # Get the min OA flow rate
2196
2273
  oa_flow_m3_per_s = 0
2197
- if self.airLoopHVACOutdoorAirSystem.is_initialized
2198
- oa_system = self.airLoopHVACOutdoorAirSystem.get
2274
+ if airLoopHVACOutdoorAirSystem.is_initialized
2275
+ oa_system = airLoopHVACOutdoorAirSystem.get
2199
2276
  controller_oa = oa_system.getControllerOutdoorAir
2200
2277
  if controller_oa.minimumOutdoorAirFlowRate.is_initialized
2201
2278
  oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
@@ -2203,28 +2280,40 @@ class OpenStudio::Model::AirLoopHVAC
2203
2280
  oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
2204
2281
  end
2205
2282
  else
2206
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, DCV not applicable because it has no OA intake.")
2283
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, DCV not applicable because it has no OA intake.")
2207
2284
  return dcv_required
2208
2285
  end
2209
2286
  oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
2210
2287
 
2211
-
2212
2288
  # Check for min OA without an economizer OR has economizer
2213
- if oa_flow_cfm < min_oa_without_economizer_cfm && self.has_economizer == false
2289
+ if oa_flow_cfm < min_oa_without_economizer_cfm && economizer? == false
2214
2290
  # Message if doesn't pass OA limit
2215
2291
  if oa_flow_cfm < min_oa_without_economizer_cfm
2216
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_without_economizer_cfm.round} cfm.")
2292
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: DCV is not required since the system min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_without_economizer_cfm.round} cfm.")
2217
2293
  end
2218
2294
  # Message if doesn't have economizer
2219
- if self.has_economizer == false
2220
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system does not have an economizer.")
2295
+ if economizer? == false
2296
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: DCV is not required since the system does not have an economizer.")
2221
2297
  end
2222
2298
  return dcv_required
2223
2299
  end
2224
2300
 
2225
2301
  # If has economizer, cfm limit is lower
2226
- if oa_flow_cfm < min_oa_with_economizer_cfm && self.has_economizer
2227
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system has an economizer, but the min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_with_economizer_cfm.round} cfm for systems with an economizer.")
2302
+ if oa_flow_cfm < min_oa_with_economizer_cfm && economizer?
2303
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: DCV is not required since the system has an economizer, but the min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_with_economizer_cfm.round} cfm for systems with an economizer.")
2304
+ return dcv_required
2305
+ end
2306
+
2307
+ # Check area and density limits
2308
+ # for all of zones on the loop
2309
+ any_zones_req_dcv = false
2310
+ thermalZones.sort.each do |zone|
2311
+ if zone.demand_control_ventilation_required?(template, climate_zone)
2312
+ any_zones_req_dcv = true
2313
+ break
2314
+ end
2315
+ end
2316
+ unless any_zones_req_dcv
2228
2317
  return dcv_required
2229
2318
  end
2230
2319
 
@@ -2232,27 +2321,29 @@ class OpenStudio::Model::AirLoopHVAC
2232
2321
  dcv_required = true
2233
2322
 
2234
2323
  return dcv_required
2235
-
2236
2324
  end
2237
2325
 
2238
2326
  # Enable demand control ventilation (DCV) for this air loop.
2327
+ # Zones on this loop that require DCV preserve
2328
+ # both per-area and per-person OA reqs.
2329
+ # Other zones have OA reqs converted
2330
+ # to per-area values only so that DCV won't impact these zones.
2239
2331
  #
2240
2332
  # @return [Bool] Returns true if required, false if not.
2241
- def enable_demand_control_ventilation()
2242
-
2333
+ def enable_demand_control_ventilation(template, climate_zone)
2243
2334
  # Get the OA intake
2244
2335
  controller_oa = nil
2245
2336
  controller_mv = nil
2246
- if self.airLoopHVACOutdoorAirSystem.is_initialized
2247
- oa_system = self.airLoopHVACOutdoorAirSystem.get
2337
+ if airLoopHVACOutdoorAirSystem.is_initialized
2338
+ oa_system = airLoopHVACOutdoorAirSystem.get
2248
2339
  controller_oa = oa_system.getControllerOutdoorAir
2249
2340
  controller_mv = controller_oa.controllerMechanicalVentilation
2250
2341
  if controller_mv.demandControlledVentilation == true
2251
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV was already enabled.")
2342
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: DCV was already enabled.")
2252
2343
  return true
2253
2344
  end
2254
2345
  else
2255
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Could not enable DCV since the system has no OA intake.")
2346
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}: Could not enable DCV since the system has no OA intake.")
2256
2347
  return false
2257
2348
  end
2258
2349
 
@@ -2262,21 +2353,29 @@ class OpenStudio::Model::AirLoopHVAC
2262
2353
  # Enable DCV in the controller mechanical ventilation
2263
2354
  controller_mv.setDemandControlledVentilation(true)
2264
2355
 
2265
- return true
2356
+ # Zones that require DCV preserve
2357
+ # both per-area and per-person OA reqs.
2358
+ # Other zones have OA reqs converted
2359
+ # to per-area values only so that DCV
2360
+ thermalZones.sort.each do |zone|
2361
+ if zone.demand_control_ventilation_required?(template, climate_zone)
2362
+ zone.convert_oa_req_to_per_area
2363
+ end
2364
+ end
2266
2365
 
2366
+ return true
2267
2367
  end
2268
2368
 
2269
2369
  # Determine if the system required supply air temperature
2270
2370
  # (SAT) reset.
2271
2371
  #
2272
- # @param (see #is_economizer_required)
2372
+ # @param (see #economizer_required?)
2273
2373
  # @return [Bool] Returns true if required, false if not.
2274
- def is_supply_air_temperature_reset_required(template, climate_zone)
2275
-
2374
+ def supply_air_temperature_reset_required?(template, climate_zone)
2276
2375
  is_sat_reset_required = false
2277
2376
 
2278
2377
  # Only required for multizone VAV systems
2279
- return is_sat_reset_required unless self.is_multizone_vav_system
2378
+ return is_sat_reset_required unless multizone_vav_system?
2280
2379
 
2281
2380
  # Not required until 90.1-2010
2282
2381
  case template
@@ -2287,7 +2386,7 @@ class OpenStudio::Model::AirLoopHVAC
2287
2386
  when 'ASHRAE 169-2006-1A',
2288
2387
  'ASHRAE 169-2006-2A',
2289
2388
  'ASHRAE 169-2006-3A'
2290
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset is not required per 6.5.3.4 Exception 1, the system is located in climate zone #{climate_zone}.")
2389
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Supply air temperature reset is not required per 6.5.3.4 Exception 1, the system is located in climate zone #{climate_zone}.")
2291
2390
  when 'ASHRAE 169-2006-1B',
2292
2391
  'ASHRAE 169-2006-2B',
2293
2392
  'ASHRAE 169-2006-3B',
@@ -2305,11 +2404,10 @@ class OpenStudio::Model::AirLoopHVAC
2305
2404
  'ASHRAE 169-2006-8A',
2306
2405
  'ASHRAE 169-2006-8B'
2307
2406
  is_sat_reset_required = true
2308
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset is required.")
2407
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Supply air temperature reset is required.")
2309
2408
  return is_sat_reset_required
2310
2409
  end
2311
2410
  end
2312
-
2313
2411
  end
2314
2412
 
2315
2413
  # Enable supply air temperature (SAT) reset based
@@ -2318,20 +2416,18 @@ class OpenStudio::Model::AirLoopHVAC
2318
2416
  # @param template [String] valid choices: '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
2319
2417
  # @return [Bool] Returns true if successful, false if not.
2320
2418
  def enable_supply_air_temperature_reset_warmest_zone(template)
2321
-
2322
2419
  # Get the current setpoint and calculate
2323
2420
  # the new setpoint.
2324
- sizing_system = self.sizingSystem
2421
+ sizing_system = sizingSystem
2325
2422
  design_sat_c = sizing_system.centralCoolingDesignSupplyAirTemperature
2326
- design_sat_f = OpenStudio::convert(design_sat_c, 'C','F').get
2327
-
2423
+ design_sat_f = OpenStudio.convert(design_sat_c, 'C', 'F').get
2328
2424
 
2329
2425
  case template
2330
- when '90.1-2004'
2331
- # 2004 has a 10F sat reset
2332
- sat_reset_r = 10
2333
- when '90.1-2007', '90.1-2010', '90.1-2013'
2334
- sat_reset_r = 5
2426
+ when '90.1-2004'
2427
+ # 2004 has a 10F sat reset
2428
+ sat_reset_r = 10
2429
+ when '90.1-2007', '90.1-2010', '90.1-2013'
2430
+ sat_reset_r = 5
2335
2431
  end
2336
2432
 
2337
2433
  sat_reset_k = OpenStudio.convert(sat_reset_r, 'R', 'K').get
@@ -2341,19 +2437,18 @@ class OpenStudio::Model::AirLoopHVAC
2341
2437
 
2342
2438
  # Create a setpoint manager
2343
2439
  sat_warmest_reset = OpenStudio::Model::SetpointManagerWarmest.new(model)
2344
- sat_warmest_reset.setName("#{self.name} SAT Warmest Reset")
2440
+ sat_warmest_reset.setName("#{name} SAT Warmest Reset")
2345
2441
  sat_warmest_reset.setStrategy('MaximumTemperature')
2346
2442
  sat_warmest_reset.setMinimumSetpointTemperature(design_sat_c)
2347
2443
  sat_warmest_reset.setMaximumSetpointTemperature(max_sat_c)
2348
2444
 
2349
2445
  # Attach the setpoint manager to the
2350
2446
  # supply outlet node of the system.
2351
- sat_warmest_reset.addToNode(self.supplyOutletNode)
2447
+ sat_warmest_reset.addToNode(supplyOutletNode)
2352
2448
 
2353
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset was enabled using a SPM Warmest with a min SAT of #{design_sat_c.round}C // #{design_sat_f.round}F and a max SAT of #{max_sat_c.round}C // #{max_sat_f.round}F.")
2449
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Supply air temperature reset was enabled using a SPM Warmest with a min SAT of #{design_sat_f.round}F and a max SAT of #{max_sat_f.round}F.")
2354
2450
 
2355
2451
  return true
2356
-
2357
2452
  end
2358
2453
 
2359
2454
  # Enable supply air temperature (SAT) reset based
@@ -2364,20 +2459,19 @@ class OpenStudio::Model::AirLoopHVAC
2364
2459
  #
2365
2460
  # @return [Bool] Returns true if successful, false if not.
2366
2461
 
2367
- def enable_supply_air_temperature_reset_outdoor_temperature()
2368
-
2462
+ def enable_supply_air_temperature_reset_outdoor_temperature
2369
2463
  # for AHU1 in Outpatient, SAT is 52F constant, no reset
2370
- return true if self.name.get == 'PVAV Outpatient F1'
2464
+ return true if name.get == 'PVAV Outpatient F1'
2371
2465
 
2372
2466
  # Get the current setpoint and calculate
2373
2467
  # the new setpoint.
2374
- sizing_system = self.sizingSystem
2468
+ sizing_system = sizingSystem
2375
2469
  sat_at_hi_oat_c = sizing_system.centralCoolingDesignSupplyAirTemperature
2376
2470
  sat_at_hi_oat_f = OpenStudio.convert(sat_at_hi_oat_c, 'C', 'F').get
2377
2471
  # 5F increase when it's cold outside,
2378
2472
  # and therefore less cooling capacity is likely required.
2379
2473
  increase_f = 5.0
2380
- sat_at_lo_oat_f = sat_at_hi_oat_f+increase_f
2474
+ sat_at_lo_oat_f = sat_at_hi_oat_f + increase_f
2381
2475
  sat_at_lo_oat_c = OpenStudio.convert(sat_at_lo_oat_f, 'F', 'C').get
2382
2476
 
2383
2477
  # Define the high and low outdoor air temperatures
@@ -2388,7 +2482,7 @@ class OpenStudio::Model::AirLoopHVAC
2388
2482
 
2389
2483
  # Create a setpoint manager
2390
2484
  sat_oa_reset = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model)
2391
- sat_oa_reset.setName("#{self.name} SAT Reset")
2485
+ sat_oa_reset.setName("#{name} SAT Reset")
2392
2486
  sat_oa_reset.setControlVariable('Temperature')
2393
2487
  sat_oa_reset.setSetpointatOutdoorLowTemperature(sat_at_lo_oat_c)
2394
2488
  sat_oa_reset.setOutdoorLowTemperature(lo_oat_c)
@@ -2397,21 +2491,19 @@ class OpenStudio::Model::AirLoopHVAC
2397
2491
 
2398
2492
  # Attach the setpoint manager to the
2399
2493
  # supply outlet node of the system.
2400
- sat_oa_reset.addToNode(self.supplyOutletNode)
2494
+ sat_oa_reset.addToNode(supplyOutletNode)
2401
2495
 
2402
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset was enabled. When OAT > #{hi_oat_f.round}F, SAT is #{sat_at_hi_oat_f.round}F. When OAT < #{lo_oat_f.round}F, SAT is #{sat_at_lo_oat_f.round}F. It varies linearly in between these points.")
2496
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Supply air temperature reset was enabled. When OAT > #{hi_oat_f.round}F, SAT is #{sat_at_hi_oat_f.round}F. When OAT < #{lo_oat_f.round}F, SAT is #{sat_at_lo_oat_f.round}F. It varies linearly in between these points.")
2403
2497
 
2404
2498
  return true
2405
-
2406
2499
  end
2407
2500
 
2408
2501
  # Determine if the system has an economizer
2409
2502
  #
2410
2503
  # @return [Bool] Returns true if required, false if not.
2411
- def has_economizer()
2412
-
2504
+ def economizer?
2413
2505
  # Get the OA system and OA controller
2414
- oa_sys = self.airLoopHVACOutdoorAirSystem
2506
+ oa_sys = airLoopHVACOutdoorAirSystem
2415
2507
  if oa_sys.is_initialized
2416
2508
  oa_sys = oa_sys.get
2417
2509
  else
@@ -2426,48 +2518,63 @@ class OpenStudio::Model::AirLoopHVAC
2426
2518
  else
2427
2519
  return true
2428
2520
  end
2429
-
2430
2521
  end
2431
2522
 
2432
2523
  # Determine if the system is a multizone VAV system
2433
2524
  #
2434
2525
  # @return [Bool] Returns true if required, false if not.
2435
- def is_multizone_vav_system()
2436
-
2437
- is_multizone_vav_system = false
2526
+ def multizone_vav_system?
2527
+ multizone_vav_system = false
2438
2528
 
2439
2529
  # Must serve more than 1 zone
2440
- if self.thermalZones.size < 2
2441
- return is_multizone_vav_system
2530
+ if thermalZones.size < 2
2531
+ return multizone_vav_system
2442
2532
  end
2443
2533
 
2444
2534
  # Must be a variable volume system
2445
2535
  has_vav_fan = false
2446
- self.supplyComponents.each do |comp|
2536
+ supplyComponents.each do |comp|
2447
2537
  if comp.to_FanVariableVolume.is_initialized
2448
2538
  has_vav_fan = true
2449
2539
  end
2450
2540
  end
2451
2541
  if has_vav_fan == false
2452
- return is_multizone_vav_system
2542
+ return multizone_vav_system
2453
2543
  end
2454
2544
 
2455
2545
  # If here, it's a multizone VAV system
2456
- is_multizone_vav_system = true
2546
+ multizone_vav_system = true
2457
2547
 
2458
- return is_multizone_vav_system
2548
+ return multizone_vav_system
2549
+ end
2550
+
2551
+ # Determine if the system has terminal reheat
2552
+ #
2553
+ # @return [Bool] returns true if has one or more reheat terminals, false if it doesn't.
2554
+ def terminal_reheat?
2555
+ has_term_rht = false
2556
+ demandComponents.each do |sc|
2557
+ if sc.to_AirTerminalSingleDuctConstantVolumeReheat.is_initialized ||
2558
+ sc.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized ||
2559
+ sc.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized ||
2560
+ sc.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized ||
2561
+ sc.to_AirTerminalSingleDuctVAVReheat.is_initialized
2562
+ has_term_rht = true
2563
+ break
2564
+ end
2565
+ end
2459
2566
 
2567
+ return has_term_rht
2460
2568
  end
2461
2569
 
2462
2570
  # Determine if the system has energy recovery already
2463
2571
  #
2464
2572
  # @return [Bool] Returns true if an ERV is present, false if not.
2465
- def has_energy_recovery()
2466
-
2573
+ def energy_recovery?
2467
2574
  has_erv = false
2468
2575
 
2469
2576
  # Get the OA system
2470
- oa_sys = self.airLoopHVACOutdoorAirSystem
2577
+ oa_sys = airLoopHVACOutdoorAirSystem
2471
2578
  if oa_sys.is_initialized
2472
2579
  oa_sys = oa_sys.get
2473
2580
  else
@@ -2482,7 +2589,6 @@ class OpenStudio::Model::AirLoopHVAC
2482
2589
  end
2483
2590
 
2484
2591
  return has_erv
2485
-
2486
2592
  end
2487
2593
 
2488
2594
  # Set the VAV damper control to single maximum or
@@ -2490,7 +2596,7 @@ class OpenStudio::Model::AirLoopHVAC
2490
2596
  #
2491
2597
  # @return [Bool] Returns true if successful, false if not
2492
2598
  # @todo see if this impacts the sizing run.
2493
- def set_vav_damper_action(template)
2599
+ def apply_vav_damper_action(template)
2494
2600
  damper_action = nil
2495
2601
  case template
2496
2602
  when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', 'NECB 2011'
@@ -2509,35 +2615,45 @@ class OpenStudio::Model::AirLoopHVAC
2509
2615
 
2510
2616
  # Set the control for any VAV reheat terminals
2511
2617
  # on this airloop.
2512
- self.demandComponents.each do |equip|
2618
+ control_type_set = false
2619
+ demandComponents.each do |equip|
2513
2620
  if equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
2514
2621
  term = equip.to_AirTerminalSingleDuctVAVReheat.get
2515
- term.setDamperHeatingAction(damper_action_eplus)
2622
+ # Dual maximum only applies to terminals with HW reheat coils
2623
+ if damper_action == 'Dual Maximum'
2624
+ if term.reheatCoil.to_CoilHeatingWater.is_initialized
2625
+ term.setDamperHeatingAction(damper_action_eplus)
2626
+ control_type_set = true
2627
+ end
2628
+ else
2629
+ term.setDamperHeatingAction(damper_action_eplus)
2630
+ control_type_set = true
2631
+ end
2516
2632
  end
2517
2633
  end
2518
2634
 
2519
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: VAV damper action was set to #{damper_action} control.")
2635
+ if control_type_set
2636
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: VAV damper action was set to #{damper_action} control.")
2637
+ end
2520
2638
 
2521
2639
  return true
2522
-
2523
2640
  end
2524
2641
 
2525
2642
  # Determine if a motorized OA damper is required
2526
- def is_motorized_oa_damper_required(template, climate_zone)
2527
-
2643
+ def motorized_oa_damper_required?(template, climate_zone)
2528
2644
  motorized_oa_damper_required = false
2529
2645
 
2530
- if self.name.to_s.include? "Outpatient F1"
2646
+ if name.to_s.include? 'Outpatient F1'
2531
2647
  motorized_oa_damper_required = true
2532
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: always has a damper, the minimum OA schedule is the same as airloop availability schedule.")
2648
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: always has a damper, the minimum OA schedule is the same as airloop availability schedule.")
2533
2649
  return motorized_oa_damper_required
2534
2650
  end
2535
2651
 
2536
2652
  # If the system has an economizer, it must have
2537
2653
  # a motorized damper.
2538
- if self.has_economizer
2654
+ if economizer?
2539
2655
  motorized_oa_damper_required = true
2540
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Because the system has an economizer, it requires a motorized OA damper.")
2656
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Because the system has an economizer, it requires a motorized OA damper.")
2541
2657
  return motorized_oa_damper_required
2542
2658
  end
2543
2659
 
@@ -2566,7 +2682,7 @@ class OpenStudio::Model::AirLoopHVAC
2566
2682
  minimum_oa_flow_cfm = 300
2567
2683
  maximum_stories = 3
2568
2684
  end
2569
- when '90.1-2010', '90.1-2013'
2685
+ when '90.1-2010', '90.1-2013'
2570
2686
  case climate_zone
2571
2687
  when 'ASHRAE 169-2006-1A',
2572
2688
  'ASHRAE 169-2006-1B',
@@ -2584,19 +2700,19 @@ class OpenStudio::Model::AirLoopHVAC
2584
2700
  end
2585
2701
 
2586
2702
  # Get the number of stories
2587
- num_stories = self.model.getBuildingStorys.size
2703
+ num_stories = model.getBuildingStorys.size
2588
2704
 
2589
2705
  # Check the number of stories exception,
2590
2706
  # which is climate-zone dependent.
2591
2707
  if num_stories < maximum_stories
2592
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Motorized OA damper not required because the building has #{num_stories} stories, less than the maximum of #{maximum_stories} stories for climate zone #{climate_zone}.")
2708
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Motorized OA damper not required because the building has #{num_stories} stories, less than the maximum of #{maximum_stories} stories for climate zone #{climate_zone}.")
2593
2709
  return motorized_oa_damper_required
2594
2710
  end
2595
2711
 
2596
2712
  # Get the min OA flow rate
2597
2713
  oa_flow_m3_per_s = 0
2598
- if self.airLoopHVACOutdoorAirSystem.is_initialized
2599
- oa_system = self.airLoopHVACOutdoorAirSystem.get
2714
+ if airLoopHVACOutdoorAirSystem.is_initialized
2715
+ oa_system = airLoopHVACOutdoorAirSystem.get
2600
2716
  controller_oa = oa_system.getControllerOutdoorAir
2601
2717
  if controller_oa.minimumOutdoorAirFlowRate.is_initialized
2602
2718
  oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
@@ -2604,14 +2720,14 @@ class OpenStudio::Model::AirLoopHVAC
2604
2720
  oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get
2605
2721
  end
2606
2722
  else
2607
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, Motorized OA damper not applicable because it has no OA intake.")
2723
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}, Motorized OA damper not applicable because it has no OA intake.")
2608
2724
  return motorized_oa_damper_required
2609
2725
  end
2610
2726
  oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
2611
2727
 
2612
2728
  # Check the OA flow rate exception
2613
2729
  if oa_flow_cfm < minimum_oa_flow_cfm
2614
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Motorized OA damper not required because the system OA intake of #{oa_flow_cfm.round} cfm is less than the minimum threshold of #{minimum_oa_flow_cfm} cfm.")
2730
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Motorized OA damper not required because the system OA intake of #{oa_flow_cfm.round} cfm is less than the minimum threshold of #{minimum_oa_flow_cfm} cfm.")
2615
2731
  return motorized_oa_damper_required
2616
2732
  end
2617
2733
 
@@ -2619,7 +2735,6 @@ class OpenStudio::Model::AirLoopHVAC
2619
2735
  motorized_oa_damper_required = true
2620
2736
 
2621
2737
  return motorized_oa_damper_required
2622
-
2623
2738
  end
2624
2739
 
2625
2740
  # Add a motorized damper by modifying the OA schedule
@@ -2637,18 +2752,17 @@ class OpenStudio::Model::AirLoopHVAC
2637
2752
  # occupancy threshold.
2638
2753
  # @return [Bool] true if successful, false if not
2639
2754
  def add_motorized_oa_damper(min_occ_pct = 0.15, occ_sch = nil)
2640
-
2641
2755
  # Get the airloop occupancy schedule if none supplied
2642
2756
  if occ_sch.nil?
2643
- occ_sch = self.get_occupancy_schedule(min_occ_pct)
2757
+ occ_sch = get_occupancy_schedule(min_occ_pct)
2644
2758
  flh = occ_sch.annual_equivalent_full_load_hrs
2645
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used to close OA damper during unoccupied hours.")
2759
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used to close OA damper during unoccupied hours.")
2646
2760
  else
2647
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Setting motorized OA damper schedule to #{occ_sch.name}.")
2761
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Setting motorized OA damper schedule to #{occ_sch.name}.")
2648
2762
  end
2649
2763
 
2650
2764
  # Get the OA system and OA controller
2651
- oa_sys = self.airLoopHVACOutdoorAirSystem
2765
+ oa_sys = airLoopHVACOutdoorAirSystem
2652
2766
  if oa_sys.is_initialized
2653
2767
  oa_sys = oa_sys.get
2654
2768
  else
@@ -2660,7 +2774,6 @@ class OpenStudio::Model::AirLoopHVAC
2660
2774
  oa_control.setMinimumOutdoorAirSchedule(occ_sch)
2661
2775
 
2662
2776
  return true
2663
-
2664
2777
  end
2665
2778
 
2666
2779
  # Remove a motorized OA damper by modifying the OA schedule
@@ -2669,9 +2782,8 @@ class OpenStudio::Model::AirLoopHVAC
2669
2782
  # This reflects the use of a backdraft gravity damper, and
2670
2783
  # increases building loads unnecessarily during unoccupied hours.
2671
2784
  def remove_motorized_oa_damper
2672
-
2673
2785
  # Get the OA system and OA controller
2674
- oa_sys = self.airLoopHVACOutdoorAirSystem
2786
+ oa_sys = airLoopHVACOutdoorAirSystem
2675
2787
  if oa_sys.is_initialized
2676
2788
  oa_sys = oa_sys.get
2677
2789
  else
@@ -2680,10 +2792,9 @@ class OpenStudio::Model::AirLoopHVAC
2680
2792
  oa_control = oa_sys.getControllerOutdoorAir
2681
2793
 
2682
2794
  # Set the minimum OA schedule to always 1 (100%)
2683
- oa_control.setMinimumOutdoorAirSchedule(self.model.alwaysOnDiscreteSchedule)
2795
+ oa_control.setMinimumOutdoorAirSchedule(model.alwaysOnDiscreteSchedule)
2684
2796
 
2685
2797
  return true
2686
-
2687
2798
  end
2688
2799
 
2689
2800
  # This method creates a schedule where the value is zero when
@@ -2699,13 +2810,12 @@ class OpenStudio::Model::AirLoopHVAC
2699
2810
  # @return [ScheduleRuleset] a ScheduleRuleset where 0 = unoccupied, 1 = occupied
2700
2811
  # @todo Speed up this method. Bottleneck is ScheduleRule.getDaySchedules
2701
2812
  def get_occupancy_schedule(occupied_percentage_threshold = 0.05)
2702
-
2703
2813
  # Get all the occupancy schedules in every space in every zone
2704
2814
  # served by this airloop. Include people added via the SpaceType
2705
2815
  # in addition to people hard-assigned to the Space itself.
2706
2816
  occ_schedules_num_occ = {}
2707
2817
  max_occ_on_airloop = 0
2708
- self.thermalZones.each do |zone|
2818
+ thermalZones.each do |zone|
2709
2819
  # Get the people objects
2710
2820
  zone.spaces.each do |space|
2711
2821
  # From the space type
@@ -2720,11 +2830,10 @@ class OpenStudio::Model::AirLoopHVAC
2720
2830
  num_ppl = people.getNumberOfPeople(space.floorArea)
2721
2831
  if occ_schedules_num_occ[num_ppl_sch].nil?
2722
2832
  occ_schedules_num_occ[num_ppl_sch] = num_ppl
2723
- max_occ_on_airloop += num_ppl
2724
2833
  else
2725
2834
  occ_schedules_num_occ[num_ppl_sch] += num_ppl
2726
- max_occ_on_airloop += num_ppl
2727
2835
  end
2836
+ max_occ_on_airloop += num_ppl
2728
2837
  end
2729
2838
  end
2730
2839
  end
@@ -2739,30 +2848,28 @@ class OpenStudio::Model::AirLoopHVAC
2739
2848
  num_ppl = people.getNumberOfPeople(space.floorArea)
2740
2849
  if occ_schedules_num_occ[num_ppl_sch].nil?
2741
2850
  occ_schedules_num_occ[num_ppl_sch] = num_ppl
2742
- max_occ_on_airloop += num_ppl
2743
2851
  else
2744
2852
  occ_schedules_num_occ[num_ppl_sch] += num_ppl
2745
- max_occ_on_airloop += num_ppl
2746
2853
  end
2854
+ max_occ_on_airloop += num_ppl
2747
2855
  end
2748
2856
  end
2749
2857
  end
2750
2858
  end
2751
2859
 
2752
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{self.name} has #{occ_schedules_num_occ.size} unique occ schedules.")
2860
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "#{name} has #{occ_schedules_num_occ.size} unique occ schedules.")
2753
2861
  occ_schedules_num_occ.each do |occ_sch, num_occ|
2754
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " #{occ_sch.name} - #{num_occ.round} people")
2862
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', " #{occ_sch.name} - #{num_occ.round} people")
2755
2863
  end
2756
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " Total #{max_occ_on_airloop.round} people on #{self.name}")
2864
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', " Total #{max_occ_on_airloop.round} people on #{name}")
2757
2865
 
2758
2866
  # For each day of the year, determine
2759
- #time_value_pairs = []
2760
- year = self.model.getYearDescription
2867
+ # time_value_pairs = []
2868
+ year = model.getYearDescription
2761
2869
  yearly_data = []
2762
2870
  yearly_times = OpenStudio::DateTimeVector.new
2763
2871
  yearly_values = []
2764
- for i in 1..365
2765
-
2872
+ (1..365).each do |i|
2766
2873
  times_on_this_day = []
2767
2874
  os_date = year.makeDate(i)
2768
2875
  day_of_week = os_date.dayOfWeek.valueName
@@ -2771,16 +2878,14 @@ class OpenStudio::Model::AirLoopHVAC
2771
2878
  occ_schedules_day_schs = {}
2772
2879
  day_sch_num_occ = {}
2773
2880
  occ_schedules_num_occ.each do |occ_sch, num_occ|
2774
-
2775
2881
  # Get the day schedules for this day
2776
2882
  # (there should only be one)
2777
2883
  day_schs = occ_sch.getDaySchedules(os_date, os_date)
2778
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "Schedule #{occ_sch.name} has #{day_schs.size} day schs") unless day_schs.size == 1
2884
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "Schedule #{occ_sch.name} has #{day_schs.size} day schs") unless day_schs.size == 1
2779
2885
  day_schs[0].times.each do |time|
2780
2886
  times_on_this_day << time.toString
2781
2887
  end
2782
2888
  day_sch_num_occ[day_schs[0]] = num_occ
2783
-
2784
2889
  end
2785
2890
 
2786
2891
  # Determine the total fraction for the airloop at each time
@@ -2789,7 +2894,6 @@ class OpenStudio::Model::AirLoopHVAC
2789
2894
  daily_values = []
2790
2895
  daily_occs = []
2791
2896
  times_on_this_day.uniq.sort.each do |time|
2792
-
2793
2897
  os_time = OpenStudio::Time.new(time)
2794
2898
  os_date_time = OpenStudio::DateTime.new(os_date, os_time)
2795
2899
  # Total number of people at each time
@@ -2811,7 +2915,6 @@ class OpenStudio::Model::AirLoopHVAC
2811
2915
  daily_os_times << os_time
2812
2916
  daily_values << occ_status
2813
2917
  daily_occs << air_loop_occ_frac.round(2)
2814
-
2815
2918
  end
2816
2919
 
2817
2920
  # OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{daily_times.join(', ')} #{daily_values.join(', ')}")
@@ -2822,28 +2925,27 @@ class OpenStudio::Model::AirLoopHVAC
2822
2925
  simple_daily_os_times = []
2823
2926
  simple_daily_values = []
2824
2927
  simple_daily_occs = []
2825
- daily_values.each_with_index do |value, i|
2826
- next if value == daily_values[i+1]
2827
- simple_daily_times << daily_times[i]
2828
- simple_daily_os_times << daily_os_times[i]
2829
- simple_daily_values << daily_values[i]
2830
- simple_daily_occs << daily_occs[i]
2928
+ daily_values.each_with_index do |value, j|
2929
+ next if value == daily_values[j + 1]
2930
+ simple_daily_times << daily_times[j]
2931
+ simple_daily_os_times << daily_os_times[j]
2932
+ simple_daily_values << daily_values[j]
2933
+ simple_daily_occs << daily_occs[j]
2831
2934
  end
2832
2935
 
2833
2936
  # OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{simple_daily_times.join(', ')} {simple_daily_values.join(', ')}")
2834
2937
 
2835
2938
  # Store the daily values
2836
- yearly_data << {'date'=>os_date,'day_of_week'=>day_of_week,'times'=>simple_daily_times,'values'=>simple_daily_values,'daily_os_times'=>simple_daily_os_times, 'daily_occs'=>simple_daily_occs}
2837
-
2939
+ yearly_data << { 'date' => os_date, 'day_of_week' => day_of_week, 'times' => simple_daily_times, 'values' => simple_daily_values, 'daily_os_times' => simple_daily_os_times, 'daily_occs' => simple_daily_occs }
2838
2940
  end
2839
2941
 
2840
2942
  # Create a TimeSeries from the data
2841
- #time_series = OpenStudio::TimeSeries.new(times, values, 'unitless')
2943
+ # time_series = OpenStudio::TimeSeries.new(times, values, 'unitless')
2842
2944
 
2843
2945
  # Make a schedule ruleset
2844
- sch_name = "#{self.name} Occ Sch"
2845
- sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(self.model)
2846
- sch_ruleset.setName("#{sch_name}")
2946
+ sch_name = "#{name} Occ Sch"
2947
+ sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
2948
+ sch_ruleset.setName(sch_name.to_s)
2847
2949
 
2848
2950
  # Default - All Occupied
2849
2951
  day_sch = sch_ruleset.defaultDaySchedule
@@ -2851,14 +2953,14 @@ class OpenStudio::Model::AirLoopHVAC
2851
2953
  day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
2852
2954
 
2853
2955
  # Winter Design Day - All Occupied
2854
- day_sch = OpenStudio::Model::ScheduleDay.new(self.model)
2956
+ day_sch = OpenStudio::Model::ScheduleDay.new(model)
2855
2957
  sch_ruleset.setWinterDesignDaySchedule(day_sch)
2856
2958
  day_sch = sch_ruleset.winterDesignDaySchedule
2857
2959
  day_sch.setName("#{sch_name} Winter Design Day")
2858
2960
  day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
2859
2961
 
2860
2962
  # Summer Design Day - All Occupied
2861
- day_sch = OpenStudio::Model::ScheduleDay.new(self.model)
2963
+ day_sch = OpenStudio::Model::ScheduleDay.new(model)
2862
2964
  sch_ruleset.setSummerDesignDaySchedule(day_sch)
2863
2965
  day_sch = sch_ruleset.summerDesignDaySchedule
2864
2966
  day_sch.setName("#{sch_name} Summer Design Day")
@@ -2866,14 +2968,14 @@ class OpenStudio::Model::AirLoopHVAC
2866
2968
 
2867
2969
  # Create ruleset schedules, attempting to create
2868
2970
  # the minimum number of unique rules.
2869
- ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'].each do |day_of_week|
2870
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{day_of_week}")
2971
+ ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].each do |weekday|
2972
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', weekday.to_s)
2871
2973
  end_of_prev_rule = yearly_data[0]['date']
2872
- yearly_data.each_with_index do |daily_data, i|
2974
+ yearly_data.each_with_index do |daily_data, k|
2873
2975
  # Skip unless it is the day of week
2874
2976
  # currently under inspection
2875
2977
  day = daily_data['day_of_week']
2876
- next unless day == day_of_week
2978
+ next unless day == weekday
2877
2979
  date = daily_data['date']
2878
2980
  times = daily_data['times']
2879
2981
  values = daily_data['values']
@@ -2884,9 +2986,9 @@ class OpenStudio::Model::AirLoopHVAC
2884
2986
  # If the next is different, or if
2885
2987
  # we've reached the end of the year,
2886
2988
  # create a new rule
2887
- if !yearly_data[i+7].nil?
2888
- next_day_times = yearly_data[i+7]['times']
2889
- next_day_values = yearly_data[i+7]['values']
2989
+ unless yearly_data[k + 7].nil?
2990
+ next_day_times = yearly_data[k + 7]['times']
2991
+ next_day_values = yearly_data[k + 7]['values']
2890
2992
  next if times == next_day_times && values == next_day_values
2891
2993
  end
2892
2994
 
@@ -2895,16 +2997,16 @@ class OpenStudio::Model::AirLoopHVAC
2895
2997
 
2896
2998
  # If here, we need to make a rule to cover from the previous
2897
2999
  # rule to today
2898
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "Making a new rule for #{day_of_week} from #{end_of_prev_rule.to_s} to #{date}")
3000
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "Making a new rule for #{weekday} from #{end_of_prev_rule} to #{date}")
2899
3001
  sch_rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset)
2900
- sch_rule.setName("#{sch_name} #{day_of_week} Rule")
3002
+ sch_rule.setName("#{sch_name} #{weekday} Rule")
2901
3003
  day_sch = sch_rule.daySchedule
2902
- day_sch.setName("#{sch_name} #{day_of_week}")
2903
- daily_os_times.each_with_index do |time, i|
2904
- value = values[i]
2905
- next if value == values[i+1] # Don't add breaks if same value
3004
+ day_sch.setName("#{sch_name} #{weekday}")
3005
+ daily_os_times.each_with_index do |time, t|
3006
+ value = values[t]
3007
+ next if value == values[t + 1] # Don't add breaks if same value
2906
3008
  day_sch.addValue(time, value)
2907
- OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " Adding value #{time}, #{value}")
3009
+ OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', " Adding value #{time}, #{value}")
2908
3010
  end
2909
3011
 
2910
3012
  # Set the dates when the rule applies
@@ -2912,23 +3014,20 @@ class OpenStudio::Model::AirLoopHVAC
2912
3014
  sch_rule.setEndDate(date)
2913
3015
 
2914
3016
  # Individual Days
2915
- sch_rule.setApplyMonday(true) if day_of_week == 'Monday'
2916
- sch_rule.setApplyTuesday(true) if day_of_week == 'Tuesday'
2917
- sch_rule.setApplyWednesday(true) if day_of_week == 'Wednesday'
2918
- sch_rule.setApplyThursday(true) if day_of_week == 'Thursday'
2919
- sch_rule.setApplyFriday(true) if day_of_week == 'Friday'
2920
- sch_rule.setApplySaturday(true) if day_of_week == 'Saturday'
2921
- sch_rule.setApplySunday(true) if day_of_week == 'Sunday'
3017
+ sch_rule.setApplyMonday(true) if weekday == 'Monday'
3018
+ sch_rule.setApplyTuesday(true) if weekday == 'Tuesday'
3019
+ sch_rule.setApplyWednesday(true) if weekday == 'Wednesday'
3020
+ sch_rule.setApplyThursday(true) if weekday == 'Thursday'
3021
+ sch_rule.setApplyFriday(true) if weekday == 'Friday'
3022
+ sch_rule.setApplySaturday(true) if weekday == 'Saturday'
3023
+ sch_rule.setApplySunday(true) if weekday == 'Sunday'
2922
3024
 
2923
3025
  # Reset the previous rule end date
2924
3026
  end_of_prev_rule = date + OpenStudio::Time.new(0, 24, 0, 0)
2925
-
2926
3027
  end
2927
-
2928
3028
  end
2929
3029
 
2930
3030
  return sch_ruleset
2931
-
2932
3031
  end
2933
3032
 
2934
3033
  # Generate the EMS used to implement the economizer
@@ -2937,33 +3036,32 @@ class OpenStudio::Model::AirLoopHVAC
2937
3036
  # the IDF yet.
2938
3037
  #
2939
3038
  def apply_single_zone_controls(template, climate_zone)
2940
-
2941
3039
  # Number of stages is determined by the template
2942
3040
  num_stages = nil
2943
3041
  case template
2944
3042
  when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', 'NECB 2011'
2945
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No special economizer controls were modeled.")
3043
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: No special economizer controls were modeled.")
2946
3044
  return true
2947
3045
  when '90.1-2004', '90.1-2007'
2948
3046
  num_stages = 1
2949
- when '90.1-2010', '90.1-2013'
3047
+ when '90.1-2010', '90.1-2013'
2950
3048
  num_stages = 2
2951
3049
  end
2952
3050
 
2953
3051
  # Scrub special characters from the system name
2954
- sn = self.name.get.to_s
2955
- snc = sn.gsub(/\W/,'').gsub('_','')
3052
+ sn = name.get.to_s
3053
+ snc = sn.gsub(/\W/, '').delete('_')
2956
3054
 
2957
3055
  # Get the zone name
2958
- zone = self.thermalZones[0]
3056
+ zone = thermalZones[0]
2959
3057
  zone_name = zone.name.get.to_s
2960
- zn_name_clean = zone_name.gsub(/\W/,'_')
3058
+ zn_name_clean = zone_name.gsub(/\W/, '_')
2961
3059
 
2962
3060
  # Zone air node
2963
3061
  zone_air_node_name = zone.zoneAirNode.name.get
2964
3062
 
2965
3063
  # Get the OA system and OA controller
2966
- oa_sys = self.airLoopHVACOutdoorAirSystem
3064
+ oa_sys = airLoopHVACOutdoorAirSystem
2967
3065
  if oa_sys.is_initialized
2968
3066
  oa_sys = oa_sys.get
2969
3067
  else
@@ -2975,27 +3073,27 @@ class OpenStudio::Model::AirLoopHVAC
2975
3073
 
2976
3074
  # Get the name of the min oa schedule
2977
3075
  min_oa_sch_name = nil
2978
- if oa_control.minimumOutdoorAirSchedule.is_initialized
2979
- min_oa_sch_name = oa_control.minimumOutdoorAirSchedule.get.name.get
2980
- else
2981
- min_oa_sch_name = self.model.alwaysOnDiscreteSchedule.name.get
2982
- end
3076
+ min_oa_sch_name = if oa_control.minimumOutdoorAirSchedule.is_initialized
3077
+ oa_control.minimumOutdoorAirSchedule.get.name.get
3078
+ else
3079
+ model.alwaysOnDiscreteSchedule.name.get
3080
+ end
2983
3081
 
2984
3082
  # Get the supply fan
2985
- if self.supplyFan.empty?
2986
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No supply fan found, cannot apply DX fan/economizer control.")
3083
+ if supplyFan.empty?
3084
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: No supply fan found, cannot apply DX fan/economizer control.")
2987
3085
  return false
2988
3086
  end
2989
- fan = self.supplyFan.get
3087
+ fan = supplyFan.get
2990
3088
  fan_name = fan.name.get
2991
3089
 
2992
3090
  # Supply outlet node
2993
- sup_out_node = self.supplyOutletNode
3091
+ sup_out_node = supplyOutletNode
2994
3092
  sup_out_node_name = sup_out_node.name.get
2995
3093
 
2996
3094
  # DX Cooling Coil
2997
3095
  dx_coil = nil
2998
- self.supplyComponents.each do |equip|
3096
+ supplyComponents.each do |equip|
2999
3097
  if equip.to_CoilCoolingDXSingleSpeed.is_initialized
3000
3098
  dx_coil = equip.to_CoilCoolingDXSingleSpeed.get
3001
3099
  elsif equip.to_CoilCoolingDXTwoSpeed.is_initialized
@@ -3003,7 +3101,7 @@ class OpenStudio::Model::AirLoopHVAC
3003
3101
  end
3004
3102
  end
3005
3103
  if dx_coil.nil?
3006
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No DX cooling coil found, cannot apply DX fan/economizer control.")
3104
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: No DX cooling coil found, cannot apply DX fan/economizer control.")
3007
3105
  return false
3008
3106
  end
3009
3107
  dx_coil_name = dx_coil.name.get
@@ -3011,7 +3109,7 @@ class OpenStudio::Model::AirLoopHVAC
3011
3109
 
3012
3110
  # Heating Coil
3013
3111
  htg_coil = nil
3014
- self.supplyComponents.each do |equip|
3112
+ supplyComponents.each do |equip|
3015
3113
  if equip.to_CoilHeatingGas.is_initialized
3016
3114
  htg_coil = equip.to_CoilHeatingGas.get
3017
3115
  elsif equip.to_CoilHeatingElectric.is_initialized
@@ -3021,7 +3119,7 @@ class OpenStudio::Model::AirLoopHVAC
3021
3119
  end
3022
3120
  end
3023
3121
  if htg_coil.nil?
3024
- OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No heating coil found, cannot apply DX fan/economizer control.")
3122
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: No heating coil found, cannot apply DX fan/economizer control.")
3025
3123
  return false
3026
3124
  end
3027
3125
  htg_coil_name = htg_coil.name.get
@@ -3029,10 +3127,10 @@ class OpenStudio::Model::AirLoopHVAC
3029
3127
  # Create an economizer maximum OA fraction schedule with
3030
3128
  # a maximum of 70% to reflect damper leakage per PNNL
3031
3129
  max_oa_sch_name = "#{snc}maxOASch"
3032
- max_oa_sch = OpenStudio::Model::ScheduleRuleset.new(self.model)
3130
+ max_oa_sch = OpenStudio::Model::ScheduleRuleset.new(model)
3033
3131
  max_oa_sch.setName(max_oa_sch_name)
3034
3132
  max_oa_sch.defaultDaySchedule.setName("#{max_oa_sch_name}Default")
3035
- max_oa_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0,24,0,0), 0.7)
3133
+ max_oa_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0.7)
3036
3134
  oa_control.setMaximumFractionofOutdoorAirSchedule(max_oa_sch)
3037
3135
 
3038
3136
  ems = "
@@ -3274,11 +3372,10 @@ class OpenStudio::Model::AirLoopHVAC
3274
3372
 
3275
3373
  # Write the ems out
3276
3374
  # File.open("#{Dir.pwd}/#{snc}_ems.idf", 'w') do |file|
3277
- # file.puts ems
3375
+ # file.puts ems
3278
3376
  # end
3279
3377
 
3280
3378
  return ems
3281
-
3282
3379
  end
3283
3380
 
3284
3381
  # Determine if static pressure reset is required for this
@@ -3290,16 +3387,15 @@ class OpenStudio::Model::AirLoopHVAC
3290
3387
  # has DDC control of VAV terminals or not, determine this
3291
3388
  # from the system itself. This may require additional information
3292
3389
  # be added to the OpenStudio data model.
3293
- # @param template [String] the standard
3390
+ # @param template [String] the template base requirements on
3294
3391
  # @param has_ddc [Bool] whether or not the system has DDC control
3295
3392
  # over VAV terminals.
3296
3393
  # return [Bool] returns true if static pressure reset is required, false if not
3297
- def is_static_pressure_reset_required(template, has_ddc)
3298
-
3394
+ def static_pressure_reset_required?(template, has_ddc)
3299
3395
  sp_reset_required = false
3300
3396
 
3301
3397
  # A big number of btu per hr as the minimum requirement
3302
- infinity_btu_per_hr = 999999999999
3398
+ infinity_btu_per_hr = 999_999_999_999
3303
3399
  minimum_capacity_btu_per_hr = infinity_btu_per_hr
3304
3400
 
3305
3401
  # Determine the minimum capacity that requires an economizer
@@ -3309,16 +3405,15 @@ class OpenStudio::Model::AirLoopHVAC
3309
3405
  when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
3310
3406
  if has_ddc
3311
3407
  sp_reset_required = true
3312
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: static pressure reset is required because the system has DDC control of VAV terminals.")
3408
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Static pressure reset is required because the system has DDC control of VAV terminals.")
3313
3409
  else
3314
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: static pressure reset not required because the system does not have DDC control of VAV terminals.")
3410
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Static pressure reset not required because the system does not have DDC control of VAV terminals.")
3315
3411
  end
3316
3412
  when 'NECB 2011'
3317
3413
  # static pressure reset not required
3318
3414
  end
3319
3415
 
3320
3416
  return sp_reset_required
3321
-
3322
3417
  end
3323
3418
 
3324
3419
  # Determine if a system's fans must shut off when
@@ -3326,8 +3421,7 @@ class OpenStudio::Model::AirLoopHVAC
3326
3421
  #
3327
3422
  # @param template [String]
3328
3423
  # @return [Bool] true if required, false if not
3329
- def is_unoccupied_fan_shutoff_required(template)
3330
-
3424
+ def unoccupied_fan_shutoff_required?(template)
3331
3425
  shutoff_required = true
3332
3426
 
3333
3427
  # Per 90.1 6.4.3.4.5, systems less than 0.75 HP
@@ -3340,18 +3434,17 @@ class OpenStudio::Model::AirLoopHVAC
3340
3434
 
3341
3435
  # Determine the system fan horsepower
3342
3436
  total_hp = 0.0
3343
- self.supply_return_exhaust_relief_fans.each do |fan|
3344
- total_hp += fan.motorHorsepower
3437
+ supply_return_exhaust_relief_fans.each do |fan|
3438
+ total_hp += fan.motor_horsepower
3345
3439
  end
3346
3440
 
3347
3441
  # Check the HP exception
3348
3442
  if total_hp < minimum_fan_hp
3349
3443
  shutoff_required = false
3350
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Unoccupied fan shutoff not required because system fan HP of #{total_hp.round(2)} HP is less than the minimum threshold of #{minimum_fan_hp} HP.")
3444
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Unoccupied fan shutoff not required because system fan HP of #{total_hp.round(2)} HP is less than the minimum threshold of #{minimum_fan_hp} HP.")
3351
3445
  end
3352
3446
 
3353
3447
  return shutoff_required
3354
-
3355
3448
  end
3356
3449
 
3357
3450
  # Shut off the system during unoccupied periods.
@@ -3368,32 +3461,30 @@ class OpenStudio::Model::AirLoopHVAC
3368
3461
  # the system will be considered unoccupied.
3369
3462
  # @return [Bool] true if successful, false if not
3370
3463
  def enable_unoccupied_fan_shutoff(min_occ_pct = 0.15)
3371
-
3372
3464
  # Set the system to night cycle
3373
3465
  night_cycle_type = 'CycleOnAny'
3374
3466
  # For VAV with PFP boxes, cycle zone fans only
3375
- if self.demandComponents('OS:AirTerminal:SingleDuct:ParallelPIU:Reheat'.to_IddObjectType).size > 0
3467
+ unless demandComponents('OS:AirTerminal:SingleDuct:ParallelPIU:Reheat'.to_IddObjectType).empty?
3376
3468
  night_cycle_type = 'CycleOnAnyZoneFansOnly'
3377
3469
  end
3378
- self.setNightCycleControlType(night_cycle_type)
3470
+ setNightCycleControlType(night_cycle_type)
3379
3471
 
3380
3472
  # Check if already using a schedule other than always on
3381
- avail_sch = self.availabilitySchedule
3382
- unless avail_sch == self.model.alwaysOnDiscreteSchedule
3383
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Availability schedule is already set to #{avail_sch.name}. Will assume this includes unoccupied shut down; no changes will be made.")
3473
+ avail_sch = availabilitySchedule
3474
+ unless avail_sch == model.alwaysOnDiscreteSchedule
3475
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Availability schedule is already set to #{avail_sch.name}. Will assume this includes unoccupied shut down; no changes will be made.")
3384
3476
  return true
3385
3477
  end
3386
3478
 
3387
3479
  # Get the airloop occupancy schedule
3388
- loop_occ_sch = self.get_occupancy_schedule(min_occ_pct)
3480
+ loop_occ_sch = get_occupancy_schedule(min_occ_pct)
3389
3481
  flh = loop_occ_sch.annual_equivalent_full_load_hrs
3390
- OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used as the HVAC operation schedule.")
3482
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used as the HVAC operation schedule.")
3391
3483
 
3392
3484
  # Set HVAC availability schedule to follow occupancy
3393
- self.setAvailabilitySchedule(loop_occ_sch)
3485
+ setAvailabilitySchedule(loop_occ_sch)
3394
3486
 
3395
3487
  return true
3396
-
3397
3488
  end
3398
3489
 
3399
3490
  # Calculate the total floor area of all zones attached
@@ -3401,16 +3492,14 @@ class OpenStudio::Model::AirLoopHVAC
3401
3492
  #
3402
3493
  # return [Double] the total floor area of all zones attached
3403
3494
  # to the air loop, in m^2.
3404
- def floor_area_served()
3405
-
3495
+ def floor_area_served
3406
3496
  total_area = 0.0
3407
3497
 
3408
- self.thermalZones.each do |zone|
3498
+ thermalZones.each do |zone|
3409
3499
  total_area += zone.floorArea
3410
3500
  end
3411
3501
 
3412
3502
  return total_area
3413
-
3414
3503
  end
3415
3504
 
3416
3505
  # Calculate the total floor area of all zones attached
@@ -3418,18 +3507,16 @@ class OpenStudio::Model::AirLoopHVAC
3418
3507
  #
3419
3508
  # return [Double] the total floor area of all zones attached
3420
3509
  # to the air loop, in m^2.
3421
- def floor_area_served_interior_zones()
3422
-
3510
+ def floor_area_served_interior_zones
3423
3511
  total_area = 0.0
3424
3512
 
3425
- self.thermalZones.each do |zone|
3513
+ thermalZones.each do |zone|
3426
3514
  # Skip zones that have exterior surface area
3427
3515
  next if zone.exteriorSurfaceArea > 0
3428
3516
  total_area += zone.floorArea
3429
3517
  end
3430
3518
 
3431
3519
  return total_area
3432
-
3433
3520
  end
3434
3521
 
3435
3522
  # Calculate the total floor area of all zones attached
@@ -3437,37 +3524,33 @@ class OpenStudio::Model::AirLoopHVAC
3437
3524
  #
3438
3525
  # return [Double] the total floor area of all zones attached
3439
3526
  # to the air loop, in m^2.
3440
- def floor_area_served_exterior_zones()
3441
-
3527
+ def floor_area_served_exterior_zones
3442
3528
  total_area = 0.0
3443
3529
 
3444
- self.thermalZones.each do |zone|
3530
+ thermalZones.each do |zone|
3445
3531
  # Skip zones that have no exterior surface area
3446
- next if zone.exteriorSurfaceArea == 0
3532
+ next if zone.exteriorSurfaceArea.zero?
3447
3533
  total_area += zone.floorArea
3448
3534
  end
3449
3535
 
3450
3536
  return total_area
3451
-
3452
3537
  end
3453
3538
 
3454
3539
  # find design_supply_air_flow_rate
3455
3540
  #
3456
3541
  # @return [Double] design_supply_air_flow_rate m^3/s
3457
- def find_design_supply_air_flow_rate()
3458
-
3542
+ def find_design_supply_air_flow_rate
3459
3543
  # Get the design_supply_air_flow_rate
3460
3544
  design_supply_air_flow_rate = nil
3461
- if self.designSupplyAirFlowRate.is_initialized
3462
- design_supply_air_flow_rate = self.designSupplyAirFlowRate.get
3463
- elsif self.autosizedDesignSupplyAirFlowRate.is_initialized
3464
- design_supply_air_flow_rate = self.autosizedDesignSupplyAirFlowRate.get
3545
+ if designSupplyAirFlowRate.is_initialized
3546
+ design_supply_air_flow_rate = designSupplyAirFlowRate.get
3547
+ elsif autosizedDesignSupplyAirFlowRate.is_initialized
3548
+ design_supply_air_flow_rate = autosizedDesignSupplyAirFlowRate.get
3465
3549
  else
3466
- OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} design sypply air flow rate is not available.")
3550
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name} design sypply air flow rate is not available.")
3467
3551
  end
3468
3552
 
3469
3553
  return design_supply_air_flow_rate
3470
-
3471
3554
  end
3472
3555
 
3473
3556
  # Determine how much data center
@@ -3479,11 +3562,10 @@ class OpenStudio::Model::AirLoopHVAC
3479
3562
  # standards space type spreadsheet instead
3480
3563
  # of relying on the standards space type name to
3481
3564
  # identify a data center.
3482
- def data_center_area_served()
3483
-
3565
+ def data_center_area_served
3484
3566
  dc_area_m2 = 0.0
3485
3567
 
3486
- self.thermalZones.each do |zone|
3568
+ thermalZones.each do |zone|
3487
3569
  zone.spaces.each do |space|
3488
3570
  # Skip spaces with no space type
3489
3571
  next if space.spaceType.empty?
@@ -3497,7 +3579,103 @@ class OpenStudio::Model::AirLoopHVAC
3497
3579
  end
3498
3580
 
3499
3581
  return dc_area_m2
3582
+ end
3583
+
3584
+ # Sets the maximum reheat temperature to the specified
3585
+ # value for all reheat terminals (of any type) on the loop.
3586
+ #
3587
+ # @param max_reheat_c [Double] the maximum reheat temperature, in C
3588
+ # @return [Bool] returns true if successful, false if not.
3589
+ def apply_maximum_reheat_temperature(max_reheat_c)
3590
+ demandComponents.each do |sc|
3591
+ if sc.to_AirTerminalSingleDuctConstantVolumeReheat.is_initialized
3592
+ term = sc.to_AirTerminalSingleDuctConstantVolumeReheat.get
3593
+ term.setMaximumReheatAirTemperature(max_reheat_c)
3594
+ elsif sc.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized
3595
+ # No control option available
3596
+ elsif sc.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
3597
+ # No control option available
3598
+ elsif sc.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.is_initialized
3599
+ term = sc.to_AirTerminalSingleDuctVAVHeatAndCoolReheat.get
3600
+ term.setMaximumReheatAirTemperature(max_reheat_c)
3601
+ elsif sc.to_AirTerminalSingleDuctVAVReheat.is_initialized
3602
+ term = sc.to_AirTerminalSingleDuctVAVReheat.get
3603
+ term.setMaximumReheatAirTemperature(max_reheat_c)
3604
+ end
3605
+ end
3606
+
3607
+ max_reheat_f = OpenStudio.convert(max_reheat_c, 'C', 'F').get
3608
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: reheat terminal maximum set to #{max_reheat_f.round} F.")
3609
+
3610
+ return true
3611
+ end
3612
+
3613
+ # Set the system sizing properties based on the zone sizing information
3614
+ #
3615
+ # @return [Bool] true if successful, false if not.
3616
+ def apply_prm_sizing_temperatures
3617
+ # Get the design heating and cooling SAT information
3618
+ # for all zones served by the system.
3619
+ htg_setpts_c = []
3620
+ clg_setpts_c = []
3621
+ thermalZones.each do |zone|
3622
+ sizing_zone = zone.sizingZone
3623
+ htg_setpts_c << sizing_zone.zoneHeatingDesignSupplyAirTemperature
3624
+ clg_setpts_c << sizing_zone.zoneCoolingDesignSupplyAirTemperature
3625
+ end
3626
+
3627
+ # Cooling SAT set to minimum zone cooling design SAT
3628
+ clg_sat_c = clg_setpts_c.min
3629
+
3630
+ # If the system has terminal reheat,
3631
+ # heating SAT is set to the same value as cooling SAT
3632
+ # and the terminals are expected to do the heating.
3633
+ # If not, heating SAT set to maximum zone heating design SAT.
3634
+ has_term_rht = terminal_reheat?
3635
+ htg_sat_c = if has_term_rht
3636
+ clg_sat_c
3637
+ else
3638
+ htg_setpts_c.max
3639
+ end
3640
+
3641
+ # Set the central SAT values
3642
+ sizing_system = sizingSystem
3643
+ sizing_system.setCentralCoolingDesignSupplyAirTemperature(clg_sat_c)
3644
+ sizing_system.setCentralHeatingDesignSupplyAirTemperature(htg_sat_c)
3645
+
3646
+ clg_sat_f = OpenStudio.convert(clg_sat_c, 'C', 'F').get
3647
+ htg_sat_f = OpenStudio.convert(htg_sat_c, 'C', 'F').get
3648
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{name}: central heating SAT set to #{htg_sat_f.round} F, cooling SAT set to #{clg_sat_f.round} F.")
3649
+
3650
+ # If it's a terminal reheat system, set the reheat terminal setpoints too
3651
+ if has_term_rht
3652
+ rht_c = htg_setpts_c.max
3653
+ apply_maximum_reheat_temperature(rht_c)
3654
+ end
3500
3655
 
3656
+ return true
3501
3657
  end
3502
3658
 
3659
+ # Determine if every zone on the system has an identical
3660
+ # multiplier. If so, return this number. If not, return 1.
3661
+ # @return [Integer] an integer representing the system multiplier.
3662
+ def system_multiplier
3663
+ mult = 1
3664
+
3665
+ # Get all the zone multipliers
3666
+ zn_mults = []
3667
+ thermalZones.each do |zone|
3668
+ zn_mults << zone.multiplier
3669
+ end
3670
+
3671
+ # Warn if there are different multipliers
3672
+ uniq_mults = zn_mults.uniq
3673
+ if uniq_mults.size > 1
3674
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{name}: not all zones on the system have an identical zone multiplier. Multipliers are: #{uniq_mults.join(', ')}.")
3675
+ else
3676
+ mult = uniq_mults[0]
3677
+ end
3678
+
3679
+ return mult
3680
+ end
3503
3681
  end