openstudio-standards 0.2.12.rc4 → 0.2.12.rc5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/OpenStudio_Standards-ashrae_90_1(space_types).xlsx +0 -0
  3. data/data/standards/OpenStudio_Standards-ashrae_90_1.xlsx +0 -0
  4. data/data/standards/test_performance_expected_dd_results.csv +950 -950
  5. data/lib/openstudio-standards.rb +8 -1
  6. data/lib/openstudio-standards/btap/btap.model.rb +1 -1
  7. data/lib/openstudio-standards/btap/economics.rb +14 -11
  8. data/lib/openstudio-standards/btap/envelope.rb +185 -257
  9. data/lib/openstudio-standards/btap/fileio.rb +1 -0
  10. data/lib/openstudio-standards/btap/geometry.rb +21 -1
  11. data/lib/openstudio-standards/btap/measures.rb +12 -11
  12. data/lib/openstudio-standards/btap/schedules.rb +3 -12
  13. data/lib/openstudio-standards/hvac_sizing/Siz.AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.rb +178 -0
  14. data/lib/openstudio-standards/hvac_sizing/Siz.CoilCoolingDXMultiSpeed.rb +8 -8
  15. data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +3 -0
  16. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +3 -3
  17. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +25 -23
  18. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXMultiSpeed.rb +91 -0
  19. data/lib/openstudio-standards/standards/Standards.CoilDX.rb +20 -2
  20. data/lib/openstudio-standards/standards/Standards.CoilHeatingGasMultiStage.rb +39 -0
  21. data/lib/openstudio-standards/standards/Standards.Model.rb +29 -0
  22. data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +37 -4
  23. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.unitary_acs.json +15 -15
  24. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.unitary_acs.json +15 -15
  25. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.unitary_acs.json +5 -5
  26. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.unitary_acs.json +15 -15
  27. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.spc_typ.json +5963 -2723
  28. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.spc_typ.json +5917 -2697
  29. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/data/nrel_zne_ready_2017.spc_typ.json +2011 -1112
  30. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/data/ze_aedg_multifamily.spc_typ.json +1946 -1106
  31. data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/btap_1980to2010.rb +2 -18
  32. data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/space_types.json +1677 -1005
  33. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +64 -13
  34. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/building_envelope.rb +31 -19
  35. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/curves.json +75 -0
  36. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/heat_pumps.json +16 -16
  37. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/space_types.json +1677 -1005
  38. data/lib/openstudio-standards/standards/necb/ECMS/data/boiler_set.json +29 -0
  39. data/lib/openstudio-standards/standards/necb/ECMS/data/curves.json +913 -0
  40. data/lib/openstudio-standards/standards/necb/ECMS/data/equip_eff_lim.json +52 -0
  41. data/lib/openstudio-standards/standards/necb/ECMS/data/erv.json +105 -0
  42. data/lib/openstudio-standards/standards/necb/ECMS/data/furnace_set.json +23 -0
  43. data/lib/openstudio-standards/standards/necb/ECMS/data/heat_pumps.json +803 -0
  44. data/lib/openstudio-standards/standards/necb/ECMS/data/heat_pumps_heating.json +787 -0
  45. data/lib/openstudio-standards/standards/necb/ECMS/data/shw_set.json +29 -0
  46. data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +87 -0
  47. data/lib/openstudio-standards/standards/necb/ECMS/erv.rb +22 -0
  48. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +1593 -0
  49. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +68 -33
  50. data/lib/openstudio-standards/standards/necb/NECB2011/beps_compliance_path.rb +24 -13
  51. data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +104 -99
  52. data/lib/openstudio-standards/standards/necb/NECB2011/data/constants.json +24 -24
  53. data/lib/openstudio-standards/standards/necb/NECB2011/data/curves.json +50 -0
  54. data/lib/openstudio-standards/standards/necb/NECB2011/data/erv.json +31 -0
  55. data/lib/openstudio-standards/standards/necb/NECB2011/data/led_lighting_data.json +2028 -0
  56. data/lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json +1745 -1297
  57. data/lib/openstudio-standards/standards/necb/NECB2011/daylighting_control.md +70 -0
  58. data/lib/openstudio-standards/standards/necb/NECB2011/demand_controlled_ventilation.md +46 -0
  59. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_multi_speed.rb +69 -107
  60. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb +24 -1
  61. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb +139 -141
  62. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb +24 -0
  63. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +344 -234
  64. data/lib/openstudio-standards/standards/necb/NECB2011/led_lighting.md +51 -0
  65. data/lib/openstudio-standards/standards/necb/NECB2011/lighting.rb +57 -9
  66. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +1060 -34
  67. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +9 -1
  68. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +1 -1
  69. data/lib/openstudio-standards/standards/necb/NECB2015/data/led_lighting_data.json +2883 -0
  70. data/lib/openstudio-standards/standards/necb/NECB2015/data/space_types.json +2554 -1916
  71. data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +32 -1
  72. data/lib/openstudio-standards/standards/necb/NECB2017/data/led_lighting_data.json +2883 -0
  73. data/lib/openstudio-standards/standards/necb/NECB2017/data/space_types.json +2554 -1916
  74. data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +29 -0
  75. data/lib/openstudio-standards/version.rb +1 -1
  76. metadata +21 -2
@@ -0,0 +1,29 @@
1
+ {
2
+ "tables": {
3
+ "shw_eff_ecm": {
4
+ "refs": [
5
+ "assumption"
6
+ ],
7
+ "table": [
8
+ {
9
+ "name": "NECB_Default",
10
+ "efficiency": null,
11
+ "part_load_curve": null,
12
+ "notes": "Do nothing."
13
+ },
14
+ {
15
+ "name": "Natural Gas Power Vent with Electric Ignition",
16
+ "efficiency": 0.94,
17
+ "part_load_curve": "SWH-EFFFPLR-NECB2011",
18
+ "notes": "From AHRI."
19
+ },
20
+ {
21
+ "name": "Natural Gas Direct Vent with Electric Ignition",
22
+ "efficiency": 0.91,
23
+ "part_load_curve": "SWH-EFFFPLR-NECB2011",
24
+ "notes": "From AHRI."
25
+ }
26
+ ]
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,87 @@
1
+ class ECMS < NECB2011
2
+
3
+ @template = self.new.class.name
4
+ register_standard(@template)
5
+
6
+ # Combine the data from the JSON files into a single hash
7
+ # Load JSON files differently depending on whether loading from
8
+ # the OpenStudio CLI embedded filesystem or from typical gem installation
9
+ def load_standards_database_new()
10
+ @standards_data = {}
11
+ @standards_data["tables"] = {}
12
+
13
+ if __dir__[0] == ':' # Running from OpenStudio CLI
14
+ embedded_files_relative('data/', /.*\.json/).each do |file|
15
+ data = JSON.parse(EmbeddedScripting.getFileAsString(file))
16
+ if not data["tables"].nil? and data["tables"].first["data_type"] == "table"
17
+ @standards_data["tables"] << data["tables"].first
18
+ else
19
+ @standards_data[data.keys.first] = data[data.keys.first]
20
+ end
21
+ end
22
+ else
23
+ files = Dir.glob("#{File.dirname(__FILE__)}/data/*.json").select { |e| File.file? e }
24
+ files.each do |file|
25
+ data = JSON.parse(File.read(file))
26
+ if not data["tables"].nil?
27
+ @standards_data["tables"] = [*@standards_data["tables"], *data["tables"]].to_h
28
+ else
29
+ @standards_data[data.keys.first] = data[data.keys.first]
30
+ end
31
+ end
32
+ end
33
+
34
+ return @standards_data
35
+ end
36
+
37
+ def initialize
38
+ super()
39
+ @template = self.class.name
40
+ @standards_data = self.load_standards_database_new()
41
+ @standards_data['curves'] = standards_data['tables']["curves"]['table']
42
+ end
43
+
44
+ def apply_system_ecm(model:, ecm_system_name: nil, template_standard:, runner: nil)
45
+ # Do nothing if nil or other usual suspects.. covering all bases for now.
46
+ return if ecm_system_name.nil? || ecm_system_name == 'none' || ecm_system_name == 'NECB_Default'
47
+
48
+ ecm_std = Standard.build("ECMS")
49
+ systems = model.getAirLoopHVACs
50
+ map_system_to_zones, system_doas_flags = ecm_std.get_map_systems_to_zones(systems)
51
+ zone_clg_eqpt_type = ecm_std.get_zone_clg_eqpt_type(model)
52
+ # when the ecm is associated with adding a new HVAC system, then remove existing system components and loops
53
+ ecm_add_method_name = "add_ecm_#{ecm_system_name.downcase}"
54
+
55
+ raise("the method #{ecm_add_method_name} does not exist in the ECM class. Please verify that this should be called.") unless ecm_std.respond_to? ecm_add_method_name
56
+
57
+ ecm_std.remove_all_zone_eqpt(systems)
58
+ ecm_std.remove_air_loops(model)
59
+ ecm_std.remove_hw_loops(model)
60
+ ecm_std.remove_chw_loops(model)
61
+ ecm_std.remove_cw_loops(model)
62
+ ecm_std.send(ecm_add_method_name,
63
+ model: model,
64
+ system_zones_map: map_system_to_zones,
65
+ system_doas_flags: system_doas_flags,
66
+ zone_clg_eqpt_type: zone_clg_eqpt_type,
67
+ standard: template_standard)
68
+ #ecm_std.add_ecm_hs09_ccashpsys(model:model,system_zones_map:,system_doas_flags:,zone_clg_eqpt_type: nil,standard:,baseboard_flag: true)
69
+
70
+ end
71
+
72
+ def apply_system_efficiencies_ecm(model:, ecm_system_name: nil)
73
+ # Do nothing if nil.
74
+ return if ecm_system_name.nil? || ecm_system_name == 'none' || ecm_system_name == 'NECB_Default'
75
+ ecm_std = Standard.build("ECMS")
76
+ # Get method name that should be present in the ECM class.
77
+ ecm_apply_eff_method_name = "apply_efficiency_ecm_#{ecm_system_name.downcase}"
78
+ # Raise exception if method does not exists.
79
+ raise("the method #{ecm_apply_eff_method_name} does not exist in the ECM class. Please verify that this should be called.") unless ecm_std.respond_to?(ecm_apply_eff_method_name)
80
+
81
+ # apply system eff method.
82
+ ecm_std.send(ecm_apply_eff_method_name, model: model, ecm_name: ecm_system_name)
83
+ end
84
+
85
+
86
+
87
+ end
@@ -0,0 +1,22 @@
1
+ class ECMS
2
+ # This method will add a skeleton erv to all air loops.
3
+ def apply_erv_ecm(model:, erv_package: nil)
4
+ # If erv is nil.. do nothing.
5
+ return if erv_package.nil? || erv_package == 'none' || erv_package == 'NECB_Default'
6
+
7
+ model.getAirLoopHVACs.each do |air_loop|
8
+ # Adds default erv to all air_loops
9
+ air_loop_hvac_apply_energy_recovery_ventilator(air_loop, nil)
10
+ end
11
+ end
12
+
13
+ # This method will set the properties of the ERV that was added above. Must be run after the standard efficiency is complete as this will overwrite
14
+ # those values. See data/erv.json to view/add different erv packages.
15
+ def apply_erv_ecm_efficiency(model:, erv_package: nil)
16
+ # If erv is nil.. do nothing.
17
+ return if erv_package.nil? || erv_package == 'none' || erv_package == 'NECB_Default'
18
+
19
+ # This calls the NECB2011 implementation of the method.
20
+ model.getHeatExchangerAirToAirSensibleAndLatents.each { |erv| heat_exchanger_air_to_air_sensible_and_latent_apply_efficiency(erv, erv_package) }
21
+ end
22
+ end
@@ -0,0 +1,1593 @@
1
+ class ECMS
2
+
3
+ # =============================================================================================================================
4
+ # Remove existing zone equipment
5
+ def remove_all_zone_eqpt(sys_objs)
6
+ sys_objs.each do |isys|
7
+ isys.thermalZones.each do |izone|
8
+ if(izone.equipment.empty?) then next end
9
+ izone.equipment.each {|icomp| icomp.remove}
10
+ end
11
+ end
12
+ end
13
+
14
+ # =============================================================================================================================
15
+ # Remove hot-water plant loops
16
+ def remove_hw_loops(model)
17
+ model.getPlantLoops.each do |iloop|
18
+ hw_loop = false
19
+ iloop.supplyComponents.each do |icomp|
20
+ if(icomp.to_BoilerHotWater.is_initialized)
21
+ hw_loop = true
22
+ break
23
+ end
24
+ end
25
+ if hw_loop then iloop.remove end
26
+ end
27
+ end
28
+
29
+ # =============================================================================================================================
30
+ # Remove chilled-water plant loops
31
+ def remove_chw_loops(model)
32
+ model.getPlantLoops.each do |iloop|
33
+ chw_loop = false
34
+ iloop.supplyComponents.each do |icomp|
35
+ if(icomp.to_ChillerElectricEIR.is_initialized)
36
+ chw_loop = true
37
+ break
38
+ end
39
+ end
40
+ if chw_loop then iloop.remove end
41
+ end
42
+ end
43
+
44
+ # =============================================================================================================================
45
+ # Remove condenser-water plant loops
46
+ def remove_cw_loops(model)
47
+ model.getPlantLoops.each do |iloop|
48
+ cw_loop = false
49
+ iloop.supplyComponents.each do |icomp|
50
+ if(icomp.to_CoolingTowerSingleSpeed.is_initialized)
51
+ cw_loop = true
52
+ end
53
+ end
54
+ if cw_loop then iloop.remove end
55
+ end
56
+ end
57
+
58
+ # =============================================================================================================================
59
+ # Remove air loops
60
+ def remove_air_loops(model)
61
+ # remove air loops
62
+ model.getAirLoopHVACs.each do |iloop|
63
+ iloop.remove
64
+ end
65
+ end
66
+
67
+ # =============================================================================================================================
68
+ # Return map of systems to zones and set flag for dedicated outdoor air unit for each system
69
+ def get_map_systems_to_zones(systems)
70
+ map_systems_to_zones = {}
71
+ system_doas_flags = {}
72
+ systems.each do |system|
73
+ zones = system.thermalZones
74
+ map_systems_to_zones[system.name.to_s] = zones
75
+ if system.sizingSystem.typeofLoadtoSizeOn.to_s == "VentilationRequirement"
76
+ system_doas_flags[system.name.to_s] = true
77
+ else
78
+ system_doas_flags[system.name.to_s] = false
79
+ end
80
+ end
81
+ return map_systems_to_zones,system_doas_flags
82
+ end
83
+
84
+ # =============================================================================================================================
85
+ # Return hash of zone and cooling equipment type in the zone
86
+ def get_zone_clg_eqpt_type(model)
87
+ zone_clg_eqpt_type = {}
88
+ model.getThermalZones.each do |zone|
89
+ zone.equipment.each do |eqpt|
90
+ if eqpt.to_ZoneHVACPackagedTerminalAirConditioner.is_initialized
91
+ zone_clg_eqpt_type[zone.name.to_s] = "ZoneHVACPackagedTerminalAirConditioner"
92
+ break
93
+ end
94
+ end
95
+ end
96
+ return zone_clg_eqpt_type
97
+ end
98
+
99
+ # =============================================================================================================================
100
+ # Return hash of flags for whether storey is conditioned and average ceiling z-coordinates of building storeys.
101
+ def get_storey_avg_clg_zcoords(model)
102
+ storey_avg_clg_zcoords = {}
103
+ model.getBuildingStorys.each do |storey|
104
+ storey_avg_clg_zcoords[storey] = []
105
+ storey_cond = false
106
+ total_area = 0.0
107
+ sum = 0.0
108
+ storey.spaces.each do |space|
109
+ # Determine if any of the spaces/zones of the storey are conditioned? If yes then the floor is considered to be conditioned
110
+ if space.thermalZone.is_initialized
111
+ zone = space.thermalZone.get
112
+ if zone.thermostat.is_initialized
113
+ if zone.thermostat.get.to_ThermostatSetpointDualSetpoint.is_initialized
114
+ if zone.thermostat.get.to_ThermostatSetpointDualSetpoint.get.heatingSetpointTemperatureSchedule.is_initialized ||
115
+ zone.thermostat.get.to_ThermostatSetpointDualSetpoint.get.coolingSetpointTemperatureSchedule.is_initialized
116
+ storey_cond = true
117
+ end
118
+ end
119
+ end
120
+ end
121
+ # Find average height of z-coordinates of ceiling/roof of floor
122
+ space.surfaces.each do |surf|
123
+ if (surf.surfaceType.to_s.upcase == "ROOFCEILING")
124
+ sum += (surf.centroid.z.to_f + space.zOrigin.to_f) * surf.grossArea.to_f
125
+ total_area += surf.grossArea.to_f
126
+ end
127
+ end
128
+ end
129
+ storey_avg_clg_zcoords[storey] << storey_cond
130
+ storey_avg_clg_zcoords[storey] << (sum / total_area)
131
+ end
132
+
133
+ return storey_avg_clg_zcoords
134
+ end
135
+
136
+ # =============================================================================================================================
137
+ # Return x,y,z coordinates of exterior wall with largest area on the lowest floor
138
+ def get_lowest_floor_ext_wall_centroid_coords(storeys_clg_zcoords)
139
+ ext_wall,ext_wall_x,ext_wall_y,ext_wall_z = nil,nil,nil,nil
140
+ storeys_clg_zcoords.keys.each do |storey|
141
+ max_area = 0.0
142
+ sorted_spaces = storey.spaces.sort_by {|space| space.name.to_s}
143
+ sorted_spaces.each do |space|
144
+ ext_walls = space.surfaces.select {|surf| (surf.surfaceType.to_s.upcase == "WALL") && (surf.outsideBoundaryCondition.to_s.upcase == "OUTDOORS")}
145
+ ext_walls = ext_walls.sort_by {|wall| wall.grossArea.to_f}
146
+ if not ext_walls.empty?
147
+ if ext_walls.last.grossArea.to_f > max_area
148
+ max_area = ext_walls.last.grossArea.to_f
149
+ ext_wall_x = ext_walls.last.centroid.x.to_f + space.xOrigin.to_f
150
+ ext_wall_y = ext_walls.last.centroid.y.to_f + space.yOrigin.to_f
151
+ ext_wall_z = ext_walls.last.centroid.z.to_f + space.zOrigin.to_f
152
+ ext_wall = ext_walls.last
153
+ end
154
+ end
155
+ end
156
+ break unless not ext_wall
157
+ end
158
+ if not ext_wall
159
+ OpenStudio.logFree(OpenStudio::Info, 'openstudiostandards.get_lowest_floor_ext_wall_centroid_coords','Did not find an exteior wall in the building!')
160
+ end
161
+
162
+ return ext_wall_x,ext_wall_y,ext_wall_z
163
+ end
164
+
165
+ # =============================================================================================================================
166
+ # Return x,y,z coordinates of space centroid
167
+ def get_space_centroid_coords(space)
168
+ total_area = 0.0
169
+ sum_x,sum_y,sum_z = 0.0,0.0,0.0
170
+ space.surfaces.each do |surf|
171
+ total_area += surf.grossArea.to_f
172
+ sum_x += (surf.centroid.x.to_f + space.xOrigin.to_f) * surf.grossArea.to_f
173
+ sum_y += (surf.centroid.y.to_f + space.yOrigin.to_f) * surf.grossArea.to_f
174
+ sum_z += (surf.centroid.z.to_f + space.zOrigin.to_f) * surf.grossArea.to_f
175
+ end
176
+ space_centroid_x = sum_x / total_area
177
+ space_centroid_y = sum_y / total_area
178
+ space_centroid_z = sum_z / total_area
179
+
180
+ return space_centroid_x,space_centroid_y,space_centroid_z
181
+ end
182
+
183
+ # =============================================================================================================================
184
+ # Return x,y,z coordinates of the centroid of the roof of the storey
185
+ def get_roof_centroid_coords(storey)
186
+ sum_x,sum_y,sum_z,total_area = 0.0,0.0,0.0,0.0
187
+ cent_x,cent_y,cent_z = nil,nil,nil
188
+ storey.spaces.each do |space|
189
+ roof_surfaces = space.surfaces.select {|surf| (surf.surfaceType.to_s.upcase == "ROOFCEILING") && (surf.outsideBoundaryCondition.to_s.upcase == "OUTDOORS")}
190
+ roof_surfaces.each do |surf|
191
+ sum_x += (surf.centroid.x.to_f + space.xOrigin.to_f) * surf.grossArea.to_f
192
+ sum_y += (surf.centroid.y.to_f + space.yOrigin.to_f) * surf.grossArea.to_f
193
+ sum_z += (surf.centroid.z.to_f + space.zOrigin.to_f) * surf.grossArea.to_f
194
+ total_area += surf.grossArea.to_f
195
+ end
196
+ end
197
+ if total_area > 0.0
198
+ cent_x = sum_x / total_area
199
+ cent_y = sum_y / total_area
200
+ cent_z = sum_z / total_area
201
+ else
202
+ OpenStudio.logFree(OpenStudio::Info, 'openstudiostandards.get_roof_centroid_coords','Did not find a roof on the top floor!')
203
+ end
204
+
205
+ return cent_x,cent_y,cent_z
206
+ end
207
+
208
+ # =============================================================================================================================
209
+ # Determine maximum equivalent and net vertical pipe runs for VRF model
210
+ def get_max_vrf_pipe_lengths(model)
211
+ # Get and sort floors average ceilings z-coordinates hash
212
+ storeys_clg_zcoords = get_storey_avg_clg_zcoords(model)
213
+ storeys_clg_zcoords = storeys_clg_zcoords.sort_by {|key,value| value[1]}.to_h # sort storeys hash based on ceiling/roof z-coordinate
214
+ if storeys_clg_zcoords.values.last[0]
215
+ # If the top floor is conditioned, then assume the top floor is not an attic floor and place the VRF outdoor unit at the roof centroid
216
+ location_cent_x,location_cent_y,location_cent_z = get_roof_centroid_coords(storeys_clg_zcoords.keys.last)
217
+ else
218
+ # If the top floor is not conditioned, then assume it's an attic floor. In this case place the VRF outdoor unit next to the centroid
219
+ # of the exterior wall with the largest area on the lowest floor.
220
+ location_cent_x,location_cent_y,location_cent_z = get_lowest_floor_ext_wall_centroid_coords(storeys_clg_zcoords)
221
+ end
222
+ # Initialize distances
223
+ max_equiv_distance = 0.0
224
+ max_vert_distance = 0.0
225
+ min_vert_distance = 0.0
226
+ storeys_clg_zcoords.keys.each do |storey|
227
+ next unless storeys_clg_zcoords[storey][0]
228
+ storey.spaces.each do |space|
229
+ # Is there a VRF terminal unit in the space/zone?
230
+ vrf_term_units = []
231
+ if space.thermalZone.is_initialized
232
+ vrf_term_units = space.thermalZone.get.equipment.select {|eqpt| eqpt.to_ZoneHVACTerminalUnitVariableRefrigerantFlow.is_initialized}
233
+ end
234
+ next unless not vrf_term_units.empty?
235
+ space_centroid_x,space_centroid_y,space_centroid_z = get_space_centroid_coords(space)
236
+ # Update max horizontal and vertical distances if needed
237
+ equiv_distance = (location_cent_x.to_f - space_centroid_x.to_f).abs +
238
+ (location_cent_y.to_f - space_centroid_y.to_f).abs +
239
+ (location_cent_z.to_f - space_centroid_z.to_f).abs
240
+ if equiv_distance > max_equiv_distance then max_equiv_distance = equiv_distance end
241
+ pos_vert_distance = [space_centroid_z.to_f-location_cent_z.to_f,0.0].max
242
+ if pos_vert_distance > max_vert_distance then max_vert_distance = pos_vert_distance end
243
+ neg_vert_distance = [space_centroid_z.to_f-location_cent_z.to_f,0.0].min
244
+ if neg_vert_distance < min_vert_distance then min_vert_distance = neg_vert_distance end
245
+ end
246
+ end
247
+ max_net_vert_distance = max_vert_distance + min_vert_distance
248
+ max_net_vert_distance = [max_net_vert_distance,0.000001].max
249
+
250
+ return max_equiv_distance,max_net_vert_distance
251
+ end
252
+
253
+ # =============================================================================================================================
254
+ # Add an outdoor VRF unit
255
+ def add_outdoor_vrf_unit(model:,ecm_name: nil,condenser_type: "AirCooled")
256
+ outdoor_vrf_unit = OpenStudio::Model::AirConditionerVariableRefrigerantFlow.new(model)
257
+ outdoor_vrf_unit.setName("VRF Outdoor Unit")
258
+ outdoor_vrf_unit.setHeatPumpWasteHeatRecovery(true)
259
+ outdoor_vrf_unit.setRatedHeatingCOP(4.0)
260
+ outdoor_vrf_unit.setRatedCoolingCOP(4.0)
261
+ outdoor_vrf_unit.setMinimumOutdoorTemperatureinHeatingMode(-25.0)
262
+ outdoor_vrf_unit.setHeatingPerformanceCurveOutdoorTemperatureType("WetBulbTemperature")
263
+ outdoor_vrf_unit.setMasterThermostatPriorityControlType("ThermostatOffsetPriority")
264
+ outdoor_vrf_unit.setDefrostControl('OnDemand')
265
+ outdoor_vrf_unit.setDefrostStrategy('ReverseCycle')
266
+ outdoor_vrf_unit.autosizeResistiveDefrostHeaterCapacity
267
+ outdoor_vrf_unit.setPipingCorrectionFactorforHeightinHeatingModeCoefficient(-0.00019231)
268
+ outdoor_vrf_unit.setPipingCorrectionFactorforHeightinCoolingModeCoefficient(-0.00019231)
269
+ outdoor_vrf_unit.setMinimumOutdoorTemperatureinHeatRecoveryMode(-5.0)
270
+ outdoor_vrf_unit.setMaximumOutdoorTemperatureinHeatRecoveryMode(26.2)
271
+ outdoor_vrf_unit.setInitialHeatRecoveryCoolingCapacityFraction(0.5)
272
+ outdoor_vrf_unit.setHeatRecoveryCoolingCapacityTimeConstant(0.15)
273
+ outdoor_vrf_unit.setInitialHeatRecoveryCoolingEnergyFraction(1.0)
274
+ outdoor_vrf_unit.setHeatRecoveryCoolingEnergyTimeConstant(0.0)
275
+ outdoor_vrf_unit.setInitialHeatRecoveryHeatingCapacityFraction(1.0)
276
+ outdoor_vrf_unit.setHeatRecoveryHeatingCapacityTimeConstant(0.15)
277
+ outdoor_vrf_unit.setInitialHeatRecoveryHeatingEnergyFraction(1.0)
278
+ outdoor_vrf_unit.setHeatRecoveryCoolingEnergyTimeConstant(0.0)
279
+ outdoor_vrf_unit.setMinimumHeatPumpPartLoadRatio(0.5)
280
+ outdoor_vrf_unit.setCondenserType(condenser_type)
281
+ outdoor_vrf_unit.setCrankcaseHeaterPowerperCompressor(0.001)
282
+ heat_defrost_eir_ft = nil
283
+ if ecm_name
284
+ search_criteria = coil_dx_find_search_criteria(outdoor_vrf_unit)
285
+ props = model_find_object(standards_data['tables']["heat_pumps_heating_ecm_#{ecm_name.downcase}"]['table'], search_criteria, 1.0, Date.today)
286
+ heat_defrost_eir_ft = model_add_curve(model, props['heat_defrost_eir_ft'])
287
+ end
288
+ if heat_defrost_eir_ft
289
+ outdoor_vrf_unit.setDefrostEnergyInputRatioModifierFunctionofTemperatureCurve(heat_defrost_eir_ft)
290
+ else
291
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{outdoor_vrf_unit.name}, cannot find heat_defrost_eir_ft curve, will not be set.")
292
+ end
293
+
294
+ return outdoor_vrf_unit
295
+ end
296
+
297
+ # =============================================================================================================================
298
+ # Add indoor VRF units and update horizontal and vertical pipe runs for outdoor VRF unit
299
+ def add_indoor_vrf_units(model:,system_zones_map:,outdoor_vrf_unit:)
300
+ always_on = model.alwaysOnDiscreteSchedule
301
+ always_off = model.alwaysOffDiscreteSchedule
302
+ system_zones_map.sort.each do |sname,zones|
303
+ zones.sort.each do |izone|
304
+ zone_vrf_fan = OpenStudio::Model::FanOnOff.new(model, always_on)
305
+ zone_vrf_fan.setName("#{izone.name} VRF Fan")
306
+ zone_vrf_clg_coil = OpenStudio::Model::CoilCoolingDXVariableRefrigerantFlow.new(model)
307
+ zone_vrf_clg_coil.setName("#{izone.name} VRF Clg Coil")
308
+ zone_vrf_htg_coil = OpenStudio::Model::CoilHeatingDXVariableRefrigerantFlow.new(model)
309
+ zone_vrf_htg_coil.setName("#{izone.name} VRF Htg Coil")
310
+ zone_vrf_unit = OpenStudio::Model::ZoneHVACTerminalUnitVariableRefrigerantFlow.new(model,zone_vrf_clg_coil,zone_vrf_htg_coil,zone_vrf_fan)
311
+ zone_vrf_unit.setName("#{izone.name} VRF Indoor Unit")
312
+ zone_vrf_unit.setOutdoorAirFlowRateDuringCoolingOperation(0.000001)
313
+ zone_vrf_unit.setOutdoorAirFlowRateDuringHeatingOperation(0.000001)
314
+ zone_vrf_unit.setOutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded(0.000001)
315
+ zone_vrf_unit.setZoneTerminalUnitOffParasiticElectricEnergyUse(0.000001)
316
+ zone_vrf_unit.setZoneTerminalUnitOnParasiticElectricEnergyUse(0.000001)
317
+ zone_vrf_unit.setSupplyAirFanOperatingModeSchedule(always_off)
318
+ zone_vrf_unit.setRatedTotalHeatingCapacitySizingRatio(1.3)
319
+ zone_vrf_unit.addToThermalZone(izone)
320
+ outdoor_vrf_unit.addTerminal(zone_vrf_unit)
321
+ # VRF terminal unit does not have a backup coil, use a unit heater as backup coil
322
+ zone_unitheater_fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) # OS does not support an OnOff fan for unit heaters
323
+ zone_unitheater_fan.setName("#{izone.name} Unit Heater Fan")
324
+ zone_unitheater_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on)
325
+ zone_unitheater_htg_coil.setName("#{izone.name} Unit Heater Htg Coil")
326
+ zone_unit_heater = OpenStudio::Model::ZoneHVACUnitHeater.new(model,always_on,zone_unitheater_fan,zone_unitheater_htg_coil)
327
+ zone_unit_heater.setName("#{izone.name} Unit Heater")
328
+ zone_unit_heater.setFanControlType("OnOff")
329
+ zone_unit_heater.addToThermalZone(izone)
330
+ end
331
+ end
332
+ # Now we can find and apply maximum horizontal and vertical distances between outdoor vrf unit and zones with vrf terminal units
333
+ max_hor_pipe_length,max_vert_pipe_length = get_max_vrf_pipe_lengths(model)
334
+ #raise("test1:#{max_hor_pipe_length},#{max_vert_pipe_length}")
335
+ outdoor_vrf_unit.setEquivalentPipingLengthusedforPipingCorrectionFactorinCoolingMode(max_hor_pipe_length)
336
+ outdoor_vrf_unit.setEquivalentPipingLengthusedforPipingCorrectionFactorinHeatingMode(max_hor_pipe_length)
337
+ outdoor_vrf_unit.setVerticalHeightusedforPipingCorrectionFactor(max_vert_pipe_length)
338
+ end
339
+
340
+ # =============================================================================================================================
341
+ # Add a dedicated outside air loop with cold-climate heat pump with electric backup
342
+ # Add cold-climate zonal terminal VRF units
343
+ def add_ecm_hs08_vrfzonal(model:,system_zones_map:,system_doas_flags:,zone_clg_eqpt_type:, standard:)
344
+ # Update system doas flags
345
+ system_doas_flags.keys.each {|sname| system_doas_flags[sname] = true}
346
+ # Add doas with cold-climate air-source heat pump and electric backup
347
+ add_ecm_hs09_ccashpsys(model: model,system_zones_map: system_zones_map,system_doas_flags: system_doas_flags,standard: standard,baseboard_flag: false)
348
+ # Add outdoor VRF unit
349
+ outdoor_vrf_unit = add_outdoor_vrf_unit(model: model,ecm_name: "hs08_vrfzonal")
350
+ # Add indoor VRF terminal units
351
+ add_indoor_vrf_units(model: model,system_zones_map: system_zones_map,outdoor_vrf_unit: outdoor_vrf_unit)
352
+ end
353
+
354
+ # =============================================================================================================================
355
+ # Apply efficiencies and performance curves for ECM 'hs08_vrfzonal'
356
+ def apply_efficiency_ecm_hs08_vrfzonal(model:,ecm_name:)
357
+ # Use same performance data as ECM "hs09_ccashpsys" for air system
358
+ apply_efficiency_ecm_hs09_ccashpsys(model: model,ecm_name: "hs09_ccashpsys")
359
+ # Apply efficiency and curves for VRF units
360
+ model.getAirConditionerVariableRefrigerantFlows.sort.each do |vrf_unit|
361
+ airconditioner_variablerefrigerantflow_cooling_apply_efficiency_and_curves(vrf_unit,ecm_name)
362
+ airconditioner_variablerefrigerantflow_heating_apply_efficiency_and_curves(vrf_unit,ecm_name)
363
+ end
364
+ # Set fan size of VRF terminal units
365
+ fan_power_per_flow_rate = 150.0 # based on Mitsubishi data: 100 low and 200 high (W-s/m3)
366
+ model.getZoneHVACTerminalUnitVariableRefrigerantFlows.each do |iunit|
367
+ fan = iunit.supplyAirFan.to_FanOnOff.get
368
+ fan_pr_rise = fan_power_per_flow_rate*(fan.fanEfficiency*fan.motorEfficiency)
369
+ fan.setPressureRise(fan_pr_rise)
370
+ end
371
+ # Set fan size of unit heaters
372
+ model.getZoneHVACUnitHeaters.each do |iunit|
373
+ fan = iunit.supplyAirFan.to_FanConstantVolume.get
374
+ fan_pr_rise = fan_power_per_flow_rate*(fan.fanEfficiency*fan.motorEfficiency)
375
+ fan.setPressureRise(fan_pr_rise)
376
+ end
377
+ end
378
+
379
+ # =============================================================================================================================
380
+ # Add air loops with cold-climate heat pump with electric backup coil.
381
+ # Add zone electric baseboards
382
+ def add_ecm_hs09_ccashpsys(model:,system_zones_map:,system_doas_flags:,zone_clg_eqpt_type: nil,standard:,baseboard_flag: true)
383
+ always_on = model.alwaysOnDiscreteSchedule
384
+ always_off = model.alwaysOffDiscreteSchedule
385
+ systems = []
386
+ system_zones_map.sort.each do |sys_name,zones|
387
+ system_data = {}
388
+ system_data[:PreheatDesignTemperature] = 7.0
389
+ system_data[:PreheatDesignHumidityRatio] = 0.008
390
+ system_data[:PrecoolDesignTemperature] = 13.0
391
+ system_data[:PrecoolDesignHumidityRatio] = 0.008
392
+ system_data[:SizingOption] = 'NonCoincident'
393
+ system_data[:CoolingDesignAirFlowMethod] = 'DesignDay'
394
+ system_data[:CoolingDesignAirFlowRate] = 0.0
395
+ system_data[:HeatingDesignAirFlowMethod] = 'DesignDay'
396
+ system_data[:HeatingDesignAirFlowRate] = 0.0
397
+ system_data[:SystemOutdoorAirMethod] = 'ZoneSum'
398
+ system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085
399
+ system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080
400
+ system_data[:MinimumSystemAirFlowRatio] = 1.0
401
+ system_data[:system_supply_air_temperature] = 20.0
402
+ system_data[:ZoneCoolingDesignSupplyAirTemperature] = 13.0
403
+ system_data[:ZoneHeatingDesignSupplyAirTemperature] = 43.0
404
+ system_data[:ZoneCoolingSizingFactor] = 1.1
405
+ system_data[:ZoneHeatingSizingFactor] = 1.3
406
+ if system_doas_flags[sys_name.to_s]
407
+ system_data[:name] = "CCASHP Makeup Air Unit"
408
+ system_data[:AllOutdoorAirinCooling] = true
409
+ system_data[:AllOutdoorAirinHeating] = true
410
+ system_data[:TypeofLoadtoSizeOn] = 'VentilationRequirement'
411
+ system_data[:CentralCoolingDesignSupplyAirTemperature] = 19.9
412
+ system_data[:CentralHeatingDesignSupplyAirTemperature] = 20.0
413
+ sat_sch = OpenStudio::Model::ScheduleRuleset.new(model)
414
+ sat_sch.setName('Makeup-Air Unit Supply Air Temp')
415
+ sat_sch.defaultDaySchedule.setName('Makeup Air Unit Supply Air Temp Default')
416
+ sat_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), system_data[:system_supply_air_temperature])
417
+ setpoint_mgr = OpenStudio::Model::SetpointManagerScheduled.new(model, sat_sch)
418
+ else
419
+ system_data[:name] = "CCASHP System"
420
+ system_data[:AllOutdoorAirinCooling] = false
421
+ system_data[:AllOutdoorAirinHeating] = false
422
+ system_data[:TypeofLoadtoSizeOn] = 'Sensible'
423
+ system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0
424
+ system_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0
425
+ if zones.size == 1
426
+ setpoint_mgr = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model)
427
+ setpoint_mgr.setControlZone(zones[0])
428
+ setpoint_mgr.setMinimumSupplyAirTemperature(13.0)
429
+ setpoint_mgr.setMaximumSupplyAirTemperature(43.0)
430
+ else
431
+ setpoint_mgr = OpenStudio::Model::SetpointManagerWarmest.new(model)
432
+ setpoint_mgr.setMinimumSetpointTemperature(13.0)
433
+ setpoint_mgr.setMaximumSetpointTemperature(43.0)
434
+ end
435
+ end
436
+ airloop = standard.common_air_loop(model: model, system_data: system_data)
437
+ # Fan
438
+ if system_doas_flags[sys_name.to_s] || zones.size == 1
439
+ sys_supply_fan = OpenStudio::Model::FanConstantVolume.new(model)
440
+ else
441
+ sys_supply_fan = OpenStudio::Model::FanVariableVolume.new(model)
442
+ sys_return_fan = OpenStudio::Model::FanVariableVolume.new(model)
443
+ sys_return_fan.setName("System Return Fan")
444
+ end
445
+ sys_supply_fan.setName("System Supply Fan")
446
+ # Cooling coil
447
+ sys_clg_coil = OpenStudio::Model::CoilCoolingDXVariableSpeed.new(model)
448
+ sys_clg_coil.setName("CCASHP DX Clg Coil")
449
+ sys_clg_coil_speeddata1 = OpenStudio::Model::CoilCoolingDXVariableSpeedSpeedData.new(model)
450
+ sys_clg_coil.addSpeed(sys_clg_coil_speeddata1)
451
+ sys_clg_coil.setNominalSpeedLevel(1)
452
+ # Electric supplemental heating coil
453
+ sys_elec_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on)
454
+ sys_elec_htg_coil.setName("CCASHP Elec Htg Coil")
455
+ # DX heating coil
456
+ sys_dx_htg_coil = OpenStudio::Model::CoilHeatingDXVariableSpeed.new(model)
457
+ sys_dx_htg_coil.setName("CCASHP DX Htg Coil")
458
+ sys_dx_htg_coil_speed1 = OpenStudio::Model::CoilHeatingDXVariableSpeedSpeedData.new(model)
459
+ sys_dx_htg_coil.addSpeed(sys_dx_htg_coil_speed1)
460
+ sys_dx_htg_coil.setNominalSpeedLevel(1)
461
+ sys_dx_htg_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(-25.0)
462
+ sys_dx_htg_coil.setDefrostStrategy("ReverseCycle")
463
+ #sys_dx_htg_coil.setDefrostStrategy("Resistive")
464
+ #sys_dx_htg_coil.setResistiveDefrostHeaterCapacity(0.001)
465
+ sys_dx_htg_coil.setDefrostControl("OnDemand")
466
+ sys_dx_htg_coil.setCrankcaseHeaterCapacity(0.001)
467
+ search_criteria = coil_dx_find_search_criteria(sys_dx_htg_coil)
468
+ props = model_find_object(standards_data['tables']["heat_pumps_heating_ecm_hs09_ccashpsys"]['table'], search_criteria, 1.0, Date.today)
469
+ heat_defrost_eir_ft = model_add_curve(model, props['heat_defrost_eir_ft'])
470
+ # This defrost curve has to be assigned here before sizing
471
+ if heat_defrost_eir_ft
472
+ sys_dx_htg_coil.setDefrostEnergyInputRatioFunctionofTemperatureCurve(heat_defrost_eir_ft)
473
+ else
474
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{sys_dx_htg_coil.name}, cannot find heat_defrost_eir_ft curve, will not be set.")
475
+ end
476
+ sys_clg_coil.addToNode(airloop.supplyOutletNode)
477
+ sys_dx_htg_coil.addToNode(airloop.supplyOutletNode)
478
+ sys_elec_htg_coil.addToNode(airloop.supplyOutletNode)
479
+ sys_supply_fan.addToNode(airloop.supplyOutletNode)
480
+ setpoint_mgr.addToNode(airloop.supplyOutletNode)
481
+ # OA controller
482
+ oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model)
483
+ oa_controller.autosizeMinimumOutdoorAirFlowRate
484
+ oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller)
485
+ oa_system.addToNode(airloop.supplyInletNode)
486
+ zones.each do |zone|
487
+ zone.sizingZone.setZoneCoolingDesignSupplyAirTemperature(13.0)
488
+ zone.sizingZone.setZoneHeatingDesignSupplyAirTemperature(43.0)
489
+ zone.sizingZone.setZoneCoolingSizingFactor(1.1)
490
+ zone.sizingZone.setZoneHeatingSizingFactor(1.3)
491
+ if zone_clg_eqpt_type
492
+ case zone_clg_eqpt_type[zone.name.to_s]
493
+ when "ZoneHVACPackagedTerminalAirConditioner"
494
+ standard.add_ptac_dx_cooling(model,zone,true)
495
+ end
496
+ end
497
+ if system_doas_flags[sys_name.to_s] || zones.size == 1
498
+ diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, always_on)
499
+ else
500
+ reheat_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on)
501
+ diffuser = OpenStudio::Model::AirTerminalSingleDuctVAVReheat.new(model, always_on, reheat_coil)
502
+ sys_return_fan.addToNode(airloop.returnAirNode.get)
503
+ diffuser.setFixedMinimumAirFlowRate(0.002 * zone.floorArea )
504
+ diffuser.setMaximumReheatAirTemperature(43.0)
505
+ diffuser.setDamperHeatingAction('Normal')
506
+ end
507
+ airloop.removeBranchForZone(zone)
508
+ airloop.addBranchForZone(zone, diffuser.to_StraightComponent)
509
+ if baseboard_flag then standard.add_zone_baseboards(baseboard_type: 'Electric', hw_loop: nil, model: model, zone: zone) end
510
+ end
511
+ systems << airloop
512
+ end
513
+
514
+ return systems
515
+ end
516
+
517
+ # =============================================================================================================================
518
+ # Apply efficiencies and performance curves for ECM 'hs09_ccashpsys'
519
+ def apply_efficiency_ecm_hs09_ccashpsys(model:,ecm_name:)
520
+ # fraction of electric backup heating coil capacity assigned to dx heating coil
521
+ fr_backup_coil_cap_as_dx_coil_cap = 0.5
522
+ model.getAirLoopHVACs.each do |isys|
523
+ clg_dx_coil = nil
524
+ htg_dx_coil = nil
525
+ backup_coil = nil
526
+ fans = []
527
+ # Find the components on the air loop
528
+ isys.supplyComponents.each do |icomp|
529
+ if icomp.to_CoilCoolingDXVariableSpeed.is_initialized
530
+ clg_dx_coil = icomp.to_CoilCoolingDXVariableSpeed.get
531
+ elsif icomp.to_CoilHeatingDXVariableSpeed.is_initialized
532
+ htg_dx_coil = icomp.to_CoilHeatingDXVariableSpeed.get
533
+ elsif icomp.to_CoilHeatingElectric.is_initialized
534
+ backup_coil = icomp.to_CoilHeatingElectric.get
535
+ elsif icomp.to_FanConstantVolume.is_initialized
536
+ fans << icomp.to_FanConstantVolume.get
537
+ elsif icomp.to_FanVariableVolume.is_initialized
538
+ fans << icomp.to_FanVariableVolume.get
539
+ end
540
+ end
541
+ if clg_dx_coil && htg_dx_coil && backup_coil
542
+ clg_dx_coil_cap = clg_dx_coil.autosizedGrossRatedTotalCoolingCapacityAtSelectedNominalSpeedLevel.to_f
543
+ htg_dx_coil_cap = htg_dx_coil.autosizedRatedHeatingCapacityAtSelectedNominalSpeedLevel.to_f
544
+ backup_coil_cap = backup_coil.autosizedNominalCapacity.to_f
545
+ fan_power = 0.0
546
+ fans.each do |ifan|
547
+ fan_power += ifan.pressureRise.to_f*ifan.autosizedMaximumFlowRate.to_f/ifan.fanEfficiency.to_f
548
+ end
549
+ # Set the DX capacities to the maximum of the fraction of the backup coil capacity or the cooling capacity needed
550
+ dx_cap = fr_backup_coil_cap_as_dx_coil_cap*backup_coil_cap
551
+ if dx_cap < (clg_dx_coil_cap+fan_power) then dx_cap = clg_dx_coil_cap+fan_power end
552
+ clg_dx_coil.setGrossRatedTotalCoolingCapacityAtSelectedNominalSpeedLevel(dx_cap)
553
+ htg_dx_coil.setRatedHeatingCapacityAtSelectedNominalSpeedLevel(dx_cap)
554
+ end
555
+ end
556
+ # Assign performance curves and COPs
557
+ model.getCoilCoolingDXVariableSpeeds.sort.each {|coil| coil_cooling_dx_variable_speed_apply_efficiency_and_curves(coil,ecm_name)}
558
+ model.getCoilHeatingDXVariableSpeeds.sort.each {|coil| coil_heating_dx_variable_speed_apply_efficiency_and_curves(coil,ecm_name)}
559
+ end
560
+
561
+ # =============================================================================================================================
562
+ # Applies the standard efficiency ratings and typical performance curves "CoilCoolingDXVariableSpeed" object.
563
+ def coil_cooling_dx_variable_speed_apply_efficiency_and_curves(coil_cooling_dx_variable_speed,ecm_name)
564
+ successfully_set_all_properties = true
565
+
566
+ # Get the search criteria
567
+ search_criteria = coil_dx_find_search_criteria(coil_cooling_dx_variable_speed)
568
+
569
+ # Get the capacity
570
+ capacity_w = coil_cooling_dx_variable_speed_find_capacity(coil_cooling_dx_variable_speed)
571
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
572
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
573
+
574
+ # Lookup efficiencies depending on whether it is a unitary AC or a heat pump
575
+ ac_props = if coil_dx_heat_pump?(coil_cooling_dx_variable_speed)
576
+ model_find_object(standards_data['tables']["heat_pumps_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
577
+ else
578
+ model_find_object(standards_data['tables']["unitary_acs_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
579
+ end
580
+
581
+ # Check to make sure properties were found
582
+ if ac_props.nil?
583
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_single_speed.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency.")
584
+ successfully_set_all_properties = false
585
+ end
586
+
587
+ # Make the COOL-CAP-FT curve
588
+ cool_cap_ft = model_add_curve(coil_cooling_dx_variable_speed.model, ac_props['cool_cap_ft'])
589
+ if cool_cap_ft
590
+ coil_cooling_dx_variable_speed.speeds.each {|speed| speed.setTotalCoolingCapacityFunctionofTemperatureCurve(cool_cap_ft)}
591
+ else
592
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name}, cannot find cool_cap_ft curve, will not be set.")
593
+ successfully_set_all_properties = false
594
+ end
595
+
596
+ # Make the COOL-CAP-FFLOW curve
597
+ cool_cap_fflow = model_add_curve(coil_cooling_dx_variable_speed.model, ac_props['cool_cap_fflow'])
598
+ if cool_cap_fflow
599
+ coil_cooling_dx_variable_speed.speeds.each {|speed| speed.setTotalCoolingCapacityFunctionofAirFlowFractionCurve(cool_cap_fflow)}
600
+ else
601
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name}, cannot find cool_cap_fflow curve, will not be set.")
602
+ successfully_set_all_properties = false
603
+ end
604
+
605
+ # Make the COOL-EIR-FT curve
606
+ cool_eir_ft = model_add_curve(coil_cooling_dx_variable_speed.model, ac_props['cool_eir_ft'])
607
+ if cool_eir_ft
608
+ coil_cooling_dx_variable_speed.speeds.each {|speed| speed.setEnergyInputRatioFunctionofTemperatureCurve(cool_eir_ft)}
609
+ else
610
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name}, cannot find cool_eir_ft curve, will not be set.")
611
+ successfully_set_all_properties = false
612
+ end
613
+
614
+ # Make the COOL-EIR-FFLOW curve
615
+ cool_eir_fflow = model_add_curve(coil_cooling_dx_variable_speed.model, ac_props['cool_eir_fflow'])
616
+ if cool_eir_fflow
617
+ coil_cooling_dx_variable_speed.speeds.each {|speed| speed.setEnergyInputRatioFunctionofAirFlowFractionCurve(cool_eir_fflow)}
618
+ else
619
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name}, cannot find cool_eir_fflow curve, will not be set.")
620
+ successfully_set_all_properties = false
621
+ end
622
+
623
+ # Make the COOL-PLF-FPLR curve
624
+ cool_plf_fplr = model_add_curve(coil_cooling_dx_variable_speed.model, ac_props['cool_plf_fplr'])
625
+ if cool_plf_fplr
626
+ coil_cooling_dx_variable_speed.setEnergyPartLoadFractionCurve(cool_plf_fplr)
627
+ else
628
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name}, cannot find cool_plf_fplr curve, will not be set.")
629
+ successfully_set_all_properties = false
630
+ end
631
+
632
+ # Find the minimum COP and rename with efficiency rating
633
+ cop = coil_cooling_dx_variable_speed_standard_minimum_cop(coil_cooling_dx_variable_speed, true,ecm_name)
634
+
635
+ # Set the efficiency values
636
+ unless cop.nil?
637
+ coil_cooling_dx_variable_speed.speeds.each {|speed| speed.setReferenceUnitGrossRatedCoolingCOP(cop.to_f)}
638
+ end
639
+
640
+ end
641
+
642
+ # =============================================================================================================================
643
+ # Applies the standard efficiency ratings and typical performance curves to "CoilHeatingVariableSpeed" object.
644
+ def coil_heating_dx_variable_speed_apply_efficiency_and_curves(coil_heating_dx_variable_speed,ecm_name)
645
+ successfully_set_all_properties = true
646
+
647
+ # Get the search criteria
648
+ search_criteria = coil_dx_find_search_criteria(coil_heating_dx_variable_speed)
649
+
650
+ # Get the capacity
651
+ capacity_w = coil_heating_dx_variable_speed_find_capacity(coil_heating_dx_variable_speed)
652
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
653
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
654
+
655
+ # Lookup efficiencies
656
+ props = model_find_object(standards_data['tables']["heat_pumps_heating_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
657
+
658
+ # Check to make sure properties were found
659
+ if props.nil?
660
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_single_speed.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency standard.")
661
+ successfully_set_all_properties = false
662
+ end
663
+
664
+ # Make the HEAT-CAP-FT curve
665
+ heat_cap_ft = model_add_curve(coil_heating_dx_variable_speed.model, props['heat_cap_ft'])
666
+ if heat_cap_ft
667
+ coil_heating_dx_variable_speed.speeds.each {|speed| speed.setHeatingCapacityFunctionofTemperatureCurve(heat_cap_ft)}
668
+ else
669
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_variable_speed.name}, cannot find heat_cap_ft curve, will not be set.")
670
+ successfully_set_all_properties = false
671
+ end
672
+
673
+ # Make the HEAT-CAP-FFLOW curve
674
+ heat_cap_fflow = model_add_curve(coil_heating_dx_variable_speed.model, props['heat_cap_fflow'])
675
+ if heat_cap_fflow
676
+ coil_heating_dx_variable_speed.speeds.each {|speed| speed.setTotalHeatingCapacityFunctionofAirFlowFractionCurve(heat_cap_fflow)}
677
+ else
678
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_variable_speed.name}, cannot find heat_cap_fflow curve, will not be set.")
679
+ successfully_set_all_properties = false
680
+ end
681
+
682
+ # Make the HEAT-EIR-FT curve
683
+ heat_eir_ft = model_add_curve(coil_heating_dx_variable_speed.model, props['heat_eir_ft'])
684
+ if heat_eir_ft
685
+ coil_heating_dx_variable_speed.speeds.each {|speed| speed.setEnergyInputRatioFunctionofTemperatureCurve(heat_eir_ft)}
686
+ else
687
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSingleSpeed', "For #{coil_heating_dx_variable_speed.name}, cannot find heat_eir_ft curve, will not be set.")
688
+ successfully_set_all_properties = false
689
+ end
690
+
691
+ # Make the HEAT-EIR-FFLOW curve
692
+ heat_eir_fflow = model_add_curve(coil_heating_dx_variable_speed.model, props['heat_eir_fflow'])
693
+ if heat_eir_fflow
694
+ coil_heating_dx_variable_speed.speeds.each {|speed| speed.setEnergyInputRatioFunctionofAirFlowFractionCurve(heat_eir_fflow)}
695
+ else
696
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_variable_speed.name}, cannot find heat_eir_fflow curve, will not be set.")
697
+ successfully_set_all_properties = false
698
+ end
699
+
700
+ # Make the HEAT-PLF-FPLR curve
701
+ heat_plf_fplr = model_add_curve(coil_heating_dx_variable_speed.model, props['heat_plf_fplr'])
702
+ if heat_plf_fplr
703
+ coil_heating_dx_variable_speed.setEnergyPartLoadFractionCurve(heat_plf_fplr)
704
+ else
705
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_variable_speed.name}, cannot find heat_plf_fplr curve, will not be set.")
706
+ successfully_set_all_properties = false
707
+ end
708
+
709
+ # Find the minimum COP and rename with efficiency rating
710
+ cop = coil_heating_dx_variable_speed_standard_minimum_cop(coil_heating_dx_variable_speed, true,ecm_name)
711
+
712
+ # Set the efficiency values
713
+ unless cop.nil?
714
+ coil_heating_dx_variable_speed.speeds.each {|speed| speed.setReferenceUnitGrossRatedHeatingCOP(cop.to_f)}
715
+ end
716
+
717
+ end
718
+
719
+ # =============================================================================================================================
720
+ # Applies the standard cooling efficiency ratings and typical performance curves to "AirConditionerVariableRefrigerantFlow" object.
721
+ def airconditioner_variablerefrigerantflow_cooling_apply_efficiency_and_curves(airconditioner_variablerefrigerantflow,ecm_name)
722
+ successfully_set_all_properties = true
723
+
724
+ # Get the search criteria
725
+ search_criteria = coil_dx_find_search_criteria(airconditioner_variablerefrigerantflow)
726
+
727
+ # Get the capacity
728
+ capacity_w = airconditioner_variablerefrigerantflow_cooling_find_capacity(airconditioner_variablerefrigerantflow)
729
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
730
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
731
+
732
+ # Lookup efficiencies
733
+ props = model_find_object(standards_data['tables']["heat_pumps_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
734
+
735
+ # Check to make sure properties were found
736
+ if props.nil?
737
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency.")
738
+ successfully_set_all_properties = false
739
+ end
740
+
741
+ # Make the COOL-CAP-FT Low curve
742
+ cool_cap_ft_low = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_cap_ft_low'])
743
+ if cool_cap_ft_low
744
+ airconditioner_variablerefrigerantflow.setCoolingCapacityRatioModifierFunctionofLowTemperatureCurve(cool_cap_ft_low)
745
+ else
746
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_cap_ft_low curve, will not be set.")
747
+ successfully_set_all_properties = false
748
+ end
749
+
750
+ # Make the COOL-CAP-FT boundary curve
751
+ cool_cap_ft_boundary = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_cap_ft_boundary'])
752
+ if cool_cap_ft_boundary
753
+ airconditioner_variablerefrigerantflow.setCoolingCapacityRatioBoundaryCurve(cool_cap_ft_boundary)
754
+ else
755
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_cap_ft_boundary curve, will not be set.")
756
+ successfully_set_all_properties = false
757
+ end
758
+
759
+ # Make the COOL-CAP-FT high curve
760
+ cool_cap_ft_high = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_cap_ft_high'])
761
+ if cool_cap_ft_high
762
+ airconditioner_variablerefrigerantflow.setCoolingCapacityRatioModifierFunctionofHighTemperatureCurve(cool_cap_ft_high)
763
+ else
764
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_cap_ft_high curve, will not be set.")
765
+ successfully_set_all_properties = false
766
+ end
767
+
768
+ # Make the COOL-EIR-FT low curve
769
+ cool_eir_ft_low = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_eir_ft_low'])
770
+ if cool_eir_ft_low
771
+ airconditioner_variablerefrigerantflow.setCoolingEnergyInputRatioModifierFunctionofLowTemperatureCurve(cool_eir_ft_low)
772
+ else
773
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_eir_ft_low curve, will not be set.")
774
+ successfully_set_all_properties = false
775
+ end
776
+
777
+ # Make the COOL-EIR-FT boundary curve
778
+ cool_eir_ft_boundary = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_eir_ft_boundary'])
779
+ if cool_eir_ft_boundary
780
+ airconditioner_variablerefrigerantflow.setCoolingEnergyInputRatioBoundaryCurve(cool_eir_ft_boundary)
781
+ else
782
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_eir_ft_boundary curve, will not be set.")
783
+ successfully_set_all_properties = false
784
+ end
785
+
786
+ # Make the COOL-EIR-FT high curve
787
+ cool_eir_ft_high = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_eir_ft_high'])
788
+ if cool_eir_ft_high
789
+ airconditioner_variablerefrigerantflow.setCoolingEnergyInputRatioModifierFunctionofHighTemperatureCurve(cool_eir_ft_high)
790
+ else
791
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_eir_ft_high curve, will not be set.")
792
+ successfully_set_all_properties = false
793
+ end
794
+
795
+ # Make the COOL-EIR-FPLR low curve
796
+ cool_eir_fplr_low = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_eir_fplr_low'])
797
+ if cool_eir_fplr_low
798
+ airconditioner_variablerefrigerantflow.setCoolingEnergyInputRatioModifierFunctionofLowPartLoadRatioCurve(cool_eir_fplr_low)
799
+ else
800
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_eir_fplr_low curve, will not be set.")
801
+ successfully_set_all_properties = false
802
+ end
803
+
804
+ # Make the COOL-EIR-FPLR high curve
805
+ cool_eir_fplr_high = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_eir_fplr_high'])
806
+ if cool_eir_fplr_high
807
+ airconditioner_variablerefrigerantflow.setCoolingEnergyInputRatioModifierFunctionofHighPartLoadRatioCurve(cool_eir_fplr_high)
808
+ else
809
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_eir_fplr_high curve, will not be set.")
810
+ successfully_set_all_properties = false
811
+ end
812
+
813
+ # Make the COOL-CCR curve
814
+ cool_ccr = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_ccr'])
815
+ if cool_ccr
816
+ airconditioner_variablerefrigerantflow.setCoolingCombinationRatioCorrectionFactorCurve(cool_ccr)
817
+ else
818
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_ccr curve, will not be set.")
819
+ successfully_set_all_properties = false
820
+ end
821
+
822
+ # Make the COOL-PLF-FPLR curve
823
+ cool_plf_fplr = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_plf_fplr'])
824
+ if cool_plf_fplr
825
+ airconditioner_variablerefrigerantflow.setCoolingPartLoadFractionCorrelationCurve(cool_plf_fplr)
826
+ else
827
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_plf_fplr curve, will not be set.")
828
+ successfully_set_all_properties = false
829
+ end
830
+
831
+ # Make the COOL-PLF-FPLR curve
832
+ cool_plf_fplr = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_plf_fplr'])
833
+ if cool_plf_fplr
834
+ airconditioner_variablerefrigerantflow.setCoolingPartLoadFractionCorrelationCurve(cool_plf_fplr)
835
+ else
836
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_plf_fplr curve, will not be set.")
837
+ successfully_set_all_properties = false
838
+ end
839
+
840
+ # Make the COOL-CAP-FPL curve
841
+ cool_cap_fpl = model_add_curve(airconditioner_variablerefrigerantflow.model, props['cool_cap_fpl'])
842
+ if cool_cap_fpl
843
+ airconditioner_variablerefrigerantflow.setPipingCorrectionFactorforLengthinCoolingModeCurve(cool_cap_fpl)
844
+ else
845
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_cap_fpl curve, will not be set.")
846
+ successfully_set_all_properties = false
847
+ end
848
+
849
+ # Find the minimum COP
850
+ cop = airconditioner_variablerefrigerantflow_cooling_standard_minimum_cop(airconditioner_variablerefrigerantflow, false, ecm_name)
851
+
852
+ # Set the efficiency values
853
+ unless cop.nil?
854
+ airconditioner_variablerefrigerantflow.setRatedCoolingCOP(cop.to_f)
855
+ end
856
+
857
+ end
858
+
859
+ # =============================================================================================================================
860
+ # Applies the standard heating efficiency ratings and typical performance curves to "AirConditionerVariableRefrigerantFlow" object.
861
+ def airconditioner_variablerefrigerantflow_heating_apply_efficiency_and_curves(airconditioner_variablerefrigerantflow,ecm_name)
862
+ successfully_set_all_properties = true
863
+
864
+ # Get the search criteria
865
+ search_criteria = coil_dx_find_search_criteria(airconditioner_variablerefrigerantflow)
866
+
867
+ # Get the capacity
868
+ capacity_w = airconditioner_variablerefrigerantflow_heating_find_capacity(airconditioner_variablerefrigerantflow)
869
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
870
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
871
+
872
+ # Lookup efficiencies
873
+ props = model_find_object(standards_data['tables']["heat_pumps_heating_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
874
+
875
+ # Check to make sure properties were found
876
+ if props.nil?
877
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heating efficiency info using #{search_criteria}, cannot apply efficiency.")
878
+ successfully_set_all_properties = false
879
+ end
880
+
881
+ # Make the HEAT-CAP-FT Low curve
882
+ heat_cap_ft_low = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_cap_ft_low'])
883
+ if heat_cap_ft_low
884
+ airconditioner_variablerefrigerantflow.setHeatingCapacityRatioModifierFunctionofLowTemperatureCurve(heat_cap_ft_low)
885
+ else
886
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_cap_ft_low curve, will not be set.")
887
+ successfully_set_all_properties = false
888
+ end
889
+
890
+ # Make the HEAT-CAP-FT boundary curve
891
+ heat_cap_ft_boundary = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_cap_ft_boundary'])
892
+ if heat_cap_ft_boundary
893
+ airconditioner_variablerefrigerantflow.setHeatingCapacityRatioBoundaryCurve(heat_cap_ft_boundary)
894
+ else
895
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standard.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_cap_ft_boundary curve, will not be set.")
896
+ successfully_set_all_properties = false
897
+ end
898
+
899
+ # Make the HEAT-CAP-FT high curve
900
+ heat_cap_ft_high = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_cap_ft_high'])
901
+ if heat_cap_ft_high
902
+ airconditioner_variablerefrigerantflow.setHeatingCapacityRatioModifierFunctionofHighTemperatureCurve(heat_cap_ft_high)
903
+ else
904
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_cap_ft_high curve, will not be set.")
905
+ successfully_set_all_properties = false
906
+ end
907
+
908
+ # Make the HEAT-EIR-FT low curve
909
+ heat_eir_ft_low = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_eir_ft_low'])
910
+ if heat_eir_ft_low
911
+ airconditioner_variablerefrigerantflow.setHeatingEnergyInputRatioModifierFunctionofLowTemperatureCurve(heat_eir_ft_low)
912
+ else
913
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_eir_ft_low curve, will not be set.")
914
+ successfully_set_all_properties = false
915
+ end
916
+
917
+ # Make the HEAT-EIR-FT boundary curve
918
+ heat_eir_ft_boundary = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_eir_ft_boundary'])
919
+ if heat_eir_ft_boundary
920
+ airconditioner_variablerefrigerantflow.setHeatingEnergyInputRatioBoundaryCurve(heat_eir_ft_boundary)
921
+ else
922
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_eir_ft_boundary curve, will not be set.")
923
+ successfully_set_all_properties = false
924
+ end
925
+
926
+ # Make the HEAT-EIR-FT high curve
927
+ heat_eir_ft_high = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_eir_ft_high'])
928
+ if heat_eir_ft_high
929
+ airconditioner_variablerefrigerantflow.setHeatingEnergyInputRatioModifierFunctionofHighTemperatureCurve(heat_eir_ft_high)
930
+ else
931
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_eir_ft_high curve, will not be set.")
932
+ successfully_set_all_properties = false
933
+ end
934
+
935
+ # Make the HEAT-EIR-FPLR low curve
936
+ heat_eir_fplr_low = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_eir_fplr_low'])
937
+ if heat_eir_fplr_low
938
+ airconditioner_variablerefrigerantflow.setHeatingEnergyInputRatioModifierFunctionofLowPartLoadRatioCurve(heat_eir_fplr_low)
939
+ else
940
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_eir_fplr_low curve, will not be set.")
941
+ successfully_set_all_properties = false
942
+ end
943
+
944
+ # Make the HEAT-EIR-FPLR high curve
945
+ heat_eir_fplr_high = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_eir_fplr_high'])
946
+ if heat_eir_fplr_high
947
+ airconditioner_variablerefrigerantflow.setHeatingEnergyInputRatioModifierFunctionofHighPartLoadRatioCurve(heat_eir_fplr_high)
948
+ else
949
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_eir_fplr_high curve, will not be set.")
950
+ successfully_set_all_properties = false
951
+ end
952
+
953
+ # Make the HEAT-HCR curve
954
+ heat_hcr = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_hcr'])
955
+ if heat_hcr
956
+ airconditioner_variablerefrigerantflow.setHeatingCombinationRatioCorrectionFactorCurve(heat_hcr)
957
+ else
958
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_hcr curve, will not be set.")
959
+ successfully_set_all_properties = false
960
+ end
961
+
962
+ # Make the HEAT-PLF-FPLR curve
963
+ heat_plf_fplr = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_plf_fplr'])
964
+ if heat_plf_fplr
965
+ airconditioner_variablerefrigerantflow.setHeatingPartLoadFractionCorrelationCurve(heat_plf_fplr)
966
+ else
967
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find cool_plf_fplr curve, will not be set.")
968
+ successfully_set_all_properties = false
969
+ end
970
+
971
+ # Make the HEAT-CAP-FPL curve
972
+ heat_cap_fpl = model_add_curve(airconditioner_variablerefrigerantflow.model, props['heat_cap_fpl'])
973
+ if heat_cap_fpl
974
+ airconditioner_variablerefrigerantflow.setPipingCorrectionFactorforLengthinHeatingModeCurve(heat_cap_fpl)
975
+ else
976
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heat_cap_fpl curve, will not be set.")
977
+ successfully_set_all_properties = false
978
+ end
979
+
980
+ # Find the minimum COP and rename with efficiency rating
981
+ cop = airconditioner_variablerefrigerantflow_heating_standard_minimum_cop(airconditioner_variablerefrigerantflow, true, ecm_name)
982
+
983
+ # Set the efficiency values
984
+ unless cop.nil?
985
+ airconditioner_variablerefrigerantflow.setRatedHeatingCOP(cop.to_f)
986
+ end
987
+
988
+ end
989
+
990
+ # =============================================================================================================================
991
+ # Find minimum efficiency for "CoilCoolingDXVariableSpeed" object
992
+ def coil_cooling_dx_variable_speed_standard_minimum_cop(coil_cooling_dx_variable_speed, rename = false,ecm_name)
993
+ search_criteria = coil_dx_find_search_criteria(coil_cooling_dx_variable_speed)
994
+ cooling_type = search_criteria['cooling_type']
995
+ heating_type = search_criteria['heating_type']
996
+ sub_category = search_criteria['subcategory']
997
+ capacity_w = coil_cooling_dx_variable_speed_find_capacity(coil_cooling_dx_variable_speed)
998
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
999
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
1000
+
1001
+ # Look up the efficiency characteristics
1002
+ ac_props = if coil_dx_heat_pump?(coil_cooling_dx_variable_speed)
1003
+ model_find_object(standards_data['tables']["heat_pumps_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
1004
+ else
1005
+ model_find_object(standards_data['tables']["unitary_acs_ecm_#{ecm_name.downcase}"]['table'], search_criteria, capacity_btu_per_hr, Date.today)
1006
+ end
1007
+
1008
+ # Check to make sure properties were found
1009
+ if ac_props.nil?
1010
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency standard.")
1011
+ successfully_set_all_properties = false
1012
+ return successfully_set_all_properties
1013
+ end
1014
+
1015
+ # Get the minimum efficiency standards
1016
+ cop = nil
1017
+
1018
+ # If specified as SEER
1019
+ unless ac_props['minimum_seasonal_energy_efficiency_ratio'].nil?
1020
+ min_seer = ac_props['minimum_seasonal_energy_efficiency_ratio']
1021
+ cop = seer_to_cop_cooling_with_fan(min_seer)
1022
+ new_comp_name = "#{coil_cooling_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_seer}SEER"
1023
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{template}: #{coil_cooling_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; SEER = #{min_seer}")
1024
+ end
1025
+
1026
+ # If specified as EER
1027
+ unless ac_props['minimum_energy_efficiency_ratio'].nil?
1028
+ min_eer = ac_props['minimum_energy_efficiency_ratio']
1029
+ cop = eer_to_cop(min_eer)
1030
+ new_comp_name = "#{coil_cooling_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1031
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{template}: #{coil_cooling_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1032
+ end
1033
+
1034
+ # if specified as SEER (heat pump)
1035
+ unless ac_props['minimum_seasonal_efficiency'].nil?
1036
+ min_seer = ac_props['minimum_seasonal_efficiency']
1037
+ cop = seer_to_cop_cooling_with_fan(min_seer)
1038
+ new_comp_name = "#{coil_cooling_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_seer}SEER"
1039
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{template}: #{coil_cooling_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; SEER = #{min_seer}")
1040
+ end
1041
+
1042
+ # If specified as EER (heat pump)
1043
+ unless ac_props['minimum_full_load_efficiency'].nil?
1044
+ min_eer = ac_props['minimum_full_load_efficiency']
1045
+ cop = eer_to_cop(min_eer)
1046
+ new_comp_name = "#{coil_cooling_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1047
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{template}: #{coil_cooling_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1048
+ end
1049
+
1050
+ # If specified as COP
1051
+ unless ac_props['minimum_coefficient_of_performance_cooling'].nil?
1052
+ cop = ac_props['minimum_coefficient_of_performance_cooling']
1053
+ new_comp_name = "#{coil_cooling_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{cop}COP"
1054
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{template}: #{coil_cooling_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; COP = #{cop}")
1055
+ end
1056
+
1057
+ # Rename
1058
+ if rename
1059
+ coil_cooling_dx_variable_speed.setName(new_comp_name)
1060
+ end
1061
+
1062
+ return cop
1063
+ end
1064
+
1065
+ # =============================================================================================================================
1066
+ # Find minimum efficiency for "CoilHeatingDXVariableSingleSpeed" object
1067
+ def coil_heating_dx_variable_speed_standard_minimum_cop(coil_heating_dx_variable_speed, rename = false,ecm_name)
1068
+ search_criteria = coil_dx_find_search_criteria(coil_heating_dx_variable_speed)
1069
+ cooling_type = search_criteria['cooling_type']
1070
+ heating_type = search_criteria['heating_type']
1071
+ sub_category = search_criteria['subcategory']
1072
+ capacity_w = coil_heating_dx_variable_speed_find_capacity(coil_heating_dx_variable_speed)
1073
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
1074
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
1075
+
1076
+ # Look up the efficiency characteristics
1077
+ props = model_find_object(standards_data['tables']["heat_pumps_heating_ecm_#{ecm_name.downcase}"], search_criteria, capacity_btu_per_hr, Date.today)
1078
+
1079
+ # Check to make sure properties were found
1080
+ if props.nil?
1081
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_variable_speed.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency standard.")
1082
+ successfully_set_all_properties = false
1083
+ return successfully_set_all_properties
1084
+ end
1085
+
1086
+ # Get the minimum efficiency standards
1087
+ cop = nil
1088
+
1089
+ # If specified as EER
1090
+ unless props['minimum_energy_efficiency_ratio'].nil?
1091
+ min_eer = props['minimum_energy_efficiency_ratio']
1092
+ cop = eer_to_cop(min_eer)
1093
+ new_comp_name = "#{coil_heating_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1094
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{template}: #{coil_heating_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1095
+ end
1096
+
1097
+ # if specified as HSPF (heat pump)
1098
+ unless props['minimum_heating_seasonal_performance_factor'].nil?
1099
+ min_hspf = props['minimum_heating_seasonal_performance_factor']
1100
+ cop = hspf_to_cop_heating_with_fan(min_hspf)
1101
+ new_comp_name = "#{coil_heating_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_seer}HSPF"
1102
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{template}: #{coil_heating_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; SEER = #{min_seer}")
1103
+ end
1104
+
1105
+ # If specified as EER (heat pump)
1106
+ unless props['minimum_full_load_efficiency'].nil?
1107
+ min_eer = props['minimum_full_load_efficiency']
1108
+ cop = eer_to_cop(min_eer)
1109
+ new_comp_name = "#{coil_heating_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1110
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{template}: #{coil_heating_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1111
+ end
1112
+
1113
+ # If specified as COP
1114
+ unless props['minimum_coefficient_of_performance_heating'].nil?
1115
+ cop = props['minimum_coefficient_of_performance_heating']
1116
+ new_comp_name = "#{coil_heating_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{cop}COP"
1117
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{template}: #{coil_heating_dx_variable_speed.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1118
+ end
1119
+
1120
+ # Rename
1121
+ if rename
1122
+ coil_heating_dx_variable_speed.setName(new_comp_name)
1123
+ end
1124
+
1125
+ return cop
1126
+ end
1127
+
1128
+ # =============================================================================================================================
1129
+ # Find minimum cooling efficiency for "AirConditionerVariableRefrigerantFlow" object
1130
+ def airconditioner_variablerefrigerantflow_cooling_standard_minimum_cop(airconditioner_variablerefrigerantflow, rename = false, ecm_name)
1131
+ search_criteria = coil_dx_find_search_criteria(airconditioner_variablerefrigerantflow)
1132
+ cooling_type = search_criteria['cooling_type']
1133
+ heating_type = search_criteria['heating_type']
1134
+ sub_category = search_criteria['subcategory']
1135
+ capacity_w = airconditioner_variablerefrigerantflow_cooling_find_capacity(airconditioner_variablerefrigerantflow)
1136
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
1137
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
1138
+
1139
+ # Look up the efficiency characteristics
1140
+ props = model_find_object(standards_data['tables']["heat_pumps_ecm_#{ecm_name.downcase}"], search_criteria, capacity_btu_per_hr, Date.today)
1141
+
1142
+ # Check to make sure properties were found
1143
+ if props.nil?
1144
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency standard.")
1145
+ successfully_set_all_properties = false
1146
+ return successfully_set_all_properties
1147
+ end
1148
+
1149
+ # Get the minimum efficiency standards
1150
+ cop = nil
1151
+
1152
+ # If specified as EER
1153
+ unless props['minimum_energy_efficiency_ratio'].nil?
1154
+ min_eer = props['minimum_energy_efficiency_ratio']
1155
+ cop = eer_to_cop(min_eer)
1156
+ new_comp_name = "#{airconditioner_variablerefrigerantflow.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1157
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1158
+ end
1159
+
1160
+ # if specified as HSPF (heat pump)
1161
+ unless props['minimum_heating_seasonal_performance_factor'].nil?
1162
+ min_hspf = props['minimum_heating_seasonal_performance_factor']
1163
+ cop = hspf_to_cop_heating_with_fan(min_hspf)
1164
+ new_comp_name = "#{coil_heating_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_seer}HSPF"
1165
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; SEER = #{min_seer}")
1166
+ end
1167
+
1168
+ # If specified as EER (heat pump)
1169
+ unless props['minimum_full_load_efficiency'].nil?
1170
+ min_eer = props['minimum_full_load_efficiency']
1171
+ cop = eer_to_cop(min_eer)
1172
+ new_comp_name = "#{airconditioner_variablerefrigerantflow.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1173
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1174
+ end
1175
+
1176
+ # If specified as COP
1177
+ unless props['minimum_coefficient_of_performance_cooling'].nil?
1178
+ cop = props['minimum_coefficient_of_performance_cooling']
1179
+ new_comp_name = "#{airconditioner_variablerefrigerantflow.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{cop}COP"
1180
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1181
+ end
1182
+
1183
+ # Rename
1184
+ if rename
1185
+ airconditioner_variablerefrigerantflow.setName(new_comp_name)
1186
+ end
1187
+
1188
+ return cop
1189
+ end
1190
+
1191
+ # =============================================================================================================================
1192
+ # Find minimum heating efficiency for "AirConditionerVariableRefrigerantFlow" object
1193
+ def airconditioner_variablerefrigerantflow_heating_standard_minimum_cop(airconditioner_variablerefrigerantflow, rename = false, ecm_name)
1194
+ search_criteria = coil_dx_find_search_criteria(airconditioner_variablerefrigerantflow)
1195
+ cooling_type = search_criteria['cooling_type']
1196
+ heating_type = search_criteria['heating_type']
1197
+ sub_category = search_criteria['subcategory']
1198
+ capacity_w = airconditioner_variablerefrigerantflow_heating_find_capacity(airconditioner_variablerefrigerantflow)
1199
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
1200
+ capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get
1201
+
1202
+ # Look up the efficiency characteristics
1203
+ props = model_find_object(standards_data['tables']["heat_pumps_heating_ecm_#{ecm_name.downcase}"], search_criteria, capacity_btu_per_hr, Date.today)
1204
+
1205
+ # Check to make sure properties were found
1206
+ if props.nil?
1207
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name}, cannot find heating efficiency info using #{search_criteria}, cannot apply efficiency standard.")
1208
+ successfully_set_all_properties = false
1209
+ return successfully_set_all_properties
1210
+ end
1211
+
1212
+ # Get the minimum efficiency standards
1213
+ cop = nil
1214
+
1215
+ # If specified as EER
1216
+ unless props['minimum_energy_efficiency_ratio'].nil?
1217
+ min_eer = props['minimum_energy_efficiency_ratio']
1218
+ cop = eer_to_cop(min_eer)
1219
+ new_comp_name = "#{airconditioner_variablerefrigerantflow.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1220
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1221
+ end
1222
+
1223
+ # if specified as HSPF (heat pump)
1224
+ unless props['minimum_heating_seasonal_performance_factor'].nil?
1225
+ min_hspf = props['minimum_heating_seasonal_performance_factor']
1226
+ cop = hspf_to_cop_heating_with_fan(min_hspf)
1227
+ new_comp_name = "#{coil_heating_dx_variable_speed.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_seer}HSPF"
1228
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; SEER = #{min_seer}")
1229
+ end
1230
+
1231
+ # If specified as EER (heat pump)
1232
+ unless props['minimum_full_load_efficiency'].nil?
1233
+ min_eer = props['minimum_full_load_efficiency']
1234
+ cop = eer_to_cop(min_eer)
1235
+ new_comp_name = "#{airconditioner_variablerefrigerantflow.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_eer}EER"
1236
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1237
+ end
1238
+
1239
+ # If specified as COP
1240
+ unless props['minimum_coefficient_of_performance_heating'].nil?
1241
+ cop = props['minimum_coefficient_of_performance_heating']
1242
+ new_comp_name = "#{airconditioner_variablerefrigerantflow.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{cop}COP"
1243
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{template}: #{airconditioner_variablerefrigerantflow.name}: #{cooling_type} #{heating_type} #{sub_category} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; EER = #{min_eer}")
1244
+ end
1245
+
1246
+ # Rename
1247
+ if rename
1248
+ airconditioner_variablerefrigerantflow.setName(new_comp_name)
1249
+ end
1250
+
1251
+ return cop
1252
+ end
1253
+
1254
+ # =============================================================================================================================
1255
+ # Find cooling capacity for "CoilCoolingDXVariableSpeed" object
1256
+ def coil_cooling_dx_variable_speed_find_capacity(coil_cooling_dx_variable_speed)
1257
+ capacity_w = nil
1258
+ if coil_cooling_dx_variable_speed.grossRatedTotalCoolingCapacityAtSelectedNominalSpeedLevel.is_initialized
1259
+ capacity_w = coil_cooling_dx_variable_speed.grossRatedTotalCoolingCapacityAtSelectedNominalSpeedLevel.get
1260
+ elsif coil_cooling_dx_variable_speed.autosizedGrossRatedTotalCoolingCapacityAtSelectedNominalSpeedLevel.is_initialized
1261
+ capacity_w = coil_cooling_dx_variable_speed.autosizedGrossRatedTotalCoolingCapacityAtSelectedNominalSpeedLevel.get
1262
+ else
1263
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXVariableSpeed', "For #{coil_cooling_dx_variable_speed.name} capacity is not available, cannot apply efficiency standard.")
1264
+ return 0.0
1265
+ end
1266
+
1267
+ return capacity_w
1268
+ end
1269
+
1270
+ # =============================================================================================================================
1271
+ # Find heating capacity for "CoilHeatingDXVariableSpeed" object
1272
+ def coil_heating_dx_variable_speed_find_capacity(coil_heating_dx_variable_speed)
1273
+ capacity_w = nil
1274
+ if coil_heating_dx_variable_speed.ratedHeatingCapacityAtSelectedNominalSpeedLevel.is_initialized
1275
+ capacity_w = coil_heating_dx_variable_speed.ratedHeatingCapacityAtSelectedNominalSpeedLevel.get
1276
+ elsif coil_heating_dx_variable_speed.autosizedRatedHeatingCapacityAtSelectedNominalSpeedLevel.is_initialized
1277
+ capacity_w = coil_heating_dx_variable_speed.autosizedRatedHeatingCapacityAtSelectedNominalSpeedLevel.get
1278
+ else
1279
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingDXVariableSpeed', "For #{coil_heating_dx_variable_speed.name} capacity is not available, cannot apply efficiency standard.")
1280
+ return 0.0
1281
+ end
1282
+
1283
+ return capacity_w
1284
+ end
1285
+
1286
+ # =============================================================================================================================
1287
+ # Find cooling capacity for "AirConditionerVariableRefrigerantFlow" object
1288
+ def airconditioner_variablerefrigerantflow_cooling_find_capacity(airconditioner_variablerefrigerantflow)
1289
+ capacity_w = nil
1290
+ if airconditioner_variablerefrigerantflow.ratedTotalCoolingCapacity.is_initialized
1291
+ capacity_w = airconditioner_variablerefrigerantflow.ratedTotalCoolingCapacity.get
1292
+ elsif airconditioner_variablerefrigerantflow.autosizedRatedTotalCoolingCapacity.is_initialized
1293
+ capacity_w = airconditioner_variablerefrigerantflow.autosizedRatedTotalCoolingCapacity.get
1294
+ airconditioner_variablerefrigerantflow.setRatedTotalCoolingCapacity(capacity_w)
1295
+ else
1296
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name} cooling capacity is not available, cannot apply efficiency standard.")
1297
+ return 0.0
1298
+ end
1299
+
1300
+ return capacity_w
1301
+ end
1302
+
1303
+ # =============================================================================================================================
1304
+ # Find heating capacity for "AirConditionerVariableRefrigerantFlow" object
1305
+ def airconditioner_variablerefrigerantflow_heating_find_capacity(airconditioner_variablerefrigerantflow)
1306
+ capacity_w = nil
1307
+ if airconditioner_variablerefrigerantflow.ratedTotalHeatingCapacity.is_initialized
1308
+ capacity_w = airconditioner_variablerefrigerantflow.ratedTotalHeatingCapacity.get
1309
+ elsif airconditioner_variablerefrigerantflow.autosizedRatedTotalHeatingCapacity.is_initialized
1310
+ capacity_w = airconditioner_variablerefrigerantflow.autosizedRatedTotalHeatingCapacity.get
1311
+ airconditioner_variablerefrigerantflow.setRatedTotalHeatingCapacity(capacity_w)
1312
+ else
1313
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirConditionerVariableRefrigerantFlow', "For #{airconditioner_variablerefrigerantflow.name} heating capacity is not available, cannot apply efficiency standard.")
1314
+ return 0.0
1315
+ end
1316
+
1317
+ return capacity_w
1318
+ end
1319
+
1320
+ # ============================================================================================================================
1321
+ # Apply boiler efficiency
1322
+ # This model takes an OS model and a boiler efficiency string or hash sent to it with the following form:
1323
+ # "boiler_eff": {
1324
+ # "name" => "NECB 88% Efficient Condensing Boiler",
1325
+ # "efficiency" => 0.88,
1326
+ # "part_load_curve" => "BOILER-EFFPLR-COND-NECB2011",
1327
+ # "notes" => "From NECB 2011."
1328
+ # }
1329
+ # If boiler_eff is nill then it does nothing. If both "efficiency" and "part_load_curve" are nil then it does
1330
+ # nothing. If a boiler_eff is passed as a string and not a hash then it looks for a "name" field in the
1331
+ # boiler_set.json file that matches boiler_eff and gets the associated boiler performance details from the file.
1332
+ # If an efficiency is set but is not between 0.01 and 1.0 it returns an error. Otherwise, it looks for plant loop
1333
+ # supply components that match the "OS_BoilerHotWater" type. If it finds one it then calls the
1334
+ # "reset_boiler_efficiency method which resets the the boiler efficiency and looks for the part load efficiency curve
1335
+ # in the curves.json file. If it finds a curve it sets the part load curve to that, otherwise it returns an error.
1336
+ # It also renames the boiler to include the "boiler_eff"["name"].
1337
+ def modify_boiler_efficiency(model:, boiler_eff: nil)
1338
+ return if boiler_eff.nil?
1339
+ # If boiler_eff is a string rather than a hash then assume it is the name of a boiler efficiency package and look
1340
+ # for a package with that name in boiler_set.json.
1341
+ if boiler_eff.is_a?(String)
1342
+ eff_packages = @standards_data['tables']['boiler_eff_ecm']['table']
1343
+ eff_package = eff_packages.select{|eff_pack_info| eff_pack_info["name"] == boiler_eff}
1344
+ if eff_package.empty?
1345
+ raise "Cannot not find #{boiler_eff} in the ECMS boiler_set.json file. Please check that the name is correctly spelled in the ECMS class boiler_set.json and in the code calling (directly or through another method) the ECMS class modify_boiler_efficiency method."
1346
+ elsif eff_package.size > 1
1347
+ raise "More than one boiler efficiency package with the name #{boiler_eff} was found. Please check the ECMS class boiler_set.json file and make sure that each boiler efficiency package has a unique name."
1348
+ else
1349
+ ecm_name = boiler_eff
1350
+ boiler_eff = {
1351
+ "name" => ecm_name,
1352
+ "efficiency" => eff_package[0]['efficiency'],
1353
+ "part_load_curve" => eff_package[0]['part_load_curve']
1354
+ }
1355
+ end
1356
+ end
1357
+ # If nothing is passed in the boiler_eff hash then assume this was not supposed to be used and return without doing
1358
+ # anything.
1359
+ return if boiler_eff["name"].nil? && boiler_eff["efficiency"].nil? && boiler_eff["part_load_curve"].nil?
1360
+ # If no efficiency or partload curve are found (either passed directly or via the boiler_set.json file) then assume
1361
+ # that the current SHW setting should not be changed. Return without changing anything.
1362
+ return if boiler_eff["efficiency"].nil? && boiler_eff["part_load_curve"].nil?
1363
+ raise "You attempted to set the efficiency of boilers in this model to nil. Please check the ECMS class boiler_set.json and make sure the efficiency is properly set" if boiler_eff["efficiency"].nil?
1364
+ raise "You attempted to set the efficiency of boilers in this model to: #{boiler_eff['efficiency']}. Please check the ECMS class boiler_set.json and make sure the efficiency you set is between 0.01 and 1.0." if (boiler_eff['efficiency'] < 0.01 || boiler_eff['efficiency'] > 1.0)
1365
+ raise "You attempted to set the part load curve of boilers in this model to nil. Please check the ECMS class boiler_set.json file and ensure that both the efficiency and part load curve are set." if boiler_eff['part_load_curve'].nil?
1366
+ model.getBoilerHotWaters.sort.each do |mod_boiler|
1367
+ reset_boiler_efficiency(model: model, component: mod_boiler.to_BoilerHotWater.get, eff: boiler_eff)
1368
+ end
1369
+ end
1370
+
1371
+ # This method takes an OS model, a "OS_BoilerHotWater" type compenent, condensing efficiency limit and an efficiency
1372
+ # hash which looks like:
1373
+ # "eff": {
1374
+ # "name": "NECB 88% Efficient Condensing Boiler",
1375
+ # "efficiency" => 0.88,
1376
+ # "part_load_curve" => "BOILER-EFFPLR-COND-NECB2011",
1377
+ # "notes" => "From NECB 2011."
1378
+ # }
1379
+ # This method sets efficiency of the boiler to whatever is entered in eff["efficiency"]. It then looks for the
1380
+ # "part_load_curve" value in the curves.json file. If it does not find one it returns an error. If it finds one it
1381
+ # reset the part load curve to whatever was found. It then determines the nominal capacity of the boiler. If the
1382
+ # nominal capacity is greater than 1W the boiler is considered a primary boiler (for the name only) if the capacity is
1383
+ # less than 1W the boiler is considered a secondary boiler (for the name only). It then renames the boiler according
1384
+ # to the following pattern:
1385
+ # "Primary/Secondary eff["name"] capacity kBtu/hr".
1386
+ def reset_boiler_efficiency(model:, component:, eff:)
1387
+ component.setNominalThermalEfficiency(eff['efficiency'])
1388
+ part_load_curve_name = eff["part_load_curve"].to_s
1389
+ existing_curve = @standards_data['curves'].select { |curve| curve['name'] == part_load_curve_name }
1390
+ raise "No boiler with the name #{part_load_curve_name} could be found in the ECMS class curves.json file. Please check both the ECMS class boiler_set.json and class curves.json files to ensure the curve is entered and referenced correctly." if existing_curve.empty?
1391
+ part_load_curve_data = (@standards_data["curves"].select { |curve| curve['name'] == part_load_curve_name })[0]
1392
+ if part_load_curve_data['independent_variable_1'].to_s.upcase == 'TEnteringBoiler'.upcase || part_load_curve_data['independent_variable_2'].to_s.upcase == 'TEnteringBoiler'.upcase
1393
+ component.setEfficiencyCurveTemperatureEvaluationVariable('EnteringBoiler')
1394
+ elsif part_load_curve_data['independent_variable_1'].to_s.upcase == 'TLeavingBoiler'.upcase || part_load_curve_data['independent_variable_2'].to_s.upcase == 'TLeavingBoiler'.upcase
1395
+ component.setEfficiencyCurveTemperatureEvaluationVariable('LeavingBoiler')
1396
+ end
1397
+ part_load_curve = model_add_curve(model, part_load_curve_name)
1398
+ if part_load_curve
1399
+ component.setNormalizedBoilerEfficiencyCurve(part_load_curve)
1400
+ if component.isNominalCapacityAutosized
1401
+ boiler_size_W = model.getAutosizedValue(component, 'Design Size Nominal Capacity', 'W').to_f
1402
+ else
1403
+ boiler_size_W = component.nominalCapacity.to_f
1404
+ end
1405
+ boiler_size_kbtu_per_hour = (OpenStudio.convert(boiler_size_W, 'W', 'kBtu/h').get)
1406
+ boiler_primacy = 'Primary '
1407
+ if boiler_size_W < 1.0
1408
+ boiler_primacy = 'Secondary '
1409
+ end
1410
+ if eff['name'].nil?
1411
+ eff_measure_name = "Revised Performance Boiler"
1412
+ else
1413
+ eff_measure_name = eff['name']
1414
+ end
1415
+ new_boiler_name = boiler_primacy + eff_measure_name + " #{boiler_size_kbtu_per_hour.round(0)}kBtu/hr #{component.nominalThermalEfficiency} Thermal Eff"
1416
+ component.setName(new_boiler_name)
1417
+ else
1418
+ raise "There was a problem setting the boiler part load curve named #{part_load_curve_name} for #{component.name}. Please ensure that the curve is entered and referenced correctly in the ECMS class curves.json and boiler_set.json files."
1419
+ end
1420
+ end
1421
+
1422
+ # ============================================================================================================================
1423
+ # Apply Furnace efficiency
1424
+ # This model takes an OS model and a furnace efficiency string or hash sent to it with the following form:
1425
+ # "furnace_eff": {
1426
+ # "name" => "NECB 85% Efficient Condensing Furnace",
1427
+ # "efficiency" => 0.85,
1428
+ # "part_load_curve" => "FURNACE-EFFPLR-COND-NECB2011",
1429
+ # "notes" => "From NECB 2011."
1430
+ # }
1431
+ # If furnace_eff is nil then it does nothing. If both "efficiency" and "part_load_curve" are nil then it does
1432
+ # nothing. If a furnace_eff is a string it looks for furnace_eff as a "name" in the furnace_set.json file to find
1433
+ # the performance details. If an efficiency is set but is not between 0.01 and 1.0 it returns an error. Otherwise,
1434
+ # it looks for air loop supply components that match the "OS_CoilHeatingGas" type. If it finds one it then calls the
1435
+ # reset_furnace_efficiency method which resets the the furnace efficiency and looks for the part load efficiency curve
1436
+ # in the curves.json file. If it finds a curve it sets the part load curve to that, otherwise it returns an error. It
1437
+ # also renames the furnace to include the "furnace_eff"["name"].
1438
+ def modify_furnace_efficiency(model:, furnace_eff: nil)
1439
+ return if furnace_eff.nil?
1440
+ # If furnace_eff is a string rather than a hash then assume it is the name of a furnace efficiency package and look
1441
+ # for a package with that name in furnace_set.json.
1442
+ if furnace_eff.is_a?(String)
1443
+ eff_packages = @standards_data['tables']['furnace_eff_ecm']['table']
1444
+ eff_package = eff_packages.select{|eff_pack_info| eff_pack_info["name"] == furnace_eff}
1445
+ if eff_package.empty?
1446
+ raise "Cannot not find #{furnace_eff} in the ECMS furnace_set.json file. Please check that the name is correctly spelled in the ECMS class furnace_set.json and in the code calling (directly or through another method) the ECMS class modify_furnace_efficiency method."
1447
+ elsif eff_package.size > 1
1448
+ raise "More than one furnace efficiency package with the name #{furnace_eff} was found. Please check the ECMS class furnace_set.json file and make sure that each furnace efficiency package has a unique name."
1449
+ else
1450
+ ecm_name = furnace_eff
1451
+ furnace_eff = {
1452
+ "name" => ecm_name,
1453
+ "efficiency" => eff_package[0]['efficiency'],
1454
+ "part_load_curve" => eff_package[0]['part_load_curve']
1455
+ }
1456
+ end
1457
+ end
1458
+ # If nothing is passed in the furnace_eff hash then assume this was not supposed to be used and return without doing
1459
+ # anything.
1460
+ return if furnace_eff["name"].nil? && furnace_eff["efficiency"].nil? && furnace_eff["part_load_curve"].nil?
1461
+ # If no efficiency or partload curve are found (either passed directly or via the furnace_set.json file) then assume
1462
+ # that the current furance performance settings should not be changed. Return without changing anything.
1463
+ return if furnace_eff["efficiency"].nil? && furnace_eff["part_load_curve"].nil?
1464
+ raise "You attempted to set the efficiency of furnaces in this model to nil. Please check the ECMS class furnace_set.json file and make sure the efficiency is set" if furnace_eff["efficiency"].nil?
1465
+ raise "You attempted to set the efficiency of furnaces in this model to: #{furnace_eff['efficiency']}. Please check the ECMS class furnace_set.json file and make sure the efficiency you set is between 0.01 and 1.0." if (furnace_eff['efficiency'] < 0.01 || furnace_eff['efficiency'] > 1.0)
1466
+ raise "You attempted to set the part load curve of furnaces in this model to nil. Please check the ECMS class furnace_set.json file and ensure that both the efficiency and part load curve are set." if furnace_eff['part_load_curve'].nil?
1467
+ model.getCoilHeatingGass.sort.each do |mod_furnace|
1468
+ reset_furnace_efficiency(model: model, component: mod_furnace.to_CoilHeatingGas.get, eff: furnace_eff)
1469
+ end
1470
+ end
1471
+
1472
+ # This method takes an OS model, a "OS_CoilHeatingGas" type compenent, and an efficiency hash which looks like:
1473
+ # "eff": {
1474
+ # "name": "NECB 85% Efficient Condensing Furnace",
1475
+ # "efficiency" => 0.85,
1476
+ # "part_load_curve" => "FURNACE-EFFPLR-COND-NECB2011",
1477
+ # "notes" => "From NECB 2011."
1478
+ # }
1479
+ # This method sets the efficiency of the furnace to whatever is entered in eff["efficiency"]. It then looks for the
1480
+ # "part_load_curve" value in the curves.json file. If it does not find one it returns an error. If it finds one it
1481
+ # reset the part load curve to whatever was found. It then renames the furnace according to the following pattern:
1482
+ # "eff["name"] + <furnace number (whatever was there before)>".
1483
+ def reset_furnace_efficiency(model:, component:, eff:)
1484
+ component.setGasBurnerEfficiency(eff['efficiency'])
1485
+ part_load_curve_name = eff["part_load_curve"].to_s
1486
+ existing_curve = @standards_data['curves'].select { |curve| curve['name'] == part_load_curve_name }
1487
+ raise "No furnace part load curve with the name #{part_load_curve_name} could be found in the ECMS class curves.json file. Please check both the ECMS class curves.json and the measure furnace_set.json files to ensure the curve is entered and referenced correctly." if existing_curve.empty?
1488
+ part_load_curve = model_add_curve(model, part_load_curve_name)
1489
+ raise "There was a problem setting the furnace part load curve named #{part_load_curve_name} for #{component.name}. Please ensure that the curve is entered and referenced correctly in the ECMS class curves.json or measure furnace_set.json files." unless part_load_curve
1490
+ component.setPartLoadFractionCorrelationCurve(part_load_curve)
1491
+ if eff['name'].nil?
1492
+ ecm_package_name = "Revised Performance Furnace"
1493
+ else
1494
+ ecm_package_name = eff['name']
1495
+ end
1496
+ furnace_num = component.name.to_s.gsub(/[^0-9]/, '')
1497
+ new_furnace_name = ecm_package_name + " #{furnace_num}"
1498
+ component.setName(new_furnace_name)
1499
+ end
1500
+
1501
+ # ============================================================================================================================
1502
+ # Apply shw efficiency
1503
+ # This model takes an OS model and a shw efficiency string or hash sent to it with the following form:
1504
+ # "shw_eff": {
1505
+ # "name" => "Natural Gas Power Vent with Electric Ignition",
1506
+ # "efficiency" => 0.94,
1507
+ # "part_load_curve" => "SWH-EFFFPLR-NECB2011"
1508
+ # "notes" => "From NECB 2011."
1509
+ # }
1510
+ # If shw_eff is nil then it does nothing. If both "efficiency" and "part_load_curve" are nil then it does nothing.
1511
+ # If shw_eff is a string then it looks for shw_eff as a "name" in the shw_set.json file for the details on the tank.
1512
+ # If an efficiency is set but is not between 0.01 and 1.0 it returns an error. Otherwise, it looks for mixed water
1513
+ # heaters. If it finds any it then calls the reset_shw_efficiency method which resets the the shw efficiency and the
1514
+ # part load curve. It also renames the shw tank with the following pattern:
1515
+ # {valume}Gal {eff_name} Water Heater - {Capacity}kBtu/hr {efficiency} Therm Eff
1516
+ def modify_shw_efficiency(model:, shw_eff: nil)
1517
+ return if shw_eff.nil?
1518
+ # If shw_eff is a string rather than a hash then assume it is the name of a shw efficiency package and look
1519
+ # for a package with that name in shw_set.json.
1520
+ if shw_eff.is_a?(String)
1521
+ eff_packages = @standards_data['tables']['shw_eff_ecm']['table']
1522
+ eff_package = eff_packages.select{|eff_pack_info| eff_pack_info["name"] == shw_eff}
1523
+ if eff_package.empty?
1524
+ raise "Cannot not find #{shw_eff} in the ECMS shw_set.json file. Please check that the name is correctly spelled in the ECMS class shw_set.json and in the code calling (directly or through another method) the ECMS class modify_shw_efficiency method."
1525
+ elsif eff_package.size > 1
1526
+ raise "More than one shw tank efficiency package with the name #{shw_eff} was found. Please check the ECMS class shw_set.json file and make sure that each shw tank efficiency package has a unique name."
1527
+ else
1528
+ ecm_name = shw_eff
1529
+ shw_eff = {
1530
+ "name" => ecm_name,
1531
+ "efficiency" => eff_package[0]['efficiency'],
1532
+ "part_load_curve" => eff_package[0]['part_load_curve']
1533
+ }
1534
+ end
1535
+ end
1536
+ # If nothing is passed in the shw_eff hash then assume this was not supposed to be used and return without doing
1537
+ # anything.
1538
+ return if shw_eff["name"].nil? && shw_eff["efficiency"].nil? && shw_eff["part_load_curve"].nil?
1539
+ # If no efficiency or partload curve are found (either passed directly or via the shw_set.json file) then assume
1540
+ # that the current shw performance settings should not be changed. Return without changing anything.
1541
+ return if shw_eff["efficiency"].nil? && shw_eff["part_load_curve"].nil?
1542
+ raise "You attempted to set the efficiency of shw tanks in this model to nil. Please check the ECMS class shw_set.json file and make sure the efficiency is set" if shw_eff["efficiency"].nil?
1543
+ raise "You attempted to set the efficiency of shw tanks in this model to: #{shw_eff['efficiency']}. Please check the ECMS class shw_set.json and make sure the efficiency you set is between 0.01 and 1.0." if (shw_eff['efficiency'] < 0.01 || shw_eff['efficiency'] > 1.0)
1544
+ raise "You attempted to set the part load curve of shw tanks in this model to nil. Please check the ECMS class shw_set.json file and ensure that both the efficiency and part load curve are set." if shw_eff['part_load_curve'].nil?
1545
+ model.getWaterHeaterMixeds.sort.each do |shw_mod|
1546
+ reset_shw_efficiency(model: model, component: shw_mod, eff: shw_eff)
1547
+ end
1548
+ end
1549
+
1550
+ # This method takes an OS model, a "OS_WaterHeaterMixed" type compenent, and an efficiency hash which looks like:
1551
+ # "eff": {
1552
+ # "name": "Natural Gas Power Vent with Electric Ignition",
1553
+ # "efficiency" => 0.94,
1554
+ # "part_load_curve" => "SWH-EFFFPLR-NECB2011",
1555
+ # "notes" => "From NECB 2011."
1556
+ # }
1557
+ # This method sets the efficiency of the shw heater to whatever is entered in eff["efficiency"]. It then looks for the
1558
+ # "part_load_curve" value in the curves.json file. If it does not find one it returns an error. If it finds one it
1559
+ # resets the part load curve to whatever was found. It then renames the shw tank according to the following pattern:
1560
+ # {valume}Gal {eff_name} Water Heater - {Capacity}kBtu/hr {efficiency} Therm Eff
1561
+ def reset_shw_efficiency(model:, component:, eff:)
1562
+ return if component.heaterFuelType.to_s.upcase == 'ELECTRICITY'
1563
+ eff_result = component.setHeaterThermalEfficiency(eff['efficiency'].to_f)
1564
+ raise "There was a problem setting the efficiency of the SHW #{component.name.to_s}. Please check the ECMS class shw_set.json file or the model." unless eff_result
1565
+ part_load_curve_name = eff["part_load_curve"].to_s
1566
+ existing_curve = @standards_data['curves'].select { |curve| curve['name'] == part_load_curve_name }
1567
+ raise "No shw tank part load curve with the name #{part_load_curve_name} could be found in the ECMS class curves.json file. Please check both the ECMS class curves.json and the measure shw_set.json files to ensure the curve is entered and referenced correctly." if existing_curve.empty?
1568
+ part_load_curve = model_add_curve(model, part_load_curve_name)
1569
+ raise "There was a problem setting the shw tank part load curve named #{part_load_curve_name} for #{component.name}. Please ensure that the curve is entered and referenced correctly in the ECMS class curves.json and shw_set.json files." unless part_load_curve
1570
+ component.setPartLoadFactorCurve(part_load_curve)
1571
+ #Get the volume and capacity of the SHW tank.
1572
+ if component.isTankVolumeAutosized
1573
+ shw_vol_gal = "auto_size"
1574
+ else
1575
+ shw_vol_m3 = component.tankVolume.to_f
1576
+ shw_vol_gal = (OpenStudio.convert(shw_vol_m3, 'm^3', 'gal').get).to_f.round(0)
1577
+ end
1578
+ if component.isHeaterMaximumCapacityAutosized
1579
+ shw_capacity_kBtu_hr = "auto_cap"
1580
+ else
1581
+ shw_capacity_W = component.heaterMaximumCapacity.to_f
1582
+ shw_capacity_kBtu_hr = (OpenStudio.convert(shw_capacity_W, 'W', 'kBtu/h').get).to_f.round(0)
1583
+ end
1584
+ # Set a default revised shw tank name if no name is present in the eff hash.
1585
+ if eff["name"].nil?
1586
+ shw_ecm_package_name = "Revised"
1587
+ else
1588
+ shw_ecm_package_name = eff["name"]
1589
+ end
1590
+ shw_name = "#{shw_vol_gal} Gal #{shw_ecm_package_name} Water Heater - #{shw_capacity_kBtu_hr}kBtu/hr #{eff["efficiency"]} Therm Eff"
1591
+ component.setName(shw_name)
1592
+ end
1593
+ end