Module: OpenstudioStandards::HVAC

Defined in:
lib/openstudio-standards/hvac/curves.rb,
lib/openstudio-standards/hvac/helpers.rb,
lib/openstudio-standards/hvac/cbecs_hvac.rb,
lib/openstudio-standards/hvac/conversions.rb,
lib/openstudio-standards/hvac/components/fan.rb,
lib/openstudio-standards/hvac/components/coil.rb,
lib/openstudio-standards/hvac/components/pump.rb,
lib/openstudio-standards/hvac/components/chiller.rb,
lib/openstudio-standards/hvac/air_loop/information.rb,
lib/openstudio-standards/hvac/components/component.rb,
lib/openstudio-standards/hvac/components/air_terminal.rb,
lib/openstudio-standards/hvac/exhaust/create_exhaust_fan.rb,
lib/openstudio-standards/hvac/components/boiler_hot_water.rb,
lib/openstudio-standards/hvac/components/coil_heating_gas.rb,
lib/openstudio-standards/hvac/components/coil_cooling_water.rb,
lib/openstudio-standards/hvac/components/coil_heating_water.rb,
lib/openstudio-standards/hvac/setpoint_managers/information.rb,
lib/openstudio-standards/hvac/components/air_conditioner_vrf.rb,
lib/openstudio-standards/hvac/components/coil_heating_electric.rb,
lib/openstudio-standards/hvac/controls/radiant_system_controls.rb,
lib/openstudio-standards/hvac/components/coil_cooling_dx_two_speed.rb,
lib/openstudio-standards/hvac/components/heat_exchanger_air_to_air.rb,
lib/openstudio-standards/hvac/components/coil_cooling_dx_multi_speed.rb,
lib/openstudio-standards/hvac/components/central_air_source_heat_pump.rb,
lib/openstudio-standards/hvac/components/coil_cooling_dx_single_speed.rb,
lib/openstudio-standards/hvac/components/coil_heating_dx_single_speed.rb,
lib/openstudio-standards/hvac/components/coil_heating_gas_multi_stage.rb,
lib/openstudio-standards/hvac/components/coil_cooling_water_to_air_heat_pump_equation_fit.rb,
lib/openstudio-standards/hvac/components/coil_heating_water_to_air_heat_pump_equation_fit.rb

Overview

The HVAC module provides methods create, modify, and get information about HVAC systems in the model

Curves collapse

Helpers collapse

CBECS HVAC collapse

Conversions collapse

Component:Fan collapse

Component:Coil collapse

Component:Pump collapse

Component:Chiller collapse

AirLoop:Information collapse

Component collapse

Component:Air Terminal collapse

Exhaust collapse

Component:Boiler collapse

Setpoint Managers:Information collapse

Component:VRF collapse

Radiant Controls collapse

Component:HX collapse

Component:CentralAirSourceHeatPump collapse

Class Method Details

.add_cbecs_hvac_system(model, standard, hvac_system_type, zones) ⇒ Boolean

Adds the HVAC system as derived from the combinations of CBECS 2012 MAINHT and MAINCL fields. Mapping between combinations and HVAC systems per www.nrel.gov/docs/fy08osti/41956.pdf Table C-31

Parameters:

Returns:

  • (Boolean)

    returns true if successful, false if not



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# File 'lib/openstudio-standards/hvac/cbecs_hvac.rb', line 16

def self.add_cbecs_hvac_system(model, standard, hvac_system_type, zones)
  # the 'zones' argument includes zones that have heating, cooling, or both
  # if the HVAC system type serves a single zone, handle zones with only heating separately by adding unit heaters
  # applies to system types PTAC, PTHP, PSZ-AC, and Window AC
  heated_and_cooled_zones = zones.select { |zone| OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
  heated_zones = zones.select { |zone| OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) }
  cooled_zones = zones.select { |zone| OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
  cooled_only_zones = zones.select { |zone| !OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
  heated_only_zones = zones.select { |zone| OpenstudioStandards::ThermalZone.thermal_zone_heated?(zone) && !OpenstudioStandards::ThermalZone.thermal_zone_cooled?(zone) }
  system_zones = heated_and_cooled_zones + cooled_only_zones

  # system type naming convention:
  # [ventilation strategy] [ cooling system and plant] [heating system and plant]

  case hvac_system_type

  when 'Baseboard electric'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Baseboard gas boiler'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Baseboard central air source heat pump'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_zones)

  when 'Baseboard district hot water'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)

  when 'Direct evap coolers with baseboard electric'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)
    standard.model_add_hvac_system(model, 'Evaporative Cooler', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'Direct evap coolers with baseboard gas boiler'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)
    standard.model_add_hvac_system(model, 'Evaporative Cooler', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'Direct evap coolers with baseboard central air source heat pump'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_zones)
    standard.model_add_hvac_system(model, 'Evaporative Cooler', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'Direct evap coolers with baseboard district hot water'
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)
    standard.model_add_hvac_system(model, 'Evaporative Cooler', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'Direct evap coolers with forced air furnace', 'Direct evap coolers with gas unit heaters'
    # Using unit heater to represent forced air furnace to limit to one airloop per thermal zone.
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)
    standard.model_add_hvac_system(model, 'Evaporative Cooler', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'Direct evap coolers with no heat'
    standard.model_add_hvac_system(model, 'Evaporative Cooler', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'DOAS with fan coil chiller with boiler'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil chiller with central air source heat pump'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil chiller with district hot water'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil chiller with baseboard electric'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'DOAS with fan coil chiller with gas unit heaters'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'DOAS with fan coil chiller with no heat'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil air-cooled chiller with boiler'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled',
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil air-cooled chiller with central air source heat pump'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled',
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil air-cooled chiller with district hot water'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled',
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil air-cooled chiller with baseboard electric'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled',
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'DOAS with fan coil air-cooled chiller with gas unit heaters'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled',
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'DOAS with fan coil air-cooled chiller with no heat'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled',
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil district chilled water with boiler'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil district chilled water with central air source heat pump'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'AirSourceHeatPump', znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'AirSourceHeatPump', znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil district chilled water with district hot water'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with fan coil district chilled water with baseboard electric'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'DOAS with fan coil district chilled water with gas unit heaters'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'DOAS with fan coil district chilled water with no heat'
    standard.model_add_hvac_system(model, 'DOAS', ht = nil, znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with VRF'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'Electricity', znht = nil, cl = 'Electricity', system_zones,
                                   air_loop_heating_type: 'DX',
                                   air_loop_cooling_type: 'DX')
    standard.model_add_hvac_system(model, 'VRF', ht = 'Electricity', znht = nil, cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'DOAS with water source heat pumps fluid cooler with boiler'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Water Source Heat Pumps', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   heat_pump_loop_cooling_type: 'FluidCooler',
                                   zone_equipment_ventilation: false)

  when 'DOAS with water source heat pumps cooling tower with boiler'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Water Source Heat Pumps', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   heat_pump_loop_cooling_type: 'CoolingTower',
                                   zone_equipment_ventilation: false)

  when 'DOAS with water source heat pumps with ground source heat pump'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'Electricity', znht = nil, cl = 'Electricity', zones,
                                   air_loop_heating_type: 'DX',
                                   air_loop_cooling_type: 'DX')
    standard.model_add_hvac_system(model, 'Ground Source Heat Pumps', ht = 'Electricity', znht = nil, cl = 'Electricity', zones,
                                   zone_equipment_ventilation: false)

  when 'DOAS with water source heat pumps district chilled water with district hot water'
    standard.model_add_hvac_system(model, 'DOAS', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Water Source Heat Pumps', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', zones,
                                   zone_equipment_ventilation: false)

  # ventilation provided by zone fan coil unit in fan coil systems
  when 'Fan coil chiller with boiler'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)

  when 'Fan coil chiller with central air source heat pump'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', zones)

  when 'Fan coil chiller with district hot water'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', zones)

  when 'Fan coil chiller with baseboard electric'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Fan coil chiller with gas unit heaters'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Fan coil chiller with no heat'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones)

  when 'Fan coil air-cooled chiller with boiler'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'Fan coil air-cooled chiller with central air source heat pump'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'Fan coil air-cooled chiller with district hot water'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'Fan coil air-cooled chiller with baseboard electric'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Fan coil air-cooled chiller with gas unit heaters'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Fan coil air-cooled chiller with no heat'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'Fan coil district chilled water with boiler'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', zones)

  when 'Fan coil district chilled water with central air source heat pump'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'AirSourceHeatPump', znht = nil, cl = 'DistrictCooling', zones)

  when 'Fan coil district chilled water with district hot water'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', zones)

  when 'Fan coil district chilled water with baseboard electric'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Fan coil district chilled water with gas unit heaters'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Fan coil district chilled water with no heat'
    standard.model_add_hvac_system(model, 'Fan Coil', ht = nil, znht = nil, cl = 'DistrictCooling', zones)

  when 'Forced air furnace'
    # includes ventilation, whereas residential forced air furnace does not.
    standard.model_add_hvac_system(model, 'Forced Air Furnace', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Gas unit heaters'
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PTAC with baseboard electric'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'PTAC with baseboard gas boiler'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PTAC with baseboard district hot water'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)

  when 'PTAC with gas unit heaters'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PTAC with electric coil'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = 'Electricity', cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    # use 'Baseboard electric' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PTAC with gas coil'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = 'NaturalGas', cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    # use gas unit heaters for heated only zones
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_only_zones)

  when 'PTAC with gas boiler'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = 'NaturalGas', znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    # use 'Baseboard gas boiler' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_only_zones)

  when 'PTAC with central air source heat pump'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    # use 'Baseboard central air source heat pump' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_only_zones)

  when 'PTAC with district hot water'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    # use 'Baseboard district hot water heat' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_only_zones)

  when 'PTAC with no heat'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTAC', ht = nil, znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)

  when 'PTHP'
    # default to have no ventilation air
    standard.model_add_hvac_system(model, 'PTHP', ht = 'Electricity', znht = nil, cl = 'Electricity', system_zones,
                                   zone_equipment_ventilation: false)
    # use 'Baseboard electric' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC with baseboard electric'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC with baseboard gas boiler'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC with baseboard district hot water'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC with gas unit heaters'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC with electric coil'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = 'Electricity', cl = 'Electricity', system_zones)
    # use 'Baseboard electric' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC with gas coil'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = 'NaturalGas', cl = 'Electricity', system_zones)
    # use gas unit heaters for heated only zones
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC with gas boiler'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = 'NaturalGas', znht = nil, cl = 'Electricity', system_zones)
    # use 'Baseboard gas boiler' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC with central air source heat pump'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = 'AirSourceHeatPump', znht = nil, cl = 'Electricity', system_zones)
    # use 'Baseboard central air source heat pump' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC with district hot water'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = 'DistrictHeating', znht = nil, cl = 'Electricity', system_zones)
    # use 'Baseboard district hot water' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC with no heat'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  when 'PSZ-AC district chilled water with baseboard electric'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'DistrictCooling', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC district chilled water with baseboard gas boiler'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'DistrictCooling', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC district chilled water with baseboard district hot water'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'DistrictCooling', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC district chilled water with gas unit heaters'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'DistrictCooling', system_zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'PSZ-AC district chilled water with electric coil'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = 'Electricity', cl = 'DistrictCooling', system_zones)
    # use 'Baseboard electric' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC district chilled water with gas coil'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = 'NaturalGas', cl = 'DistrictCooling', system_zones)
    # use 'Baseboard electric' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC district chilled water with gas boiler'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', system_zones)
    # use 'Baseboard gas boiler' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC district chilled water with central air source heat pump'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = 'AirSourceHeatPump', znht = nil, cl = 'DistrictCooling', system_zones)
    # use 'Baseboard central air source heat pump' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC district chilled water with district hot water'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', system_zones)
    # use 'Baseboard district hot water' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_only_zones)

  when 'PSZ-AC district chilled water with no heat'
    standard.model_add_hvac_system(model, 'PSZ-AC', ht = nil, znht = nil, cl = 'DistrictCooling', cooled_zones)

  when 'PSZ-HP'
    standard.model_add_hvac_system(model, 'PSZ-HP', ht = 'Electricity', znht = nil, cl = 'Electricity', system_zones)
    # use 'Baseboard electric' for heated only zones
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  # PVAV systems by default use a DX coil for cooling
  when 'PVAV with gas boiler reheat', 'Packaged VAV Air Loop with Boiler' # second enumeration for backwards compatibility with Tenant Star project
    standard.model_add_hvac_system(model, 'PVAV Reheat', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_only_zones)

  when 'PVAV with central air source heat pump reheat'
    standard.model_add_hvac_system(model, 'PVAV Reheat', ht = 'AirSourceHeatPump', znht = 'AirSourceHeatPump', cl = 'Electricity', zones)

  when 'PVAV with district hot water reheat'
    standard.model_add_hvac_system(model, 'PVAV Reheat', ht = 'DistrictHeating', znht = 'DistrictHeating', cl = 'Electricity', zones)

  when 'PVAV with PFP boxes'
    standard.model_add_hvac_system(model, 'PVAV PFP Boxes', ht = 'Electricity', znht = 'Electricity', cl = 'Electricity', system_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PVAV with gas heat with electric reheat', 'PVAV with gas coil heat with electric reheat'
    standard.model_add_hvac_system(model, 'PVAV Reheat', ht = 'Gas', znht = 'Electricity', cl = 'Electricity', system_zones,
                                   air_loop_heating_type: 'Gas')
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_only_zones)

  when 'PVAV with gas boiler heat with electric reheat'
    standard.model_add_hvac_system(model, 'PVAV Reheat', ht = 'Gas', znht = 'Electricity', cl = 'Electricity', zones)

  # all residential systems do not have ventilation
  when 'Residential AC with baseboard electric'
    standard.model_add_hvac_system(model, 'Residential AC', ht = nil, znht = nil, cl = nil, cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Residential AC with baseboard gas boiler'
    standard.model_add_hvac_system(model, 'Residential AC', ht = nil, znht = nil, cl = nil, cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Residential AC with baseboard central air source heat pump'
    standard.model_add_hvac_system(model, 'Residential AC', ht = nil, znht = nil, cl = nil, cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_zones)

  when 'Residential AC with baseboard district hot water'
    standard.model_add_hvac_system(model, 'Residential AC', ht = nil, znht = nil, cl = nil, cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)

  when 'Residential AC with residential forced air furnace'
    standard.model_add_hvac_system(model, 'Residential Forced Air Furnace with AC', ht = nil, znht = nil, cl = nil, zones)

  when 'Residential AC with no heat'
    standard.model_add_hvac_system(model, 'Residential AC', ht = nil, znht = nil, cl = nil, cooled_zones)

  when 'Residential heat pump'
    standard.model_add_hvac_system(model, 'Residential Air Source Heat Pump', ht = 'Electricity', znht = nil, cl = 'Electricity', zones)

  when 'Residential heat pump with no cooling'
    standard.model_add_hvac_system(model, 'Residential Air Source Heat Pump', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Residential forced air furnace'
    standard.model_add_hvac_system(model, 'Residential Forced Air Furnace', ht = 'NaturalGas', znht = nil, cl = nil, zones)

  when 'VAV chiller with gas boiler reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'Electricity', zones)

  when 'VAV chiller with central air source heat pump reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'AirSourceHeatPump', znht = 'AirSourceHeatPump', cl = 'Electricity', zones)

  when 'VAV chiller with district hot water reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'DistrictHeating', znht = 'DistrictHeating', cl = 'Electricity', zones)

  when 'VAV chiller with PFP boxes'
    standard.model_add_hvac_system(model, 'VAV PFP Boxes', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'Electricity', zones)

  when 'VAV chiller with gas coil reheat'
    standard.model_add_hvac_system(model, 'VAV Gas Reheat', ht = 'NaturalGas', ht = 'NaturalGas', cl = 'Electricity', zones)

  when 'VAV chiller with no reheat with baseboard electric'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'VAV chiller with no reheat with gas unit heaters'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'VAV chiller with no reheat with zone heat pump'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones)
    # Using PTHP to represent zone heat pump to limit to one airloop per thermal zone.
    standard.model_add_hvac_system(model, 'PTHP', ht = 'Electricity', znht = nil, cl = 'Electricity', zones)

  when 'VAV air-cooled chiller with gas boiler reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'VAV air-cooled chiller with central air source heat pump reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'AirSourceHeatPump', znht = 'AirSourceHeatPump', cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'VAV air-cooled chiller with district hot water reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'DistrictHeating', znht = 'DistrictHeating', cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'VAV air-cooled chiller with PFP boxes'
    standard.model_add_hvac_system(model, 'VAV PFP Boxes', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'VAV air-cooled chiller with gas coil reheat'
    standard.model_add_hvac_system(model, 'VAV Gas Reheat', ht = 'NaturalGas', ht = 'NaturalGas', cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')

  when 'VAV air-cooled chiller with no reheat with baseboard electric'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'VAV air-cooled chiller with no reheat with gas unit heaters'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'VAV air-cooled chiller with no reheat with zone heat pump'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   chilled_water_loop_cooling_type: 'AirCooled')
    # Using PTHP to represent zone heat pump to limit to one airloop per thermal zone.
    standard.model_add_hvac_system(model, 'PTHP', ht = 'Electricity', znht = nil, cl = 'Electricity', zones)

  when 'VAV district chilled water with gas boiler reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'DistrictCooling', zones)

  when 'VAV district chilled water with central air source heat pump reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'AirSourceHeatPump', znht = 'AirSourceHeatPump', cl = 'DistrictCooling', zones)

  when 'VAV district chilled water with district hot water reheat'
    standard.model_add_hvac_system(model, 'VAV Reheat', ht = 'DistrictHeating', znht = 'DistrictHeating', cl = 'DistrictCooling', zones)

  when 'VAV district chilled water with PFP boxes'
    standard.model_add_hvac_system(model, 'VAV PFP Boxes', ht = 'NaturalGas', znht = 'NaturalGas', cl = 'DistrictCooling', zones)

  when 'VAV district chilled water with gas coil reheat'
    standard.model_add_hvac_system(model, 'VAV Gas Reheat', ht = 'NaturalGas', ht = 'NaturalGas', cl = 'DistrictCooling', zones)

  when 'VAV district chilled water with no reheat with baseboard electric'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'VAV district chilled water with no reheat with gas unit heaters'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'VAV district chilled water with no reheat with zone heat pump'
    standard.model_add_hvac_system(model, 'VAV No Reheat', ht = 'NaturalGas', znht = nil, cl = 'DistrictCooling', zones)
    # Using PTHP to represent zone heat pump to limit to one airloop per thermal zone.
    standard.model_add_hvac_system(model, 'PTHP', ht = 'Electricity', znht = nil, cl = 'Electricity', zones)

  when 'VRF'
    standard.model_add_hvac_system(model, 'VRF', ht = 'Electricity', znht = nil, cl = 'Electricity', zones)

  when 'Water source heat pumps fluid cooler with boiler'
    standard.model_add_hvac_system(model, 'Water Source Heat Pumps', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   heat_pump_loop_cooling_type: 'FluidCooler')

  when 'Water source heat pumps cooling tower with boiler'
    standard.model_add_hvac_system(model, 'Water Source Heat Pumps', ht = 'NaturalGas', znht = nil, cl = 'Electricity', zones,
                                   heat_pump_loop_cooling_type: 'CoolingTower')

  when 'Water source heat pumps with ground source heat pump'
    standard.model_add_hvac_system(model, 'Ground Source Heat Pumps', ht = 'Electricity', znht = nil, cl = 'Electricity', zones)

  when 'Water source heat pumps district chilled water with district hot water'
    standard.model_add_hvac_system(model, 'Water Source Heat Pumps', ht = 'DistrictHeating', znht = nil, cl = 'DistrictCooling', zones)

  when 'Window AC with baseboard electric'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'Electricity', znht = nil, cl = nil, heated_zones)

  when 'Window AC with baseboard gas boiler'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Window AC with baseboard central air source heat pump'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_zones)

  when 'Window AC with baseboard district hot water'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)
    standard.model_add_hvac_system(model, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)

  when 'Window AC with forced air furnace'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)
    standard.model_add_hvac_system(model, 'Forced Air Furnace', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Window AC with unit heaters'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)
    standard.model_add_hvac_system(model, 'Unit Heaters', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)

  when 'Window AC with no heat'
    standard.model_add_hvac_system(model, 'Window AC', ht = nil, znht = nil, cl = 'Electricity', cooled_zones)

  else
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "CBECS HVAC system type #{hvac_system_type} not recognized.")
    return false
  end
  return true
end

.afue_to_thermal_eff(afue) ⇒ Double

A helper method to convert from AFUE to thermal efficiency

Parameters:

  • afue (Double)

    Annual Fuel Utilization Efficiency

Returns:

  • (Double)

    Thermal efficiency (%)



191
192
193
# File 'lib/openstudio-standards/hvac/conversions.rb', line 191

def self.afue_to_thermal_eff(afue)
  return afue
end

.air_conditioner_variable_refrigerant_flow_get_cooling_capacity(air_conditioner_variable_refrigerant_flow) ⇒ Double

Return the cooling capacity in W of a AirConditionerVariableRefrigerantFlow

Parameters:

  • air_conditioner_variable_refrigerant_flow (OpenStudio::Model::AirConditionerVariableRefrigerantFlow)

    vrf unit

Returns:

  • (Double)

    cooling capacity in W



450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/openstudio-standards/hvac/components/air_conditioner_vrf.rb', line 450

def self.air_conditioner_variable_refrigerant_flow_get_cooling_capacity(air_conditioner_variable_refrigerant_flow)
  capacity_w = nil
  if air_conditioner_variable_refrigerant_flow.grossRatedTotalCoolingCapacity.is_initialized
    capacity_w = air_conditioner_variable_refrigerant_flow.grossRatedTotalCoolingCapacity.get
  elsif air_conditioner_variable_refrigerant_flow.autosizedGrossRatedTotalCoolingCapacity.is_initialized
    capacity_w = air_conditioner_variable_refrigerant_flow.autosizedGrossRatedTotalCoolingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.air_conditioner_vrf', "For #{air_conditioner_variable_refrigerant_flow.name} cooling capacity is not available.")
    return 0.0
  end

  return capacity_w
end

.air_conditioner_variable_refrigerant_flow_get_heating_capacity(air_conditioner_variable_refrigerant_flow) ⇒ Double

Return the heating capacity in W of a AirConditionerVariableRefrigerantFlow

Parameters:

  • air_conditioner_variable_refrigerant_flow (OpenStudio::Model::AirConditionerVariableRefrigerantFlow)

    vrf unit

Returns:

  • (Double)

    heating capacity in W



468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/openstudio-standards/hvac/components/air_conditioner_vrf.rb', line 468

def self.air_conditioner_variable_refrigerant_flow_get_heating_capacity(air_conditioner_variable_refrigerant_flow)
  capacity_w = nil
  if air_conditioner_variable_refrigerant_flow.grossRatedHeatingCapacity.is_initialized
    capacity_w = air_conditioner_variable_refrigerant_flow.grossRatedHeatingCapacity.get
  elsif air_conditioner_variable_refrigerant_flow.autosizedGrossRatedHeatingCapacity.is_initialized
    capacity_w = air_conditioner_variable_refrigerant_flow.autosizedGrossRatedHeatingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.air_conditioner_vrf', "For #{air_conditioner_variable_refrigerant_flow.name} heating capacity is not available.")
    return 0.0
  end

  return capacity_w
end

.air_loop_hvac_direct_evap?(air_loop_hvac) ⇒ Boolean

Returns whether air loop HVAC is a direct evaporative system

Parameters:

  • air_loop_hvac (<OpenStudio::Model::AirLoopHVAC>)

    OpenStudio AirLoopHVAC object

Returns:

  • (Boolean)

    returns true if successful, false if not



11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/openstudio-standards/hvac/air_loop/information.rb', line 11

def self.air_loop_hvac_direct_evap?(air_loop_hvac)
  # check if direct evap
  is_direct_evap = false
  air_loop_hvac.supplyComponents.each do |component|
    # Get the object type
    obj_type = component.iddObjectType.valueName.to_s
    case obj_type
    when 'OS_EvaporativeCooler_Direct_ResearchSpecial', 'OS_EvaporativeCooler_Indirect_ResearchSpecial'
      is_direct_evap = true
    end
  end
  return is_direct_evap
end

.air_loop_hvac_unitary_system?(air_loop_hvac) ⇒ Boolean

Returns whether air loop HVAC is a unitary system

Parameters:

  • air_loop_hvac (<OpenStudio::Model::AirLoopHVAC>)

    OpenStudio AirLoopHVAC object

Returns:

  • (Boolean)

    returns true if air_loop_hvac is a unitary system, false if not



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/openstudio-standards/hvac/air_loop/information.rb', line 29

def self.air_loop_hvac_unitary_system?(air_loop_hvac)
  # check if unitary system
  is_unitary_system = false
  air_loop_hvac.supplyComponents.each do |component|
    # Get the object type
    obj_type = component.iddObjectType.valueName.to_s
    case obj_type
    when 'OS_AirLoopHVAC_UnitarySystem', 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir', 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir_MultiSpeed', 'OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass'
      is_unitary_system = true
    end
  end
  return is_unitary_system
end

.air_terminal_single_duct_vav_reheat_reheat_type(air_terminal_single_duct_vav_reheat) ⇒ String

Determines whether the terminal has a NaturalGas, Electricity, or HotWater reheat coil.

Parameters:

  • air_terminal_single_duct_vav_reheat (OpenStudio::Model::AirTerminalSingleDuctVAVReheat)

    the air terminal object

Returns:

  • (String)

    reheat type. One of NaturalGas, Electricity, or HotWater.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/openstudio-standards/hvac/components/air_terminal.rb', line 11

def self.air_terminal_single_duct_vav_reheat_reheat_type(air_terminal_single_duct_vav_reheat)
  type = nil

  if air_terminal_single_duct_vav_reheat.to_AirTerminalSingleDuctVAVNoReheat.is_initialized
    return type
  end

  # Get the reheat coil
  rht_coil = air_terminal_single_duct_vav_reheat.reheatCoil
  if rht_coil.to_CoilHeatingElectric.is_initialized
    type = 'Electricity'
  elsif rht_coil.to_CoilHeatingWater.is_initialized
    type = 'HotWater'
  elsif rht_coil.to_CoilHeatingGas.is_initialized
    type = 'NaturalGas'
  end

  return type
end

.boiler_hot_water_get_capacity(boiler_hot_water) ⇒ Double

Return the capacity in W of a BoilerHotWater

Parameters:

  • boiler_hot_water (OpenStudio::Model::BoilerHotWater)

    hot water boiler object

Returns:

  • (Double)

    capacity in W



131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/openstudio-standards/hvac/components/boiler_hot_water.rb', line 131

def self.boiler_hot_water_get_capacity(boiler_hot_water)
  capacity_w = nil
  if boiler_hot_water.nominalCapacity.is_initialized
    capacity_w = boiler_hot_water.nominalCapacity.get
  elsif boiler_hot_water.autosizedNominalCapacity.is_initialized
    capacity_w = boiler_hot_water.autosizedNominalCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.boiler_hot_water', "For #{boiler_hot_water.name} capacity is not available.")
    return capacity_w
  end

  return capacity_w
end

.boiler_hot_water_get_water_flow_rate(boiler_hot_water) ⇒ Double

Return the design water flow rate in m^3/s of a BoilerHotWater

Parameters:

  • boiler_hot_water (OpenStudio::Model::BoilerHotWater)

    hot water boiler object

Returns:

  • (Double)

    design water flow rate in m^3/s



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/openstudio-standards/hvac/components/boiler_hot_water.rb', line 149

def self.boiler_hot_water_get_water_flow_rate(boiler_hot_water)
  design_water_flow_rate_m3_per_s = nil
  if boiler_hot_water.designWaterFlowRate.is_initialized
    design_water_flow_rate_m3_per_s = boiler_hot_water.designWaterFlowRate.get
  elsif boiler_hot_water.autosizedDesignWaterFlowRate.is_initialized
    design_water_flow_rate_m3_per_s = boiler_hot_water.autosizedDesignWaterFlowRate.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.boiler_hot_water', "For #{boiler_hot_water.name} design water flow rate is not available.")
    return design_water_flow_rate_m3_per_s
  end

  return design_water_flow_rate_m3_per_s
end

.chiller_electric_get_capacity(chiller_electric_eir) ⇒ Double

Return the capacity in W of a ChillerElectricEIR

Parameters:

  • chiller_electric_eir (OpenStudio::Model::ChillerElectricEIR)

    chiller object

Returns:

  • (Double)

    capacity in W



11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/openstudio-standards/hvac/components/chiller.rb', line 11

def self.chiller_electric_get_capacity(chiller_electric_eir)
  capacity_w = nil
  if chiller_electric_eir.referenceCapacity.is_initialized
    capacity_w = chiller_electric_eir.referenceCapacity.get
  elsif chiller_electric_eir.autosizedReferenceCapacity.is_initialized
    capacity_w = chiller_electric_eir.autosizedReferenceCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.chiller', "For #{chiller_electric_eir.name} capacity is not available.")
    return capacity_w
  end

  return capacity_w
end

.coil_cooling_dx_multi_speed_get_capacity(coil_cooling_dx_multi_speed, multiplier: nil) ⇒ Double

Return the capacity in W of a CoilCoolingDXMultiSpeed

Parameters:

  • coil_cooling_dx_multi_speed (OpenStudio::Model::CoilCoolingDXMultiSpeed)

    coil cooling dx multi speed object

  • multiplier (Double) (defaults to: nil)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/openstudio-standards/hvac/components/coil_cooling_dx_multi_speed.rb', line 13

def self.coil_cooling_dx_multi_speed_get_capacity(coil_cooling_dx_multi_speed, multiplier: nil)
  capacity_w = nil
  clg_stages = coil_cooling_dx_multi_speed.stages
  if clg_stages.last.grossRatedTotalCoolingCapacity.is_initialized
    capacity_w = clg_stages.last.grossRatedTotalCoolingCapacity.get
  elsif (clg_stages.size == 1) && coil_cooling_dx_multi_speed.stages[0].autosizedSpeedRatedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_multi_speed.stages[0].autosizedSpeedRatedTotalCoolingCapacity.get
  elsif (clg_stages.size == 2) && coil_cooling_dx_multi_speed.stages[1].autosizedGrossRatedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_multi_speed.stages[1].autosizedGrossRatedTotalCoolingCapacity.get
  elsif (clg_stages.size == 3) && coil_cooling_dx_multi_speed.stages[2].autosizedGrossRatedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_multi_speed.stages[2].autosizedSpeedRatedTotalCoolingCapacity.get
  elsif (clg_stages.size == 4) && coil_cooling_dx_multi_speed.stages[3].autosizedGrossRatedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_multi_speed.stages[3].autosizedGrossRatedTotalCoolingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_cooling_dx_multi_speed', "For #{coil_cooling_dx_multi_speed.name} capacity is not available.")
    return capacity_w
  end

  if !multiplier.nil? && multiplier > 1
    total_cap = capacity_w
    capacity_w /= multiplier
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC.coil_cooling_dx_multi_speed', "For #{coil_cooling_dx_multi_speed.name}, total capacity of #{OpenStudio.convert(total_cap, 'W', 'kBtu/hr').get.round(2)}kBTU/hr was divided by the zone multiplier of #{multiplier} to give #{capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get.round(2)}kBTU/hr.")
  end

  return capacity_w
end

.coil_cooling_dx_single_speed_get_capacity(coil_cooling_dx_single_speed, multiplier: nil) ⇒ Double

Return the capacity in W of a CoilCoolingDXSingleSpeed

Parameters:

  • coil_cooling_dx_single_speed (OpenStudio::Model::CoilCoolingDXSingleSpeed)

    coil cooling dx single speed object

  • multiplier (Double) (defaults to: nil)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/openstudio-standards/hvac/components/coil_cooling_dx_single_speed.rb', line 280

def self.coil_cooling_dx_single_speed_get_capacity(coil_cooling_dx_single_speed, multiplier: nil)
  capacity_w = nil
  if coil_cooling_dx_single_speed.ratedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_single_speed.ratedTotalCoolingCapacity.get
  elsif coil_cooling_dx_single_speed.autosizedRatedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_single_speed.autosizedRatedTotalCoolingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_cooling_dx_single_speed', "For #{coil_cooling_dx_single_speed.name} capacity is not available.")
    return capacity_w
  end

  if !multiplier.nil? && multiplier > 1
    total_cap = capacity_w
    capacity_w /= multiplier
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC.coil_cooling_dx_single_speed', "For #{coil_cooling_dx_single_speed.name}, total capacity of #{OpenStudio.convert(total_cap, 'W', 'kBtu/hr').get.round(2)}kBTU/hr was divided by the zone multiplier of #{multiplier} to give #{capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get.round(2)}kBTU/hr.")
  end

  return capacity_w
end

.coil_cooling_dx_two_speed_get_capacity(coil_cooling_dx_two_speed, multiplier: nil) ⇒ Double

Return the capacity in W of a CoilCoolingDXTwoSpeed

Parameters:

  • coil_cooling_dx_two_speed (OpenStudio::Model::CoilCoolingDXTwoSpeed)

    coil cooling dx two speed object

  • multiplier (Double) (defaults to: nil)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/openstudio-standards/hvac/components/coil_cooling_dx_two_speed.rb', line 167

def self.coil_cooling_dx_two_speed_get_capacity(coil_cooling_dx_two_speed, multiplier: nil)
  capacity_w = nil
  if coil_cooling_dx_two_speed.ratedHighSpeedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_two_speed.ratedHighSpeedTotalCoolingCapacity.get
  elsif coil_cooling_dx_two_speed.autosizedRatedHighSpeedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_dx_two_speed.autosizedRatedHighSpeedTotalCoolingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_cooling_dx_two_speed', "For #{coil_cooling_dx_two_speed.name} capacity is not available.")
    return capacity_w
  end

  if !multiplier.nil? && multiplier > 1
    total_cap = capacity_w
    capacity_w /= multiplier
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC.coil_cooling_dx_two_speed', "For #{coil_cooling_dx_two_speed.name}, total capacity of #{OpenStudio.convert(total_cap, 'W', 'kBtu/hr').get.round(2)}kBTU/hr was divided by the zone multiplier of #{multiplier} to give #{capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get.round(2)}kBTU/hr.")
  end

  return capacity_w
end

.coil_cooling_water_to_air_heat_pump_get_capacity(coil_cooling_water_to_air_heat_pump, multiplier: nil) ⇒ Double

Return the capacity in W of a CoilCoolingWaterToAirHeatPumpEquationFit

Parameters:

  • coil_cooling_water_to_air_heat_pump (OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit)

    coil cooling object

  • multiplier (Double) (defaults to: nil)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/openstudio-standards/hvac/components/coil_cooling_water_to_air_heat_pump_equation_fit.rb', line 162

def self.coil_cooling_water_to_air_heat_pump_get_capacity(coil_cooling_water_to_air_heat_pump, multiplier: nil)
  capacity_w = nil
  if coil_cooling_water_to_air_heat_pump.ratedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_water_to_air_heat_pump.ratedTotalCoolingCapacity.get
  elsif coil_cooling_water_to_air_heat_pump.autosizedRatedTotalCoolingCapacity.is_initialized
    capacity_w = coil_cooling_water_to_air_heat_pump.autosizedRatedTotalCoolingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_cooling_water_to_air_heat_pump_equation_fit', "For #{coil_cooling_water_to_air_heat_pump.name} capacity is not available.")
    return capacity_w
  end

  if !multiplier.nil? && multiplier > 1
    total_cap = capacity_w
    capacity_w /= multiplier
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC.coil_cooling_water_to_air_heat_pump', "For #{coil_cooling_dx_twcoil_cooling_water_to_air_heat_pumpo_speed.name}, total capacity of #{OpenStudio.convert(total_cap, 'W', 'kBtu/hr').get.round(2)}kBTU/hr was divided by the zone multiplier of #{multiplier} to give #{capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get.round(2)}kBTU/hr.")
  end

  return capacity_w
end

.coil_dx_heat_pump?(coil_dx) ⇒ Boolean

Determine if it is a heat pump

Parameters:

  • coil_dx (OpenStudio::Model::StraightComponent)

    coil cooling object, allowable types: CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, CoilCoolingDXMultiSpeed

Returns:

  • (Boolean)

    returns true if it is a heat pump, false if not



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/openstudio-standards/hvac/components/coil.rb', line 46

def self.coil_dx_heat_pump?(coil_dx)
  heat_pump = false

  if coil_dx.airLoopHVAC.empty?
    if coil_dx.containingHVACComponent.is_initialized
      containing_comp = coil_dx.containingHVACComponent.get
      if containing_comp.to_AirLoopHVACUnitarySystem.is_initialized
        htg_coil = containing_comp.to_AirLoopHVACUnitarySystem.get.heatingCoil
        unless htg_coil.empty?
          heat_pump = ['OS_Coil_Heating_DX_SingleSpeed',
                       'OS_Coil_Heating_DX_MultiSpeed',
                       'OS_Coil_Heating_DX_VariableSpeed',
                       'OS_Coil_Heating_WaterToAirHeatPump_EquationFit'].include?(htg_coil.get.iddObjectType.valueName.to_s)
        end
      elsif containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.is_initialized
        heat_pump = true
      elsif containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.is_initialized
        htg_coil = containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get.heatingCoil
        if htg_coil.to_CoilHeatingDXMultiSpeed.is_initialized then heat_pump = true end
      end
    elsif coil_dx.containingZoneHVACComponent.is_initialized
      containing_comp = coil_dx.containingZoneHVACComponent.get
      # PTHP
      if containing_comp.to_ZoneHVACPackagedTerminalHeatPump.is_initialized
        heat_pump = true
      end
      # @todo Add other zone hvac systems
    end
  else
    if !coil_dx.airLoopHVAC.get.supplyComponents('OS:Coil:Heating:DX:SingleSpeed'.to_IddObjectType).empty? ||
       !coil_dx.airLoopHVAC.get.supplyComponents('OS:Coil:Heating:DX:MultiSpeed'.to_IddObjectType).empty? ||
       !coil_dx.airLoopHVAC.get.supplyComponents('OS:Coil:Heating:DX:VariableSpeed'.to_IddObjectType).empty?
      heat_pump = true
    end
  end

  return heat_pump
end

.coil_dx_heating_type(coil_dx) ⇒ String

Determine the heating type for a dx cooling coil. Possible choices are: ‘Electric Resistance or None’, ‘All Other’.

Parameters:

  • coil_dx (OpenStudio::Model::StraightComponent)

    coil cooling object, allowable types: CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, CoilCoolingDXMultiSpeed

Returns:

  • (String)

    the heating type



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/openstudio-standards/hvac/components/coil.rb', line 90

def self.coil_dx_heating_type(coil_dx)
  htg_type = nil

  # If Unitary or Zone HVAC (could be a unitary system on an air loop)
  if coil_dx.airLoopHVAC.empty?
    if coil_dx.containingHVACComponent.is_initialized
      containing_comp = coil_dx.containingHVACComponent.get
      if containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.is_initialized
        htg_type = 'Electric Resistance or None'
      elsif containing_comp.to_AirLoopHVACUnitarySystem.is_initialized
        htg_coil = containing_comp.to_AirLoopHVACUnitarySystem.get.heatingCoil
        if containing_comp.name.to_s.include? 'Minisplit'
          htg_type = 'All Other'
        elsif htg_coil.is_initialized
          htg_coil = htg_coil.get
          if htg_coil.to_CoilHeatingElectric.is_initialized || htg_coil.to_CoilHeatingDXMultiSpeed.is_initialized
            htg_type = 'Electric Resistance or None'
          elsif htg_coil.to_CoilHeatingGas.is_initialized || htg_coil.to_CoilHeatingGasMultiStage.is_initialized || htg_coil.to_CoilHeatingWater.is_initialized
            htg_type = 'All Other'
          end
        else
          htg_type = 'Electric Resistance or None'
        end
      elsif containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.is_initialized
        htg_coil = containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get.heatingCoil
        supp_htg_coil = containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get.supplementalHeatingCoil
        if htg_coil.to_CoilHeatingDXMultiSpeed.is_initialized || supp_htg_coil.to_CoilHeatingElectric.is_initialized
          htg_type = 'Electric Resistance or None'
        elsif htg_coil.to_CoilHeatingGas.is_initialized || htg_coil.to_CoilHeatingGasMultiStage.is_initialized || htg_coil.to_CoilHeatingWater.is_initialized
          htg_type = 'All Other'
        end
      end
      # @todo Add other unitary systems
    elsif coil_dx.containingZoneHVACComponent.is_initialized
      containing_comp = coil_dx.containingZoneHVACComponent.get
      # PTAC
      if containing_comp.to_ZoneHVACPackagedTerminalAirConditioner.is_initialized
        htg_coil = containing_comp.to_ZoneHVACPackagedTerminalAirConditioner.get.heatingCoil
        if htg_coil.to_CoilHeatingElectric.is_initialized
          htg_type = 'Electric Resistance or None'
        elsif htg_coil.to_CoilHeatingGas.is_initialized || htg_coil.to_CoilHeatingGasMultiStage.is_initialized || htg_coil.to_CoilHeatingWater.is_initialized
          htg_type = 'All Other'
        end
      # PTHP
      elsif containing_comp.to_ZoneHVACPackagedTerminalHeatPump.is_initialized
        htg_type = 'Electric Resistance or None'
      end
      # @todo Add other zone hvac systems
    end
  end

  # If on an AirLoop
  if coil_dx.airLoopHVAC.is_initialized
    air_loop = coil_dx.airLoopHVAC.get
    htg_type = if !air_loop.supplyComponents('OS:Coil:Heating:Gas'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:Water'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:DX:SingleSpeed'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:DX:MultiSpeed'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:DX:VariableSpeed'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:Gas:MultiStage'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:Desuperheater'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:WaterToAirHeatPump:EquationFit'.to_IddObjectType).empty?
                 'All Other'
               elsif !air_loop.supplyComponents('OS:Coil:Heating:Electric'.to_IddObjectType).empty?
                 'Electric Resistance or None'
               else
                 'Electric Resistance or None'
               end
  end

  return htg_type
end

.coil_dx_subcategory(coil_dx) ⇒ String

TODO:

Add add split system vs single package to model object

Finds the subcategory. Possible choices are: Single Package, Split System, PTAC, or PTHP

Parameters:

  • coil_dx (OpenStudio::Model::StraightComponent)

    coil cooling object, allowable types: CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, CoilCoolingDXMultiSpeed

Returns:

  • (String)

    the coil_dx_subcategory(coil_dx)



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/openstudio-standards/hvac/components/coil.rb', line 14

def self.coil_dx_subcategory(coil_dx)
  sub_category = 'Single Package'

  # Fallback to the name, mainly for library export
  if coil_dx.name.get.to_s.include?('Single Package')
    sub_category = 'Single Package'
  elsif coil_dx.name.get.to_s.include?('Split System') ||
        coil_dx.name.get.to_s.include?('Central Air Source HP')
    sub_category = 'Split System'
  elsif coil_dx.name.get.to_s.include?('Minisplit HP')
    sub_category = 'Minisplit System'
  elsif coil_dx.name.get.to_s.include?('CRAC')
    sub_category = 'CRAC'
  end

  if coil_dx.airLoopHVAC.empty? && coil_dx.containingZoneHVACComponent.is_initialized
    containing_comp = coil_dx.containingZoneHVACComponent.get
    # PTHP
    if containing_comp.to_ZoneHVACPackagedTerminalHeatPump.is_initialized
      sub_category = 'PTHP'
    end
    # @todo Add other zone hvac systems
  end

  return sub_category
end

.coil_heating_dx_single_speed_get_capacity(coil_heating_dx_single_speed, multiplier: 1.0) ⇒ Double

Return the capacity in W of a CoilHeatingDXSingleSpeed

Parameters:

  • coil_heating_dx_single_speed (OpenStudio::Model::CoilHeatingDXSingleSpeed)

    coil heating dx single speed object

  • multiplier (Double) (defaults to: 1.0)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/openstudio-standards/hvac/components/coil_heating_dx_single_speed.rb', line 206

def self.coil_heating_dx_single_speed_get_capacity(coil_heating_dx_single_speed, multiplier: 1.0)
  capacity_w = nil
  if coil_heating_dx_single_speed.ratedTotalHeatingCapacity.is_initialized
    capacity_w = coil_heating_dx_single_speed.ratedTotalHeatingCapacity.get
  elsif coil_heating_dx_single_speed.autosizedRatedTotalHeatingCapacity.is_initialized
    capacity_w = coil_heating_dx_single_speed.autosizedRatedTotalHeatingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_heating_dx_single_speed', "For #{coil_heating_dx_single_speed.name} capacity is not available.")
    return capacity_w
  end

  return capacity_w
end

.coil_heating_gas_get_capacity(coil_heating_gas, multiplier: nil) ⇒ Double

Return the capacity in W of a CoilHeatingGas

Parameters:

  • coil_heating_gas (OpenStudio::Model::CoilHeatingGas)

    coil heating gas object

  • multiplier (Double) (defaults to: nil)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/openstudio-standards/hvac/components/coil_heating_gas.rb', line 70

def self.coil_heating_gas_get_capacity(coil_heating_gas, multiplier: nil)
  capacity_w = nil
  if coil_heating_gas.nominalCapacity.is_initialized
    capacity_w = coil_heating_gas.nominalCapacity.get
  elsif coil_heating_gas.autosizedNominalCapacity.is_initialized
    capacity_w = coil_heating_gas.autosizedNominalCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_heating_gas', "For #{coil_heating_gas.name} capacity is not available.")
    return capacity_w
  end

  if !multiplier.nil? && multiplier > 1
    total_cap = capacity_w
    capacity_w /= multiplier
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC.coil_heating_gas', "For #{coil_heating_gas.name}, total capacity of #{OpenStudio.convert(total_cap, 'W', 'kBtu/hr').get.round(2)}kBTU/hr was divided by the zone multiplier of #{multiplier} to give #{capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get.round(2)}kBTU/hr.")
  end

  return capacity_w
end

.coil_heating_gas_multi_stage_get_capacity(coil_heating_gas_multi_stage) ⇒ Double

Return the capacity in W of a CoilHeatingGasMultiStage

Parameters:

  • coil_heating_gas_multi_stage (OpenStudio::Model::CoilHeatingGasMultiStage)

    coil heating gas multi stage object

Returns:

  • (Double)

    capacity in W



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/openstudio-standards/hvac/components/coil_heating_gas_multi_stage.rb', line 12

def self.coil_heating_gas_multi_stage_get_capacity(coil_heating_gas_multi_stage)
  capacity_w = nil
  htg_stages = coil_heating_gas_multi_stage.stages
  if htg_stages.last.nominalCapacity.is_initialized
    capacity_w = htg_stages.last.nominalCapacity.get
  elsif (htg_stages.size == 1) && coil_heating_gas_multi_stage.stages[0].autosizedNominalCapacity.is_initialized
    capacity_w = coil_heating_gas_multi_stage.stages[0].autosizedNominalCapacity.get
  elsif (htg_stages.size == 2) && coil_heating_gas_multi_stage.stages[1].autosizedNominalCapacity.is_initialized
    capacity_w = coil_heating_gas_multi_stage.stages[1].autosizedNominalCapacity.get
  elsif (htg_stages.size == 3) && coil_heating_gas_multi_stage.stages[2].autosizedNominalCapacity.is_initialized
    capacity_w = coil_heating_gas_multi_stage.stages[2].autosizedNominalCapacity.get
  elsif (htg_stages.size == 4) && coil_heating_gas_multi_stage.stages[3].autosizedNominalCapacity.is_initialized
    capacity_w = coil_heating_gas_multi_stage.stages[3].autosizedNominalCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_heating_gas_multistage', "For #{coil_heating_gas_multi_stage.name} capacity is not available.")
    return capacity_w
  end

  return capacity_w
end

.coil_heating_get_paired_coil_cooling_capacity(coil_heating) ⇒ Double

Return the cooling capacity of the DX cooling coil paired with the heating coil

Parameters:

  • coil_heating (OpenStudio::Model::CoilHeatingDXSingleSpeed, OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit)

    coil heating object

Returns:

  • (Double)

    capacity in W to be used for find object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/openstudio-standards/hvac/components/coil.rb', line 174

def self.coil_heating_get_paired_coil_cooling_capacity(coil_heating)
  capacity_w = nil

  # Get the paired cooling coil
  clg_coil = nil

  # Unitary and zone equipment
  if coil_heating.airLoopHVAC.empty?
    if coil_heating.containingHVACComponent.is_initialized
      containing_comp = coil_heating.containingHVACComponent.get
      if containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.is_initialized
        clg_coil = containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.get.coolingCoil
      elsif containing_comp.to_AirLoopHVACUnitarySystem.is_initialized
        unitary = containing_comp.to_AirLoopHVACUnitarySystem.get
        if unitary.coolingCoil.is_initialized
          clg_coil = unitary.coolingCoil.get
        end
      end
      # @todo Add other unitary systems
    elsif coil_heating.containingZoneHVACComponent.is_initialized
      containing_comp = coil_heating.containingZoneHVACComponent.get
      # PTHP
      if containing_comp.to_ZoneHVACPackagedTerminalHeatPump.is_initialized
        pthp = containing_comp.to_ZoneHVACPackagedTerminalHeatPump.get
        clg_coil = containing_comp.to_ZoneHVACPackagedTerminalHeatPump.get.coolingCoil
      end
    end
  end

  # On AirLoop directly
  if coil_heating.airLoopHVAC.is_initialized
    air_loop = coil_heating.airLoopHVAC.get

    # Check for the presence of any other type of cooling coil
    clg_types = ['OS:Coil:Cooling:DX:SingleSpeed',
                 'OS:Coil:Cooling:DX:TwoSpeed',
                 'OS:Coil:Cooling:DX:MultiSpeed']
    clg_types.each do |ct|
      coils = air_loop.supplyComponents(ct.to_IddObjectType)
      next if coils.empty?

      clg_coil = coils[0].to_StraightComponent.get
      # Stop on first DX cooling coil found
      break
    end
  end

  # If no paired cooling coil was found, throw an error and fall back to the heating capacity
  if clg_coil.nil?
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil', "For #{coil_heating.name}, the paired DX cooling coil could not be found to determine capacity. Using the coil's heating capacity instead, which will incorrectly select efficiency levels for most standards.")

    if coil_heating.to_CoilHeatingDXSingleSpeed.is_initialized
      coil_heating = coil_heating.to_CoilHeatingDXSingleSpeed.get
      capacity_w = OpenstudioStandards::HVAC.coil_heating_dx_single_speed_get_capacity(coil_heating)
    elsif coil_heating.to_CoilHeatingWaterToAirHeatPumpEquationFit.is_initialized
      coil_heating = coil_heating.to_CoilHeatingWaterToAirHeatPumpEquationFit.get
      capacity_w = OpenstudioStandards::HVAC.coil_heating_water_to_air_heat_pump_equation_fit_get_capacity(coil_heating)
    elsif coil_heating.to_CoilHeatingGas.is_initialized
      coil_heating = coil_heating.to_CoilHeatingGas.get
      capacity_w = OpenstudioStandards::HVAC.coil_heating_gas_get_capacity(coil_heating)
    elsif coil_heating.to_CoilHeatingGasMultiStage.is_initialized
      coil_heating = coil_heating.to_CoilHeatingGasMultiStage.get
      capacity_w = OpenstudioStandards::HVAC.coil_heating_gas_multi_stage_get_capacity(coil_heating)
    else
      # If the coil is not a supported coil type, we cannot determine the capacity
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil', "For #{coil_heating.name}, the coil is not a supported coil type and cannot determine capacity.")
      return nil
    end

    # return nil if no capacity is available
    if capacity_w.nil?
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil', "For #{coil_heating.name} capacity is not available.")
      return nil
    end

    return capacity_w
  end

  multiplier = 1.0
  if ['PTAC', 'PTHP'].include?(OpenstudioStandards::HVAC.coil_dx_subcategory(clg_coil))
    thermal_zone = OpenstudioStandards::HVAC.hvac_component_get_thermal_zone(clg_coil)
    multiplier = thermal_zone.multiplier if !thermal_zone.nil?
  end

  # If a coil was found, cast to the correct type
  if clg_coil.to_CoilCoolingDXSingleSpeed.is_initialized
    clg_coil = clg_coil.to_CoilCoolingDXSingleSpeed.get
    capacity_w = OpenstudioStandards::HVAC.coil_cooling_dx_single_speed_get_capacity(clg_coil, multiplier: multiplier)
  elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
    clg_coil = clg_coil.to_CoilCoolingDXTwoSpeed.get
    capacity_w = OpenstudioStandards::HVAC.coil_cooling_dx_two_speed_get_capacity(clg_coil, multiplier: multiplier)
  elsif clg_coil.to_CoilCoolingDXMultiSpeed.is_initialized
    clg_coil = clg_coil.to_CoilCoolingDXMultiSpeed.get
    capacity_w = OpenstudioStandards::HVAC.coil_cooling_dx_multi_speed_get_capacity(clg_coil, multiplier: multiplier)
  elsif clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
    clg_coil = clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.get
    capacity_w = OpenstudioStandards::HVAC.coil_cooling_water_to_air_heat_pump_get_capacity(clg_coil, multiplier: multiplier)
  end

  return capacity_w
end

.coil_heating_water_to_air_heat_pump_equation_fit_get_capacity(coil_heating_water_to_air_heat_pump, multiplier: 1.0) ⇒ Double

Return the capacity in W of a CoilHeatingWaterToAirHeatPumpEquationFit

Parameters:

  • coil_heating_water_to_air_heat_pump (OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit)

    coil coheating water to air heat pump object

  • multiplier (Double) (defaults to: 1.0)

    zone multiplier, if applicable

Returns:

  • (Double)

    capacity in W



133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/openstudio-standards/hvac/components/coil_heating_water_to_air_heat_pump_equation_fit.rb', line 133

def self.coil_heating_water_to_air_heat_pump_equation_fit_get_capacity(coil_heating_water_to_air_heat_pump, multiplier: 1.0)
  capacity_w = nil
  if coil_heating_water_to_air_heat_pump.ratedHeatingCapacity.is_initialized
    capacity_w = coil_heating_water_to_air_heat_pump.ratedHeatingCapacity.get
  elsif coil_heating_water_to_air_heat_pump.autosizedRatedHeatingCapacity.is_initialized
    capacity_w = coil_heating_water_to_air_heat_pump.autosizedRatedHeatingCapacity.get
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.coil_heating_water_to_air_heat_pump_equation_fit', "For #{coil_heating_water_to_air_heat_pump.name} capacity is not available.")
    return capacity_w
  end

  return capacity_w
end

.combustion_eff_to_thermal_eff(combustion_eff) ⇒ Double

A helper method to convert from combustion efficiency to thermal efficiency

Parameters:

  • combustion_eff (Double)

    Combustion efficiency (%)

Returns:

  • (Double)

    Thermal efficiency (%)



209
210
211
# File 'lib/openstudio-standards/hvac/conversions.rb', line 209

def self.combustion_eff_to_thermal_eff(combustion_eff)
  return combustion_eff - 0.007
end

.convert_curve_biquadratic(coeffs, ip_to_si: true) ⇒ Array<Double>

Convert biquadratic curves that are a function of temperature from IP (F) to SI © or vice-versa. The curve is of the form z = C1 + C2*x + C3*x^2 + C4*y + C5*y^2 + C6*x*y where C1, C2, … are the coefficients, x is the first independent variable (in F or C) y is the second independent variable (in F or C) and z is the resulting value

Parameters:

  • coeffs (Array<Double>)

    an array of 6 coefficients, in order

Returns:

  • (Array<Double>)

    the revised coefficients in the new unit system

Author:

  • Scott Horowitz, NREL



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/openstudio-standards/hvac/curves.rb', line 18

def self.convert_curve_biquadratic(coeffs, ip_to_si: true)
  if ip_to_si
    # Convert IP curves to SI curves
    si_coeffs = []
    si_coeffs << (coeffs[0] + (32.0 * (coeffs[1] + coeffs[3])) + (1024.0 * (coeffs[2] + coeffs[4] + coeffs[5])))
    si_coeffs << ((9.0 / 5.0 * coeffs[1]) + (576.0 / 5.0 * coeffs[2]) + (288.0 / 5.0 * coeffs[5]))
    si_coeffs << (81.0 / 25.0 * coeffs[2])
    si_coeffs << ((9.0 / 5.0 * coeffs[3]) + (576.0 / 5.0 * coeffs[4]) + (288.0 / 5.0 * coeffs[5]))
    si_coeffs << (81.0 / 25.0 * coeffs[4])
    si_coeffs << (81.0 / 25.0 * coeffs[5])
    return si_coeffs
  else
    # Convert SI curves to IP curves
    ip_coeffs = []
    ip_coeffs << (coeffs[0] - (160.0 / 9.0 * (coeffs[1] + coeffs[3])) + (25_600.0 / 81.0 * (coeffs[2] + coeffs[4] + coeffs[5])))
    ip_coeffs << (5.0 / 9.0 * (coeffs[1] - (320.0 / 9.0 * coeffs[2]) - (160.0 / 9.0 * coeffs[5])))
    ip_coeffs << (25.0 / 81.0 * coeffs[2])
    ip_coeffs << (5.0 / 9.0 * (coeffs[3] - (320.0 / 9.0 * coeffs[4]) - (160.0 / 9.0 * coeffs[5])))
    ip_coeffs << (25.0 / 81.0 * coeffs[4])
    ip_coeffs << (25.0 / 81.0 * coeffs[5])
    return ip_coeffs
  end
end

.cop_heating_to_cop_heating_no_fan(coph47, capacity_w) ⇒ Double

Convert from COP_H to COP (no fan) for heat pump heating coils

Parameters:

  • coph47 (Double)

    coefficient of performance at 47F Tdb, 42F Twb

  • capacity_w (Double)

    the heating capacity at AHRI rating conditions, in W

Returns:

  • (Double)

    Coefficient of Performance (COP)



61
62
63
64
65
66
67
68
# File 'lib/openstudio-standards/hvac/conversions.rb', line 61

def self.cop_heating_to_cop_heating_no_fan(coph47, capacity_w)
  # Convert the capacity to Btu/hr
  capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get

  cop = (1.48E-7 * coph47 * capacity_btu_per_hr) + (1.062 * coph47)

  return cop
end

.cop_no_fan_to_eer(cop, capacity_w = nil) ⇒ Double

Convert from COP (no fan) to EER

Parameters:

  • cop (Double)

    COP

Returns:

  • (Double)

    Energy Efficiency Ratio (EER)



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/openstudio-standards/hvac/conversions.rb', line 121

def self.cop_no_fan_to_eer(cop, capacity_w = nil)
  if capacity_w.nil?
    # From Thornton et al. 2011
    # r is the ratio of supply fan power to total equipment power at the rating condition,
    # assumed to be 0.12 for the reference buildngs per Thornton et al. 2011.
    r = 0.12
    eer = OpenStudio.convert(1.0, 'W', 'Btu/h').get * ((cop * (1 - r)) - r)
  else
    # The 90.1-2013 method
    # Convert the capacity to Btu/hr
    capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
    eer = cop / ((7.84E-8 * capacity_btu_per_hr) + 0.338)
  end

  return eer
end

.cop_no_fan_to_seer(cop) ⇒ Double

Convert from COP to SEER

Parameters:

  • cop (Double)

    COP

Returns:

  • (Double)

    Seasonal Energy Efficiency Ratio



23
24
25
26
27
28
# File 'lib/openstudio-standards/hvac/conversions.rb', line 23

def self.cop_no_fan_to_seer(cop)
  delta = (0.3796**2) - (4.0 * 0.0076 * cop)
  seer = ((-delta**0.5) + 0.3796) / (2.0 * 0.0076)

  return seer
end

.cop_to_eer(cop) ⇒ Double

Convert from COP to EER

Parameters:

  • cop (Double)

    Coefficient of Performance (COP)

Returns:

  • (Double)

    Energy Efficiency Ratio (EER)



166
167
168
# File 'lib/openstudio-standards/hvac/conversions.rb', line 166

def self.cop_to_eer(cop)
  return cop * OpenStudio.convert(1.0, 'W', 'Btu/h').get
end

.cop_to_kw_per_ton(cop) ⇒ Double

Convert from COP to kW/ton

Parameters:

  • cop (Double)

    Coefficient of Performance (COP)

Returns:

  • (Double)

    kW of input power per ton of cooling



174
175
176
# File 'lib/openstudio-standards/hvac/conversions.rb', line 174

def self.cop_to_kw_per_ton(cop)
  return 3.517 / cop
end

.cop_to_seer(cop) ⇒ Double

Convert from COP to SEER (with fan) for cooling coils per the method specified in Thornton et al. 2011

Parameters:

  • cop (Double)

    Coefficient of Performance (COP)

Returns:

  • (Double)

    seasonal energy efficiency ratio (SEER)



47
48
49
50
51
52
53
# File 'lib/openstudio-standards/hvac/conversions.rb', line 47

def self.cop_to_seer(cop)
  eer = OpenstudioStandards::HVAC.cop_to_eer(cop)
  delta = (1.1088**2) - (4.0 * 0.0182 * eer)
  seer = (1.1088 - (delta**0.5)) / (2.0 * 0.0182)

  return seer
end

.create_air_conditioner_variable_refrigerant_flow(model, name: 'VRF System', schedule: nil, type: nil, cooling_cop: 4.287, heating_cop: 4.147, heat_recovery: true, defrost_strategy: 'ReverseCycle', condenser_type: 'AirCooled', condenser_loop: nil, master_zone: nil, priority_control_type: 'LoadPriority') ⇒ OpenStudio::Model::AirConditionerVariableRefrigerantFlow

Create AirConditionerVariableRefrigerantFlow object Enters in default curves for coil by type of coil

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • name (String) (defaults to: 'VRF System')

    the name of the system, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • type (String) (defaults to: nil)

    the type of unit to reference for the correct curve set

  • cooling_cop (Double) (defaults to: 4.287)

    rated cooling coefficient of performance

  • heating_cop (Double) (defaults to: 4.147)

    rated heating coefficient of performance

  • heat_recovery (Boolean) (defaults to: true)

    does the unit have heat recovery

  • defrost_strategy (String) (defaults to: 'ReverseCycle')

    type of defrost strategy. options are ReverseCycle or Resistive. Default curve set is for ReverseCycle.

  • condenser_type (String) (defaults to: 'AirCooled')

    type of condenser options are AirCooled (default), WaterCooled, and EvaporativelyCooled. if WaterCooled, the user most include a condenser_loop

  • master_zone (<OpenStudio::Model::ThermalZone>) (defaults to: nil)

    master control zone to switch between heating and cooling

  • priority_control_type (String) (defaults to: 'LoadPriority')

    type of master thermostat priority control type options are LoadPriority, ZonePriority, ThermostatOffsetPriority, MasterThermostatPriority

Returns:

  • (OpenStudio::Model::AirConditionerVariableRefrigerantFlow)

    the vrf unit



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/openstudio-standards/hvac/components/air_conditioner_vrf.rb', line 25

def self.create_air_conditioner_variable_refrigerant_flow(model,
                                                          name: 'VRF System',
                                                          schedule: nil,
                                                          type: nil,
                                                          cooling_cop: 4.287,
                                                          heating_cop: 4.147,
                                                          heat_recovery: true,
                                                          defrost_strategy: 'ReverseCycle',
                                                          condenser_type: 'AirCooled',
                                                          condenser_loop: nil,
                                                          master_zone: nil,
                                                          priority_control_type: 'LoadPriority')
  vrf_outdoor_unit = OpenStudio::Model::AirConditionerVariableRefrigerantFlow.new(model)

  # set name
  if name.nil?
    vrf_outdoor_unit.setName('VRF System')
  else
    vrf_outdoor_unit.setName(name)
  end

  # set availability schedule
  if schedule.nil?
    # default always on
    availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    availability_schedule = model_add_schedule(model, schedule)

    if availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      availability_schedule = model.alwaysOffDiscreteSchedule
    elsif availability_schedule.nil?
      availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    availability_schedule = schedule
  else
    availability_schedule = model.alwaysOnDiscreteSchedule
  end
  vrf_outdoor_unit.setAvailabilitySchedule(availability_schedule)

  # set cops
  if model.version < OpenStudio::VersionString.new('2.9.0')
    vrf_outdoor_unit.setRatedCoolingCOP(cooling_cop)
  else
    vrf_outdoor_unit.setGrossRatedCoolingCOP(cooling_cop)
  end
  vrf_outdoor_unit.setRatedHeatingCOP(heating_cop)

  # heat recovery
  if heat_recovery
    vrf_outdoor_unit.setHeatPumpWasteHeatRecovery(true)
  else
    vrf_outdoor_unit.setHeatPumpWasteHeatRecovery(false)
  end

  # defrost strategy
  vrf_outdoor_unit.setDefrostStrategy(defrost_strategy)

  # defaults
  vrf_outdoor_unit.setMinimumOutdoorTemperatureinCoolingMode(-15.0)
  vrf_outdoor_unit.setMaximumOutdoorTemperatureinCoolingMode(50.0)
  vrf_outdoor_unit.setMinimumOutdoorTemperatureinHeatingMode(-25.0)
  vrf_outdoor_unit.setMaximumOutdoorTemperatureinHeatingMode(16.1)
  vrf_outdoor_unit.setMinimumOutdoorTemperatureinHeatRecoveryMode(-10.0)
  vrf_outdoor_unit.setMaximumOutdoorTemperatureinHeatRecoveryMode(27.2)
  vrf_outdoor_unit.setEquivalentPipingLengthusedforPipingCorrectionFactorinCoolingMode(30.48)
  vrf_outdoor_unit.setEquivalentPipingLengthusedforPipingCorrectionFactorinHeatingMode(30.48)
  vrf_outdoor_unit.setVerticalHeightusedforPipingCorrectionFactor(10.668)

  # condenser type
  if condenser_type == 'WaterCooled'
    vrf_outdoor_unit.setString(56, condenser_type)
    # require condenser_loop
    unless condenser_loop
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', 'Must specify condenser_loop for vrf_outdoor_unit if WaterCooled')
    end
    condenser_loop.addDemandBranchForComponent(vrf_outdoor_unit)
  elsif condenser_type == 'EvaporativelyCooled'
    vrf_outdoor_unit.setString(56, condenser_type)
  end

  # set master zone
  unless master_zone.nil?
    vrf_outdoor_unit.setZoneforMasterThermostatLocation(master_zone)
    vrf_outdoor_unit.setMasterThermostatPriorityControlType(priority_control_type)
  end

  vrf_cool_cap_f_of_low_temp = nil
  vrf_cool_cap_ratio_boundary = nil
  vrf_cool_cap_f_of_high_temp = nil
  vrf_cool_eir_f_of_low_temp = nil
  vrf_cool_eir_ratio_boundary = nil
  vrf_cool_eir_f_of_high_temp = nil
  vrf_cooling_eir_low_plr = nil
  vrf_cooling_eir_high_plr = nil
  vrf_cooling_comb_ratio = nil
  vrf_cooling_cplffplr = nil
  vrf_heat_cap_f_of_low_temp = nil
  vrf_heat_cap_ratio_boundary = nil
  vrf_heat_cap_f_of_high_temp = nil
  vrf_heat_eir_f_of_low_temp = nil
  vrf_heat_eir_boundary = nil
  vrf_heat_eir_f_of_high_temp = nil
  vrf_heating_eir_low_plr = nil
  vrf_heating_eir_hi_plr = nil
  vrf_heating_comb_ratio = nil
  vrf_heating_cplffplr = nil
  vrf_defrost_eir_f_of_temp = nil

  # curve sets
  if type == 'OS default'

    # use OS default curves

  else # default curve set

    # based on DAIKINREYQ 120 on BCL

    # Cooling Capacity Ratio Modifier Function of Low Temperature Curve
    vrf_cool_cap_f_of_low_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_cool_cap_f_of_low_temp.setName('vrf_cool_cap_f_of_low_temp')
    vrf_cool_cap_f_of_low_temp.setCoefficient1Constant(-1.69653019339465)
    vrf_cool_cap_f_of_low_temp.setCoefficient2x(0.207248180531939)
    vrf_cool_cap_f_of_low_temp.setCoefficient3xPOW2(-0.00343146229659024)
    vrf_cool_cap_f_of_low_temp.setCoefficient4y(0.016381597419714)
    vrf_cool_cap_f_of_low_temp.setCoefficient5yPOW2(-6.7387172629965e-05)
    vrf_cool_cap_f_of_low_temp.setCoefficient6xTIMESY(-0.000849848402870241)
    vrf_cool_cap_f_of_low_temp.setMinimumValueofx(13.9)
    vrf_cool_cap_f_of_low_temp.setMaximumValueofx(23.9)
    vrf_cool_cap_f_of_low_temp.setMinimumValueofy(-5.0)
    vrf_cool_cap_f_of_low_temp.setMaximumValueofy(43.3)
    vrf_cool_cap_f_of_low_temp.setMinimumCurveOutput(0.59)
    vrf_cool_cap_f_of_low_temp.setMaximumCurveOutput(1.33)

    # Cooling Capacity Ratio Boundary Curve
    vrf_cool_cap_ratio_boundary = OpenStudio::Model::CurveCubic.new(model)
    vrf_cool_cap_ratio_boundary.setName('vrf_cool_cap_ratio_boundary')
    vrf_cool_cap_ratio_boundary.setCoefficient1Constant(25.73)
    vrf_cool_cap_ratio_boundary.setCoefficient2x(-0.03150043)
    vrf_cool_cap_ratio_boundary.setCoefficient3xPOW2(-0.01416595)
    vrf_cool_cap_ratio_boundary.setCoefficient4xPOW3(0.0)
    vrf_cool_cap_ratio_boundary.setMinimumValueofx(11.0)
    vrf_cool_cap_ratio_boundary.setMaximumValueofx(30.0)

    # Cooling Capacity Ratio Modifier Function of High Temperature Curve
    vrf_cool_cap_f_of_high_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_cool_cap_f_of_high_temp.setName('vrf_cool_cap_f_of_high_temp')
    vrf_cool_cap_f_of_high_temp.setCoefficient1Constant(0.6867358)
    vrf_cool_cap_f_of_high_temp.setCoefficient2x(0.0207631)
    vrf_cool_cap_f_of_high_temp.setCoefficient3xPOW2(0.0005447)
    vrf_cool_cap_f_of_high_temp.setCoefficient4y(-0.0016218)
    vrf_cool_cap_f_of_high_temp.setCoefficient5yPOW2(-4.259e-07)
    vrf_cool_cap_f_of_high_temp.setCoefficient6xTIMESY(-0.0003392)
    vrf_cool_cap_f_of_high_temp.setMinimumValueofx(15.0)
    vrf_cool_cap_f_of_high_temp.setMaximumValueofx(24.0)
    vrf_cool_cap_f_of_high_temp.setMinimumValueofy(16.0)
    vrf_cool_cap_f_of_high_temp.setMaximumValueofy(43.0)

    # Cooling Energy Input Ratio Modifier Function of Low Temperature Curve
    vrf_cool_eir_f_of_low_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_cool_eir_f_of_low_temp.setName('vrf_cool_eir_f_of_low_temp')
    vrf_cool_eir_f_of_low_temp.setCoefficient1Constant(-1.61908214818635)
    vrf_cool_eir_f_of_low_temp.setCoefficient2x(0.185964818731756)
    vrf_cool_eir_f_of_low_temp.setCoefficient3xPOW2(-0.00389610393381592)
    vrf_cool_eir_f_of_low_temp.setCoefficient4y(-0.00901995326324613)
    vrf_cool_eir_f_of_low_temp.setCoefficient5yPOW2(0.00030340007815629)
    vrf_cool_eir_f_of_low_temp.setCoefficient6xTIMESY(0.000476048529099348)
    vrf_cool_eir_f_of_low_temp.setMinimumValueofx(13.9)
    vrf_cool_eir_f_of_low_temp.setMaximumValueofx(23.9)
    vrf_cool_eir_f_of_low_temp.setMinimumValueofy(-5.0)
    vrf_cool_eir_f_of_low_temp.setMaximumValueofy(43.3)
    vrf_cool_eir_f_of_low_temp.setMinimumCurveOutput(0.27)
    vrf_cool_eir_f_of_low_temp.setMaximumCurveOutput(1.15)

    # Cooling Energy Input Ratio Boundary Curve
    vrf_cool_eir_ratio_boundary = OpenStudio::Model::CurveCubic.new(model)
    vrf_cool_eir_ratio_boundary.setName('vrf_cool_eir_ratio_boundary')
    vrf_cool_eir_ratio_boundary.setCoefficient1Constant(25.73473775)
    vrf_cool_eir_ratio_boundary.setCoefficient2x(-0.03150043)
    vrf_cool_eir_ratio_boundary.setCoefficient3xPOW2(-0.01416595)
    vrf_cool_eir_ratio_boundary.setCoefficient4xPOW3(0.0)
    vrf_cool_eir_ratio_boundary.setMinimumValueofx(15.0)
    vrf_cool_eir_ratio_boundary.setMaximumValueofx(24.0)

    # Cooling Energy Input Ratio Modifier Function of High Temperature Curve
    vrf_cool_eir_f_of_high_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_cool_eir_f_of_high_temp.setName('vrf_cool_eir_f_of_high_temp')
    vrf_cool_eir_f_of_high_temp.setCoefficient1Constant(-1.4395110176)
    vrf_cool_eir_f_of_high_temp.setCoefficient2x(0.1619850459)
    vrf_cool_eir_f_of_high_temp.setCoefficient3xPOW2(-0.0034911781)
    vrf_cool_eir_f_of_high_temp.setCoefficient4y(0.0269442645)
    vrf_cool_eir_f_of_high_temp.setCoefficient5yPOW2(0.0001346163)
    vrf_cool_eir_f_of_high_temp.setCoefficient6xTIMESY(-0.0006714941)
    vrf_cool_eir_f_of_high_temp.setMinimumValueofx(15.0)
    vrf_cool_eir_f_of_high_temp.setMaximumValueofx(23.9)
    vrf_cool_eir_f_of_high_temp.setMinimumValueofy(16.8)
    vrf_cool_eir_f_of_high_temp.setMaximumValueofy(43.3)

    # Cooling Energy Input Ratio Modifier Function of Low Part-Load Ratio Curve
    vrf_cooling_eir_low_plr = OpenStudio::Model::CurveCubic.new(model)
    vrf_cooling_eir_low_plr.setName('vrf_cool_eir_f_of_low_temp')
    vrf_cooling_eir_low_plr.setCoefficient1Constant(0.0734992169827752)
    vrf_cooling_eir_low_plr.setCoefficient2x(0.334783365234032)
    vrf_cooling_eir_low_plr.setCoefficient3xPOW2(0.591613015486343)
    vrf_cooling_eir_low_plr.setCoefficient4xPOW3(0.0)
    vrf_cooling_eir_low_plr.setMinimumValueofx(0.25)
    vrf_cooling_eir_low_plr.setMaximumValueofx(1.0)
    vrf_cooling_eir_low_plr.setMinimumCurveOutput(0.0)
    vrf_cooling_eir_low_plr.setMaximumCurveOutput(1.0)

    # Cooling Energy Input Ratio Modifier Function of High Part-Load Ratio Curve
    vrf_cooling_eir_high_plr = OpenStudio::Model::CurveCubic.new(model)
    vrf_cooling_eir_high_plr.setName('vrf_cooling_eir_high_plr')
    vrf_cooling_eir_high_plr.setCoefficient1Constant(1.0)
    vrf_cooling_eir_high_plr.setCoefficient2x(0.0)
    vrf_cooling_eir_high_plr.setCoefficient3xPOW2(0.0)
    vrf_cooling_eir_high_plr.setCoefficient4xPOW3(0.0)
    vrf_cooling_eir_high_plr.setMinimumValueofx(1.0)
    vrf_cooling_eir_high_plr.setMaximumValueofx(1.5)

    # Cooling Combination Ratio Correction Factor Curve
    vrf_cooling_comb_ratio = OpenStudio::Model::CurveCubic.new(model)
    vrf_cooling_comb_ratio.setName('vrf_cooling_comb_ratio')
    vrf_cooling_comb_ratio.setCoefficient1Constant(0.24034)
    vrf_cooling_comb_ratio.setCoefficient2x(-0.21873)
    vrf_cooling_comb_ratio.setCoefficient3xPOW2(1.97941)
    vrf_cooling_comb_ratio.setCoefficient4xPOW3(-1.02636)
    vrf_cooling_comb_ratio.setMinimumValueofx(0.5)
    vrf_cooling_comb_ratio.setMaximumValueofx(2.0)
    vrf_cooling_comb_ratio.setMinimumCurveOutput(0.5)
    vrf_cooling_comb_ratio.setMaximumCurveOutput(1.056)

    # Cooling Part-Load Fraction Correlation Curve
    vrf_cooling_cplffplr = OpenStudio::Model::CurveCubic.new(model)
    vrf_cooling_cplffplr.setName('vrf_cooling_cplffplr')
    vrf_cooling_cplffplr.setCoefficient1Constant(0.85)
    vrf_cooling_cplffplr.setCoefficient2x(0.15)
    vrf_cooling_cplffplr.setCoefficient3xPOW2(0.0)
    vrf_cooling_cplffplr.setCoefficient4xPOW3(0.0)
    vrf_cooling_cplffplr.setMinimumValueofx(1.0)
    vrf_cooling_cplffplr.setMaximumValueofx(1.0)

    # Heating Capacity Ratio Modifier Function of Low Temperature Curve Name
    vrf_heat_cap_f_of_low_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_heat_cap_f_of_low_temp.setName('vrf_heat_cap_f_of_low_temp')
    vrf_heat_cap_f_of_low_temp.setCoefficient1Constant(0.983220174655636)
    vrf_heat_cap_f_of_low_temp.setCoefficient2x(0.0157167577703294)
    vrf_heat_cap_f_of_low_temp.setCoefficient3xPOW2(-0.000835032422884084)
    vrf_heat_cap_f_of_low_temp.setCoefficient4y(0.0522939264581759)
    vrf_heat_cap_f_of_low_temp.setCoefficient5yPOW2(-0.000531556035364549)
    vrf_heat_cap_f_of_low_temp.setCoefficient6xTIMESY(-0.00190605953116024)
    vrf_heat_cap_f_of_low_temp.setMinimumValueofx(16.1)
    vrf_heat_cap_f_of_low_temp.setMaximumValueofx(23.9)
    vrf_heat_cap_f_of_low_temp.setMinimumValueofy(-25.0)
    vrf_heat_cap_f_of_low_temp.setMaximumValueofy(13.3)
    vrf_heat_cap_f_of_low_temp.setMinimumCurveOutput(0.515151515151515)
    vrf_heat_cap_f_of_low_temp.setMaximumCurveOutput(1.2)

    # Heating Capacity Ratio Boundary Curve Name
    vrf_heat_cap_ratio_boundary = OpenStudio::Model::CurveCubic.new(model)
    vrf_heat_cap_ratio_boundary.setName('vrf_heat_cap_ratio_boundary')
    vrf_heat_cap_ratio_boundary.setCoefficient1Constant(58.577)
    vrf_heat_cap_ratio_boundary.setCoefficient2x(-3.0255)
    vrf_heat_cap_ratio_boundary.setCoefficient3xPOW2(0.0193)
    vrf_heat_cap_ratio_boundary.setCoefficient4xPOW3(0.0)
    vrf_heat_cap_ratio_boundary.setMinimumValueofx(15)
    vrf_heat_cap_ratio_boundary.setMaximumValueofx(23.9)

    # Heating Capacity Ratio Modifier Function of High Temperature Curve Name
    vrf_heat_cap_f_of_high_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_heat_cap_f_of_high_temp.setName('vrf_heat_cap_f_of_high_temp')
    vrf_heat_cap_f_of_high_temp.setCoefficient1Constant(2.5859872368)
    vrf_heat_cap_f_of_high_temp.setCoefficient2x(-0.0953227101)
    vrf_heat_cap_f_of_high_temp.setCoefficient3xPOW2(0.0009553288)
    vrf_heat_cap_f_of_high_temp.setCoefficient4y(0.0)
    vrf_heat_cap_f_of_high_temp.setCoefficient5yPOW2(0.0)
    vrf_heat_cap_f_of_high_temp.setCoefficient6xTIMESY(0.0)
    vrf_heat_cap_f_of_high_temp.setMinimumValueofx(21.1)
    vrf_heat_cap_f_of_high_temp.setMaximumValueofx(27.2)
    vrf_heat_cap_f_of_high_temp.setMinimumValueofy(-944)
    vrf_heat_cap_f_of_high_temp.setMaximumValueofy(15)

    # Heating Energy Input Ratio Modifier Function of Low Temperature Curve Name
    vrf_heat_eir_f_of_low_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_heat_eir_f_of_low_temp.setName('vrf_heat_eir_f_of_low_temp')
    vrf_heat_eir_f_of_low_temp.setCoefficient1Constant(0.756830029796909)
    vrf_heat_eir_f_of_low_temp.setCoefficient2x(0.0457499799042671)
    vrf_heat_eir_f_of_low_temp.setCoefficient3xPOW2(-0.00136357240431388)
    vrf_heat_eir_f_of_low_temp.setCoefficient4y(0.0554884599902023)
    vrf_heat_eir_f_of_low_temp.setCoefficient5yPOW2(-0.00120700875497686)
    vrf_heat_eir_f_of_low_temp.setCoefficient6xTIMESY(-0.00303329271420931)
    vrf_heat_eir_f_of_low_temp.setMinimumValueofx(16.1)
    vrf_heat_eir_f_of_low_temp.setMaximumValueofx(23.9)
    vrf_heat_eir_f_of_low_temp.setMinimumValueofy(-25.0)
    vrf_heat_eir_f_of_low_temp.setMaximumValueofy(13.3)
    vrf_heat_eir_f_of_low_temp.setMinimumCurveOutput(0.7)
    vrf_heat_eir_f_of_low_temp.setMaximumCurveOutput(1.184)

    # Heating Energy Input Ratio Boundary Curve Name
    vrf_heat_eir_boundary = OpenStudio::Model::CurveCubic.new(model)
    vrf_heat_eir_boundary.setName('vrf_heat_eir_boundary')
    vrf_heat_eir_boundary.setCoefficient1Constant(58.577)
    vrf_heat_eir_boundary.setCoefficient2x(-3.0255)
    vrf_heat_eir_boundary.setCoefficient3xPOW2(0.0193)
    vrf_heat_eir_boundary.setCoefficient4xPOW3(0.0)
    vrf_heat_eir_boundary.setMinimumValueofx(15.0)
    vrf_heat_eir_boundary.setMaximumValueofx(23.9)

    # Heating Energy Input Ratio Modifier Function of High Temperature Curve Name
    vrf_heat_eir_f_of_high_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_heat_eir_f_of_high_temp.setName('vrf_heat_eir_f_of_high_temp')
    vrf_heat_eir_f_of_high_temp.setCoefficient1Constant(1.3885703646)
    vrf_heat_eir_f_of_high_temp.setCoefficient2x(-0.0229771462)
    vrf_heat_eir_f_of_high_temp.setCoefficient3xPOW2(0.000537274)
    vrf_heat_eir_f_of_high_temp.setCoefficient4y(-0.0273936962)
    vrf_heat_eir_f_of_high_temp.setCoefficient5yPOW2(0.0004030426)
    vrf_heat_eir_f_of_high_temp.setCoefficient6xTIMESY(-5.9786e-05)
    vrf_heat_eir_f_of_high_temp.setMinimumValueofx(21.1)
    vrf_heat_eir_f_of_high_temp.setMaximumValueofx(27.2)
    vrf_heat_eir_f_of_high_temp.setMinimumValueofy(0.0)
    vrf_heat_eir_f_of_high_temp.setMaximumValueofy(1.0)

    # Heating Performance Curve Outdoor Temperature Type
    vrf_outdoor_unit.setHeatingPerformanceCurveOutdoorTemperatureType('WetBulbTemperature')

    # Heating Energy Input Ratio Modifier Function of Low Part-Load Ratio Curve Name
    vrf_heating_eir_low_plr = OpenStudio::Model::CurveCubic.new(model)
    vrf_heating_eir_low_plr.setName('vrf_heating_eir_low_plr')
    vrf_heating_eir_low_plr.setCoefficient1Constant(0.0724906507105475)
    vrf_heating_eir_low_plr.setCoefficient2x(0.658189977561701)
    vrf_heating_eir_low_plr.setCoefficient3xPOW2(0.269259536275246)
    vrf_heating_eir_low_plr.setCoefficient4xPOW3(0.0)
    vrf_heating_eir_low_plr.setMinimumValueofx(0.25)
    vrf_heating_eir_low_plr.setMaximumValueofx(1.0)
    vrf_heating_eir_low_plr.setMinimumCurveOutput(0.0)
    vrf_heating_eir_low_plr.setMaximumCurveOutput(1.0)

    # Heating Energy Input Ratio Modifier Function of High Part-Load Ratio Curve Name
    vrf_heating_eir_hi_plr = OpenStudio::Model::CurveCubic.new(model)
    vrf_heating_eir_hi_plr.setName('vrf_heating_eir_hi_plr')
    vrf_heating_eir_hi_plr.setCoefficient1Constant(1.0)
    vrf_heating_eir_hi_plr.setCoefficient2x(0.0)
    vrf_heating_eir_hi_plr.setCoefficient3xPOW2(0.0)
    vrf_heating_eir_hi_plr.setCoefficient4xPOW3(0.0)
    vrf_heating_eir_hi_plr.setMinimumValueofx(1.0)
    vrf_heating_eir_hi_plr.setMaximumValueofx(1.5)

    # Heating Combination Ratio Correction Factor Curve Name
    vrf_heating_comb_ratio = OpenStudio::Model::CurveCubic.new(model)
    vrf_heating_comb_ratio.setName('vrf_heating_comb_ratio')
    vrf_heating_comb_ratio.setCoefficient1Constant(0.62115)
    vrf_heating_comb_ratio.setCoefficient2x(-1.55798)
    vrf_heating_comb_ratio.setCoefficient3xPOW2(3.36817)
    vrf_heating_comb_ratio.setCoefficient4xPOW3(-1.4224)
    vrf_heating_comb_ratio.setMinimumValueofx(0.5)
    vrf_heating_comb_ratio.setMaximumValueofx(2.0)
    vrf_heating_comb_ratio.setMinimumCurveOutput(0.5)
    vrf_heating_comb_ratio.setMaximumCurveOutput(1.155)

    # Heating Part-Load Fraction Correlation Curve Name
    vrf_heating_cplffplr = OpenStudio::Model::CurveCubic.new(model)
    vrf_heating_cplffplr.setName('vrf_heating_cplffplr')
    vrf_heating_cplffplr.setCoefficient1Constant(0.85)
    vrf_heating_cplffplr.setCoefficient2x(0.15)
    vrf_heating_cplffplr.setCoefficient3xPOW2(0.0)
    vrf_heating_cplffplr.setCoefficient4xPOW3(0.0)
    vrf_heating_cplffplr.setMinimumValueofx(1.0)
    vrf_heating_cplffplr.setMaximumValueofx(1.0)

    # Defrost Energy Input Ratio Modifier Function of Temperature Curve
    vrf_defrost_eir_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    vrf_defrost_eir_f_of_temp.setName('vrf_defrost_eir_f_of_temp')
    vrf_defrost_eir_f_of_temp.setCoefficient1Constant(-1.61908214818635)
    vrf_defrost_eir_f_of_temp.setCoefficient2x(0.185964818731756)
    vrf_defrost_eir_f_of_temp.setCoefficient3xPOW2(-0.00389610393381592)
    vrf_defrost_eir_f_of_temp.setCoefficient4y(-0.00901995326324613)
    vrf_defrost_eir_f_of_temp.setCoefficient5yPOW2(0.00030340007815629)
    vrf_defrost_eir_f_of_temp.setCoefficient6xTIMESY(0.000476048529099348)
    vrf_defrost_eir_f_of_temp.setMinimumValueofx(13.9)
    vrf_defrost_eir_f_of_temp.setMaximumValueofx(23.9)
    vrf_defrost_eir_f_of_temp.setMinimumValueofy(-5.0)
    vrf_defrost_eir_f_of_temp.setMaximumValueofy(50.0)
    vrf_defrost_eir_f_of_temp.setMinimumCurveOutput(0.27)
    vrf_defrost_eir_f_of_temp.setMaximumCurveOutput(1.155)

    # set defrost control
    vrf_outdoor_unit.setDefrostStrategy('ReverseCycle')
    vrf_outdoor_unit.setDefrostControl('OnDemand')
  end

  # override defrost strategy if specified
  if defrost_strategy == 'Resistive'
    vrf_defrost_eir_f_of_temp = nil
    vrf_outdoor_unit.setDefrostStrategy('Resistive')
  end

  vrf_outdoor_unit.setCoolingCapacityRatioModifierFunctionofLowTemperatureCurve(vrf_cool_cap_f_of_low_temp) unless vrf_cool_cap_f_of_low_temp.nil?
  vrf_outdoor_unit.setCoolingCapacityRatioBoundaryCurve(vrf_cool_cap_ratio_boundary) unless vrf_cool_cap_ratio_boundary.nil?
  vrf_outdoor_unit.setCoolingCapacityRatioModifierFunctionofHighTemperatureCurve(vrf_cool_cap_f_of_high_temp) unless vrf_cool_cap_f_of_high_temp.nil?
  vrf_outdoor_unit.setCoolingEnergyInputRatioModifierFunctionofLowTemperatureCurve(vrf_cool_eir_f_of_low_temp) unless vrf_cool_eir_f_of_low_temp.nil?
  vrf_outdoor_unit.setCoolingEnergyInputRatioBoundaryCurve(vrf_cool_eir_ratio_boundary) unless vrf_cool_eir_ratio_boundary.nil?
  vrf_outdoor_unit.setCoolingEnergyInputRatioModifierFunctionofHighTemperatureCurve(vrf_cool_eir_f_of_high_temp) unless vrf_cool_eir_f_of_high_temp.nil?
  vrf_outdoor_unit.setCoolingEnergyInputRatioModifierFunctionofLowPartLoadRatioCurve(vrf_cooling_eir_low_plr) unless vrf_cooling_eir_low_plr.nil?
  vrf_outdoor_unit.setCoolingEnergyInputRatioModifierFunctionofHighPartLoadRatioCurve(vrf_cooling_eir_high_plr) unless vrf_cooling_eir_high_plr.nil?
  vrf_outdoor_unit.setCoolingCombinationRatioCorrectionFactorCurve(vrf_cooling_comb_ratio) unless vrf_cooling_comb_ratio.nil?
  vrf_outdoor_unit.setCoolingPartLoadFractionCorrelationCurve(vrf_cooling_cplffplr) unless vrf_cooling_cplffplr.nil?
  vrf_outdoor_unit.setHeatingCapacityRatioModifierFunctionofLowTemperatureCurve(vrf_heat_cap_f_of_low_temp) unless vrf_heat_cap_f_of_low_temp.nil?
  vrf_outdoor_unit.setHeatingCapacityRatioBoundaryCurve(vrf_heat_cap_ratio_boundary) unless vrf_heat_cap_ratio_boundary.nil?
  vrf_outdoor_unit.setHeatingCapacityRatioModifierFunctionofHighTemperatureCurve(vrf_heat_cap_f_of_high_temp) unless vrf_heat_cap_f_of_high_temp.nil?
  vrf_outdoor_unit.setHeatingEnergyInputRatioModifierFunctionofLowTemperatureCurve(vrf_heat_eir_f_of_low_temp) unless vrf_heat_eir_f_of_low_temp.nil?
  vrf_outdoor_unit.setHeatingEnergyInputRatioBoundaryCurve(vrf_heat_eir_boundary) unless vrf_heat_eir_boundary.nil?
  vrf_outdoor_unit.setHeatingEnergyInputRatioModifierFunctionofHighTemperatureCurve(vrf_heat_eir_f_of_high_temp) unless vrf_heat_eir_f_of_high_temp.nil?
  vrf_outdoor_unit.setHeatingEnergyInputRatioModifierFunctionofLowPartLoadRatioCurve(vrf_heating_eir_low_plr) unless vrf_heating_eir_low_plr.nil?
  vrf_outdoor_unit.setHeatingEnergyInputRatioModifierFunctionofHighPartLoadRatioCurve(vrf_heating_eir_hi_plr) unless vrf_heating_eir_hi_plr.nil?
  vrf_outdoor_unit.setHeatingCombinationRatioCorrectionFactorCurve(vrf_heating_comb_ratio) unless vrf_heating_comb_ratio.nil?
  vrf_outdoor_unit.setHeatingPartLoadFractionCorrelationCurve(vrf_heating_cplffplr) unless vrf_heating_cplffplr.nil?
  vrf_outdoor_unit.setDefrostEnergyInputRatioModifierFunctionofTemperatureCurve(vrf_defrost_eir_f_of_temp) unless vrf_defrost_eir_f_of_temp.nil?

  return vrf_outdoor_unit
end

.create_boiler_hot_water(model, hot_water_loop: nil, name: 'Boiler', fuel_type: 'NaturalGas', draft_type: 'Natural', nominal_thermal_efficiency: 0.80, eff_curve_temp_eval_var: 'LeavingBoiler', flow_mode: 'LeavingSetpointModulated', lvg_temp_dsgn_f: 180.0, out_temp_lmt_f: 203.0, min_plr: 0.0, max_plr: 1.2, opt_plr: 1.0, sizing_factor: nil) ⇒ OpenStudio::Model::BoilerHotWater

Create BoilerHotWater object

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • hot_water_loop (<OpenStudio::Model::PlantLoop>) (defaults to: nil)

    a hot water loop served by the boiler

  • name (String) (defaults to: 'Boiler')

    the name of the boiler, or nil in which case it will be defaulted

  • fuel_type (String) (defaults to: 'NaturalGas')

    type of fuel serving the boiler

  • draft_type (String) (defaults to: 'Natural')

    Boiler type Condensing, MechanicalNoncondensing, Natural (default)

  • nominal_thermal_efficiency (Double) (defaults to: 0.80)

    boiler nominal thermal efficiency

  • eff_curve_temp_eval_var (String) (defaults to: 'LeavingBoiler')

    LeavingBoiler or EnteringBoiler temperature for the boiler efficiency curve

  • flow_mode (String) (defaults to: 'LeavingSetpointModulated')

    boiler flow mode

  • lvg_temp_dsgn_f (Double) (defaults to: 180.0)

    boiler leaving design temperature in degrees Fahrenheit note that this field is deprecated in OS versions 3.0+

  • out_temp_lmt_f (Double) (defaults to: 203.0)

    boiler outlet temperature limit in degrees Fahrenheit

  • min_plr (Double) (defaults to: 0.0)

    boiler minimum part load ratio

  • max_plr (Double) (defaults to: 1.2)

    boiler maximum part load ratio

  • opt_plr (Double) (defaults to: 1.0)

    boiler optimum part load ratio

  • sizing_factor (Double) (defaults to: nil)

    boiler oversizing factor

Returns:

  • (OpenStudio::Model::BoilerHotWater)

    the boiler object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/openstudio-standards/hvac/components/boiler_hot_water.rb', line 25

def self.create_boiler_hot_water(model,
                                 hot_water_loop: nil,
                                 name: 'Boiler',
                                 fuel_type: 'NaturalGas',
                                 draft_type: 'Natural',
                                 nominal_thermal_efficiency: 0.80,
                                 eff_curve_temp_eval_var: 'LeavingBoiler',
                                 flow_mode: 'LeavingSetpointModulated',
                                 lvg_temp_dsgn_f: 180.0, # 82.22 degrees Celsius
                                 out_temp_lmt_f: 203.0, # 95.0 degrees Celsius
                                 min_plr: 0.0,
                                 max_plr: 1.2,
                                 opt_plr: 1.0,
                                 sizing_factor: nil)

  # create the boiler
  boiler = OpenStudio::Model::BoilerHotWater.new(model)
  if name.nil?
    if hot_water_loop.nil?
      boiler.setName('Boiler')
    else
      boiler.setName("#{hot_water_loop.name} Boiler")
    end
  else
    boiler.setName(name)
  end

  if fuel_type.nil? || fuel_type == 'Gas'
    boiler.setFuelType('NaturalGas')
  elsif fuel_type == 'Propane' || fuel_type == 'PropaneGas'
    boiler.setFuelType('Propane')
  else
    boiler.setFuelType(fuel_type)
  end

  if nominal_thermal_efficiency.nil?
    boiler.setNominalThermalEfficiency(0.8)
  else
    boiler.setNominalThermalEfficiency(nominal_thermal_efficiency)
  end

  if eff_curve_temp_eval_var.nil?
    boiler.setEfficiencyCurveTemperatureEvaluationVariable('LeavingBoiler')
  else
    boiler.setEfficiencyCurveTemperatureEvaluationVariable(eff_curve_temp_eval_var)
  end

  if flow_mode.nil?
    boiler.setBoilerFlowMode('LeavingSetpointModulated')
  else
    boiler.setBoilerFlowMode(flow_mode)
  end

  if model.version < OpenStudio::VersionString.new('3.0.0')
    if lvg_temp_dsgn_f.nil?
      boiler.setDesignWaterOutletTemperature(OpenStudio.convert(180.0, 'F', 'C').get)
    else
      boiler.setDesignWaterOutletTemperature(OpenStudio.convert(lvg_temp_dsgn_f, 'F', 'C').get)
    end
  end

  if out_temp_lmt_f.nil?
    boiler.setWaterOutletUpperTemperatureLimit(OpenStudio.convert(203.0, 'F', 'C').get)
  else
    boiler.setWaterOutletUpperTemperatureLimit(OpenStudio.convert(out_temp_lmt_f, 'F', 'C').get)
  end

  # logic to set different defaults for condensing boilers if not specified
  if draft_type == 'Condensing'
    if model.version < OpenStudio::VersionString.new('3.0.0') && lvg_temp_dsgn_f.nil?
      # default to 120 degrees Fahrenheit (48.49 degrees Celsius)
      boiler.setDesignWaterOutletTemperature(OpenStudio.convert(120.0, 'F', 'C').get)
    end
    boiler.setNominalThermalEfficiency(0.96) if nominal_thermal_efficiency.nil?
  end

  if min_plr.nil?
    boiler.setMinimumPartLoadRatio(0.0)
  else
    boiler.setMinimumPartLoadRatio(min_plr)
  end

  if max_plr.nil?
    boiler.setMaximumPartLoadRatio(1.2)
  else
    boiler.setMaximumPartLoadRatio(max_plr)
  end

  if opt_plr.nil?
    boiler.setOptimumPartLoadRatio(1.0)
  else
    boiler.setOptimumPartLoadRatio(opt_plr)
  end

  boiler.setSizingFactor(sizing_factor) unless sizing_factor.nil?

  # add to supply side of hot water loop if specified
  hot_water_loop&.addSupplyBranchForComponent(boiler)

  return boiler
end

.create_central_air_source_heat_pump(model, hot_water_loop, name: nil, cop: 3.65) ⇒ OpenStudio::Model::PlantComponentUserDefined

TODO:

update curve to better calculate based on the rated cop

TODO:

refactor to use the new EnergyPlus central air source heat pump object when it becomes available set hot_water_loop to an optional keyword argument, and add input keyword arguments for other characteristics

Create CentralAirSourceHeatPump object using PlantComponentUserDefined

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • hot_water_loop (<OpenStudio::Model::PlantLoop>)

    a hot water loop served by the central air source heat pump

  • name (String) (defaults to: nil)

    the name of the central air source heat pump, or nil in which case it will be defaulted

  • cop (Double) (defaults to: 3.65)

    air source heat pump rated cop

Returns:

  • (OpenStudio::Model::PlantComponentUserDefined)

    a plant component representing the air source heat pump



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/openstudio-standards/hvac/components/central_air_source_heat_pump.rb', line 17

def self.create_central_air_source_heat_pump(model,
                                             hot_water_loop,
                                             name: nil,
                                             cop: 3.65)

  # create the PlantComponentUserDefined object as a proxy for the Central Air Source Heat Pump
  plant_comp = OpenStudio::Model::PlantComponentUserDefined.new(model)
  if name.nil?
    if hot_water_loop.nil?
      name = 'Central Air Source Heat Pump'
    else
      name = "#{hot_water_loop.name} Central Air Source Heat Pump"
    end
  end

  # change equipment name for EMS validity
  plant_comp.setName(OpenstudioStandards::HVAC.ems_friendly_name(name))

  # set plant component properties
  plant_comp.setPlantLoadingMode('MeetsLoadWithNominalCapacityHiOutLimit')
  plant_comp.setPlantLoopFlowRequestMode('NeedsFlowIfLoopOn')

  # plant design volume flow rate internal variable
  vdot_des_int_var = OpenStudio::Model::EnergyManagementSystemInternalVariable.new(model, 'Plant Design Volume Flow Rate')
  vdot_des_int_var.setName("#{plant_comp.name}_Vdot_Des_Int_Var")
  vdot_des_int_var.setInternalDataIndexKeyName(hot_water_loop.handle.to_s)

  # inlet temperature internal variable
  tin_int_var = OpenStudio::Model::EnergyManagementSystemInternalVariable.new(model, 'Inlet Temperature for Plant Connection 1')
  tin_int_var.setName("#{plant_comp.name}_Tin_Int_Var")
  tin_int_var.setInternalDataIndexKeyName(plant_comp.handle.to_s)

  # inlet mass flow rate internal variable
  mdot_int_var = OpenStudio::Model::EnergyManagementSystemInternalVariable.new(model, 'Inlet Mass Flow Rate for Plant Connection 1')
  mdot_int_var.setName("#{plant_comp.name}_Mdot_Int_Var")
  mdot_int_var.setInternalDataIndexKeyName(plant_comp.handle.to_s)

  # inlet specific heat internal variable
  cp_int_var = OpenStudio::Model::EnergyManagementSystemInternalVariable.new(model, 'Inlet Specific Heat for Plant Connection 1')
  cp_int_var.setName("#{plant_comp.name}_Cp_Int_Var")
  cp_int_var.setInternalDataIndexKeyName(plant_comp.handle.to_s)

  # inlet density internal variable
  rho_int_var = OpenStudio::Model::EnergyManagementSystemInternalVariable.new(model, 'Inlet Density for Plant Connection 1')
  rho_int_var.setName("#{plant_comp.name}_rho_Int_Var")
  rho_int_var.setInternalDataIndexKeyName(plant_comp.handle.to_s)

  # load request internal variable
  load_int_var = OpenStudio::Model::EnergyManagementSystemInternalVariable.new(model, 'Load Request for Plant Connection 1')
  load_int_var.setName("#{plant_comp.name}_Load_Int_Var")
  load_int_var.setInternalDataIndexKeyName(plant_comp.handle.to_s)

  # supply outlet node setpoint temperature sensor
  setpt_mgr_sch_sen = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
  setpt_mgr_sch_sen.setName("#{plant_comp.name}_Setpt_Mgr_Temp_Sen")
  hot_water_loop.supplyOutletNode.setpointManagers.each do |m|
    if m.to_SetpointManagerScheduled.is_initialized
      setpt_mgr_sch_sen.setKeyName(m.to_SetpointManagerScheduled.get.schedule.name.to_s)
    end
  end

  # outdoor air drybulb temperature sensor
  oa_dbt_sen = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Drybulb Temperature')
  oa_dbt_sen.setName("#{plant_comp.name}_OA_DBT_Sen")
  oa_dbt_sen.setKeyName('Environment')

  # minimum mass flow rate actuator
  mdot_min_act = plant_comp.minimumMassFlowRateActuator.get
  mdot_min_act.setName("#{plant_comp.name}_Mdot_Min_Act")

  # maximum mass flow rate actuator
  mdot_max_act = plant_comp.maximumMassFlowRateActuator.get
  mdot_max_act.setName("#{plant_comp.name}_Mdot_Max_Act")

  # design flow rate actuator
  vdot_des_act = plant_comp.designVolumeFlowRateActuator.get
  vdot_des_act.setName("#{plant_comp.name}_Vdot_Des_Act")

  # minimum loading capacity actuator
  cap_min_act = plant_comp.minimumLoadingCapacityActuator.get
  cap_min_act.setName("#{plant_comp.name}_Cap_Min_Act")

  # maximum loading capacity actuator
  cap_max_act = plant_comp.maximumLoadingCapacityActuator.get
  cap_max_act.setName("#{plant_comp.name}_Cap_Max_Act")

  # optimal loading capacity actuator
  cap_opt_act = plant_comp.optimalLoadingCapacityActuator.get
  cap_opt_act.setName("#{plant_comp.name}_Cap_Opt_Act")

  # outlet temperature actuator
  tout_act = plant_comp.outletTemperatureActuator.get
  tout_act.setName("#{plant_comp.name}_Tout_Act")

  # mass flow rate actuator
  mdot_req_act = plant_comp.massFlowRateActuator.get
  mdot_req_act.setName("#{plant_comp.name}_Mdot_Req_Act")

  # heat pump COP curve
  constant_coeff = 1.932 + (cop - 3.65)
  hp_cop_curve = OpenStudio::Model::CurveQuadratic.new(model)
  hp_cop_curve.setCoefficient1Constant(constant_coeff)
  hp_cop_curve.setCoefficient2x(0.227674286)
  hp_cop_curve.setCoefficient3xPOW2(-0.007313143)
  hp_cop_curve.setMinimumValueofx(1.67)
  hp_cop_curve.setMaximumValueofx(12.78)
  hp_cop_curve.setInputUnitTypeforX('Temperature')
  hp_cop_curve.setOutputUnitType('Dimensionless')

  # heat pump COP curve index variable
  hp_cop_curve_idx_var = OpenStudio::Model::EnergyManagementSystemCurveOrTableIndexVariable.new(model, hp_cop_curve)

  # high outlet temperature limit actuator
  tout_max_act = OpenStudio::Model::EnergyManagementSystemActuator.new(plant_comp, 'Plant Connection 1', 'High Outlet Temperature Limit')
  tout_max_act.setName("#{plant_comp.name}_Tout_Max_Act")

  # init program
  init_pgrm = plant_comp.plantInitializationProgram.get
  init_pgrm.setName("#{plant_comp.name}_Init_Pgrm")
  init_pgrm_body = <<-EMS
  SET Loop_Exit_Temp = #{hot_water_loop.sizingPlant.designLoopExitTemperature}
  SET Loop_Delta_Temp = #{hot_water_loop.sizingPlant.loopDesignTemperatureDifference}
  SET Cp = @CPHW Loop_Exit_Temp
  SET rho = @RhoH2O Loop_Exit_Temp
  SET #{vdot_des_act.handle} = #{vdot_des_int_var.handle}
  SET #{mdot_min_act.handle} = 0
  SET Mdot_Max = #{vdot_des_int_var.handle} * rho
  SET #{mdot_max_act.handle} = Mdot_Max
  SET Cap = Mdot_Max * Cp * Loop_Delta_Temp
  SET #{cap_min_act.handle} = 0
  SET #{cap_max_act.handle} = Cap
  SET #{cap_opt_act.handle} = 1 * Cap
  EMS
  init_pgrm.setBody(init_pgrm_body)

  # sim program
  sim_pgrm = plant_comp.plantSimulationProgram.get
  sim_pgrm.setName("#{plant_comp.name}_Sim_Pgrm")
  sim_pgrm_body = <<-EMS
  SET tmp = #{load_int_var.handle}
  SET tmp = #{tin_int_var.handle}
  SET tmp = #{mdot_int_var.handle}
  SET #{tout_max_act.handle} = 75.0
  IF #{load_int_var.handle} == 0
  SET #{tout_act.handle} = #{tin_int_var.handle}
  SET #{mdot_req_act.handle} = 0
  SET Elec = 0
  RETURN
  ENDIF
  IF #{load_int_var.handle} >= #{cap_max_act.handle}
  SET Qdot = #{cap_max_act.handle}
  SET Mdot = #{mdot_max_act.handle}
  SET #{mdot_req_act.handle} = Mdot
  SET #{tout_act.handle} = (Qdot / (Mdot * #{cp_int_var.handle})) + #{tin_int_var.handle}
  IF #{tout_act.handle} > #{tout_max_act.handle}
  SET #{tout_act.handle} = #{tout_max_act.handle}
  SET Qdot = Mdot * #{cp_int_var.handle} * (#{tout_act.handle} - #{tin_int_var.handle})
  ENDIF
  ELSE
  SET Qdot = #{load_int_var.handle}
  SET #{tout_act.handle} = #{setpt_mgr_sch_sen.handle}
  SET Mdot = Qdot / (#{cp_int_var.handle} * (#{tout_act.handle} - #{tin_int_var.handle}))
  SET #{mdot_req_act.handle} = Mdot
  ENDIF
  SET Tdb = #{oa_dbt_sen.handle}
  SET COP = @CurveValue #{hp_cop_curve_idx_var.handle} Tdb
  SET EIR = 1 / COP
  SET Pwr = Qdot * EIR
  SET Elec = Pwr * SystemTimestep * 3600
  EMS
  sim_pgrm.setBody(sim_pgrm_body)

  # init program calling manager
  init_mgr = plant_comp.plantInitializationProgramCallingManager.get
  init_mgr.setName("#{plant_comp.name}_Init_Pgrm_Mgr")

  # sim program calling manager
  sim_mgr = plant_comp.plantSimulationProgramCallingManager.get
  sim_mgr.setName("#{plant_comp.name}_Sim_Pgrm_Mgr")

  # metered output variable
  elec_mtr_out_var = OpenStudio::Model::EnergyManagementSystemMeteredOutputVariable.new(model, "#{plant_comp.name} Electricity Consumption")
  elec_mtr_out_var.setName("#{plant_comp.name} Electricity Consumption")
  elec_mtr_out_var.setEMSVariableName('Elec')
  elec_mtr_out_var.setUpdateFrequency('SystemTimestep')
  elec_mtr_out_var.setString(4, sim_pgrm.handle.to_s)
  elec_mtr_out_var.setResourceType('Electricity')
  elec_mtr_out_var.setGroupType('HVAC')
  elec_mtr_out_var.setEndUseCategory('Heating')
  elec_mtr_out_var.setEndUseSubcategory('')
  elec_mtr_out_var.setUnits('J')

  # add to supply side of hot water loop if specified
  hot_water_loop&.addSupplyBranchForComponent(plant_comp)

  # add operation scheme
  htg_op_scheme = OpenStudio::Model::PlantEquipmentOperationHeatingLoad.new(model)
  htg_op_scheme.addEquipment(1000000000.0, plant_comp)
  hot_water_loop.setPlantEquipmentOperationHeatingLoad(htg_op_scheme)

  return plant_comp
end

.create_coil_cooling_dx_single_speed(model, air_loop_node: nil, name: '1spd DX Clg Coil', schedule: nil, type: nil, cop: nil) ⇒ OpenStudio::Model::CoilCoolingDXTwoSpeed

Create CoilCoolingDXSingleSpeed object Enters in default curves for coil by type of coil

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: '1spd DX Clg Coil')

    the name of the system, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • type (String) (defaults to: nil)

    the type of single speed DX coil to reference the correct curve set

  • cop (Double) (defaults to: nil)

    rated cooling coefficient of performance

Returns:

  • (OpenStudio::Model::CoilCoolingDXTwoSpeed)

    the DX cooling coil



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/openstudio-standards/hvac/components/coil_cooling_dx_single_speed.rb', line 17

def self.create_coil_cooling_dx_single_speed(model,
                                             air_loop_node: nil,
                                             name: '1spd DX Clg Coil',
                                             schedule: nil,
                                             type: nil,
                                             cop: nil)
  clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model)

  # add to air loop if specified
  clg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  clg_coil.setName(name)

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  clg_coil.setAvailabilitySchedule(coil_availability_schedule)

  # set coil cop
  clg_coil.setRatedCOP(cop) unless cop.nil?

  clg_cap_f_of_temp = nil
  clg_cap_f_of_flow = nil
  clg_energy_input_ratio_f_of_temp = nil
  clg_energy_input_ratio_f_of_flow = nil
  clg_part_load_ratio = nil

  # curve sets
  case type
  when 'OS default'
    # use OS defaults

  when 'Heat Pump'
    # "PSZ-AC_Unitary_PackagecoolCapFT"
    clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_cap_f_of_temp.setCoefficient1Constant(0.766956)
    clg_cap_f_of_temp.setCoefficient2x(0.0107756)
    clg_cap_f_of_temp.setCoefficient3xPOW2(-0.0000414703)
    clg_cap_f_of_temp.setCoefficient4y(0.00134961)
    clg_cap_f_of_temp.setCoefficient5yPOW2(-0.000261144)
    clg_cap_f_of_temp.setCoefficient6xTIMESY(0.000457488)
    clg_cap_f_of_temp.setMinimumValueofx(12.78)
    clg_cap_f_of_temp.setMaximumValueofx(23.89)
    clg_cap_f_of_temp.setMinimumValueofy(21.1)
    clg_cap_f_of_temp.setMaximumValueofy(46.1)

    clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_cap_f_of_flow.setCoefficient1Constant(0.8)
    clg_cap_f_of_flow.setCoefficient2x(0.2)
    clg_cap_f_of_flow.setCoefficient3xPOW2(0.0)
    clg_cap_f_of_flow.setMinimumValueofx(0.5)
    clg_cap_f_of_flow.setMaximumValueofx(1.5)

    clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(0.297145)
    clg_energy_input_ratio_f_of_temp.setCoefficient2x(0.0430933)
    clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.000748766)
    clg_energy_input_ratio_f_of_temp.setCoefficient4y(0.00597727)
    clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.000482112)
    clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.000956448)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofx(12.78)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofx(23.89)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofy(21.1)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.1)

    clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.156)
    clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.1816)
    clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0256)
    clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.5)
    clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.5)

    clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
    clg_part_load_ratio.setCoefficient1Constant(0.85)
    clg_part_load_ratio.setCoefficient2x(0.15)
    clg_part_load_ratio.setCoefficient3xPOW2(0.0)
    clg_part_load_ratio.setMinimumValueofx(0.0)
    clg_part_load_ratio.setMaximumValueofx(1.0)

  when 'PSZ-AC'
    # Defaults to "DOE Ref DX Clg Coil Cool-Cap-fT"
    clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_cap_f_of_temp.setCoefficient1Constant(0.9712123)
    clg_cap_f_of_temp.setCoefficient2x(-0.015275502)
    clg_cap_f_of_temp.setCoefficient3xPOW2(0.0014434524)
    clg_cap_f_of_temp.setCoefficient4y(-0.00039321)
    clg_cap_f_of_temp.setCoefficient5yPOW2(-0.0000068364)
    clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.0002905956)
    clg_cap_f_of_temp.setMinimumValueofx(-100.0)
    clg_cap_f_of_temp.setMaximumValueofx(100.0)
    clg_cap_f_of_temp.setMinimumValueofy(-100.0)
    clg_cap_f_of_temp.setMaximumValueofy(100.0)

    clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_cap_f_of_flow.setCoefficient1Constant(1.0)
    clg_cap_f_of_flow.setCoefficient2x(0.0)
    clg_cap_f_of_flow.setCoefficient3xPOW2(0.0)
    clg_cap_f_of_flow.setMinimumValueofx(-100.0)
    clg_cap_f_of_flow.setMaximumValueofx(100.0)

    # "DOE Ref DX Clg Coil Cool-EIR-fT",
    clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(0.28687133)
    clg_energy_input_ratio_f_of_temp.setCoefficient2x(0.023902164)
    clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.000810648)
    clg_energy_input_ratio_f_of_temp.setCoefficient4y(0.013458546)
    clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.0003389364)
    clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.0004870044)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofx(-100.0)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofx(100.0)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofy(-100.0)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofy(100.0)

    clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.0)
    clg_energy_input_ratio_f_of_flow.setCoefficient2x(0.0)
    clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0)
    clg_energy_input_ratio_f_of_flow.setMinimumValueofx(-100.0)
    clg_energy_input_ratio_f_of_flow.setMaximumValueofx(100.0)

    # "DOE Ref DX Clg Coil Cool-PLF-fPLR"
    clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
    clg_part_load_ratio.setCoefficient1Constant(0.90949556)
    clg_part_load_ratio.setCoefficient2x(0.09864773)
    clg_part_load_ratio.setCoefficient3xPOW2(-0.00819488)
    clg_part_load_ratio.setMinimumValueofx(0.0)
    clg_part_load_ratio.setMaximumValueofx(1.0)
    clg_part_load_ratio.setMinimumCurveOutput(0.7)
    clg_part_load_ratio.setMaximumCurveOutput(1.0)

  when 'Window AC'
    # Performance curves
    # From Frigidaire 10.7 EER unit in Winkler et. al. Lab Testing of Window ACs (2013)
    # @note These coefficients are in SI UNITS
    cool_cap_ft_coeffs_si = [0.6405, 0.01568, 0.0004531, 0.001615, -0.0001825, 0.00006614]
    cool_eir_ft_coeffs_si = [2.287, -0.1732, 0.004745, 0.01662, 0.000484, -0.001306]
    cool_cap_fflow_coeffs = [0.887, 0.1128, 0]
    cool_eir_fflow_coeffs = [1.763, -0.6081, 0]
    cool_plf_fplr_coeffs = [0.78, 0.22, 0]

    # Make the curves
    clg_cap_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_cap_ft_coeffs_si, name: 'RoomAC-Cap-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_cap_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_cap_fflow_coeffs, name: 'RoomAC-Cap-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_energy_input_ratio_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_eir_ft_coeffs_si, name: 'RoomAC-EIR-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_energy_input_ratio_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_eir_fflow_coeffs, name: 'RoomAC-EIR-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_part_load_ratio = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_plf_fplr_coeffs, name: 'RoomAC-PLF-fPLR', min_x: 0, max_x: 1, min_out: 0, max_out: 1, is_dimensionless: true)

  when 'Residential Central AC'
    # Performance curves
    # These coefficients are in IP UNITS
    cool_cap_ft_coeffs_ip = [3.670270705, -0.098652414, 0.000955906, 0.006552414, -0.0000156, -0.000131877]
    cool_eir_ft_coeffs_ip = [-3.302695861, 0.137871531, -0.001056996, -0.012573945, 0.000214638, -0.000145054]
    cool_cap_fflow_coeffs = [0.718605468, 0.410099989, -0.128705457]
    cool_eir_fflow_coeffs = [1.32299905, -0.477711207, 0.154712157]
    cool_plf_fplr_coeffs = [0.8, 0.2, 0]

    # Convert coefficients from IP to SI
    cool_cap_ft_coeffs_si = OpenstudioStandards::HVAC.convert_curve_biquadratic(cool_cap_ft_coeffs_ip)
    cool_eir_ft_coeffs_si = OpenstudioStandards::HVAC.convert_curve_biquadratic(cool_eir_ft_coeffs_ip)

    # Make the curves
    clg_cap_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_cap_ft_coeffs_si, name: 'AC-Cap-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_cap_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_cap_fflow_coeffs, name: 'AC-Cap-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_energy_input_ratio_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_eir_ft_coeffs_si, name: 'AC-EIR-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_energy_input_ratio_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_eir_fflow_coeffs, name: 'AC-EIR-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_part_load_ratio = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_plf_fplr_coeffs, name: 'AC-PLF-fPLR', min_x: 0, max_x: 1, min_out: 0, max_out: 1, is_dimensionless: true)

  when 'Residential Central ASHP'
    # Performance curves
    # These coefficients are in IP UNITS
    cool_cap_ft_coeffs_ip = [3.68637657, -0.098352478, 0.000956357, 0.005838141, -0.0000127, -0.000131702]
    cool_eir_ft_coeffs_ip = [-3.437356399, 0.136656369, -0.001049231, -0.0079378, 0.000185435, -0.0001441]
    cool_cap_fflow_coeffs = [0.718664047, 0.41797409, -0.136638137]
    cool_eir_fflow_coeffs = [1.143487507, -0.13943972, -0.004047787]
    cool_plf_fplr_coeffs = [0.8, 0.2, 0]

    # Convert coefficients from IP to SI
    cool_cap_ft_coeffs_si = OpenstudioStandards::HVAC.convert_curve_biquadratic(cool_cap_ft_coeffs_ip)
    cool_eir_ft_coeffs_si = OpenstudioStandards::HVAC.convert_curve_biquadratic(cool_eir_ft_coeffs_ip)

    # Make the curves
    clg_cap_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_cap_ft_coeffs_si, name: 'Cool-Cap-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_cap_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_cap_fflow_coeffs, name: 'Cool-Cap-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_energy_input_ratio_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_eir_ft_coeffs_si, name: 'Cool-EIR-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_energy_input_ratio_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_eir_fflow_coeffs, name: 'Cool-EIR-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_part_load_ratio = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_plf_fplr_coeffs, name: 'Cool-PLF-fPLR', min_x: 0, max_x: 1, min_out: 0, max_out: 1, is_dimensionless: true)

  else # default curve set, type == 'Split AC' || 'PTAC'
    clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_cap_f_of_temp.setCoefficient1Constant(0.942587793)
    clg_cap_f_of_temp.setCoefficient2x(0.009543347)
    clg_cap_f_of_temp.setCoefficient3xPOW2(0.00068377)
    clg_cap_f_of_temp.setCoefficient4y(-0.011042676)
    clg_cap_f_of_temp.setCoefficient5yPOW2(0.000005249)
    clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.00000972)
    clg_cap_f_of_temp.setMinimumValueofx(12.77778)
    clg_cap_f_of_temp.setMaximumValueofx(23.88889)
    clg_cap_f_of_temp.setMinimumValueofy(23.88889)
    clg_cap_f_of_temp.setMaximumValueofy(46.11111)

    clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_cap_f_of_flow.setCoefficient1Constant(0.8)
    clg_cap_f_of_flow.setCoefficient2x(0.2)
    clg_cap_f_of_flow.setCoefficient3xPOW2(0)
    clg_cap_f_of_flow.setMinimumValueofx(0.5)
    clg_cap_f_of_flow.setMaximumValueofx(1.5)

    clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(0.342414409)
    clg_energy_input_ratio_f_of_temp.setCoefficient2x(0.034885008)
    clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.0006237)
    clg_energy_input_ratio_f_of_temp.setCoefficient4y(0.004977216)
    clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.000437951)
    clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.000728028)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofx(12.77778)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofx(23.88889)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofy(23.88889)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.11111)

    clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.1552)
    clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.1808)
    clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0256)
    clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.5)
    clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.5)

    clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
    clg_part_load_ratio.setCoefficient1Constant(0.85)
    clg_part_load_ratio.setCoefficient2x(0.15)
    clg_part_load_ratio.setCoefficient3xPOW2(0.0)
    clg_part_load_ratio.setMinimumValueofx(0.0)
    clg_part_load_ratio.setMaximumValueofx(1.0)
    clg_part_load_ratio.setMinimumCurveOutput(0.7)
    clg_part_load_ratio.setMaximumCurveOutput(1.0)
  end

  clg_coil.setTotalCoolingCapacityFunctionOfTemperatureCurve(clg_cap_f_of_temp) unless clg_cap_f_of_temp.nil?
  clg_coil.setTotalCoolingCapacityFunctionOfFlowFractionCurve(clg_cap_f_of_flow) unless clg_cap_f_of_flow.nil?
  clg_coil.setEnergyInputRatioFunctionOfTemperatureCurve(clg_energy_input_ratio_f_of_temp) unless clg_energy_input_ratio_f_of_temp.nil?
  clg_coil.setEnergyInputRatioFunctionOfFlowFractionCurve(clg_energy_input_ratio_f_of_flow) unless clg_energy_input_ratio_f_of_flow.nil?
  clg_coil.setPartLoadFractionCorrelationCurve(clg_part_load_ratio) unless clg_part_load_ratio.nil?

  return clg_coil
end

.create_coil_cooling_dx_two_speed(model, air_loop_node: nil, name: '2spd DX Clg Coil', schedule: nil, type: nil) ⇒ OpenStudio::Model::CoilCoolingDXTwoSpeed

Create CoilCoolingDXTwoSpeed object Enters in default curves for coil by type of coil

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: '2spd DX Clg Coil')

    the name of the system, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • type (String) (defaults to: nil)

    the type of two speed DX coil to reference the correct curve set

Returns:

  • (OpenStudio::Model::CoilCoolingDXTwoSpeed)

    the DX cooling coil



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/openstudio-standards/hvac/components/coil_cooling_dx_two_speed.rb', line 16

def self.create_coil_cooling_dx_two_speed(model,
                                          air_loop_node: nil,
                                          name: '2spd DX Clg Coil',
                                          schedule: nil,
                                          type: nil)
  clg_coil = OpenStudio::Model::CoilCoolingDXTwoSpeed.new(model)

  # add to air loop if specified
  clg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  clg_coil.setName(name)

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  clg_coil.setAvailabilitySchedule(coil_availability_schedule)

  clg_cap_f_of_temp = nil
  clg_cap_f_of_flow = nil
  clg_energy_input_ratio_f_of_temp = nil
  clg_energy_input_ratio_f_of_flow = nil
  clg_part_load_ratio = nil
  clg_cap_f_of_temp_low_spd = nil
  clg_energy_input_ratio_f_of_temp_low_spd = nil

  # curve sets
  if type == 'OS default'
    # use OS defaults
  elsif type == 'Residential Minisplit HP'
    # Performance curves
    # These coefficients are in SI units
    cool_cap_ft_coeffs_si = [0.7531983499655835, 0.003618193903031667, 0.0, 0.006574385031351544, -6.87181191015432e-05, 0.0]
    cool_eir_ft_coeffs_si = [-0.06376924779982301, -0.0013360593470367282, 1.413060577993827e-05, 0.019433076486584752, -4.91395947154321e-05, -4.909341249475308e-05]
    cool_cap_fflow_coeffs = [1, 0, 0]
    cool_eir_fflow_coeffs = [1, 0, 0]
    cool_plf_fplr_coeffs = [0.89, 0.11, 0]

    # Make the curves
    clg_cap_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_cap_ft_coeffs_si, name: 'Cool-Cap-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_cap_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_cap_fflow_coeffs, name: 'Cool-Cap-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_energy_input_ratio_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_eir_ft_coeffs_si, name: 'Cool-EIR-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_energy_input_ratio_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_eir_fflow_coeffs, name: 'Cool-EIR-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    clg_part_load_ratio = OpenstudioStandards::HVAC.create_curve_quadratic(model, cool_plf_fplr_coeffs, name: 'Cool-PLF-fPLR', min_x: 0, max_x: 1, min_out: 0, max_out: 1, is_dimensionless: true)
    clg_cap_f_of_temp_low_spd = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_cap_ft_coeffs_si, name: 'Cool-Cap-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_energy_input_ratio_f_of_temp_low_spd = OpenstudioStandards::HVAC.create_curve_biquadratic(model, cool_eir_ft_coeffs_si, name: 'Cool-EIR-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    clg_coil.setRatedLowSpeedSensibleHeatRatio(0.73)
    clg_coil.setCondenserType('AirCooled')
  else # default curve set, type == 'PSZ-AC' || 'Split AC' || 'PTAC'
    clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_cap_f_of_temp.setCoefficient1Constant(0.42415)
    clg_cap_f_of_temp.setCoefficient2x(0.04426)
    clg_cap_f_of_temp.setCoefficient3xPOW2(-0.00042)
    clg_cap_f_of_temp.setCoefficient4y(0.00333)
    clg_cap_f_of_temp.setCoefficient5yPOW2(-0.00008)
    clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.00021)
    clg_cap_f_of_temp.setMinimumValueofx(17.0)
    clg_cap_f_of_temp.setMaximumValueofx(22.0)
    clg_cap_f_of_temp.setMinimumValueofy(13.0)
    clg_cap_f_of_temp.setMaximumValueofy(46.0)

    clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_cap_f_of_flow.setCoefficient1Constant(0.77136)
    clg_cap_f_of_flow.setCoefficient2x(0.34053)
    clg_cap_f_of_flow.setCoefficient3xPOW2(-0.11088)
    clg_cap_f_of_flow.setMinimumValueofx(0.75918)
    clg_cap_f_of_flow.setMaximumValueofx(1.13877)

    clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(1.23649)
    clg_energy_input_ratio_f_of_temp.setCoefficient2x(-0.02431)
    clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(0.00057)
    clg_energy_input_ratio_f_of_temp.setCoefficient4y(-0.01434)
    clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.00063)
    clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.00038)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofx(17.0)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofx(22.0)
    clg_energy_input_ratio_f_of_temp.setMinimumValueofy(13.0)
    clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.0)

    clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.20550)
    clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.32953)
    clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.12308)
    clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.75918)
    clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.13877)

    clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
    clg_part_load_ratio.setCoefficient1Constant(0.77100)
    clg_part_load_ratio.setCoefficient2x(0.22900)
    clg_part_load_ratio.setCoefficient3xPOW2(0.0)
    clg_part_load_ratio.setMinimumValueofx(0.0)
    clg_part_load_ratio.setMaximumValueofx(1.0)

    clg_cap_f_of_temp_low_spd = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_cap_f_of_temp_low_spd.setCoefficient1Constant(0.42415)
    clg_cap_f_of_temp_low_spd.setCoefficient2x(0.04426)
    clg_cap_f_of_temp_low_spd.setCoefficient3xPOW2(-0.00042)
    clg_cap_f_of_temp_low_spd.setCoefficient4y(0.00333)
    clg_cap_f_of_temp_low_spd.setCoefficient5yPOW2(-0.00008)
    clg_cap_f_of_temp_low_spd.setCoefficient6xTIMESY(-0.00021)
    clg_cap_f_of_temp_low_spd.setMinimumValueofx(17.0)
    clg_cap_f_of_temp_low_spd.setMaximumValueofx(22.0)
    clg_cap_f_of_temp_low_spd.setMinimumValueofy(13.0)
    clg_cap_f_of_temp_low_spd.setMaximumValueofy(46.0)

    clg_energy_input_ratio_f_of_temp_low_spd = OpenStudio::Model::CurveBiquadratic.new(model)
    clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient1Constant(1.23649)
    clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient2x(-0.02431)
    clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient3xPOW2(0.00057)
    clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient4y(-0.01434)
    clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient5yPOW2(0.00063)
    clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient6xTIMESY(-0.00038)
    clg_energy_input_ratio_f_of_temp_low_spd.setMinimumValueofx(17.0)
    clg_energy_input_ratio_f_of_temp_low_spd.setMaximumValueofx(22.0)
    clg_energy_input_ratio_f_of_temp_low_spd.setMinimumValueofy(13.0)
    clg_energy_input_ratio_f_of_temp_low_spd.setMaximumValueofy(46.0)

    clg_coil.setRatedLowSpeedSensibleHeatRatio(OpenStudio::OptionalDouble.new(0.69))
    clg_coil.setBasinHeaterCapacity(10)
    clg_coil.setBasinHeaterSetpointTemperature(2.0)
  end

  clg_coil.setTotalCoolingCapacityFunctionOfTemperatureCurve(clg_cap_f_of_temp) unless clg_cap_f_of_temp.nil?
  clg_coil.setTotalCoolingCapacityFunctionOfFlowFractionCurve(clg_cap_f_of_flow) unless clg_cap_f_of_flow.nil?
  clg_coil.setEnergyInputRatioFunctionOfTemperatureCurve(clg_energy_input_ratio_f_of_temp) unless clg_energy_input_ratio_f_of_temp.nil?
  clg_coil.setEnergyInputRatioFunctionOfFlowFractionCurve(clg_energy_input_ratio_f_of_flow) unless clg_energy_input_ratio_f_of_flow.nil?
  clg_coil.setPartLoadFractionCorrelationCurve(clg_part_load_ratio) unless clg_part_load_ratio.nil?
  clg_coil.setLowSpeedTotalCoolingCapacityFunctionOfTemperatureCurve(clg_cap_f_of_temp_low_spd) unless clg_cap_f_of_temp_low_spd.nil?
  clg_coil.setLowSpeedEnergyInputRatioFunctionOfTemperatureCurve(clg_energy_input_ratio_f_of_temp_low_spd) unless clg_energy_input_ratio_f_of_temp_low_spd.nil?

  return clg_coil
end

.create_coil_cooling_water(model, chilled_water_loop, air_loop_node: nil, name: 'Clg Coil', schedule: nil, design_inlet_water_temperature: nil, design_inlet_air_temperature: nil, design_outlet_air_temperature: nil) ⇒ OpenStudio::Model::CoilCoolingWater

Create CoilCoolingWater object

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • chilled_water_loop (<OpenStudio::Model::PlantLoop>)

    the coil will be placed on the demand side of this plant loop

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: 'Clg Coil')

    the name of the coil, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • design_inlet_water_temperature (Double) (defaults to: nil)

    design inlet water temperature in degrees Celsius, default is nil

  • design_inlet_air_temperature (Double) (defaults to: nil)

    design inlet air temperature in degrees Celsius, default is nil

  • design_outlet_air_temperature (Double) (defaults to: nil)

    design outlet air temperature in degrees Celsius, default is nil

Returns:

  • (OpenStudio::Model::CoilCoolingWater)

    the cooling coil



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/openstudio-standards/hvac/components/coil_cooling_water.rb', line 18

def self.create_coil_cooling_water(model,
                                   chilled_water_loop,
                                   air_loop_node: nil,
                                   name: 'Clg Coil',
                                   schedule: nil,
                                   design_inlet_water_temperature: nil,
                                   design_inlet_air_temperature: nil,
                                   design_outlet_air_temperature: nil)
  clg_coil = OpenStudio::Model::CoilCoolingWater.new(model)

  # add to chilled water loop
  chilled_water_loop.addDemandBranchForComponent(clg_coil)

  # add to air loop if specified
  clg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  if name.nil?
    clg_coil.setName('Clg Coil')
  else
    clg_coil.setName(name)
  end

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  clg_coil.setAvailabilitySchedule(coil_availability_schedule)

  # rated temperatures
  if design_inlet_water_temperature.nil?
    clg_coil.autosizeDesignInletWaterTemperature
  else
    clg_coil.setDesignInletWaterTemperature(design_inlet_water_temperature)
  end
  clg_coil.setDesignInletAirTemperature(design_inlet_air_temperature) unless design_inlet_air_temperature.nil?
  clg_coil.setDesignOutletAirTemperature(design_outlet_air_temperature) unless design_outlet_air_temperature.nil?

  # defaults
  clg_coil.setHeatExchangerConfiguration('CrossFlow')

  # coil controller properties
  # @note These inputs will get overwritten if addToNode or addDemandBranchForComponent is called on the htg_coil object after this
  clg_coil_controller = clg_coil.controllerWaterCoil.get
  clg_coil_controller.setName("#{clg_coil.name} Controller")
  clg_coil_controller.setAction('Reverse')
  clg_coil_controller.setMinimumActuatedFlow(0.0)

  return clg_coil
end

.create_coil_cooling_water_to_air_heat_pump_equation_fit(model, plant_loop, air_loop_node: nil, name: 'Water-to-Air HP Clg Coil', type: nil, cop: 3.4) ⇒ OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit

Create CoilCoolingWaterToAirHeatPumpEquationFit object Enters in default curves for coil by type of coil

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • plant_loop (<OpenStudio::Model::PlantLoop>)

    the coil will be placed on the demand side of this plant loop

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: 'Water-to-Air HP Clg Coil')

    the name of the system, or nil in which case it will be defaulted

  • type (String) (defaults to: nil)

    the type of coil to reference the correct curve set

  • cop (Double) (defaults to: 3.4)

    rated cooling coefficient of performance

Returns:

  • (OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit)

    the cooling coil



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/openstudio-standards/hvac/components/coil_cooling_water_to_air_heat_pump_equation_fit.rb', line 17

def self.create_coil_cooling_water_to_air_heat_pump_equation_fit(model,
                                                                 plant_loop,
                                                                 air_loop_node: nil,
                                                                 name: 'Water-to-Air HP Clg Coil',
                                                                 type: nil,
                                                                 cop: 3.4)
  clg_coil = OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.new(model)

  # add to air loop if specified
  clg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  clg_coil.setName(name)

  # add to plant loop
  if plant_loop.nil?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', 'No plant loop supplied for cooling coil')
    return false
  end
  plant_loop.addDemandBranchForComponent(clg_coil)

  # set coil cop
  if cop.nil?
    clg_coil.setRatedCoolingCoefficientofPerformance(3.4)
  else
    clg_coil.setRatedCoolingCoefficientofPerformance(cop)
  end

  # curve sets
  if type == 'OS default'
    # use OS default curves
  else # default curve set
    if model.version < OpenStudio::VersionString.new('3.2.0')
      clg_coil.setTotalCoolingCapacityCoefficient1(-4.30266987344639)
      clg_coil.setTotalCoolingCapacityCoefficient2(7.18536990534372)
      clg_coil.setTotalCoolingCapacityCoefficient3(-2.23946714486189)
      clg_coil.setTotalCoolingCapacityCoefficient4(0.139995928440879)
      clg_coil.setTotalCoolingCapacityCoefficient5(0.102660179888915)
      clg_coil.setSensibleCoolingCapacityCoefficient1(6.0019444814887)
      clg_coil.setSensibleCoolingCapacityCoefficient2(22.6300677244073)
      clg_coil.setSensibleCoolingCapacityCoefficient3(-26.7960783730934)
      clg_coil.setSensibleCoolingCapacityCoefficient4(-1.72374720346819)
      clg_coil.setSensibleCoolingCapacityCoefficient5(0.490644802367817)
      clg_coil.setSensibleCoolingCapacityCoefficient6(0.0693119353468141)
      clg_coil.setCoolingPowerConsumptionCoefficient1(-5.67775976415698)
      clg_coil.setCoolingPowerConsumptionCoefficient2(0.438988156976704)
      clg_coil.setCoolingPowerConsumptionCoefficient3(5.845277342193)
      clg_coil.setCoolingPowerConsumptionCoefficient4(0.141605667000125)
      clg_coil.setCoolingPowerConsumptionCoefficient5(-0.168727936032429)
    else
      if model.getCurveByName('Water to Air Heat Pump Total Cooling Capacity Curve').is_initialized
        total_cooling_capacity_curve = model.getCurveByName('Water to Air Heat Pump Total Cooling Capacity Curve').get
        total_cooling_capacity_curve = total_cooling_capacity_curve.to_CurveQuadLinear.get
      else
        total_cooling_capacity_curve = OpenStudio::Model::CurveQuadLinear.new(model)
        total_cooling_capacity_curve.setName('Water to Air Heat Pump Total Cooling Capacity Curve')
        total_cooling_capacity_curve.setCoefficient1Constant(-4.30266987344639)
        total_cooling_capacity_curve.setCoefficient2w(7.18536990534372)
        total_cooling_capacity_curve.setCoefficient3x(-2.23946714486189)
        total_cooling_capacity_curve.setCoefficient4y(0.139995928440879)
        total_cooling_capacity_curve.setCoefficient5z(0.102660179888915)
        total_cooling_capacity_curve.setMinimumValueofw(-100)
        total_cooling_capacity_curve.setMaximumValueofw(100)
        total_cooling_capacity_curve.setMinimumValueofx(-100)
        total_cooling_capacity_curve.setMaximumValueofx(100)
        total_cooling_capacity_curve.setMinimumValueofy(0)
        total_cooling_capacity_curve.setMaximumValueofy(100)
        total_cooling_capacity_curve.setMinimumValueofz(0)
        total_cooling_capacity_curve.setMaximumValueofz(100)
      end
      clg_coil.setTotalCoolingCapacityCurve(total_cooling_capacity_curve)

      if model.getCurveByName('Water to Air Heat Pump Sensible Cooling Capacity Curve').is_initialized
        sensible_cooling_capacity_curve = model.getCurveByName('Water to Air Heat Pump Sensible Cooling Capacity Curve').get
        sensible_cooling_capacity_curve = sensible_cooling_capacity_curve.to_CurveQuintLinear.get
      else
        sensible_cooling_capacity_curve = OpenStudio::Model::CurveQuintLinear.new(model)
        sensible_cooling_capacity_curve.setName('Water to Air Heat Pump Sensible Cooling Capacity Curve')
        sensible_cooling_capacity_curve.setCoefficient1Constant(6.0019444814887)
        sensible_cooling_capacity_curve.setCoefficient2v(22.6300677244073)
        sensible_cooling_capacity_curve.setCoefficient3w(-26.7960783730934)
        sensible_cooling_capacity_curve.setCoefficient4x(-1.72374720346819)
        sensible_cooling_capacity_curve.setCoefficient5y(0.490644802367817)
        sensible_cooling_capacity_curve.setCoefficient6z(0.0693119353468141)
        sensible_cooling_capacity_curve.setMinimumValueofw(-100)
        sensible_cooling_capacity_curve.setMaximumValueofw(100)
        sensible_cooling_capacity_curve.setMinimumValueofx(-100)
        sensible_cooling_capacity_curve.setMaximumValueofx(100)
        sensible_cooling_capacity_curve.setMinimumValueofy(0)
        sensible_cooling_capacity_curve.setMaximumValueofy(100)
        sensible_cooling_capacity_curve.setMinimumValueofz(0)
        sensible_cooling_capacity_curve.setMaximumValueofz(100)
      end
      clg_coil.setSensibleCoolingCapacityCurve(sensible_cooling_capacity_curve)

      if model.getCurveByName('Water to Air Heat Pump Cooling Power Consumption Curve').is_initialized
        cooling_power_consumption_curve = model.getCurveByName('Water to Air Heat Pump Cooling Power Consumption Curve').get
        cooling_power_consumption_curve = cooling_power_consumption_curve.to_CurveQuadLinear.get
      else
        cooling_power_consumption_curve = OpenStudio::Model::CurveQuadLinear.new(model)
        cooling_power_consumption_curve.setName('Water to Air Heat Pump Cooling Power Consumption Curve')
        cooling_power_consumption_curve.setCoefficient1Constant(-5.67775976415698)
        cooling_power_consumption_curve.setCoefficient2w(0.438988156976704)
        cooling_power_consumption_curve.setCoefficient3x(5.845277342193)
        cooling_power_consumption_curve.setCoefficient4y(0.141605667000125)
        cooling_power_consumption_curve.setCoefficient5z(-0.168727936032429)
        cooling_power_consumption_curve.setMinimumValueofw(-100)
        cooling_power_consumption_curve.setMaximumValueofw(100)
        cooling_power_consumption_curve.setMinimumValueofx(-100)
        cooling_power_consumption_curve.setMaximumValueofx(100)
        cooling_power_consumption_curve.setMinimumValueofy(0)
        cooling_power_consumption_curve.setMaximumValueofy(100)
        cooling_power_consumption_curve.setMinimumValueofz(0)
        cooling_power_consumption_curve.setMaximumValueofz(100)
      end
      clg_coil.setCoolingPowerConsumptionCurve(cooling_power_consumption_curve)
    end

    # part load fraction correlation curve added as a required curve in OS v3.7.0
    if model.version > OpenStudio::VersionString.new('3.6.1')
      if model.getCurveByName('Water to Air Heat Pump Part Load Fraction Correlation Curve').is_initialized
        part_load_correlation_curve = model.getCurveByName('Water to Air Heat Pump Part Load Fraction Correlation Curve').get
        part_load_correlation_curve = part_load_correlation_curve.to_CurveLinear.get
      else
        part_load_correlation_curve = OpenStudio::Model::CurveLinear.new(model)
        part_load_correlation_curve.setName('Water to Air Heat Pump Part Load Fraction Correlation Curve')
        part_load_correlation_curve.setCoefficient1Constant(0.833746458696111)
        part_load_correlation_curve.setCoefficient2x(0.166253541303889)
        part_load_correlation_curve.setMinimumValueofx(0)
        part_load_correlation_curve.setMaximumValueofx(1)
        part_load_correlation_curve.setMinimumCurveOutput(0)
        part_load_correlation_curve.setMaximumCurveOutput(1)
      end
      clg_coil.setPartLoadFractionCorrelationCurve(part_load_correlation_curve)
    end
  end

  return clg_coil
end

.create_coil_heating_dx_single_speed(model, air_loop_node: nil, name: '1spd DX Htg Coil', schedule: nil, type: nil, cop: 3.3, defrost_strategy: 'ReverseCycle') ⇒ OpenStudio::Model::CoilHeatingDXSingleSpeed

Create CoilHeatingDXSingleSpeed object Enters in default curves for coil by type of coil

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: '1spd DX Htg Coil')

    the name of the system, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • type (String) (defaults to: nil)

    the type of single speed DX coil to reference the correct curve set

  • cop (Double) (defaults to: 3.3)

    rated heating coefficient of performance

  • defrost_strategy (String) (defaults to: 'ReverseCycle')

    type of defrost strategy. options are reverse-cycle or resistive

Returns:

  • (OpenStudio::Model::CoilHeatingDXSingleSpeed)

    the DX heating coil



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/openstudio-standards/hvac/components/coil_heating_dx_single_speed.rb', line 18

def self.create_coil_heating_dx_single_speed(model,
                                             air_loop_node: nil,
                                             name: '1spd DX Htg Coil',
                                             schedule: nil,
                                             type: nil,
                                             cop: 3.3,
                                             defrost_strategy: 'ReverseCycle')
  htg_coil = OpenStudio::Model::CoilHeatingDXSingleSpeed.new(model)

  # add to air loop if specified
  htg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  htg_coil.setName(name)

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  htg_coil.setAvailabilitySchedule(coil_availability_schedule)

  # set coil cop
  if cop.nil?
    htg_coil.setRatedCOP(3.3)
  else
    htg_coil.setRatedCOP(cop)
  end

  htg_cap_f_of_temp = nil
  htg_cap_f_of_flow = nil
  htg_energy_input_ratio_f_of_temp = nil
  htg_energy_input_ratio_f_of_flow = nil
  htg_part_load_fraction = nil
  def_eir_f_of_temp = nil

  # curve sets
  case type
  when 'OS default'
    # use OS defaults
  when 'Residential Central Air Source HP'
    # Performance curves
    # These coefficients are in IP UNITS
    heat_cap_ft_coeffs_ip = [0.566333415, -0.000744164, -0.0000103, 0.009414634, 0.0000506, -0.00000675]
    heat_eir_ft_coeffs_ip = [0.718398423, 0.003498178, 0.000142202, -0.005724331, 0.00014085, -0.000215321]
    heat_cap_fflow_coeffs = [0.694045465, 0.474207981, -0.168253446]
    heat_eir_fflow_coeffs = [2.185418751, -1.942827919, 0.757409168]
    heat_plf_fplr_coeffs = [0.8, 0.2, 0]
    defrost_eir_coeffs = [0.1528, 0, 0, 0, 0, 0]

    # Convert coefficients from IP to SI
    heat_cap_ft_coeffs_si = OpenstudioStandards::HVAC.convert_curve_biquadratic(heat_cap_ft_coeffs_ip)
    heat_eir_ft_coeffs_si = OpenstudioStandards::HVAC.convert_curve_biquadratic(heat_eir_ft_coeffs_ip)

    htg_cap_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, heat_cap_ft_coeffs_si, name: 'Heat-Cap-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    htg_cap_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, heat_cap_fflow_coeffs, name: 'Heat-Cap-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    htg_energy_input_ratio_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, heat_eir_ft_coeffs_si, name: 'Heat-EIR-fT', min_x: 0, max_x: 100, min_y: 0, max_y: 100)
    htg_energy_input_ratio_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, heat_eir_fflow_coeffs, name: 'Heat-EIR-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    htg_part_load_fraction = OpenstudioStandards::HVAC.create_curve_quadratic(model, heat_plf_fplr_coeffs, name: 'Heat-PLF-fPLR', min_x: 0, max_x: 1, min_out: 0, max_out: 1, is_dimensionless: true)

    # Heating defrost curve for reverse cycle
    def_eir_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, defrost_eir_coeffs, name: 'DefrostEIR', min_x: -100, max_x: 100, min_y: -100, max_y: 100)
  when 'Residential Minisplit HP'
    # Performance curves
    # These coefficients are in SI UNITS
    heat_cap_ft_coeffs_si = [1.14715889038462, -0.010386676170938, 0, 0.00865384615384615, 0, 0]
    heat_eir_ft_coeffs_si = [0.9999941697687026, 0.004684593830254383, 5.901286675833333e-05, -0.0028624467783091973, 1.3041120194135802e-05, -0.00016172918478765433]
    heat_cap_fflow_coeffs = [1, 0, 0]
    heat_eir_fflow_coeffs = [1, 0, 0]
    heat_plf_fplr_coeffs = [0.89, 0.11, 0]
    defrost_eir_coeffs = [0.1528, 0, 0, 0, 0, 0]

    htg_cap_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, heat_cap_ft_coeffs_si, name: 'Heat-Cap-fT', min_x: -100, max_x: 100, min_y: -100, max_y: 100)
    htg_cap_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, heat_cap_fflow_coeffs, name: 'Heat-Cap-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    htg_energy_input_ratio_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, heat_eir_ft_coeffs_si, name: 'Heat-EIR-fT', min_x: -100, max_x: 100, min_y: -100, max_y: 100)
    htg_energy_input_ratio_f_of_flow = OpenstudioStandards::HVAC.create_curve_quadratic(model, heat_eir_fflow_coeffs, name: 'Heat-EIR-fFF', min_x: 0, max_x: 2, min_out: 0, max_out: 2, is_dimensionless: true)
    htg_part_load_fraction = OpenstudioStandards::HVAC.create_curve_quadratic(model, heat_plf_fplr_coeffs, name: 'Heat-PLF-fPLR', min_x: 0, max_x: 1, min_out: 0.6, max_out: 1, is_dimensionless: true)

    # Heating defrost curve for reverse cycle
    def_eir_f_of_temp = OpenstudioStandards::HVAC.create_curve_biquadratic(model, defrost_eir_coeffs, name: 'Defrost EIR', min_x: -100, max_x: 100, min_y: -100, max_y: 100)
  else # default curve set
    htg_cap_f_of_temp = OpenStudio::Model::CurveCubic.new(model)
    htg_cap_f_of_temp.setName("#{htg_coil.name} Htg Cap Func of Temp Curve")
    htg_cap_f_of_temp.setCoefficient1Constant(0.758746)
    htg_cap_f_of_temp.setCoefficient2x(0.027626)
    htg_cap_f_of_temp.setCoefficient3xPOW2(0.000148716)
    htg_cap_f_of_temp.setCoefficient4xPOW3(0.0000034992)
    htg_cap_f_of_temp.setMinimumValueofx(-20.0)
    htg_cap_f_of_temp.setMaximumValueofx(20.0)

    htg_cap_f_of_flow = OpenStudio::Model::CurveCubic.new(model)
    htg_cap_f_of_flow.setName("#{htg_coil.name} Htg Cap Func of Flow Frac Curve")
    htg_cap_f_of_flow.setCoefficient1Constant(0.84)
    htg_cap_f_of_flow.setCoefficient2x(0.16)
    htg_cap_f_of_flow.setCoefficient3xPOW2(0.0)
    htg_cap_f_of_flow.setCoefficient4xPOW3(0.0)
    htg_cap_f_of_flow.setMinimumValueofx(0.5)
    htg_cap_f_of_flow.setMaximumValueofx(1.5)

    htg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveCubic.new(model)
    htg_energy_input_ratio_f_of_temp.setName("#{htg_coil.name} EIR Func of Temp Curve")
    htg_energy_input_ratio_f_of_temp.setCoefficient1Constant(1.19248)
    htg_energy_input_ratio_f_of_temp.setCoefficient2x(-0.0300438)
    htg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(0.00103745)
    htg_energy_input_ratio_f_of_temp.setCoefficient4xPOW3(-0.000023328)
    htg_energy_input_ratio_f_of_temp.setMinimumValueofx(-20.0)
    htg_energy_input_ratio_f_of_temp.setMaximumValueofx(20.0)

    htg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
    htg_energy_input_ratio_f_of_flow.setName("#{htg_coil.name} EIR Func of Flow Frac Curve")
    htg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.3824)
    htg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.4336)
    htg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0512)
    htg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.0)
    htg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.0)

    htg_part_load_fraction = OpenStudio::Model::CurveQuadratic.new(model)
    htg_part_load_fraction.setName("#{htg_coil.name} PLR Correlation Curve")
    htg_part_load_fraction.setCoefficient1Constant(0.85)
    htg_part_load_fraction.setCoefficient2x(0.15)
    htg_part_load_fraction.setCoefficient3xPOW2(0.0)
    htg_part_load_fraction.setMinimumValueofx(0.0)
    htg_part_load_fraction.setMaximumValueofx(1.0)

    unless defrost_strategy == 'Resistive'
      def_eir_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
      def_eir_f_of_temp.setName("#{htg_coil.name} Defrost EIR Func of Temp Curve")
      def_eir_f_of_temp.setCoefficient1Constant(0.297145)
      def_eir_f_of_temp.setCoefficient2x(0.0430933)
      def_eir_f_of_temp.setCoefficient3xPOW2(-0.000748766)
      def_eir_f_of_temp.setCoefficient4y(0.00597727)
      def_eir_f_of_temp.setCoefficient5yPOW2(0.000482112)
      def_eir_f_of_temp.setCoefficient6xTIMESY(-0.000956448)
      def_eir_f_of_temp.setMinimumValueofx(-23.33333)
      def_eir_f_of_temp.setMaximumValueofx(29.44444)
      def_eir_f_of_temp.setMinimumValueofy(-23.33333)
      def_eir_f_of_temp.setMaximumValueofy(29.44444)
    end
  end

  if type == 'PSZ-AC'
    htg_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(-12.2)
    htg_coil.setMaximumOutdoorDryBulbTemperatureforDefrostOperation(1.67)
    htg_coil.setCrankcaseHeaterCapacity(50.0)
    htg_coil.setMaximumOutdoorDryBulbTemperatureforCrankcaseHeaterOperation(4.4)
    htg_coil.setDefrostControl('OnDemand')

    def_eir_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
    def_eir_f_of_temp.setName("#{htg_coil.name} Defrost EIR Func of Temp Curve")
    def_eir_f_of_temp.setCoefficient1Constant(0.297145)
    def_eir_f_of_temp.setCoefficient2x(0.0430933)
    def_eir_f_of_temp.setCoefficient3xPOW2(-0.000748766)
    def_eir_f_of_temp.setCoefficient4y(0.00597727)
    def_eir_f_of_temp.setCoefficient5yPOW2(0.000482112)
    def_eir_f_of_temp.setCoefficient6xTIMESY(-0.000956448)
    def_eir_f_of_temp.setMinimumValueofx(-23.33333)
    def_eir_f_of_temp.setMaximumValueofx(29.44444)
    def_eir_f_of_temp.setMinimumValueofy(-23.33333)
    def_eir_f_of_temp.setMaximumValueofy(29.44444)
  end

  htg_coil.setTotalHeatingCapacityFunctionofTemperatureCurve(htg_cap_f_of_temp) unless htg_cap_f_of_temp.nil?
  htg_coil.setTotalHeatingCapacityFunctionofFlowFractionCurve(htg_cap_f_of_flow) unless htg_cap_f_of_flow.nil?
  htg_coil.setEnergyInputRatioFunctionofTemperatureCurve(htg_energy_input_ratio_f_of_temp) unless htg_energy_input_ratio_f_of_temp.nil?
  htg_coil.setEnergyInputRatioFunctionofFlowFractionCurve(htg_energy_input_ratio_f_of_flow) unless htg_energy_input_ratio_f_of_flow.nil?
  htg_coil.setPartLoadFractionCorrelationCurve(htg_part_load_fraction) unless htg_part_load_fraction.nil?
  htg_coil.setDefrostEnergyInputRatioFunctionofTemperatureCurve(def_eir_f_of_temp) unless def_eir_f_of_temp.nil?
  htg_coil.setDefrostStrategy(defrost_strategy)
  htg_coil.setDefrostControl('OnDemand')

  return htg_coil
end

.create_coil_heating_electric(model, air_loop_node: nil, name: 'Electric Htg Coil', schedule: nil, nominal_capacity: nil, efficiency: 1.0) ⇒ OpenStudio::Model::CoilHeatingElectric

Create CoilHeatingElectric object

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: 'Electric Htg Coil')

    the name of the system, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • nominal_capacity (Double) (defaults to: nil)

    rated nominal capacity

  • efficiency (Double) (defaults to: 1.0)

    rated heating efficiency

Returns:

  • (OpenStudio::Model::CoilHeatingElectric)

    the electric heating coil



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/openstudio-standards/hvac/components/coil_heating_electric.rb', line 16

def self.create_coil_heating_electric(model,
                                      air_loop_node: nil,
                                      name: 'Electric Htg Coil',
                                      schedule: nil,
                                      nominal_capacity: nil,
                                      efficiency: 1.0)
  htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model)

  # add to air loop if specified
  htg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  htg_coil.setName(name)

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  htg_coil.setAvailabilitySchedule(coil_availability_schedule)

  # set capacity
  htg_coil.setNominalCapacity(nominal_capacity) unless nominal_capacity.nil?

  # set efficiency
  htg_coil.setEfficiency(efficiency) unless efficiency.nil?

  return htg_coil
end

.create_coil_heating_gas(model, air_loop_node: nil, name: 'Gas Htg Coil', schedule: nil, nominal_capacity: nil, efficiency: 0.80) ⇒ OpenStudio::Model::CoilHeatingGas

Create CoilHeatingGas object

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: 'Gas Htg Coil')

    the name of the system, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • nominal_capacity (Double) (defaults to: nil)

    rated nominal capacity

  • efficiency (Double) (defaults to: 0.80)

    rated heating efficiency

Returns:

  • (OpenStudio::Model::CoilHeatingGas)

    the gas heating coil



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/openstudio-standards/hvac/components/coil_heating_gas.rb', line 16

def self.create_coil_heating_gas(model,
                                 air_loop_node: nil,
                                 name: 'Gas Htg Coil',
                                 schedule: nil,
                                 nominal_capacity: nil,
                                 efficiency: 0.80)
  htg_coil = OpenStudio::Model::CoilHeatingGas.new(model)

  # add to air loop if specified
  htg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  htg_coil.setName(name)

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  htg_coil.setAvailabilitySchedule(coil_availability_schedule)

  # set capacity
  htg_coil.setNominalCapacity(nominal_capacity) unless nominal_capacity.nil?

  # set efficiency
  htg_coil.setGasBurnerEfficiency(efficiency)

  # defaults
  if model.version < OpenStudio::VersionString.new('3.7.0')
    htg_coil.setParasiticElectricLoad(0.0)
    htg_coil.setParasiticGasLoad(0.0)
  else
    htg_coil.setOnCycleParasiticElectricLoad(0.0)
    htg_coil.setOffCycleParasiticGasLoad(0.0)
  end

  return htg_coil
end

.create_coil_heating_water(model, hot_water_loop, air_loop_node: nil, name: 'Htg Coil', schedule: nil, rated_inlet_water_temperature: nil, rated_outlet_water_temperature: nil, rated_inlet_air_temperature: 16.6, rated_outlet_air_temperature: 32.2, controller_convergence_tolerance: 0.1) ⇒ OpenStudio::Model::CoilHeatingWater

Create CoilHeatingWater object

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • hot_water_loop (<OpenStudio::Model::PlantLoop>)

    the coil will be placed on the demand side of this plant loop

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: 'Htg Coil')

    the name of the coil, or nil in which case it will be defaulted

  • schedule (String) (defaults to: nil)

    name of the availability schedule, or [<OpenStudio::Model::Schedule>] Schedule object, or nil in which case default to always on

  • rated_inlet_water_temperature (Double) (defaults to: nil)

    rated inlet water temperature in degrees Celsius, default is hot water loop design exit temperature

  • rated_outlet_water_temperature (Double) (defaults to: nil)

    rated outlet water temperature in degrees Celsius, default is hot water loop design return temperature

  • rated_inlet_air_temperature (Double) (defaults to: 16.6)

    rated inlet air temperature in degrees Celsius, default is 16.6 (62F)

  • rated_outlet_air_temperature (Double) (defaults to: 32.2)

    rated outlet air temperature in degrees Celsius, default is 32.2 (90F)

  • controller_convergence_tolerance (Double) (defaults to: 0.1)

    controller convergence tolerance

Returns:

  • (OpenStudio::Model::CoilHeatingWater)

    the heating coil



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/openstudio-standards/hvac/components/coil_heating_water.rb', line 20

def self.create_coil_heating_water(model,
                                   hot_water_loop,
                                   air_loop_node: nil,
                                   name: 'Htg Coil',
                                   schedule: nil,
                                   rated_inlet_water_temperature: nil,
                                   rated_outlet_water_temperature: nil,
                                   rated_inlet_air_temperature: 16.6,
                                   rated_outlet_air_temperature: 32.2,
                                   controller_convergence_tolerance: 0.1)
  htg_coil = OpenStudio::Model::CoilHeatingWater.new(model)

  # add to hot water loop
  hot_water_loop.addDemandBranchForComponent(htg_coil)

  # add to air loop if specified
  htg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  if name.nil?
    htg_coil.setName('Htg Coil')
  else
    htg_coil.setName(name)
  end

  # set coil availability schedule
  if schedule.nil?
    # default always on
    coil_availability_schedule = model.alwaysOnDiscreteSchedule
  elsif schedule.instance_of?(String)
    coil_availability_schedule = model_add_schedule(model, schedule)

    if coil_availability_schedule.nil? && schedule == 'alwaysOffDiscreteSchedule'
      coil_availability_schedule = model.alwaysOffDiscreteSchedule
    elsif coil_availability_schedule.nil?
      coil_availability_schedule = model.alwaysOnDiscreteSchedule
    end
  elsif !schedule.to_Schedule.empty?
    coil_availability_schedule = schedule
  end
  htg_coil.setAvailabilitySchedule(coil_availability_schedule)

  # rated water temperatures, use hot water loop temperatures if defined
  if rated_inlet_water_temperature.nil?
    rated_inlet_water_temperature = hot_water_loop.sizingPlant.designLoopExitTemperature
    htg_coil.setRatedInletWaterTemperature(rated_inlet_water_temperature)
  else
    htg_coil.setRatedInletWaterTemperature(rated_inlet_water_temperature)
  end
  if rated_outlet_water_temperature.nil?
    rated_outlet_water_temperature = rated_inlet_water_temperature - hot_water_loop.sizingPlant.loopDesignTemperatureDifference
    htg_coil.setRatedOutletWaterTemperature(rated_outlet_water_temperature)
  else
    htg_coil.setRatedOutletWaterTemperature(rated_outlet_water_temperature)
  end

  # rated air temperatures
  if rated_inlet_air_temperature.nil?
    htg_coil.setRatedInletAirTemperature(16.6)
  else
    htg_coil.setRatedInletAirTemperature(rated_inlet_air_temperature)
  end
  if rated_outlet_air_temperature.nil?
    htg_coil.setRatedOutletAirTemperature(32.2)
  else
    htg_coil.setRatedOutletAirTemperature(rated_outlet_air_temperature)
  end

  # coil controller properties
  # @note These inputs will get overwritten if addToNode or addDemandBranchForComponent is called on the htg_coil object after this
  htg_coil_controller = htg_coil.controllerWaterCoil.get
  htg_coil_controller.setName("#{htg_coil.name} Controller")
  htg_coil_controller.setMinimumActuatedFlow(0.0)
  htg_coil_controller.setControllerConvergenceTolerance(controller_convergence_tolerance) unless controller_convergence_tolerance.nil?

  return htg_coil
end

.create_coil_heating_water_to_air_heat_pump_equation_fit(model, plant_loop, air_loop_node: nil, name: 'Water-to-Air HP Htg Coil', type: nil, cop: 4.2) ⇒ OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit

Create CoilHeatingWaterToAirHeatPumpEquationFit object Enters in default curves for coil by type of coil

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • plant_loop (<OpenStudio::Model::PlantLoop>)

    the coil will be placed on the demand side of this plant loop

  • air_loop_node (<OpenStudio::Model::Node>) (defaults to: nil)

    the coil will be placed on this node of the air loop

  • name (String) (defaults to: 'Water-to-Air HP Htg Coil')

    the name of the system, or nil in which case it will be defaulted

  • type (String) (defaults to: nil)

    the type of coil to reference the correct curve set

  • cop (Double) (defaults to: 4.2)

    rated heating coefficient of performance

Returns:

  • (OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit)

    the heating coil



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/openstudio-standards/hvac/components/coil_heating_water_to_air_heat_pump_equation_fit.rb', line 17

def self.create_coil_heating_water_to_air_heat_pump_equation_fit(model,
                                                                 plant_loop,
                                                                 air_loop_node: nil,
                                                                 name: 'Water-to-Air HP Htg Coil',
                                                                 type: nil,
                                                                 cop: 4.2)
  htg_coil = OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.new(model)

  # add to air loop if specified
  htg_coil.addToNode(air_loop_node) unless air_loop_node.nil?

  # set coil name
  htg_coil.setName(name)

  # add to plant loop
  if plant_loop.nil?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', 'No plant loop supplied for heating coil')
    return false
  end
  plant_loop.addDemandBranchForComponent(htg_coil)

  # set coil cop
  if cop.nil?
    htg_coil.setRatedHeatingCoefficientofPerformance(4.2)
  else
    htg_coil.setRatedHeatingCoefficientofPerformance(cop)
  end

  # curve sets
  if type == 'OS default'
    # use OS default curves
  else # default curve set
    if model.version < OpenStudio::VersionString.new('3.2.0')
      htg_coil.setHeatingCapacityCoefficient1(0.237847462869254)
      htg_coil.setHeatingCapacityCoefficient2(-3.35823796081626)
      htg_coil.setHeatingCapacityCoefficient3(3.80640467406376)
      htg_coil.setHeatingCapacityCoefficient4(0.179200417311554)
      htg_coil.setHeatingCapacityCoefficient5(0.12860719846082)
      htg_coil.setHeatingPowerConsumptionCoefficient1(-3.79175529243238)
      htg_coil.setHeatingPowerConsumptionCoefficient2(3.38799239505527)
      htg_coil.setHeatingPowerConsumptionCoefficient3(1.5022612076303)
      htg_coil.setHeatingPowerConsumptionCoefficient4(-0.177653510577989)
      htg_coil.setHeatingPowerConsumptionCoefficient5(-0.103079864171839)
    else
      if model.getCurveByName('Water to Air Heat Pump Heating Capacity Curve').is_initialized
        heating_capacity_curve = model.getCurveByName('Water to Air Heat Pump Heating Capacity Curve').get
        heating_capacity_curve = heating_capacity_curve.to_CurveQuadLinear.get
      else
        heating_capacity_curve = OpenStudio::Model::CurveQuadLinear.new(model)
        heating_capacity_curve.setName('Water to Air Heat Pump Heating Capacity Curve')
        heating_capacity_curve.setCoefficient1Constant(0.237847462869254)
        heating_capacity_curve.setCoefficient2w(-3.35823796081626)
        heating_capacity_curve.setCoefficient3x(3.80640467406376)
        heating_capacity_curve.setCoefficient4y(0.179200417311554)
        heating_capacity_curve.setCoefficient5z(0.12860719846082)
        heating_capacity_curve.setMinimumValueofw(-100)
        heating_capacity_curve.setMaximumValueofw(100)
        heating_capacity_curve.setMinimumValueofx(-100)
        heating_capacity_curve.setMaximumValueofx(100)
        heating_capacity_curve.setMinimumValueofy(0)
        heating_capacity_curve.setMaximumValueofy(100)
        heating_capacity_curve.setMinimumValueofz(0)
        heating_capacity_curve.setMaximumValueofz(100)
      end
      htg_coil.setHeatingCapacityCurve(heating_capacity_curve)

      if model.getCurveByName('Water to Air Heat Pump Heating Power Consumption Curve').is_initialized
        heating_power_consumption_curve = model.getCurveByName('Water to Air Heat Pump Heating Power Consumption Curve').get
        heating_power_consumption_curve = heating_power_consumption_curve.to_CurveQuadLinear.get
      else
        heating_power_consumption_curve = OpenStudio::Model::CurveQuadLinear.new(model)
        heating_power_consumption_curve.setName('Water to Air Heat Pump Heating Power Consumption Curve')
        heating_power_consumption_curve.setCoefficient1Constant(-3.79175529243238)
        heating_power_consumption_curve.setCoefficient2w(3.38799239505527)
        heating_power_consumption_curve.setCoefficient3x(1.5022612076303)
        heating_power_consumption_curve.setCoefficient4y(-0.177653510577989)
        heating_power_consumption_curve.setCoefficient5z(-0.103079864171839)
        heating_power_consumption_curve.setMinimumValueofw(-100)
        heating_power_consumption_curve.setMaximumValueofw(100)
        heating_power_consumption_curve.setMinimumValueofx(-100)
        heating_power_consumption_curve.setMaximumValueofx(100)
        heating_power_consumption_curve.setMinimumValueofy(0)
        heating_power_consumption_curve.setMaximumValueofy(100)
        heating_power_consumption_curve.setMinimumValueofz(0)
        heating_power_consumption_curve.setMaximumValueofz(100)
      end
      htg_coil.setHeatingPowerConsumptionCurve(heating_power_consumption_curve)
    end

    # part load fraction correlation curve added as a required curve in OS v3.7.0
    if model.version > OpenStudio::VersionString.new('3.6.1')
      if model.getCurveByName('Water to Air Heat Pump Part Load Fraction Correlation Curve').is_initialized
        part_load_correlation_curve = model.getCurveByName('Water to Air Heat Pump Part Load Fraction Correlation Curve').get
        part_load_correlation_curve = part_load_correlation_curve.to_CurveLinear.get
      else
        part_load_correlation_curve = OpenStudio::Model::CurveLinear.new(model)
        part_load_correlation_curve.setName('Water to Air Heat Pump Part Load Fraction Correlation Curve')
        part_load_correlation_curve.setCoefficient1Constant(0.833746458696111)
        part_load_correlation_curve.setCoefficient2x(0.166253541303889)
        part_load_correlation_curve.setMinimumValueofx(0)
        part_load_correlation_curve.setMaximumValueofx(1)
        part_load_correlation_curve.setMinimumCurveOutput(0)
        part_load_correlation_curve.setMaximumCurveOutput(1)
      end
      htg_coil.setPartLoadFractionCorrelationCurve(part_load_correlation_curve)
    end
  end

  return htg_coil
end

.create_curve_bicubic(model, coeffs, name: 'CurveBicubic', min_x: nil, max_x: nil, min_y: nil, max_y: nil, min_out: nil, max_out: nil) ⇒ OpenStudio::Model::CurveBicubic

Create a bicubic curve of the form z = C1 + C2*x + C3*x^2 + C4*y + C5*y^2 + C6*x*y + C7*x^3 + C8*y^3 + C9*x^2*y + C10*x*y^2

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • coeffs (Array<Double>)

    an array of 10 coefficients, in order

  • name (String) (defaults to: 'CurveBicubic')

    the name of the curve

  • min_x (Double) (defaults to: nil)

    the minimum value of independent variable X that will be used

  • max_x (Double) (defaults to: nil)

    the maximum value of independent variable X that will be used

  • min_y (Double) (defaults to: nil)

    the minimum value of independent variable Y that will be used

  • max_y (Double) (defaults to: nil)

    the maximum value of independent variable Y that will be used

  • min_out (Double) (defaults to: nil)

    the minimum value of dependent variable Z

  • max_out (Double) (defaults to: nil)

    the maximum value of dependent variable Z

Returns:

  • (OpenStudio::Model::CurveBicubic)

    a bicubic curve

Author:

  • Scott Horowitz, NREL



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/openstudio-standards/hvac/curves.rb', line 88

def self.create_curve_bicubic(model, coeffs, name: 'CurveBicubic', min_x: nil, max_x: nil, min_y: nil, max_y: nil, min_out: nil, max_out: nil)
  curve = OpenStudio::Model::CurveBicubic.new(model)
  curve.setName(name)
  curve.setCoefficient1Constant(coeffs[0])
  curve.setCoefficient2x(coeffs[1])
  curve.setCoefficient3xPOW2(coeffs[2])
  curve.setCoefficient4y(coeffs[3])
  curve.setCoefficient5yPOW2(coeffs[4])
  curve.setCoefficient6xTIMESY(coeffs[5])
  curve.setCoefficient7xPOW3(coeffs[6])
  curve.setCoefficient8yPOW3(coeffs[7])
  curve.setCoefficient9xPOW2TIMESY(coeffs[8])
  curve.setCoefficient10xTIMESYPOW2(coeffs[9])
  curve.setMinimumValueofx(min_x) unless min_x.nil?
  curve.setMaximumValueofx(max_x) unless max_x.nil?
  curve.setMinimumValueofy(min_y) unless min_y.nil?
  curve.setMaximumValueofy(max_y) unless max_y.nil?
  curve.setMinimumCurveOutput(min_out) unless min_out.nil?
  curve.setMaximumCurveOutput(max_out) unless max_out.nil?
  return curve
end

.create_curve_biquadratic(model, coeffs, name: 'CurveBiquadratic', min_x: nil, max_x: nil, min_y: nil, max_y: nil, min_out: nil, max_out: nil) ⇒ OpenStudio::Model::CurveBiquadratic

Create a biquadratic curve of the form z = C1 + C2*x + C3*x^2 + C4*y + C5*y^2 + C6*x*y

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • coeffs (Array<Double>)

    an array of 6 coefficients, in order

  • name (String) (defaults to: 'CurveBiquadratic')

    the name of the curve

  • min_x (Double) (defaults to: nil)

    the minimum value of independent variable X that will be used

  • max_x (Double) (defaults to: nil)

    the maximum value of independent variable X that will be used

  • min_y (Double) (defaults to: nil)

    the minimum value of independent variable Y that will be used

  • max_y (Double) (defaults to: nil)

    the maximum value of independent variable Y that will be used

  • min_out (Double) (defaults to: nil)

    the minimum value of dependent variable Z

  • max_out (Double) (defaults to: nil)

    the maximum value of dependent variable Z

Returns:

  • (OpenStudio::Model::CurveBiquadratic)

    a biquadratic curve

Author:

  • Scott Horowitz, NREL



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/openstudio-standards/hvac/curves.rb', line 56

def self.create_curve_biquadratic(model, coeffs, name: 'CurveBiquadratic', min_x: nil, max_x: nil, min_y: nil, max_y: nil, min_out: nil, max_out: nil)
  curve = OpenStudio::Model::CurveBiquadratic.new(model)
  curve.setName(name)
  curve.setCoefficient1Constant(coeffs[0])
  curve.setCoefficient2x(coeffs[1])
  curve.setCoefficient3xPOW2(coeffs[2])
  curve.setCoefficient4y(coeffs[3])
  curve.setCoefficient5yPOW2(coeffs[4])
  curve.setCoefficient6xTIMESY(coeffs[5])
  curve.setMinimumValueofx(min_x) unless min_x.nil?
  curve.setMaximumValueofx(max_x) unless max_x.nil?
  curve.setMinimumValueofy(min_y) unless min_y.nil?
  curve.setMaximumValueofy(max_y) unless max_y.nil?
  curve.setMinimumCurveOutput(min_out) unless min_out.nil?
  curve.setMaximumCurveOutput(max_out) unless max_out.nil?
  return curve
end

.create_curve_cubic(model, coeffs, name: 'CurveCubic', min_x: nil, max_x: nil, min_out: nil, max_out: nil) ⇒ OpenStudio::Model::CurveCubic

Create a cubic curve of the form z = C1 + C2*x + C3*x^2 + C4*x^3

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • coeffs (Array<Double>)

    an array of 4 coefficients, in order

  • name (String) (defaults to: 'CurveCubic')

    the name of the curve

  • min_x (Double) (defaults to: nil)

    the minimum value of independent variable X that will be used

  • max_x (Double) (defaults to: nil)

    the maximum value of independent variable X that will be used

  • min_out (Double) (defaults to: nil)

    the minimum value of dependent variable Z

  • max_out (Double) (defaults to: nil)

    the maximum value of dependent variable Z

Returns:

  • (OpenStudio::Model::CurveCubic)

    a cubic curve

Author:

  • Scott Horowitz, NREL



153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/openstudio-standards/hvac/curves.rb', line 153

def self.create_curve_cubic(model, coeffs, name: 'CurveCubic', min_x: nil, max_x: nil, min_out: nil, max_out: nil)
  curve = OpenStudio::Model::CurveCubic.new(model)
  curve.setName(name)
  curve.setCoefficient1Constant(coeffs[0])
  curve.setCoefficient2x(coeffs[1])
  curve.setCoefficient3xPOW2(coeffs[2])
  curve.setCoefficient4xPOW3(coeffs[3])
  curve.setMinimumValueofx(min_x) unless min_x.nil?
  curve.setMaximumValueofx(max_x) unless max_x.nil?
  curve.setMinimumCurveOutput(min_out) unless min_out.nil?
  curve.setMaximumCurveOutput(max_out) unless max_out.nil?
  return curve
end

.create_curve_exponent(model, coeffs, name: 'CurveExponent', min_x: nil, max_x: nil, min_out: nil, max_out: nil) ⇒ OpenStudio::Model::CurveExponent

Create an exponential curve of the form z = C1 + C2*x^C3

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • coeffs (Array<Double>)

    an array of 3 coefficients, in order

  • name (String) (defaults to: 'CurveExponent')

    the name of the curve

  • min_x (Double) (defaults to: nil)

    the minimum value of independent variable X that will be used

  • max_x (Double) (defaults to: nil)

    the maximum value of independent variable X that will be used

  • min_out (Double) (defaults to: nil)

    the minimum value of dependent variable Z

  • max_out (Double) (defaults to: nil)

    the maximum value of dependent variable Z

Returns:

  • (OpenStudio::Model::CurveExponent)

    an exponent curve

Author:

  • Scott Horowitz, NREL



179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/openstudio-standards/hvac/curves.rb', line 179

def self.create_curve_exponent(model, coeffs, name: 'CurveExponent', min_x: nil, max_x: nil, min_out: nil, max_out: nil)
  curve = OpenStudio::Model::CurveExponent.new(model)
  curve.setName(name)
  curve.setCoefficient1Constant(coeffs[0])
  curve.setCoefficient2Constant(coeffs[1])
  curve.setCoefficient3Constant(coeffs[2])
  curve.setMinimumValueofx(min_x) unless min_x.nil?
  curve.setMaximumValueofx(max_x) unless max_x.nil?
  curve.setMinimumCurveOutput(min_out) unless min_out.nil?
  curve.setMaximumCurveOutput(max_out) unless max_out.nil?
  return curve
end

.create_curve_quadratic(model, coeffs, name: 'CurveQuadratic', min_x: nil, max_x: nil, min_out: nil, max_out: nil, is_dimensionless: false) ⇒ OpenStudio::Model::CurveQuadratic

Create a quadratic curve of the form z = C1 + C2*x + C3*x^2

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • coeffs (Array<Double>)

    an array of 3 coefficients, in order

  • name (String) (defaults to: 'CurveQuadratic')

    the name of the curve

  • min_x (Double) (defaults to: nil)

    the minimum value of independent variable X that will be used

  • max_x (Double) (defaults to: nil)

    the maximum value of independent variable X that will be used

  • min_out (Double) (defaults to: nil)

    the minimum value of dependent variable Z

  • max_out (Double) (defaults to: nil)

    the maximum value of dependent variable Z

  • is_dimensionless (Boolean) (defaults to: false)

    if true, the X independent variable is considered unitless and the resulting output dependent variable is considered unitless

Returns:

  • (OpenStudio::Model::CurveQuadratic)

    a quadratic curve

Author:

  • Scott Horowitz, NREL



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/openstudio-standards/hvac/curves.rb', line 124

def self.create_curve_quadratic(model, coeffs, name: 'CurveQuadratic', min_x: nil, max_x: nil, min_out: nil, max_out: nil, is_dimensionless: false)
  curve = OpenStudio::Model::CurveQuadratic.new(model)
  curve.setName(name)
  curve.setCoefficient1Constant(coeffs[0])
  curve.setCoefficient2x(coeffs[1])
  curve.setCoefficient3xPOW2(coeffs[2])
  curve.setMinimumValueofx(min_x) unless min_x.nil?
  curve.setMaximumValueofx(max_x) unless max_x.nil?
  curve.setMinimumCurveOutput(min_out) unless min_out.nil?
  curve.setMaximumCurveOutput(max_out) unless max_out.nil?
  if is_dimensionless
    curve.setInputUnitTypeforX('Dimensionless')
    curve.setOutputUnitType('Dimensionless')
  end
  return curve
end

.create_exhaust_fan(exhaust_zone, make_up_air_source_zone: nil, make_up_air_fraction: 0.5) ⇒ OpenStudio::Model::FanZoneExhaust

Create and add an exhaust fan to a thermal zone.

Parameters:

  • exhaust_zone (OpenStudio::Model::ThermalZone)

    The zone with the exhaust fan

  • make_up_air_source_zone (OpenStudio::Model::ThermalZone) (defaults to: nil)

    An optional source zone for make-up air

  • make_up_air_fraction (Double) (defaults to: 0.5)

    The fraction of make-up sourced from make_up_air_source_zone

Returns:

  • (OpenStudio::Model::FanZoneExhaust)

    The created exhaust fan



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/openstudio-standards/hvac/exhaust/create_exhaust_fan.rb', line 13

def self.create_exhaust_fan(exhaust_zone,
                            make_up_air_source_zone: nil,
                            make_up_air_fraction: 0.5)
  # load exhaust fan data
  data = JSON.parse(File.read("#{File.dirname(__FILE__)}/data/typical_exhaust.json"), symbolize_names: true)

  # loop through spaces to get standards space information
  space_type_hash = {}
  exhaust_zone.spaces.each do |space|
    next unless space.spaceType.is_initialized
    next unless space.partofTotalFloorArea

    space_type = space.spaceType.get
    if space_type_hash.key?(space_type)
      space_type_hash[space_type][:floor_area_m2] += space.floorArea * space.multiplier
    else
      next unless space_type.standardsBuildingType.is_initialized
      next unless space_type.standardsSpaceType.is_initialized


      standards_space_type = space_type.standardsSpaceType.get
      standards_building_type = space_type.standardsBuildingType.get

      exhaust_fan_properties = data[:space_types].select { |hash| (hash[:space_type] == standards_space_type) && (hash[:building_type] == standards_building_type) }

      # skip spaces with no exhaust fan information defined
      next if exhaust_fan_properties.empty?

      exhaust_fan_properties = exhaust_fan_properties[0]

      space_type_hash[space_type] = {}
      space_type_hash[space_type][:floor_area_m2] = space.floorArea * space.multiplier
      space_type_hash[space_type][:exhaust_cfm_per_area_ft2] = exhaust_fan_properties[:exhaust_per_area]
    end
  end

  # total exhaust
  exhaust_m3_per_s = 0.0
  space_type_hash.each do |space_type, fields|
    floor_area_ft2 = OpenStudio.convert(fields[:floor_area_m2], 'm^2', 'ft^2').get
    cfm = fields[:exhaust_cfm_per_area_ft2].to_f * floor_area_ft2.to_f
    exhaust_m3_per_s += OpenStudio.convert(cfm, 'cfm', 'm^3/s').get
  end

  if exhaust_m3_per_s.zero?
    OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.HVAC.create_exhaust_fan', "Calculated zero flow rate for thermal zone #{exhaust_zone.name}. No exhaust fan added.")
    return nil
  end

  # placeholders for exhaust schedules
  # @todo get the building HVAC schedule
  exhaust_availability_schedule = exhaust_zone.model.alwaysOnDiscreteSchedule
  exhaust_flow_fraction_schedule = exhaust_zone.model.alwaysOnDiscreteSchedule

  # add exhaust fan
  zone_exhaust_fan = OpenStudio::Model::FanZoneExhaust.new(exhaust_zone.model)
  zone_exhaust_fan.setName("#{exhaust_zone.name} Exhaust Fan")
  zone_exhaust_fan.setAvailabilitySchedule(exhaust_availability_schedule)
  zone_exhaust_fan.setFlowFractionSchedule(exhaust_flow_fraction_schedule)
  zone_exhaust_fan.setMaximumFlowRate(exhaust_m3_per_s)
  zone_exhaust_fan.setEndUseSubcategory('Zone Exhaust Fans')
  zone_exhaust_fan.addToThermalZone(exhaust_zone)

  # add objects to account for makeup air
  unless make_up_air_source_zone.nil?
    # add balanced exhaust schedule to zone_exhaust_fan
    balanced_exhaust_schedule = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(make_up_air_source_zone.model, make_up_air_fraction,
                                                                                                name: "#{exhaust_zone.name} Balanced Exhaust Fraction Schedule",
                                                                                                schedule_type_limit: 'Fraction')
    zone_exhaust_fan.setBalancedExhaustFractionSchedule(balanced_exhaust_schedule)

    # use max value of balanced exhaust fraction schedule for maximum flow rate
    max_sch_val = OpenstudioStandards::Schedules.schedule_get_min_max(balanced_exhaust_schedule)['max']
    transfer_air_m3_per_s = exhaust_m3_per_s * max_sch_val

    # add dummy exhaust fan to account for loss of transfer air
    transfer_air_source_zone_exhaust = OpenStudio::Model::FanZoneExhaust.new(exhaust_zone.model)
    transfer_air_source_zone_exhaust.setName("#{exhaust_zone.name} Transfer Air Source")
    transfer_air_source_zone_exhaust.setAvailabilitySchedule(exhaust_availability_schedule)
    transfer_air_source_zone_exhaust.setMaximumFlowRate(transfer_air_m3_per_s)
    transfer_air_source_zone_exhaust.setFanEfficiency(1.0)
    transfer_air_source_zone_exhaust.setPressureRise(0.0)
    transfer_air_source_zone_exhaust.setEndUseSubcategory('Zone Exhaust Fans')
    transfer_air_source_zone_exhaust.addToThermalZone(make_up_air_source_zone)

    # add zone mixing
    zone_mixing = OpenStudio::Model::ZoneMixing.new(exhaust_zone)
    zone_mixing.setSchedule(exhaust_flow_fraction_schedule)
    zone_mixing.setSourceZone(make_up_air_source_zone)
    zone_mixing.setDesignFlowRate(transfer_air_m3_per_s)
  end

  return zone_exhaust_fan
end

.create_fan_constant_volume(model, fan_name: nil, fan_efficiency: nil, pressure_rise: nil, motor_efficiency: nil, motor_in_airstream_fraction: nil, end_use_subcategory: nil) ⇒ OpenStudio::Model::FanConstantVolume

creates a constant volume fan

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • fan_name (String) (defaults to: nil)

    fan name

  • fan_efficiency (Double) (defaults to: nil)

    fan efficiency

  • pressure_rise (Double) (defaults to: nil)

    fan pressure rise in Pa

  • motor_efficiency (Double) (defaults to: nil)

    fan motor efficiency

  • motor_in_airstream_fraction (Double) (defaults to: nil)

    fraction of motor heat in airstream

  • end_use_subcategory (String) (defaults to: nil)

    end use subcategory name

Returns:

  • (OpenStudio::Model::FanConstantVolume)

    constant volume fan object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 45

def self.create_fan_constant_volume(model,
                                    fan_name: nil,
                                    fan_efficiency: nil,
                                    pressure_rise: nil,
                                    motor_efficiency: nil,
                                    motor_in_airstream_fraction: nil,
                                    end_use_subcategory: nil)
  fan = OpenStudio::Model::FanConstantVolume.new(model)
  fan.setName(fan_name) unless fan_name.nil?
  fan.setFanEfficiency(fan_efficiency) unless fan_efficiency.nil?
  fan.setPressureRise(pressure_rise) unless pressure_rise.nil?
  fan.setEndUseSubcategory(end_use_subcategory) unless end_use_subcategory.nil?
  fan.setMotorEfficiency(motor_efficiency) unless motor_efficiency.nil?
  fan.setMotorInAirstreamFraction(motor_in_airstream_fraction) unless motor_in_airstream_fraction.nil?

  return fan
end

.create_fan_on_off(model, fan_name: nil, fan_efficiency: nil, pressure_rise: nil, motor_efficiency: nil, motor_in_airstream_fraction: nil, end_use_subcategory: nil) ⇒ OpenStudio::Model::FanOnOff

creates an on off fan

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • fan_name (String) (defaults to: nil)

    fan name

  • fan_efficiency (Double) (defaults to: nil)

    fan efficiency

  • pressure_rise (Double) (defaults to: nil)

    fan pressure rise in Pa

  • motor_efficiency (Double) (defaults to: nil)

    fan motor efficiency

  • motor_in_airstream_fraction (Double) (defaults to: nil)

    fraction of motor heat in airstream

  • end_use_subcategory (String) (defaults to: nil)

    end use subcategory name

Returns:

  • (OpenStudio::Model::FanOnOff)

    on off fan object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 17

def self.create_fan_on_off(model,
                           fan_name: nil,
                           fan_efficiency: nil,
                           pressure_rise: nil,
                           motor_efficiency: nil,
                           motor_in_airstream_fraction: nil,
                           end_use_subcategory: nil)
  fan = OpenStudio::Model::FanOnOff.new(model)
  fan.setName(fan_name) unless fan_name.nil?
  fan.setFanEfficiency(fan_efficiency) unless fan_efficiency.nil?
  fan.setPressureRise(pressure_rise) unless pressure_rise.nil?
  fan.setMotorEfficiency(motor_efficiency) unless motor_efficiency.nil?
  fan.setMotorInAirstreamFraction(motor_in_airstream_fraction) unless motor_in_airstream_fraction.nil?
  fan.setEndUseSubcategory(end_use_subcategory) unless end_use_subcategory.nil?

  return fan
end

.create_fan_system_model(model, fan_name: nil, pressure_rise: nil, motor_efficiency: nil, motor_in_airstream_fraction: nil, end_use_subcategory: nil, design_power_sizing_method: 'PowerPerFlowPerPressure', power_per_flow_rate: nil, power_per_flow_rate_per_pressure: 1.66667, speed_control_method: 'Continuous', speeds: nil) ⇒ OpenStudio::Model::FanSystemModel

creates a fan system model

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • fan_name (String) (defaults to: nil)

    fan name

  • pressure_rise (Double) (defaults to: nil)

    fan pressure rise in Pa

  • motor_efficiency (Double) (defaults to: nil)

    fan motor efficiency

  • motor_in_airstream_fraction (Double) (defaults to: nil)

    fraction of motor heat in airstream

  • end_use_subcategory (String) (defaults to: nil)

    end use subcategory name

  • design_power_sizing_method (String) (defaults to: 'PowerPerFlowPerPressure')

    design power sizing method, options are PowerPerFlow or PowerPerFlowPerPressure

  • power_per_flow_rate (Double) (defaults to: nil)

    power per flow rate in W/(m^3/s)

  • power_per_flow_rate_per_pressure (Double) (defaults to: 1.66667)

    power per flow rate per pressure in W/(m^3/s)/Pa

  • speed_control_method (String) (defaults to: 'Continuous')

    speed control method, options are Discrete or Continuous

  • speeds (Array<[Integer, Integer]>) (defaults to: nil)

    array of pairs of air flow rate fraction and electric power fraction for discrete speed fans

Returns:

  • (OpenStudio::Model::FanSystemModel)

    fan system model object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 154

def self.create_fan_system_model(model,
                                 fan_name: nil,
                                 pressure_rise: nil,
                                 motor_efficiency: nil,
                                 motor_in_airstream_fraction: nil,
                                 end_use_subcategory: nil,
                                 design_power_sizing_method: 'PowerPerFlowPerPressure',
                                 power_per_flow_rate: nil,
                                 power_per_flow_rate_per_pressure: 1.66667,
                                 speed_control_method: 'Continuous',
                                 speeds: nil)
  fan = OpenStudio::Model::FanSystemModel.new(model)
  fan.setName(fan_name) unless fan_name.nil?
  fan.setDesignPressureRise(pressure_rise) unless pressure_rise.nil?
  fan.setMotorEfficiency(motor_efficiency) unless motor_efficiency.nil?
  fan.setMotorInAirStreamFraction(motor_in_airstream_fraction) unless motor_in_airstream_fraction.nil?
  fan.setEndUseSubcategory(end_use_subcategory) unless end_use_subcategory.nil?
  fan.setDesignPowerSizingMethod(design_power_sizing_method) unless design_power_sizing_method.nil?
  fan.setElectricPowerPerUnitFlowRate(power_per_flow_rate) unless power_per_flow_rate.nil?
  fan.setElectricPowerPerUnitFlowRatePerUnitPressure(power_per_flow_rate_per_pressure) unless power_per_flow_rate_per_pressure.nil?
  fan.setSpeedControlMethod(speed_control_method) unless speed_control_method.nil?
  if !speeds.nil? && (speed_control_method == 'Discrete')
    speeds.each { |pair| fan.addSpeed(pair[0], pair[1]) }
  end

  return fan
end

.create_fan_variable_volume(model, fan_name: nil, fan_efficiency: nil, pressure_rise: nil, motor_efficiency: nil, motor_in_airstream_fraction: nil, end_use_subcategory: nil, fan_curve: nil, fan_power_minimum_flow_rate_input_method: nil, fan_power_minimum_flow_rate_fraction: nil, fan_power_coefficient_1: nil, fan_power_coefficient_2: nil, fan_power_coefficient_3: nil, fan_power_coefficient_4: nil, fan_power_coefficient_5: nil) ⇒ OpenStudio::Model::FanVariableVolume

creates a variable volume fan

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • fan_name (String) (defaults to: nil)

    fan name

  • fan_efficiency (Double) (defaults to: nil)

    fan efficiency

  • pressure_rise (Double) (defaults to: nil)

    fan pressure rise in Pa

  • motor_efficiency (Double) (defaults to: nil)

    fan motor efficiency

  • motor_in_airstream_fraction (Double) (defaults to: nil)

    fraction of motor heat in airstream

  • end_use_subcategory (String) (defaults to: nil)

    end use subcategory name

  • fan_curve (String) (defaults to: nil)

    fan curve for variable volume fans. See fan_variable_volume_set_control_type() for options.

  • fan_power_minimum_flow_rate_input_method (String) (defaults to: nil)

    options are Fraction, FixedFlowRate

  • fan_power_minimum_flow_rate_fraction (Double) (defaults to: nil)

    minimum flow rate fraction. overrides data in fan curve.

  • fan_power_coefficient_1 (Double) (defaults to: nil)

    fan power coefficient 1. overrides data in fan curve.

  • fan_power_coefficient_2 (Double) (defaults to: nil)

    fan power coefficient 2. overrides data in fan curve.

  • fan_power_coefficient_3 (Double) (defaults to: nil)

    fan power coefficient 3. overrides data in fan curve.

  • fan_power_coefficient_4 (Double) (defaults to: nil)

    fan power coefficient 4. overrides data in fan curve.

  • fan_power_coefficient_5 (Double) (defaults to: nil)

    fan power coefficient 5. overrides data in fan curve.

Returns:

  • (OpenStudio::Model::FanVariableVolume)

    variable volume fan object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 81

def self.create_fan_variable_volume(model,
                                    fan_name: nil,
                                    fan_efficiency: nil,
                                    pressure_rise: nil,
                                    motor_efficiency: nil,
                                    motor_in_airstream_fraction: nil,
                                    end_use_subcategory: nil,
                                    fan_curve: nil,
                                    fan_power_minimum_flow_rate_input_method: nil,
                                    fan_power_minimum_flow_rate_fraction: nil,
                                    fan_power_coefficient_1: nil,
                                    fan_power_coefficient_2: nil,
                                    fan_power_coefficient_3: nil,
                                    fan_power_coefficient_4: nil,
                                    fan_power_coefficient_5: nil)
  fan = OpenStudio::Model::FanVariableVolume.new(model)
  fan.setName(fan_name) unless fan_name.nil?
  fan.setFanEfficiency(fan_efficiency) unless fan_efficiency.nil?
  fan.setPressureRise(pressure_rise) unless pressure_rise.nil?
  fan.setEndUseSubcategory(end_use_subcategory) unless end_use_subcategory.nil?
  fan.setMotorEfficiency(motor_efficiency) unless motor_efficiency.nil?
  fan.setMotorInAirstreamFraction(motor_in_airstream_fraction) unless motor_in_airstream_fraction.nil?
  OpenstudioStandards::HVAC.fan_variable_volume_set_control_type(fan, control_type: fan_curve) unless fan_curve.nil?
  fan.setFanPowerMinimumFlowRateInputMethod(fan_power_minimum_flow_rate_input_method) unless fan_power_minimum_flow_rate_input_method.nil?
  fan.setFanPowerMinimumFlowFraction(fan_power_minimum_flow_rate_fraction) unless fan_power_minimum_flow_rate_fraction.nil?
  fan.setFanPowerCoefficient1(fan_power_coefficient_1) unless fan_power_coefficient_1.nil?
  fan.setFanPowerCoefficient2(fan_power_coefficient_2) unless fan_power_coefficient_2.nil?
  fan.setFanPowerCoefficient3(fan_power_coefficient_3) unless fan_power_coefficient_3.nil?
  fan.setFanPowerCoefficient4(fan_power_coefficient_4) unless fan_power_coefficient_4.nil?
  fan.setFanPowerCoefficient5(fan_power_coefficient_5) unless fan_power_coefficient_5.nil?

  return fan
end

.create_fan_zone_exhaust(model, fan_name: nil, fan_efficiency: nil, pressure_rise: nil, system_availability_manager_coupling_mode: nil, end_use_subcategory: nil) ⇒ OpenStudio::Model::FanZoneExhaust

creates a zone exhaust fan

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • fan_name (String) (defaults to: nil)

    fan name

  • fan_efficiency (Double) (defaults to: nil)

    fan efficiency

  • pressure_rise (Double) (defaults to: nil)

    fan pressure rise in Pa

  • end_use_subcategory (String) (defaults to: nil)

    end use subcategory name

  • system_availability_manager_coupling_mode (String) (defaults to: nil)

    coupling mode, options are Coupled, Decoupled

Returns:

  • (OpenStudio::Model::FanZoneExhaust)

    the exhaust fan



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 124

def self.create_fan_zone_exhaust(model,
                                 fan_name: nil,
                                 fan_efficiency: nil,
                                 pressure_rise: nil,
                                 system_availability_manager_coupling_mode: nil,
                                 end_use_subcategory: nil)
  fan = OpenStudio::Model::FanZoneExhaust.new(model)
  fan.setName(fan_name) unless fan_name.nil?
  fan.setFanEfficiency(fan_efficiency) unless fan_efficiency.nil?
  fan.setPressureRise(pressure_rise) unless pressure_rise.nil?
  fan.setEndUseSubcategory(end_use_subcategory) unless end_use_subcategory.nil?
  fan.setSystemAvailabilityManagerCouplingMode(system_availability_manager_coupling_mode) unless system_availability_manager_coupling_mode.nil?

  return fan
end

.create_heat_exchanger_air_to_air_sensible_and_latent(model, name: nil, type: nil, economizer_lockout: nil, supply_air_outlet_temperature_control: nil, frost_control_type: nil, nominal_electric_power: nil, sensible_heating_100_eff: nil, sensible_heating_75_eff: nil, latent_heating_100_eff: nil, latent_heating_75_eff: nil, sensible_cooling_100_eff: nil, sensible_cooling_75_eff: nil, latent_cooling_100_eff: nil, latent_cooling_75_eff: nil) ⇒ <OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent>

creates HeatExchangerAirToAirSensibleAndLatent object with user-input effectiveness values

Parameters:

  • model (<OpenStudio::Model::Model>)

    OpenStudio model

  • name (String) (defaults to: nil)
  • type (String) (defaults to: nil)

    Heat Exchanger Type. One of ‘Plate’, ‘Rotary’

  • economizer_lockout (Boolean) (defaults to: nil)

    whether heat exchanger is locked out during economizing

  • supply_air_outlet_temperature_control (Boolean) (defaults to: nil)
  • frost_control_type (String) (defaults to: nil)

    heat exchanger frost control type. One of ‘None’, ‘ExhaustAirRecirculation’, ‘ExhaustOnly’, ‘MinimumExhaustTemperature’

  • nominal_electric_power (Float) (defaults to: nil)

    Nominal electric power

  • sensible_heating_100_eff (Float) (defaults to: nil)
  • sensible_heating_75_eff (Float) (defaults to: nil)
  • latent_heating_100_eff (Float) (defaults to: nil)
  • latent_heating_75_eff (Float) (defaults to: nil)
  • sensible_cooling_100_eff (Float) (defaults to: nil)
  • sensible_cooling_75_eff (Float) (defaults to: nil)
  • latent_cooling_100_eff (Float) (defaults to: nil)
  • latent_cooling_75_eff (Float) (defaults to: nil)

Returns:

  • (<OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent>)

    OpenStudio HeatExchangerAirToAirSensibleAndLatent object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/openstudio-standards/hvac/components/heat_exchanger_air_to_air.rb', line 25

def self.create_heat_exchanger_air_to_air_sensible_and_latent(model,
                                                              name: nil,
                                                              type: nil,
                                                              economizer_lockout: nil,
                                                              supply_air_outlet_temperature_control: nil,
                                                              frost_control_type: nil,
                                                              nominal_electric_power: nil,
                                                              sensible_heating_100_eff: nil,
                                                              sensible_heating_75_eff: nil,
                                                              latent_heating_100_eff: nil,
                                                              latent_heating_75_eff: nil,
                                                              sensible_cooling_100_eff: nil,
                                                              sensible_cooling_75_eff: nil,
                                                              latent_cooling_100_eff: nil,
                                                              latent_cooling_75_eff: nil)
  heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model)
  heat_exchanger.setName(name) unless name.nil?

  unless type.nil?
    if OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.heatExchangerTypeValues.include?(type)
      heat_exchanger.setHeatExchangerType(type)
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC', "Entered heat exchanger type #{type} not a valid type value. Enter one of #{OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.heatExchangerTypeValues}")
    end
  end

  unless frost_control_type.nil?
    if OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.frostControlTypeValues.include?(frost_control_type)
      heat_exchanger.setFrostControlType(frost_control_type)
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC', "Entered heat exchanger frost control type #{frost_control_type} not a valid type value. Enter one of #{OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.frostControlTypeValues}")
    end
  end

  heat_exchanger.setEconomizerLockout(economizer_lockout) unless economizer_lockout.nil?
  heat_exchanger.setSupplyAirOutletTemperatureControl(supply_air_outlet_temperature_control) unless supply_air_outlet_temperature_control.nil?
  heat_exchanger.setNominalElectricPower(nominal_electric_power) unless nominal_electric_power.nil?

  if model.version < OpenStudio::VersionString.new('3.8.0')
    heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(sensible_heating_100_eff) unless sensible_heating_100_eff.nil?
    heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(sensible_heating_75_eff) unless sensible_heating_75_eff.nil?
    heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(latent_heating_100_eff) unless latent_heating_100_eff.nil?
    heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(latent_heating_75_eff) unless latent_heating_75_eff.nil?
    heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(sensible_cooling_100_eff) unless sensible_cooling_100_eff.nil?
    heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(sensible_cooling_75_eff) unless sensible_cooling_75_eff.nil?
    heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(latent_cooling_100_eff) unless latent_cooling_100_eff.nil?
    heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(latent_cooling_75_eff) unless latent_cooling_75_eff.nil?
  else
    effectiveness_inputs = Hash.new { |hash, key| hash[key] = {} }
    effectiveness_inputs['Sensible Heating'][0.75] = sensible_heating_75_eff.to_f unless sensible_heating_75_eff.nil?
    effectiveness_inputs['Sensible Heating'][1.0] = sensible_heating_100_eff.to_f unless sensible_heating_100_eff.nil?
    effectiveness_inputs['Latent Heating'][0.75] = latent_heating_75_eff.to_f unless latent_heating_75_eff.nil?
    effectiveness_inputs['Latent Heating'][1.0] = latent_heating_100_eff.to_f unless latent_heating_100_eff.nil?
    effectiveness_inputs['Sensible Cooling'][0.75] = sensible_cooling_75_eff.to_f unless sensible_cooling_75_eff.nil?
    effectiveness_inputs['Sensible Cooling'][1.0] = sensible_cooling_100_eff.to_f unless sensible_cooling_100_eff.nil?
    effectiveness_inputs['Latent Cooling'][0.75] = latent_cooling_75_eff.to_f unless latent_cooling_75_eff.nil?
    effectiveness_inputs['Latent Cooling'][1.0] = latent_cooling_100_eff.to_f unless latent_cooling_100_eff.nil?

    if effectiveness_inputs.values.all?(&:empty?)
      OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC', 'Creating heat exchanger with historical effectiveness curves')
      defaults = true
    else
      defaults = false
    end

    OpenstudioStandards::HVAC.heat_exchanger_air_to_air_set_effectiveness_values(heat_exchanger, defaults: defaults, values: effectiveness_inputs)
  end

  return heat_exchanger
end

.create_heat_exchanger_effectiveness_table(heat_exchanger, type, values_hash) ⇒ <OpenStudio::Model::TableLookup>

creates LookupTable objects to define effectiveness of HeatExchangerAirToAirSensibleAndLatent objects

Parameters:

  • heat_exchanger (<OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent>)

    OpenStudio heat exchanger object to update

  • type (String)

    type of curve to create. one of ‘Sensible Heating’, ‘Latent Heating, ’Sensible Cooling’, ‘Latent Cooling’

  • values_hash (Hash(Float=>Float))

    user_input flow decimal fraction => effectiveness decimal fraction pairs, e.g. (0.75 => 0.81, 1.0 => 0.76)

Returns:

  • (<OpenStudio::Model::TableLookup>)

    lookup table object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/openstudio-standards/hvac/components/heat_exchanger_air_to_air.rb', line 102

def self.create_heat_exchanger_effectiveness_table(heat_exchanger, type, values_hash)
  # validate inputs
  types = ['Sensible Heating', 'Latent Heating', 'Sensible Cooling', 'Latent Cooling']
  unless types.include? type
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "#{__method__} requires effectiveness type as one of #{types}")
    return false
  end

  def self.validate_effectiveness_hash(values_hash)
    values_hash.all? do |key, value|
      key.is_a?(Float) && value.is_a?(Float) && key.between?(0.0, 1.0) && value.between?(0.0, 1.0)
    end
  end

  if !validate_effectiveness_hash(values_hash)
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "#{__method__} require values_hash to have keys and values between 0.0 and 1.0: #{values_hash}")
    return false
  end

  # look for existing curve
  type_a = type.split
  method_name = "#{type_a[0].downcase}Effectivenessof#{type_a[1]}AirFlowCurve"

  # use existing independent variable object if found and matches inputs
  iv = nil
  table = OpenStudio::Model::OptionalTableLookup.new
  if heat_exchanger.send(method_name).is_initialized
    table = heat_exchanger.send(method_name).get.to_TableLookup
    curve = table.get
    iv_existing = curve.independentVariables.first
    if values_hash.keys.sort == iv_existing.values.sort
      iv = iv_existing
    end
  end

  # otherwise create new independent variable
  if iv.nil?
    iv = OpenStudio::Model::TableIndependentVariable.new(heat_exchanger.model)
    iv.setName("#{heat_exchanger.name.get}_#{type.gsub(' ', '')}_IndependentVariable")
    iv.setInterpolationMethod('Linear')
    iv.setExtrapolationMethod('Linear')
    iv.setMinimumValue(0.0)
    iv.setMaximumValue(10.0)
    iv.setUnitType('Dimensionless')
    values_hash.keys.sort.each { |k| iv.addValue(k) }
  end

  # create new lookup table
  t = OpenStudio::Model::TableLookup.new(heat_exchanger.model)
  t.setName("#{heat_exchanger.name.get}_#{type.gsub(/ible|ent|ing|\s/, '')}Eff")
  t.addIndependentVariable(iv)
  t.setNormalizationMethod('DivisorOnly')
  t.setMinimumOutput(0.0)
  t.setMaximumOutput(10.0)
  t.setOutputUnitType('Dimensionless')
  values = values_hash.sort.map { |a| a[1] }
  # protect against setting normalization divisor to zero for zero effectiveness
  values[-1].zero? ? t.setNormalizationDivisor(1) : t.setNormalizationDivisor(values[-1])
  values.each { |v| t.addOutputValue(v) }

  # remove curve if found
  table.get.remove if table.is_initialized

  return t
end

.create_typical_fan(model, typical_fan, fan_name: nil, fan_efficiency: nil, pressure_rise: nil, motor_efficiency: nil, motor_in_airstream_fraction: nil, end_use_subcategory: nil, fan_power_minimum_flow_rate_input_method: nil, fan_power_minimum_flow_rate_fraction: nil, fan_curve: nil, system_availability_manager_coupling_mode: nil) ⇒ OpenStudio::Model::StraightComponent

create a fan with properties for a typical fan type

Parameters:

  • model (OpenStudio::Model::Model)

    OpenStudio model object

  • typical_fan (String)

    the kind of typical fan to create

  • fan_name (String) (defaults to: nil)

    fan name

  • fan_efficiency (Double) (defaults to: nil)

    fan efficiency

  • pressure_rise (Double) (defaults to: nil)

    fan pressure rise in Pa

  • motor_efficiency (Double) (defaults to: nil)

    fan motor efficiency

  • motor_in_airstream_fraction (Double) (defaults to: nil)

    fraction of motor heat in airstream

  • end_use_subcategory (String) (defaults to: nil)

    end use subcategory name

  • fan_curve (String) (defaults to: nil)

    fan curve for variable volume fans. See fan_variable_volume_set_control_type() for options.

  • system_availability_manager_coupling_mode (String) (defaults to: nil)

    coupling mode, options are Coupled, Decoupled

Returns:

  • (OpenStudio::Model::StraightComponent)

    fan object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 195

def self.create_typical_fan(model, typical_fan,
                            fan_name: nil,
                            fan_efficiency: nil,
                            pressure_rise: nil,
                            motor_efficiency: nil,
                            motor_in_airstream_fraction: nil,
                            end_use_subcategory: nil,
                            fan_power_minimum_flow_rate_input_method: nil,
                            fan_power_minimum_flow_rate_fraction: nil,
                            fan_curve: nil,
                            system_availability_manager_coupling_mode: nil)
  fan_json = JSON.parse(File.read("#{__dir__}/data/fans.json"), symbolize_names: true)
  fan_data = fan_json[:fans].select { |hash| hash[:name] == typical_fan }

  if fan_data.empty?
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC.fan', "Could not find fan data for typical fan '#{typical_fan}'.")
    return nil
  end
  fan_data = fan_data[0]

  # use argument values if provided, otherwise use values from the fan data
  fan_efficiency ||= fan_data[:fan_efficiency]
  pressure_rise_inh2o ||= fan_data[:pressure_rise]
  pressure_rise_pa = pressure_rise_inh2o ? OpenStudio.convert(pressure_rise_inh2o, 'inH_{2}O', 'Pa').get : nil # convert pressure rise to Pa
  motor_efficiency ||= fan_data[:motor_efficiency]
  motor_in_airstream_fraction ||= fan_data[:motor_in_airstream_fraction]

  case fan_data[:type]
  when 'OnOff'
    fan = OpenstudioStandards::HVAC.create_fan_on_off(model,
                                                      fan_name: fan_name,
                                                      fan_efficiency: fan_efficiency,
                                                      pressure_rise: pressure_rise_pa,
                                                      motor_efficiency: motor_efficiency,
                                                      motor_in_airstream_fraction: motor_in_airstream_fraction,
                                                      end_use_subcategory: end_use_subcategory)
  when 'ConstantVolume'
    fan = OpenstudioStandards::HVAC.create_fan_constant_volume(model,
                                                               fan_name: fan_name,
                                                               fan_efficiency: fan_efficiency,
                                                               pressure_rise: pressure_rise_pa,
                                                               motor_efficiency: motor_efficiency,
                                                               motor_in_airstream_fraction: motor_in_airstream_fraction,
                                                               end_use_subcategory: end_use_subcategory)
  when 'VariableVolume'
    fan_curve ||= fan_data[:fan_curve]
    fan = OpenstudioStandards::HVAC.create_fan_variable_volume(model,
                                                               fan_name: fan_name,
                                                               fan_efficiency: fan_efficiency,
                                                               pressure_rise: pressure_rise_pa,
                                                               motor_efficiency: motor_efficiency,
                                                               motor_in_airstream_fraction: motor_in_airstream_fraction,
                                                               end_use_subcategory: end_use_subcategory,
                                                               fan_curve: fan_curve,
                                                               fan_power_minimum_flow_rate_input_method: fan_power_minimum_flow_rate_input_method,
                                                               fan_power_minimum_flow_rate_fraction: fan_power_minimum_flow_rate_fraction)
  when 'ZoneExhaust'
    fan = OpenstudioStandards::HVAC.create_fan_zone_exhaust(model,
                                                            fan_name: fan_name,
                                                            fan_efficiency: fan_efficiency,
                                                            pressure_rise: pressure_rise,
                                                            end_use_subcategory: end_use_subcategory,
                                                            system_availability_manager_coupling_mode: system_availability_manager_coupling_mode)
  end

  return fan
end

.eer_to_cop(eer) ⇒ Double

Convert from EER to COP

Parameters:

  • eer (Double)

    Energy Efficiency Ratio (EER)

Returns:

  • (Double)

    Coefficient of Performance (COP)



158
159
160
# File 'lib/openstudio-standards/hvac/conversions.rb', line 158

def self.eer_to_cop(eer)
  return eer / OpenStudio.convert(1.0, 'W', 'Btu/h').get
end

.eer_to_cop_no_fan(eer, capacity_w = nil) ⇒ Double

Convert from EER to COP (no fan)

Parameters:

  • eer (Double)

    Energy Efficiency Ratio (EER)

  • capacity_w (Double) (defaults to: nil)

    the heating capacity at AHRI rating conditions, in W

Returns:

  • (Double)

    Coefficient of Performance (COP)



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/openstudio-standards/hvac/conversions.rb', line 99

def self.eer_to_cop_no_fan(eer, capacity_w = nil)
  if capacity_w.nil?
    # From Thornton et al. 2011
    # r is the ratio of supply fan power to total equipment power at the rating condition,
    # assumed to be 0.12 for the reference buildings per Thornton et al. 2011.
    r = 0.12
    cop = ((eer / OpenStudio.convert(1.0, 'W', 'Btu/h').get) + r) / (1 - r)
  else
    # The 90.1-2013 method
    # Convert the capacity to Btu/hr
    capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
    cop = (7.84E-8 * eer * capacity_btu_per_hr) + (0.338 * eer)
  end

  return cop
end

.ems_friendly_name(name) ⇒ String

converts existing string to ems friendly string

Parameters:

  • name (String)

    original name

Returns:

  • (String)

    the resulting EMS friendly string



346
347
348
349
350
351
352
353
354
355
# File 'lib/openstudio-standards/hvac/helpers.rb', line 346

def self.ems_friendly_name(name)
  # replace white space and special characters with underscore
  # \W is equivalent to [^a-zA-Z0-9_]
  new_name = name.to_s.gsub(/\W/, '_')

  # prepend ems_ in case the name starts with a number
  new_name = "ems_#{new_name}"

  return new_name
end

.fan_variable_volume_set_control_type(fan_variable_volume, control_type: 'Multi Zone VAV with Static Pressure Setpoint Reset') ⇒ Boolean

Modify the fan curve coefficients to reflect a specific type of control.

Parameters:

  • fan_variable_volume (OpenStudio::Model::FanVariableVolume)

    variable volume fan object

  • control_type (String) (defaults to: 'Multi Zone VAV with Static Pressure Setpoint Reset')

    valid choices are: Single Zone VAV Multi Zone VAV with Fixed Static Pressure Setpoint Multi Zone VAV with Static Pressure Setpoint Reset Multi Zone VAV with Discharge Dampers Multi Zone VAV with Airfoil or Backward Incline riding the curve Multi Zone VAV with Airfoil or Backward Incline with Inlet Vanes Multi Zone VAV with Forward Curved fans riding the curve Multi Zone VAV with Forward Curved with Inlet Vanes Multi Zone VAV with Vane-axial with Variable Pitch Blades

Returns:

  • (Boolean)

    returns true if successful, false if not



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/openstudio-standards/hvac/components/fan.rb', line 277

def self.fan_variable_volume_set_control_type(fan_variable_volume, control_type: 'Multi Zone VAV with Static Pressure Setpoint Reset')
  case control_type
  when 'Single Zone VAV'
    # Baseline for System Type 11 from ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.027827882
    coeff_b = 0.026583195
    coeff_c = -0.0870687
    coeff_d = 1.03091975
    min_pct_pwr = 0.1
  when 'Multi Zone VAV with Fixed Static Pressure Setpoint'
    # Appendix G baseline from ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.0013
    coeff_b = 0.1470
    coeff_c = 0.9506
    coeff_d = -0.0998
    min_pct_pwr = 0.2
  when 'Multi Zone VAV with Static Pressure Setpoint Reset'
    # OpenStudio default, baseline for System Types 5-8 from ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.040759894
    coeff_b = 0.08804497
    coeff_c = -0.07292612
    coeff_d = 0.943739823
    min_pct_pwr = 0.1
  when 'Multi Zone VAV with Discharge Dampers'
    coeff_a = 0.18984763
    coeff_b = 0.31447014
    coeff_c = 0.49568211
    coeff_d = 0.0
    min_pct_pwr = 0.25
  when 'Multi Zone VAV with Airfoil or Backward Incline riding the curve'
    # From ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.1631
    coeff_b = 1.5901
    coeff_c = -0.8817
    coeff_d = 0.1281
    min_pct_pwr = 0.7
  when 'Multi Zone VAV with Airfoil or Backward Incline with Inlet Vanes'
    # From ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.9977
    coeff_b = -0.659
    coeff_c = 0.9547
    coeff_d = -0.2936
    min_pct_pwr = 0.5
  when 'Multi Zone VAV with Forward Curved fans riding the curve'
    # From ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.1224
    coeff_b = 0.612
    coeff_c = 0.5983
    coeff_d = -0.3334
    min_pct_pwr = 0.3
  when 'Multi Zone VAV with Forward Curved with Inlet Vanes'
    # From ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.3038
    coeff_b = -0.7608
    coeff_c = 2.2729
    coeff_d = -0.8169
    min_pct_pwr = 0.3
  when 'Multi Zone VAV with Vane-axial with Variable Pitch Blades'
    # From ANSI/ASHRAE/IES Standard 90.1-2016 - Energy Standard for Buildings Except Low-Rise Residential Performance Rating Method
    coeff_a = 0.1639
    coeff_b = -0.4016
    coeff_c = 1.9909
    coeff_d = -0.7541
    min_pct_pwr = 0.2
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.fan', "Fan control type '#{control_type}' not recognized, fan power coefficients will not be changed.")
    return false
  end

  # Set the coefficients
  fan_variable_volume.setFanPowerCoefficient1(coeff_a) unless coeff_a.nil?
  fan_variable_volume.setFanPowerCoefficient2(coeff_b) unless coeff_b.nil?
  fan_variable_volume.setFanPowerCoefficient3(coeff_c) unless coeff_c.nil?
  fan_variable_volume.setFanPowerCoefficient4(coeff_d) unless coeff_d.nil?

  # Set the fan minimum power
  unless min_pct_pwr.nil?
    fan_variable_volume.setFanPowerMinimumFlowRateInputMethod('Fraction')
    fan_variable_volume.setFanPowerMinimumFlowFraction(min_pct_pwr)
  end

  OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.HVAC.fan', "For #{fan_variable_volume.name}: Set fan curve coefficients to reflect control type of '#{control_type}'.")

  return true
end

.heat_exchanger_air_to_air_set_effectiveness_values(heat_exchanger, defaults: false, values: nil) ⇒ <OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent>

Applies historical default or user-input effectiveness values to a HeatExchanger:AirToAir:SensibleAndLatent object

Parameters:

  • heat_exchanger (<OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent>)

    OpenStudio HeatExchangerAirToAirSensibleAndLatent object to update

  • defaults (Boolean) (defaults to: false)

    flag to apply historical default curves

  • values (Hash{String=>Hash{Float=>Float}}) (defaults to: nil)

    user-input effectiveness values, where keys are one of ‘Sensible Heating’, ‘Latent Heating, ’Sensible Cooling’, ‘Latent Cooling’ and value is a hash of (flow decimal fraction => effectivess decimal fraction), e.g. (0.75 => 0.81, 1.0 => 0.76)

Returns:

  • (<OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent>)

    modified OpenStudio HeatExchangerAirToAirSensibleAndLatent object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/openstudio-standards/hvac/components/heat_exchanger_air_to_air.rb', line 176

def self.heat_exchanger_air_to_air_set_effectiveness_values(heat_exchanger, defaults: false, values: nil)
  if defaults
    heat_exchanger.assignHistoricalEffectivenessCurves
    if values.nil?
      return heat_exchanger
    end
  elsif values.nil?
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC', "#{__method__} called with defaults=false and no values provided. #{heat_exchanger.name.get} will not be modified")
    return heat_exchanger
  end

  values.each do |type, values_hash|
    # ensure values_hash has one value at 100% flow
    unless values_hash.key?(1.0)
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "Effectiveness Values for #{type} do not include 100% flow effectivenss. Cannot set effectiveness curves")
      return false
    end

    lookup_table = OpenstudioStandards::HVAC.create_heat_exchanger_effectiveness_table(heat_exchanger, type, values_hash)
    type_a = type.split
    heat_exchanger.send("set#{type_a[0]}Effectivenessat100#{type_a[1]}AirFlow", values_hash[1.0])
    heat_exchanger.send("set#{type_a[0]}Effectivenessof#{type_a[1]}AirFlowCurve", lookup_table)
  end

  return heat_exchanger
end

.hspf_to_cop(hspf) ⇒ Double

Convert from HSPF to COP (with fan) for heat pump heating coils

Parameters:

  • hspf (Double)

    heating seasonal performance factor (HSPF)

Returns:

  • (Double)

    Coefficient of Performance (COP)



86
87
88
89
90
# File 'lib/openstudio-standards/hvac/conversions.rb', line 86

def self.hspf_to_cop(hspf)
  cop = (-0.0255 * hspf * hspf) + (0.6239 * hspf)

  return cop
end

.hspf_to_cop_no_fan(hspf) ⇒ Double

Convert from HSPF to COP (no fan) for heat pump heating coils

Parameters:

  • hspf (Double)

    heating seasonal performance factor (HSPF)

Returns:

  • (Double)

    Coefficient of Performance (COP)



75
76
77
78
79
# File 'lib/openstudio-standards/hvac/conversions.rb', line 75

def self.hspf_to_cop_no_fan(hspf)
  cop = (-0.0296 * hspf * hspf) + (0.7134 * hspf)

  return cop
end

.hvac_component_get_thermal_zone(hvac_component) ⇒ OpenStudio::Model::ThermalZone

Returns the thermal zone associated with an HVAC component

Parameters:

  • hvac_component (OpenStudio::Model::HVACComponent)

    HVAC component object

Returns:



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/openstudio-standards/hvac/components/component.rb', line 11

def self.hvac_component_get_thermal_zone(hvac_component)
  if hvac_component.to_ZoneHVACComponent.is_initialized
    hvac_component = hvac_component.to_ZoneHVACComponent.get
    if hvac_component.thermalZone.is_initialized
      return hvac_component.thermalZone.get
    end
  end

  if hvac_component.containingHVACComponent.is_initialized
    hvac_component = hvac_component.containingHVACComponent.get
    if hvac_component.to_AirLoopHVACUnitarySystem.is_initialized
      unitary = hvac_component.to_AirLoopHVACUnitarySystem.get
      if unitary.controllingZoneorThermostatLocation.is_initialized
        return unitary.controllingZoneorThermostatLocation.get
      end
    end
  end

  if hvac_component.containingZoneHVACComponent.is_initialized
    hvac_component = hvac_component.containingZoneHVACComponent.get
    if hvac_component.thermalZone.is_initialized
      return hvac_component.thermalZone.get
    end
  end

  return nil
end

.ieer_to_cop_no_fan(ieer) ⇒ Double

TODO:

Implement methods to handle IEER modeling

Note:

IEER is a weighted-average efficiency metrics at different load percentages, operataional and environemental conditions

Note:

IEER should be modeled by using performance curves that match a targeted efficiency values

Note:

This method estimates what a reasonable full load rated EER would be for a targeted IEER value

Note:

The regression used in this method is based on a survey of over 1,000 rated AHRI units with IEER ranging from 11.8 to 25.6

Convert from IEER to COP (no fan)

Parameters:

  • ieer (Double)

    Energy Efficiency Ratio (EER)

Returns:

  • (Double)

    Coefficient of Performance (COP)



148
149
150
151
152
# File 'lib/openstudio-standards/hvac/conversions.rb', line 148

def self.ieer_to_cop_no_fan(ieer)
  eer = (0.0183 * ieer * ieer) - (0.4552 * ieer) + 13.21

  return OpenstudioStandards::HVAC.eer_to_cop_no_fan(eer)
end

.kw_per_ton_to_cop(kw_per_ton) ⇒ Double

A helper method to convert from kW/ton to COP

Parameters:

  • kw_per_ton (Double)

    kW of input power per ton of cooling

Returns:

  • (Double)

    Coefficient of Performance (COP)



182
183
184
# File 'lib/openstudio-standards/hvac/conversions.rb', line 182

def self.kw_per_ton_to_cop(kw_per_ton)
  return 3.517 / kw_per_ton
end

.model_add_radiant_basic_controls(model, zone, radiant_loop, radiant_temperature_control_type: 'SurfaceFaceTemperature', slab_setpoint_oa_control: false, switch_over_time: 24.0, slab_sp_at_oat_low: 73, slab_oat_low: 65, slab_sp_at_oat_high: 68, slab_oat_high: 80) ⇒ Object

Native EnergyPlus objects implement a control for a single thermal zone with a radiant system.

Parameters:

  • zone (OpenStudio::Model::ThermalZone)

    ] zone to add radiant controls

  • radiant_loop (OpenStudio::Model::ZoneHVACLowTempRadiantVarFlow)

    ] radiant loop in thermal zone

  • radiant_temperature_control_type (String) (defaults to: 'SurfaceFaceTemperature')

    determines the controlled temperature for the radiant system options are ‘SurfaceFaceTemperature’, ‘SurfaceInteriorTemperature’

  • slab_setpoint_oa_control (Bool) (defaults to: false)

    True if slab setpoint is to be varied based on outdoor air temperature

  • switch_over_time (Double) (defaults to: 24.0)

    Time limitation for when the system can switch between heating and cooling

  • slab_sp_at_oat_low (Double) (defaults to: 73)

    radiant slab temperature setpoint, in F, at the outdoor high temperature.

  • slab_oat_low (Double) (defaults to: 65)

    outdoor drybulb air temperature, in F, for low radiant slab setpoint.

  • slab_sp_at_oat_high (Double) (defaults to: 68)

    radiant slab temperature setpoint, in F, at the outdoor low temperature.

  • slab_oat_high (Double) (defaults to: 80)

    outdoor drybulb air temperature, in F, for high radiant slab setpoint.



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# File 'lib/openstudio-standards/hvac/controls/radiant_system_controls.rb', line 469

def self.model_add_radiant_basic_controls(model, zone, radiant_loop,
                                          radiant_temperature_control_type: 'SurfaceFaceTemperature',
                                          slab_setpoint_oa_control: false,
                                          switch_over_time: 24.0,
                                          slab_sp_at_oat_low: 73,
                                          slab_oat_low: 65,
                                          slab_sp_at_oat_high: 68,
                                          slab_oat_high: 80)
  zone_name = zone.name.to_s.gsub(/[ +-.]/, '_')

  if model.version < OpenStudio::VersionString.new('3.1.1')
    coil_cooling_radiant = radiant_loop.coolingCoil.to_CoilCoolingLowTempRadiantVarFlow.get
    coil_heating_radiant = radiant_loop.heatingCoil.to_CoilHeatingLowTempRadiantVarFlow.get
  else
    coil_cooling_radiant = radiant_loop.coolingCoil.get.to_CoilCoolingLowTempRadiantVarFlow.get
    coil_heating_radiant = radiant_loop.heatingCoil.get.to_CoilHeatingLowTempRadiantVarFlow.get
  end

  #####
  # Define radiant system parameters
  ####
  # set radiant system temperature and setpoint control type
  unless ['surfacefacetemperature', 'surfaceinteriortemperature'].include? radiant_temperature_control_type.downcase
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model',
                      "Control sequences not compatible with '#{radiant_temperature_control_type}' radiant system control. Defaulting to 'SurfaceFaceTemperature'.")
    radiant_temperature_control_type = 'SurfaceFaceTemperature'
  end

  radiant_loop.setTemperatureControlType(radiant_temperature_control_type)

  # get existing switchover time schedule or create one if needed
  sch_radiant_switchover = model.getScheduleRulesetByName('Radiant System Switchover')
  if sch_radiant_switchover.is_initialized
    sch_radiant_switchover = sch_radiant_switchover.get
  else
    sch_radiant_switchover = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                            switch_over_time,
                                                                                            name: 'Radiant System Switchover',
                                                                                            schedule_type_limit: 'Dimensionless')
  end

  # set radiant system switchover schedule
  radiant_loop.setChangeoverDelayTimePeriodSchedule(sch_radiant_switchover.to_Schedule.get)

  if slab_setpoint_oa_control
    # get weather file from model
    weather_file = model.getWeatherFile
    if weather_file.initialized
      # get annual outdoor dry bulb temperature
      annual_oat = weather_file.file.get.data.collect { |dat| dat.dryBulbTemperature.get }

      # calculate a nhrs rolling average from annual outdoor dry bulb temperature
      nhrs = 24
      last_nhrs_oat_in_year = annual_oat.last(nhrs - 1)
      combined_oat = last_nhrs_oat_in_year + annual_oat
      oat_rolling_average = combined_oat.each_cons(nhrs).map { |e| e.reduce(&:+).fdiv(nhrs).round(2) }

      # use rolling average to calculate slab setpoint temperature

      # convert temperature from IP to SI units
      slab_sp_at_oat_low_si = OpenStudio.convert(slab_sp_at_oat_low, 'F', 'C').get
      slab_oat_low_si = OpenStudio.convert(slab_oat_low, 'F', 'C').get
      slab_sp_at_oat_high_si = OpenStudio.convert(slab_sp_at_oat_high, 'F', 'C').get
      slab_oat_high_si = OpenStudio.convert(slab_oat_high, 'F', 'C').get

      # calculate relationship between slab setpoint and slope
      slope_num = slab_sp_at_oat_high_si - slab_sp_at_oat_low_si
      slope_den = slab_oat_high_si - slab_oat_low_si
      sp_and_oat_slope = slope_num.fdiv(slope_den).round(4)

      slab_setpoint = oat_rolling_average.map { |e| (slab_sp_at_oat_low_si + ((e - slab_oat_low_si) * sp_and_oat_slope)).round(1) }

      # input upper limits on slab setpoint
      slab_sp_upper_limit = [slab_sp_at_oat_high_si, slab_sp_at_oat_low_si].max
      slab_sp_lower_limit = [slab_sp_at_oat_high_si, slab_sp_at_oat_low_si].min
      slab_setpoint.map! { |e| e > slab_sp_upper_limit ? slab_sp_upper_limit.round(1) : e }

      # input lower limits on slab setpoint
      slab_setpoint.map! { |e| e < slab_sp_lower_limit ? slab_sp_lower_limit.round(1) : e }

      # convert to timeseries
      yd = model.getYearDescription
      start_date = yd.makeDate(1, 1)
      interval = OpenStudio::Time.new(1.0 / 24.0)
      time_series = OpenStudio::TimeSeries.new(start_date, interval, OpenStudio.createVector(slab_setpoint), 'C')

      # check for pre-existing schedule in model
      schedule_interval = model.getScheduleByName('Sch_Radiant_SlabSetP_Based_On_Rolling_Mean_OAT')
      if schedule_interval.is_initialized && schedule_interval.get.to_ScheduleFixedInterval.is_initialized
        schedule_interval = schedule_interval.get.to_ScheduleFixedInterval.get
        schedule_interval.setTimeSeries(time_series)
      else
        # create fixed interval schedule for slab setpoint
        schedule_interval = OpenStudio::Model::ScheduleFixedInterval.new(model)
        schedule_interval.setName('Sch_Radiant_SlabSetP_Based_On_Rolling_Mean_OAT')
        schedule_interval.setTimeSeries(time_series)
        sch_type_limits_obj = OpenstudioStandards::Schedules.create_schedule_type_limits(model, standard_schedule_type_limit: 'Temperature')
        schedule_interval.setScheduleTypeLimits(sch_type_limits_obj)
      end

      # assign slab setpoint schedule
      coil_heating_radiant.setHeatingControlTemperatureSchedule(sch_radiant_slab_setp)
      coil_cooling_radiant.setCoolingControlTemperatureSchedule(sch_radiant_slab_setp)
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model',
                        'Model does not have a weather file associated with it. Define to implement slab setpoint based on outdoor weather.')
    end
  else
    # radiant system cooling control setpoint
    slab_setpoint = 22
    sch_radiant_clgsetp = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                          slab_setpoint + 0.1,
                                                                                          name: "#{zone_name}_Sch_Radiant_ClgSetP",
                                                                                          schedule_type_limit: 'Temperature')
    coil_cooling_radiant.setCoolingControlTemperatureSchedule(sch_radiant_clgsetp)

    # radiant system heating control setpoint
    sch_radiant_htgsetp = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                          slab_setpoint,
                                                                                          name: "#{zone_name}_Sch_Radiant_HtgSetP",
                                                                                          schedule_type_limit: 'Temperature')
    coil_heating_radiant.setHeatingControlTemperatureSchedule(sch_radiant_htgsetp)
  end
end

.model_add_radiant_proportional_controls(model, zone, radiant_loop, radiant_temperature_control_type: 'SurfaceFaceTemperature', use_zone_occupancy_for_control: true, occupied_percentage_threshold: 0.10, model_occ_hr_start: 6.0, model_occ_hr_end: 18.0, proportional_gain: 0.3, switch_over_time: 24.0) ⇒ Object

TODO:

model_occ_hr_start and model_occ_hr_end from zone occupancy schedules

These EnergyPlus objects implement a proportional control for a single thermal zone with a radiant system.

Parameters:

  • zone (OpenStudio::Model::ThermalZone)

    ] zone to add radiant controls

  • radiant_loop (OpenStudio::Model::ZoneHVACLowTempRadiantVarFlow)

    ] radiant loop in thermal zone

  • radiant_temperature_control_type (String) (defaults to: 'SurfaceFaceTemperature')

    determines the controlled temperature for the radiant system options are ‘SurfaceFaceTemperature’, ‘SurfaceInteriorTemperature’

  • use_zone_occupancy_for_control (Boolean) (defaults to: true)

    Set to true if radiant system is to use specific zone occupancy objects for CBE control strategy. If false, then it will use values in model_occ_hr_start and model_occ_hr_end for all radiant zones. default to true.

  • occupied_percentage_threshold (Double) (defaults to: 0.10)

    the minimum fraction (0 to 1) that counts as occupied if this parameter is set, the returned ScheduleRuleset will be 0 = unoccupied, 1 = occupied otherwise the ScheduleRuleset will be the weighted fractional occupancy schedule

  • model_occ_hr_start (Double) (defaults to: 6.0)

    Starting decimal hour of whole building occupancy

  • model_occ_hr_end (Double) (defaults to: 18.0)

    Ending decimal hour of whole building occupancy

  • proportional_gain (Double) (defaults to: 0.3)

    Proportional gain constant (recommended 0.3 or less).

  • switch_over_time (Double) (defaults to: 24.0)

    Time limitation for when the system can switch between heating and cooling



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/openstudio-standards/hvac/controls/radiant_system_controls.rb', line 24

def self.model_add_radiant_proportional_controls(model, zone, radiant_loop,
                                                 radiant_temperature_control_type: 'SurfaceFaceTemperature',
                                                 use_zone_occupancy_for_control: true,
                                                 occupied_percentage_threshold: 0.10,
                                                 model_occ_hr_start: 6.0,
                                                 model_occ_hr_end: 18.0,
                                                 proportional_gain: 0.3,
                                                 switch_over_time: 24.0)
  zone_name = OpenstudioStandards::HVAC.ems_friendly_name(zone.name)
  zone_timestep = model.getTimestep.numberOfTimestepsPerHour

  if model.version < OpenStudio::VersionString.new('3.1.1')
    coil_cooling_radiant = radiant_loop.coolingCoil.to_CoilCoolingLowTempRadiantVarFlow.get
    coil_heating_radiant = radiant_loop.heatingCoil.to_CoilHeatingLowTempRadiantVarFlow.get
  else
    coil_cooling_radiant = radiant_loop.coolingCoil.get.to_CoilCoolingLowTempRadiantVarFlow.get
    coil_heating_radiant = radiant_loop.heatingCoil.get.to_CoilHeatingLowTempRadiantVarFlow.get
  end

  #####
  # Define radiant system parameters
  ####
  # set radiant system temperature and setpoint control type
  unless ['surfacefacetemperature', 'surfaceinteriortemperature'].include? radiant_temperature_control_type.downcase
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model',
                      "Control sequences not compatible with '#{radiant_temperature_control_type}' radiant system control. Defaulting to 'SurfaceFaceTemperature'.")
    radiant_temperature_control_type = 'SurfaceFaceTemperature'
  end

  radiant_loop.setTemperatureControlType(radiant_temperature_control_type)

  #####
  # List of schedule objects used to hold calculation results
  ####

  # get existing switchover time schedule or create one if needed
  sch_radiant_switchover = model.getScheduleRulesetByName('Radiant System Switchover')
  if sch_radiant_switchover.is_initialized
    sch_radiant_switchover = sch_radiant_switchover.get
  else
    sch_radiant_switchover = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                            switch_over_time,
                                                                                            name: 'Radiant System Switchover',
                                                                                            schedule_type_limit: 'Dimensionless')
  end

  # set radiant system switchover schedule
  radiant_loop.setChangeoverDelayTimePeriodSchedule(sch_radiant_switchover.to_Schedule.get)

  # Calculated active slab heating and cooling temperature setpoint.
  # radiant system cooling control actuator
  sch_radiant_clgsetp = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                        26.0,
                                                                                        name: "#{zone_name}_Sch_Radiant_ClgSetP",
                                                                                        schedule_type_limit: 'Temperature')
  coil_cooling_radiant.setCoolingControlTemperatureSchedule(sch_radiant_clgsetp)
  cmd_cold_water_ctrl = OpenStudio::Model::EnergyManagementSystemActuator.new(sch_radiant_clgsetp,
                                                                              'Schedule:Year',
                                                                              'Schedule Value')
  cmd_cold_water_ctrl.setName("#{zone_name}_cmd_cold_water_ctrl")

  # radiant system heating control actuator
  sch_radiant_htgsetp = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                        20.0,
                                                                                        name: "#{zone_name}_Sch_Radiant_HtgSetP",
                                                                                        schedule_type_limit: 'Temperature')
  coil_heating_radiant.setHeatingControlTemperatureSchedule(sch_radiant_htgsetp)
  cmd_hot_water_ctrl = OpenStudio::Model::EnergyManagementSystemActuator.new(sch_radiant_htgsetp,
                                                                            'Schedule:Year',
                                                                            'Schedule Value')
  cmd_hot_water_ctrl.setName("#{zone_name}_cmd_hot_water_ctrl")

  # Calculated cooling setpoint error. Calculated from upper comfort limit minus setpoint offset and 'measured' controlled zone temperature.
  sch_csp_error = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                  0.0,
                                                                                  name: "#{zone_name}_Sch_CSP_Error",
                                                                                  schedule_type_limit: 'Temperature')
  cmd_csp_error = OpenStudio::Model::EnergyManagementSystemActuator.new(sch_csp_error,
                                                                        'Schedule:Year',
                                                                        'Schedule Value')
  cmd_csp_error.setName("#{zone_name}_cmd_csp_error")

  # Calculated heating setpoint error. Calculated from lower comfort limit plus setpoint offset and 'measured' controlled zone temperature.
  sch_hsp_error = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                  0.0,
                                                                                  name: "#{zone_name}_Sch_HSP_Error",
                                                                                  schedule_type_limit: 'Temperature')
  cmd_hsp_error = OpenStudio::Model::EnergyManagementSystemActuator.new(sch_hsp_error,
                                                                        'Schedule:Year',
                                                                        'Schedule Value')
  cmd_hsp_error.setName("#{zone_name}_cmd_hsp_error")

  #####
  # List of global variables used in EMS scripts
  ####

  # Proportional  gain constant (recommended 0.3 or less).
  prp_k = model.getEnergyManagementSystemGlobalVariableByName('prp_k')
  if prp_k.is_initialized
    prp_k = prp_k.get
  else
    prp_k = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, 'prp_k')
  end

  # Upper slab temperature setpoint limit (recommended no higher than 29C (84F))
  upper_slab_sp_lim = model.getEnergyManagementSystemGlobalVariableByName('upper_slab_sp_lim')
  if upper_slab_sp_lim.is_initialized
    upper_slab_sp_lim = upper_slab_sp_lim.get
  else
    upper_slab_sp_lim = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, 'upper_slab_sp_lim')
  end

  # Lower slab temperature setpoint limit (recommended no lower than 19C (66F))
  lower_slab_sp_lim = model.getEnergyManagementSystemGlobalVariableByName('lower_slab_sp_lim')
  if lower_slab_sp_lim.is_initialized
    lower_slab_sp_lim = lower_slab_sp_lim.get
  else
    lower_slab_sp_lim = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, 'lower_slab_sp_lim')
  end

  # Temperature offset used as a safety factor for thermal control (recommend 0.5C (1F)).
  ctrl_temp_offset = model.getEnergyManagementSystemGlobalVariableByName('ctrl_temp_offset')
  if ctrl_temp_offset.is_initialized
    ctrl_temp_offset = ctrl_temp_offset.get
  else
    ctrl_temp_offset = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, 'ctrl_temp_offset')
  end

  # Hour where slab setpoint is to be changed
  hour_of_slab_sp_change = model.getEnergyManagementSystemGlobalVariableByName('hour_of_slab_sp_change')
  if hour_of_slab_sp_change.is_initialized
    hour_of_slab_sp_change = hour_of_slab_sp_change.get
  else
    hour_of_slab_sp_change = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, 'hour_of_slab_sp_change')
  end

  #####
  # List of zone specific variables used in EMS scripts
  ####

  # Maximum 'measured' temperature in zone during occupied times. Default setup uses mean air temperature.
  # Other possible choices are operative and mean radiant temperature.
  zone_max_ctrl_temp = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "#{zone_name}_max_ctrl_temp")

  # Minimum 'measured' temperature in zone during occupied times. Default setup uses mean air temperature.
  # Other possible choices are operative and mean radiant temperature.
  zone_min_ctrl_temp = OpenStudio::Model::EnergyManagementSystemGlobalVariable.new(model, "#{zone_name}_min_ctrl_temp")

  #####
  # List of 'sensors' used in the EMS programs
  ####

  # Controlled zone temperature for the zone.
  zone_ctrl_temperature = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Air Temperature')
  zone_ctrl_temperature.setName("#{zone_name}_ctrl_temperature")
  zone_ctrl_temperature.setKeyName(zone.name.get)

  # check for zone thermostat and replace heat/cool schedules for radiant system control
  # if there is no zone thermostat, then create one
  zone_thermostat = zone.thermostatSetpointDualSetpoint

  if zone_thermostat.is_initialized
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Replacing thermostat schedules in zone #{zone.name} for radiant system control.")
    zone_thermostat = zone.thermostatSetpointDualSetpoint.get
  else
    OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Zone #{zone.name} does not have a thermostat. Creating a thermostat for radiant system control.")
    zone_thermostat = OpenStudio::Model::ThermostatSetpointDualSetpoint.new(model)
    zone_thermostat.setName("#{zone_name}_Thermostat_DualSetpoint")
  end

  # create new heating and cooling schedules to be used with all radiant systems
  zone_htg_thermostat = model.getScheduleRulesetByName('Radiant System Heating Setpoint')
  if zone_htg_thermostat.is_initialized
    zone_htg_thermostat = zone_htg_thermostat.get
  else
    zone_htg_thermostat = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                          20.0,
                                                                                          name: 'Radiant System Heating Setpoint',
                                                                                          schedule_type_limit: 'Temperature')
  end

  zone_clg_thermostat = model.getScheduleRulesetByName('Radiant System Cooling Setpoint')
  if zone_clg_thermostat.is_initialized
    zone_clg_thermostat = zone_clg_thermostat.get
  else
    zone_clg_thermostat = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
                                                                                          26.0,
                                                                                          name: 'Radiant System Cooling Setpoint',
                                                                                          schedule_type_limit: 'Temperature')
  end

  # implement new heating and cooling schedules
  zone_thermostat.setHeatingSetpointTemperatureSchedule(zone_htg_thermostat)
  zone_thermostat.setCoolingSetpointTemperatureSchedule(zone_clg_thermostat)

  # Upper comfort limit for the zone. Taken from existing thermostat schedules in the zone.
  zone_upper_comfort_limit = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
  zone_upper_comfort_limit.setName("#{zone_name}_upper_comfort_limit")
  zone_upper_comfort_limit.setKeyName(zone_clg_thermostat.name.get)

  # Lower comfort limit for the zone. Taken from existing thermostat schedules in the zone.
  zone_lower_comfort_limit = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
  zone_lower_comfort_limit.setName("#{zone_name}_lower_comfort_limit")
  zone_lower_comfort_limit.setKeyName(zone_htg_thermostat.name.get)

  # Radiant system water flow rate used to determine if there is active hydronic cooling in the radiant system.
  zone_rad_cool_operation = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'System Node Mass Flow Rate')
  zone_rad_cool_operation.setName("#{zone_name}_rad_cool_operation")
  zone_rad_cool_operation.setKeyName(coil_cooling_radiant.to_StraightComponent.get.inletModelObject.get.name.get)

  # Radiant system water flow rate used to determine if there is active hydronic heating in the radiant system.
  zone_rad_heat_operation = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'System Node Mass Flow Rate')
  zone_rad_heat_operation.setName("#{zone_name}_rad_heat_operation")
  zone_rad_heat_operation.setKeyName(coil_heating_radiant.to_StraightComponent.get.inletModelObject.get.name.get)

  # Radiant system switchover delay time period schedule
  # used to determine if there is active hydronic cooling/heating in the radiant system.
  zone_rad_switch_over = model.getEnergyManagementSystemSensorByName('radiant_switch_over_time')

  unless zone_rad_switch_over.is_initialized
    zone_rad_switch_over = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
    zone_rad_switch_over.setName('radiant_switch_over_time')
    zone_rad_switch_over.setKeyName(sch_radiant_switchover.name.get)
  end

  # Last 24 hours trend for radiant system in cooling mode.
  zone_rad_cool_operation_trend = OpenStudio::Model::EnergyManagementSystemTrendVariable.new(model, zone_rad_cool_operation)
  zone_rad_cool_operation_trend.setName("#{zone_name}_rad_cool_operation_trend")
  zone_rad_cool_operation_trend.setNumberOfTimestepsToBeLogged(zone_timestep * 48)

  # Last 24 hours trend for radiant system in heating mode.
  zone_rad_heat_operation_trend = OpenStudio::Model::EnergyManagementSystemTrendVariable.new(model, zone_rad_heat_operation)
  zone_rad_heat_operation_trend.setName("#{zone_name}_rad_heat_operation_trend")
  zone_rad_heat_operation_trend.setNumberOfTimestepsToBeLogged(zone_timestep * 48)

  # use zone occupancy objects for radiant system control if selected
  if use_zone_occupancy_for_control

    # get annual occupancy schedule for zone
    occ_schedule_ruleset = OpenstudioStandards::ThermalZone.thermal_zone_get_occupancy_schedule(zone,
                                                                                                sch_name: "#{zone.name} Radiant System Occupied Schedule",
                                                                                                occupied_percentage_threshold: occupied_percentage_threshold)
  else

    occ_schedule_ruleset = model.getScheduleRulesetByName('Whole Building Radiant System Occupied Schedule')
    if occ_schedule_ruleset.is_initialized
      occ_schedule_ruleset = occ_schedule_ruleset.get
    else
      # create occupancy schedules
      occ_schedule_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
      occ_schedule_ruleset.setName('Whole Building Radiant System Occupied Schedule')

      start_hour = model_occ_hr_end.to_i
      start_minute = ((model_occ_hr_end % 1) * 60).to_i
      end_hour = model_occ_hr_start.to_i
      end_minute = ((model_occ_hr_start % 1) * 60).to_i

      if end_hour > start_hour
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, start_hour, start_minute, 0), 1.0)
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, end_hour, end_minute, 0), 0.0)
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1.0) if end_hour < 24
      elsif start_hour > end_hour
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, end_hour, end_minute, 0), 0.0)
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, start_hour, start_minute, 0), 1.0)
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0.0) if start_hour < 24
      else
        occ_schedule_ruleset.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1.0)
      end
    end
  end

  # create ems sensor for zone occupied status
  zone_occupied_status = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
  zone_occupied_status.setName("#{zone_name}_occupied_status")
  zone_occupied_status.setKeyName(occ_schedule_ruleset.name.get)

  # Last 24 hours trend for zone occupied status
  zone_occupied_status_trend = OpenStudio::Model::EnergyManagementSystemTrendVariable.new(model, zone_occupied_status)
  zone_occupied_status_trend.setName("#{zone_name}_occupied_status_trend")
  zone_occupied_status_trend.setNumberOfTimestepsToBeLogged(zone_timestep * 48)

  #####
  # List of EMS programs to implement the proportional control for the radiant system.
  ####

  # Initialize global constant values used in EMS programs.
  set_constant_values_prg_body = <<-EMS
    SET prp_k              = #{proportional_gain},
    SET ctrl_temp_offset   = 0.5,
    SET upper_slab_sp_lim  = 29,
    SET lower_slab_sp_lim  = 19,
    SET hour_of_slab_sp_change = 18
  EMS

  set_constant_values_prg = model.getEnergyManagementSystemProgramByName('Set_Constant_Values')
  if set_constant_values_prg.is_initialized
    set_constant_values_prg = set_constant_values_prg.get
  else
    set_constant_values_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
    set_constant_values_prg.setName('Set_Constant_Values')
    set_constant_values_prg.setBody(set_constant_values_prg_body)
  end

  # Initialize zone specific constant values used in EMS programs.
  set_constant_zone_values_prg_body = <<-EMS
    SET #{zone_name}_max_ctrl_temp      = #{zone_name}_lower_comfort_limit,
    SET #{zone_name}_min_ctrl_temp      = #{zone_name}_upper_comfort_limit,
    SET #{zone_name}_cmd_csp_error      = 0,
    SET #{zone_name}_cmd_hsp_error      = 0,
    SET #{zone_name}_cmd_cold_water_ctrl = #{zone_name}_upper_comfort_limit,
    SET #{zone_name}_cmd_hot_water_ctrl  = #{zone_name}_lower_comfort_limit
  EMS

  set_constant_zone_values_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
  set_constant_zone_values_prg.setName("#{zone_name}_Set_Constant_Values")
  set_constant_zone_values_prg.setBody(set_constant_zone_values_prg_body)

  # Calculate maximum and minimum 'measured' controlled temperature in the zone
  calculate_minmax_ctrl_temp_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
  calculate_minmax_ctrl_temp_prg.setName("#{zone_name}_Calculate_Extremes_In_Zone")
  calculate_minmax_ctrl_temp_prg_body = <<-EMS
    IF (#{zone_name}_occupied_status == 1),
        IF #{zone_name}_ctrl_temperature > #{zone_name}_max_ctrl_temp,
            SET #{zone_name}_max_ctrl_temp = #{zone_name}_ctrl_temperature,
        ENDIF,
        IF #{zone_name}_ctrl_temperature < #{zone_name}_min_ctrl_temp,
            SET #{zone_name}_min_ctrl_temp = #{zone_name}_ctrl_temperature,
        ENDIF,
    ELSE,
      SET #{zone_name}_max_ctrl_temp = #{zone_name}_lower_comfort_limit,
      SET #{zone_name}_min_ctrl_temp = #{zone_name}_upper_comfort_limit,
    ENDIF
  EMS
  calculate_minmax_ctrl_temp_prg.setBody(calculate_minmax_ctrl_temp_prg_body)

  # Calculate errors from comfort zone limits and 'measured' controlled temperature in the zone.
  calculate_errors_from_comfort_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
  calculate_errors_from_comfort_prg.setName("#{zone_name}_Calculate_Errors_From_Comfort")
  calculate_errors_from_comfort_prg_body = <<-EMS
    IF (CurrentTime == (hour_of_slab_sp_change - ZoneTimeStep)),
        SET #{zone_name}_cmd_csp_error = (#{zone_name}_upper_comfort_limit - ctrl_temp_offset) - #{zone_name}_max_ctrl_temp,
        SET #{zone_name}_cmd_hsp_error = (#{zone_name}_lower_comfort_limit + ctrl_temp_offset) - #{zone_name}_min_ctrl_temp,
    ENDIF
  EMS
  calculate_errors_from_comfort_prg.setBody(calculate_errors_from_comfort_prg_body)

  # Calculate the new active slab temperature setpoint for heating and cooling
  calculate_slab_ctrl_setpoint_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
  calculate_slab_ctrl_setpoint_prg.setName("#{zone_name}_Calculate_Slab_Ctrl_Setpoint")
  calculate_slab_ctrl_setpoint_prg_body = <<-EMS
    SET #{zone_name}_cont_cool_oper = @TrendSum #{zone_name}_rad_cool_operation_trend radiant_switch_over_time/ZoneTimeStep,
    SET #{zone_name}_cont_heat_oper = @TrendSum #{zone_name}_rad_heat_operation_trend radiant_switch_over_time/ZoneTimeStep,
    SET #{zone_name}_occupied_hours = @TrendSum #{zone_name}_occupied_status_trend 24/ZoneTimeStep,
    IF (#{zone_name}_cont_cool_oper > 0) && (#{zone_name}_occupied_hours > 0) && (CurrentTime == hour_of_slab_sp_change),
      SET #{zone_name}_cmd_hot_water_ctrl = #{zone_name}_cmd_hot_water_ctrl + (#{zone_name}_cmd_csp_error*prp_k),
    ELSEIF (#{zone_name}_cont_heat_oper > 0) && (#{zone_name}_occupied_hours > 0) && (CurrentTime == hour_of_slab_sp_change),
      SET #{zone_name}_cmd_hot_water_ctrl = #{zone_name}_cmd_hot_water_ctrl + (#{zone_name}_cmd_hsp_error*prp_k),
    ELSE,
      SET #{zone_name}_cmd_hot_water_ctrl = #{zone_name}_cmd_hot_water_ctrl,
    ENDIF,
    IF (#{zone_name}_cmd_hot_water_ctrl < lower_slab_sp_lim),
      SET #{zone_name}_cmd_hot_water_ctrl = lower_slab_sp_lim,
    ELSEIF (#{zone_name}_cmd_hot_water_ctrl > upper_slab_sp_lim),
      SET #{zone_name}_cmd_hot_water_ctrl = upper_slab_sp_lim,
    ENDIF,
    SET #{zone_name}_cmd_cold_water_ctrl = #{zone_name}_cmd_hot_water_ctrl + 0.01
  EMS
  calculate_slab_ctrl_setpoint_prg.setBody(calculate_slab_ctrl_setpoint_prg_body)

  #####
  # List of EMS program manager objects
  ####

  initialize_constant_parameters = model.getEnergyManagementSystemProgramCallingManagerByName('Initialize_Constant_Parameters')
  if initialize_constant_parameters.is_initialized
    initialize_constant_parameters = initialize_constant_parameters.get
    # add program if it does not exist in manager
    existing_program_names = initialize_constant_parameters.programs.collect { |prg| prg.name.get.downcase }
    unless existing_program_names.include? set_constant_values_prg.name.get.downcase
      initialize_constant_parameters.addProgram(set_constant_values_prg)
    end
  else
    initialize_constant_parameters = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
    initialize_constant_parameters.setName('Initialize_Constant_Parameters')
    initialize_constant_parameters.setCallingPoint('BeginNewEnvironment')
    initialize_constant_parameters.addProgram(set_constant_values_prg)
  end

  initialize_constant_parameters_after_warmup = model.getEnergyManagementSystemProgramCallingManagerByName('Initialize_Constant_Parameters_After_Warmup')
  if initialize_constant_parameters_after_warmup.is_initialized
    initialize_constant_parameters_after_warmup = initialize_constant_parameters_after_warmup.get
    # add program if it does not exist in manager
    existing_program_names = initialize_constant_parameters_after_warmup.programs.collect { |prg| prg.name.get.downcase }
    unless existing_program_names.include? set_constant_values_prg.name.get.downcase
      initialize_constant_parameters_after_warmup.addProgram(set_constant_values_prg)
    end
  else
    initialize_constant_parameters_after_warmup = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
    initialize_constant_parameters_after_warmup.setName('Initialize_Constant_Parameters_After_Warmup')
    initialize_constant_parameters_after_warmup.setCallingPoint('AfterNewEnvironmentWarmUpIsComplete')
    initialize_constant_parameters_after_warmup.addProgram(set_constant_values_prg)
  end

  zone_initialize_constant_parameters = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
  zone_initialize_constant_parameters.setName("#{zone_name}_Initialize_Constant_Parameters")
  zone_initialize_constant_parameters.setCallingPoint('BeginNewEnvironment')
  zone_initialize_constant_parameters.addProgram(set_constant_zone_values_prg)

  zone_initialize_constant_parameters_after_warmup = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
  zone_initialize_constant_parameters_after_warmup.setName("#{zone_name}_Initialize_Constant_Parameters_After_Warmup")
  zone_initialize_constant_parameters_after_warmup.setCallingPoint('AfterNewEnvironmentWarmUpIsComplete')
  zone_initialize_constant_parameters_after_warmup.addProgram(set_constant_zone_values_prg)

  average_building_temperature = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
  average_building_temperature.setName("#{zone_name}_Average_Building_Temperature")
  average_building_temperature.setCallingPoint('EndOfZoneTimestepAfterZoneReporting')
  average_building_temperature.addProgram(calculate_minmax_ctrl_temp_prg)
  average_building_temperature.addProgram(calculate_errors_from_comfort_prg)

  programs_at_beginning_of_timestep = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
  programs_at_beginning_of_timestep.setName("#{zone_name}_Programs_At_Beginning_Of_Timestep")
  programs_at_beginning_of_timestep.setCallingPoint('BeginTimestepBeforePredictor')
  programs_at_beginning_of_timestep.addProgram(calculate_slab_ctrl_setpoint_prg)

  #####
  # List of variables for output.
  ####

  zone_max_ctrl_temp_output = OpenStudio::Model::EnergyManagementSystemOutputVariable.new(model, zone_max_ctrl_temp)
  zone_max_ctrl_temp_output.setName("#{zone_name} Maximum occupied temperature in zone")
  zone_min_ctrl_temp_output = OpenStudio::Model::EnergyManagementSystemOutputVariable.new(model, zone_min_ctrl_temp)
  zone_min_ctrl_temp_output.setName("#{zone_name} Minimum occupied temperature in zone")
end

.pump_get_brake_horsepower(pump) ⇒ Double

Determines the brake horsepower of the pump based on flow rate, pressure rise, and impeller efficiency.

Parameters:

  • pump (OpenStudio::Model::StraightComponent)

    pump object, allowable types: PumpConstantSpeed, PumpVariableSpeed

Returns:

  • (Double)

    brake horsepower



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/openstudio-standards/hvac/components/pump.rb', line 72

def self.pump_get_brake_horsepower(pump)
  # Get flow rate (whether autosized or hard-sized)
  flow_m3_per_s = OpenstudioStandards::HVAC.pump_get_rated_flow_rate(pump)

  # E+ default impeller efficiency
  # http://bigladdersoftware.com/epx/docs/8-4/engineering-reference/component-sizing.html#pump-sizing
  impeller_eff = 0.78

  # Get the pressure rise (Pa)
  pressure_rise_pa = pump.ratedPumpHead

  # Calculate the pump power (W)
  pump_power_w = pressure_rise_pa * flow_m3_per_s / impeller_eff

  # Convert to HP
  pump_power_hp = pump_power_w / 745.7 # 745.7 W/HP

  return pump_power_hp
end

.pump_get_motor_horsepower(pump) ⇒ Double

Determines the horsepower of the pump motor, including motor efficiency and pump impeller efficiency.

Parameters:

  • pump (OpenStudio::Model::StraightComponent)

    pump object, allowable types: PumpConstantSpeed, PumpVariableSpeed

Returns:

  • (Double)

    motor horsepower



97
98
99
100
101
102
103
104
105
# File 'lib/openstudio-standards/hvac/components/pump.rb', line 97

def self.pump_get_motor_horsepower(pump)
  # Get the pump power
  pump_power_w = OpenstudioStandards::HVAC.pump_get_power(pump)

  # Convert to HP
  pump_hp = pump_power_w / 745.7 # 745.7 W/HP

  return pump_hp
end

.pump_get_power(pump) ⇒ Double

Determines the pump power (W) based on flow rate, pressure rise, and total pump efficiency(impeller eff * motor eff). Uses the E+ default assumption of 0.78 impeller efficiency.

Parameters:

  • pump (OpenStudio::Model::StraightComponent)

    pump object, allowable types: PumpConstantSpeed, PumpVariableSpeed

Returns:

  • (Double)

    pump power in watts



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/openstudio-standards/hvac/components/pump.rb', line 41

def self.pump_get_power(pump)
  # Get flow rate (whether autosized or hard-sized)
  flow_m3_per_s = OpenstudioStandards::HVAC.pump_get_rated_flow_rate(pump)

  # E+ default impeller efficiency
  # http://bigladdersoftware.com/epx/docs/8-4/engineering-reference/component-sizing.html#pump-sizing
  impeller_eff = 0.78

  # Get the motor efficiency
  motor_eff = pump.motorEfficiency

  # Calculate the total efficiency
  # which includes both motor and
  # impeller efficiency.
  pump_total_eff = impeller_eff * motor_eff

  # Get the pressure rise (Pa)
  pressure_rise_pa = pump.ratedPumpHead

  # Calculate the pump power (W)
  pump_power_w = pressure_rise_pa * flow_m3_per_s / pump_total_eff

  return pump_power_w
end

.pump_get_rated_flow_rate(pump) ⇒ Object

Get the pump flow rate (whether autosized or hard-sized)

return [Double] flow rate in m^3/s

Parameters:

  • pump (OpenStudio::Model::StraightComponent)

    pump object, allowable types: PumpConstantSpeed, PumpVariableSpeed, HeaderedPumpsConstantSpeed, HeaderedPumpsVariableSpeed



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/openstudio-standards/hvac/components/pump.rb', line 11

def self.pump_get_rated_flow_rate(pump)
  flow_m3_per_s = 0.0
  if pump.to_PumpVariableSpeed.is_initialized || pump.to_PumpConstantSpeed.is_initialized
    if pump.ratedFlowRate.is_initialized
      flow_m3_per_s = pump.ratedFlowRate.get
    elsif pump.autosizedRatedFlowRate.is_initialized
      flow_m3_per_s = pump.autosizedRatedFlowRate.get
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.pump', "For #{pump.name}, could not find rated pump Flow Rate.")
    end
  elsif pump.to_HeaderedPumpsVariableSpeed.is_initialized || pump.to_HeaderedPumpsConstantSpeed.is_initialized
    if pump.totalRatedFlowRate.is_initialized
      flow_m3_per_s = pump.totalRatedFlowRate.get
    elsif pump.autosizedTotalRatedFlowRate.is_initialized
      flow_m3_per_s = pump.autosizedTotalRatedFlowRate.get
    else
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.pump', "For #{pump.name}, could not find rated pump Flow Rate.")
    end
  end

  return flow_m3_per_s
end

.pump_get_rated_w_per_gpm(pump) ⇒ Double

Determines the rated watts per GPM of the pump

Parameters:

  • pump (OpenStudio::Model::StraightComponent)

    pump object, allowable types: PumpConstantSpeed, PumpVariableSpeed

Returns:

  • (Double)

    rated power consumption per flow in watts per gpm, W*min/gal



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/openstudio-standards/hvac/components/pump.rb', line 112

def self.pump_get_rated_w_per_gpm(pump)
  # Get design power (whether autosized or hard-sized)
  rated_power_w = 0
  if pump.ratedPowerConsumption.is_initialized
    rated_power_w = pump.ratedPowerConsumption.get
  elsif pump.autosizedRatedPowerConsumption.is_initialized
    rated_power_w = pump.autosizedRatedPowerConsumption.get
  else
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC.pump', "For #{pump.name}, could not find rated pump power consumption, cannot determine w per gpm correctly.")
    return 0.0
  end

  rated_m3_per_s = OpenstudioStandards::HVAC.pump_get_rated_flow_rate(pump)
  rated_w_per_m3s = rated_power_w / rated_m3_per_s
  rated_w_per_gpm = OpenStudio.convert(rated_w_per_m3s, 'W*s/m^3', 'W*min/gal').get

  return rated_w_per_gpm
end

.pump_variable_speed_set_control_type(pump_variable_speed, control_type: 'Riding Curve') ⇒ Boolean

Set the pump curve coefficients based on the specified control type.

Parameters:

  • pump_variable_speed (OpenStudio::Model::PumpVariableSpeed, OpenStudio::Model::HeaderedPumpsVariableSpeed)

    variable speed pump or headered variable speed pumps object

  • control_type (String) (defaults to: 'Riding Curve')

    valid choices are Riding Curve, VSD No Reset, VSD DP Reset

Returns:

  • (Boolean)

    returns true if successful, false if not



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/openstudio-standards/hvac/components/pump.rb', line 136

def self.pump_variable_speed_set_control_type(pump_variable_speed, control_type: 'Riding Curve')
  # Determine the coefficients
  coeff_a = nil
  coeff_b = nil
  coeff_c = nil
  coeff_d = nil
  case control_type
  when 'Constant Flow'
    coeff_a = 0.0
    coeff_b = 1.0
    coeff_c = 0.0
    coeff_d = 0.0
  when 'Riding Curve'
    coeff_a = 0.0
    coeff_b = 3.2485
    coeff_c = -4.7443
    coeff_d = 2.5294
  when 'VSD No Reset'
    coeff_a = 0.0
    coeff_b = 0.5726
    coeff_c = -0.301
    coeff_d = 0.7347
  when 'VSD DP Reset'
    coeff_a = 0.0
    coeff_b = 0.0205
    coeff_c = 0.4101
    coeff_d = 0.5753
  else
    OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.HVAC.pump', "Pump control type '#{control_type}' not recognized, pump coefficients will not be changed.")
    return false
  end

  # Set the coefficients
  pump_variable_speed.setCoefficient1ofthePartLoadPerformanceCurve(coeff_a)
  pump_variable_speed.setCoefficient2ofthePartLoadPerformanceCurve(coeff_b)
  pump_variable_speed.setCoefficient3ofthePartLoadPerformanceCurve(coeff_c)
  pump_variable_speed.setCoefficient4ofthePartLoadPerformanceCurve(coeff_d)
  pump_variable_speed.setPumpControlType('Intermittent')

  # Append the control type to the pump name
  # self.setName("#{self.name} #{control_type}")

  return true
end

.remove_air_loops(model) ⇒ OpenStudio::Model::Model

Remove all air loops in model

Parameters:

Returns:



11
12
13
14
15
# File 'lib/openstudio-standards/hvac/helpers.rb', line 11

def self.remove_air_loops(model)
  model.getAirLoopHVACs.each(&:remove)

  return model
end

.remove_all_hvac(model) ⇒ OpenStudio::Model::Model

Remove all HVAC equipment including service hot water loops and zone exhaust fans

Parameters:

Returns:



121
122
123
124
125
126
127
128
129
# File 'lib/openstudio-standards/hvac/helpers.rb', line 121

def self.remove_all_hvac(model)
  OpenstudioStandards::HVAC.remove_air_loops(model)
  OpenstudioStandards::HVAC.remove_all_plant_loops(model)
  OpenstudioStandards::HVAC.remove_vrf(model)
  OpenstudioStandards::HVAC.remove_all_zone_equipment(model)
  OpenstudioStandards::HVAC.remove_unused_curves(model)

  return model
end

.remove_all_plant_loops(model) ⇒ OpenStudio::Model::Model

Remove all plant loops in model including those used for service hot water

Parameters:

Returns:



42
43
44
45
46
# File 'lib/openstudio-standards/hvac/helpers.rb', line 42

def self.remove_all_plant_loops(model)
  model.getPlantLoops.each(&:remove)

  return model
end

.remove_all_zone_equipment(model) ⇒ OpenStudio::Model::Model

Remove all zone equipment including exhaust fans

Parameters:

Returns:



81
82
83
84
85
86
87
# File 'lib/openstudio-standards/hvac/helpers.rb', line 81

def self.remove_all_zone_equipment(model)
  model.getThermalZones.each do |zone|
    zone.equipment.each(&:remove)
  end

  return model
end

.remove_hvac(model) ⇒ OpenStudio::Model::Model

Remove HVAC equipment except for service hot water loops and zone exhaust fans

Parameters:

Returns:



107
108
109
110
111
112
113
114
115
# File 'lib/openstudio-standards/hvac/helpers.rb', line 107

def self.remove_hvac(model)
  OpenstudioStandards::HVAC.remove_air_loops(model)
  OpenstudioStandards::HVAC.remove_plant_loops(model)
  OpenstudioStandards::HVAC.remove_vrf(model)
  OpenstudioStandards::HVAC.remove_zone_equipment(model)
  OpenstudioStandards::HVAC.remove_unused_curves(model)

  return model
end

.remove_plant_loops(model) ⇒ OpenStudio::Model::Model

Remove plant loops in model except those used for service hot water

Parameters:

Returns:



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/openstudio-standards/hvac/helpers.rb', line 21

def self.remove_plant_loops(model)
  plant_loops = model.getPlantLoops
  plant_loops.each do |plant_loop|
    shw_use = false
    plant_loop.demandComponents.each do |component|
      if component.to_WaterUseConnections.is_initialized || component.to_CoilWaterHeatingDesuperheater.is_initialized
        shw_use = true
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "#{plant_loop.name} is used for SHW or refrigeration heat reclaim and will not be removed.")
        break
      end
    end
    plant_loop.remove unless shw_use
  end

  return model
end

.remove_unused_curves(model) ⇒ OpenStudio::Model::Model

Remove unused performance curves

Parameters:

Returns:



93
94
95
96
97
98
99
100
101
# File 'lib/openstudio-standards/hvac/helpers.rb', line 93

def self.remove_unused_curves(model)
  model.getCurves.each do |curve|
    if curve.directUseCount == 0
      model.removeObject(curve.handle)
    end
  end

  return model
end

.remove_vrf(model) ⇒ OpenStudio::Model::Model

Remove VRF units

Parameters:

Returns:



52
53
54
55
56
57
# File 'lib/openstudio-standards/hvac/helpers.rb', line 52

def self.remove_vrf(model)
  model.getAirConditionerVariableRefrigerantFlows.each(&:remove)
  model.getZoneHVACTerminalUnitVariableRefrigerantFlows.each(&:remove)

  return model
end

.remove_zone_equipment(model) ⇒ OpenStudio::Model::Model

Remove zone equipment except for exhaust fans

Parameters:

Returns:



63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/openstudio-standards/hvac/helpers.rb', line 63

def self.remove_zone_equipment(model)
  model.getThermalZones.each do |zone|
    zone.equipment.each do |equipment|
      if equipment.to_FanZoneExhaust.is_initialized
        OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "#{equipment.name} is a zone exhaust fan and will not be removed.")
      else
        equipment.remove
      end
    end
  end

  return model
end

.rename_air_loop_nodes(model) ⇒ OpenStudio::Model::Model

renames air loop nodes to readable values

Parameters:

Returns:



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/openstudio-standards/hvac/helpers.rb', line 135

def self.rename_air_loop_nodes(model)
  # rename all hvac components on air loops
  model.getHVACComponents.sort.each do |component|
    next if component.to_Node.is_initialized # skip nodes

    unless component.airLoopHVAC.empty?
      # rename water to air component outlet nodes
      if component.to_WaterToAirComponent.is_initialized
        component = component.to_WaterToAirComponent.get
        unless component.airOutletModelObject.empty?
          component_outlet_object = component.airOutletModelObject.get
          next unless component_outlet_object.to_Node.is_initialized

          component_outlet_object.setName("#{component.name} Outlet Air Node")
        end
      end

      # rename air to air component nodes
      if component.to_AirToAirComponent.is_initialized
        component = component.to_AirToAirComponent.get
        unless component.primaryAirOutletModelObject.empty?
          component_outlet_object = component.primaryAirOutletModelObject.get
          next unless component_outlet_object.to_Node.is_initialized

          component_outlet_object.setName("#{component.name} Primary Outlet Air Node")
        end
        unless component.secondaryAirInletModelObject.empty?
          component_inlet_object = component.secondaryAirInletModelObject.get
          next unless component_inlet_object.to_Node.is_initialized

          component_inlet_object.setName("#{component.name} Secondary Inlet Air Node")
        end
      end

      # rename straight component outlet nodes
      if component.to_StraightComponent.is_initialized && !component.to_StraightComponent.get.outletModelObject.empty?
        component_outlet_object = component.to_StraightComponent.get.outletModelObject.get
        next unless component_outlet_object.to_Node.is_initialized

        component_outlet_object.setName("#{component.name} Outlet Air Node")
      end
    end

    # rename zone hvac component nodes
    if component.to_ZoneHVACComponent.is_initialized
      component = component.to_ZoneHVACComponent.get
      unless component.airInletModelObject.empty?
        component_inlet_object = component.airInletModelObject.get
        next unless component_inlet_object.to_Node.is_initialized

        component_inlet_object.setName("#{component.name} Inlet Air Node")
      end
      unless component.airOutletModelObject.empty?
        component_outlet_object = component.airOutletModelObject.get
        next unless component_outlet_object.to_Node.is_initialized

        component_outlet_object.setName("#{component.name} Outlet Air Node")
      end
    end
  end

  # rename supply side nodes
  model.getAirLoopHVACs.sort.each do |air_loop|
    air_loop_name = air_loop.name.to_s
    air_loop.demandInletNode.setName("#{air_loop_name} Demand Inlet Node")
    air_loop.demandOutletNode.setName("#{air_loop_name} Demand Outlet Node")
    air_loop.supplyInletNode.setName("#{air_loop_name} Supply Inlet Node")
    air_loop.supplyOutletNode.setName("#{air_loop_name} Supply Outlet Node")

    unless air_loop.reliefAirNode.empty?
      relief_node = air_loop.reliefAirNode.get
      relief_node.setName("#{air_loop_name} Relief Air Node")
    end

    unless air_loop.mixedAirNode.empty?
      mixed_node = air_loop.mixedAirNode.get
      mixed_node.setName("#{air_loop_name} Mixed Air Node")
    end

    # rename outdoor air system and nodes
    unless air_loop.airLoopHVACOutdoorAirSystem.empty?
      oa_system = air_loop.airLoopHVACOutdoorAirSystem.get
      unless oa_system.outboardOANode.empty?
        oa_node = oa_system.outboardOANode.get
        oa_node.setName("#{air_loop_name} Outdoor Air Node")
      end
    end
  end

  # rename zone air and terminal nodes
  model.getThermalZones.sort.each do |zone|
    zone.zoneAirNode.setName("#{zone.name} Zone Air Node")

    unless zone.returnAirModelObject.empty?
      zone.returnAirModelObject.get.setName("#{zone.name} Return Air Node")
    end

    unless zone.airLoopHVACTerminal.empty?
      terminal_unit = zone.airLoopHVACTerminal.get
      if terminal_unit.to_StraightComponent.is_initialized
        component = terminal_unit.to_StraightComponent.get
        component.inletModelObject.get.setName("#{terminal_unit.name} Inlet Air Node")
      end
    end
  end

  # rename zone equipment list objects
  model.getZoneHVACEquipmentLists.sort.each do |obj|
    begin
      zone = obj.thermalZone
      obj.setName("#{zone.name} Zone HVAC Equipment List")
    rescue StandardError => e
      OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Removing ZoneHVACEquipmentList #{obj.name}; missing thermal zone.")
      obj.remove
    end
  end

  return model
end

.rename_plant_loop_nodes(model) ⇒ OpenStudio::Model::Model

renames plant loop nodes to readable values

Parameters:

Returns:



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/openstudio-standards/hvac/helpers.rb', line 259

def self.rename_plant_loop_nodes(model)
  # rename all hvac components on plant loops
  model.getHVACComponents.sort.each do |component|
    next if component.to_Node.is_initialized # skip nodes

    unless component.plantLoop.empty?
      # rename straight component nodes
      # some inlet or outlet nodes may get renamed again
      if component.to_StraightComponent.is_initialized
        unless component.to_StraightComponent.get.inletModelObject.empty?
          component_inlet_object = component.to_StraightComponent.get.inletModelObject.get
          next unless component_inlet_object.to_Node.is_initialized

          component_inlet_object.setName("#{component.name} Inlet Water Node")
        end
        unless component.to_StraightComponent.get.outletModelObject.empty?
          component_outlet_object = component.to_StraightComponent.get.outletModelObject.get
          next unless component_outlet_object.to_Node.is_initialized

          component_outlet_object.setName("#{component.name} Outlet Water Node")
        end
      end

      # rename water to air component nodes
      if component.to_WaterToAirComponent.is_initialized
        component = component.to_WaterToAirComponent.get
        unless component.waterInletModelObject.empty?
          component_inlet_object = component.waterInletModelObject.get
          next unless component_inlet_object.to_Node.is_initialized

          component_inlet_object.setName("#{component.name} Inlet Water Node")
        end
        unless component.waterOutletModelObject.empty?
          component_outlet_object = component.waterOutletModelObject.get
          next unless component_outlet_object.to_Node.is_initialized

          component_outlet_object.setName("#{component.name} Outlet Water Node")
        end
      end

      # rename water to water component nodes
      if component.to_WaterToWaterComponent.is_initialized
        component = component.to_WaterToWaterComponent.get
        unless component.demandInletModelObject.empty?
          demand_inlet_object = component.demandInletModelObject.get
          next unless demand_inlet_object.to_Node.is_initialized

          demand_inlet_object.setName("#{component.name} Demand Inlet Water Node")
        end
        unless component.demandOutletModelObject.empty?
          demand_outlet_object = component.demandOutletModelObject.get
          next unless demand_outlet_object.to_Node.is_initialized

          demand_outlet_object.setName("#{component.name} Demand Outlet Water Node")
        end
        unless component.supplyInletModelObject.empty?
          supply_inlet_object = component.supplyInletModelObject.get
          next unless supply_inlet_object.to_Node.is_initialized

          supply_inlet_object.setName("#{component.name} Supply Inlet Water Node")
        end
        unless component.supplyOutletModelObject.empty?
          supply_outlet_object = component.supplyOutletModelObject.get
          next unless supply_outlet_object.to_Node.is_initialized

          supply_outlet_object.setName("#{component.name} Supply Outlet Water Node")
        end
      end
    end
  end

  # rename plant nodes
  model.getPlantLoops.sort.each do |plant_loop|
    plant_loop_name = plant_loop.name.to_s
    plant_loop.demandInletNode.setName("#{plant_loop_name} Demand Inlet Node")
    plant_loop.demandOutletNode.setName("#{plant_loop_name} Demand Outlet Node")
    plant_loop.supplyInletNode.setName("#{plant_loop_name} Supply Inlet Node")
    plant_loop.supplyOutletNode.setName("#{plant_loop_name} Supply Outlet Node")
  end

  return model
end

.seer_to_cop(seer) ⇒ Double

Convert from SEER to COP (with fan) for cooling coils per the method specified in Thornton et al. 2011

Parameters:

  • seer (Double)

    seasonal energy efficiency ratio (SEER)

Returns:

  • (Double)

    Coefficient of Performance (COP)



35
36
37
38
39
40
# File 'lib/openstudio-standards/hvac/conversions.rb', line 35

def self.seer_to_cop(seer)
  eer = (-0.0182 * seer * seer) + (1.1088 * seer)
  cop = OpenstudioStandards::HVAC.eer_to_cop(eer)

  return cop
end

.seer_to_cop_no_fan(seer) ⇒ Double

Convert from SEER to COP (no fan) for cooling coils

Parameters:

  • seer (Double)

    seasonal energy efficiency ratio (SEER)

Returns:

  • (Double)

    Coefficient of Performance (COP)



12
13
14
15
16
# File 'lib/openstudio-standards/hvac/conversions.rb', line 12

def self.seer_to_cop_no_fan(seer)
  cop = (-0.0076 * seer * seer) + (0.3796 * seer)

  return cop
end

.setpoint_manager_min_max_temperature(spm) ⇒ Hash

Get the min and max setpoint values for a setpoint manager

Parameters:

  • spm (<OpenStudio::Model::SetpointManager>)

    OpenStudio SetpointManager object

Returns:

  • (Hash)

    returns as hash with ‘min_temp’ and ‘max_temp’ in degrees Fahrenheit



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/openstudio-standards/hvac/setpoint_managers/information.rb', line 11

def self.setpoint_manager_min_max_temperature(spm)
  # use @standard to not build each time
  std = Standard.build('90.1-2013') # unused; just to access methods
  # Determine the min and max design temperatures
  loop_op_min_f = nil
  loop_op_max_f = nil
  obj_type = spm.iddObjectType.valueName.to_s
  case obj_type
  when 'OS_SetpointManager_Scheduled'
    sch = spm.to_SetpointManagerScheduled.get.schedule
    if sch.to_ScheduleRuleset.is_initialized
      min_c = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(sch.to_ScheduleRuleset.get)['min']
      max_c = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(sch.to_ScheduleRuleset.get)['max']
    elsif sch.to_ScheduleConstant.is_initialized
      min_c = OpenstudioStandards::Schedules.schedule_constant_get_min_max(sch.to_ScheduleConstant.get)['min']
      max_c = OpenstudioStandards::Schedules.schedule_constant_get_min_max(sch.to_ScheduleConstant.get)['max']
    else
      OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "Could not find min and max values for #{obj_type} Setpoint Manager.")
    end
    loop_op_min_f = OpenStudio.convert(min_c, 'C', 'F').get
    loop_op_max_f = OpenStudio.convert(max_c, 'C', 'F').get
  when 'OS_SetpointManager_SingleZone_Reheat'
    spm = spm.to_SetpointManagerSingleZoneReheat.get
    loop_op_min_f = OpenStudio.convert(spm.minimumSupplyAirTemperature, 'C', 'F').get
    loop_op_max_f = OpenStudio.convert(spm.maximumSupplyAirTemperature, 'C', 'F').get
  when 'OS_SetpointManager_Warmest'
    spm = spm.to_SetpointManagerWarmest.get
    loop_op_min_f = OpenStudio.convert(spm.minimumSetpointTemperature, 'C', 'F').get
    loop_op_max_f = OpenStudio.convert(spm.maximumSetpointTemperature, 'C', 'F').get
  when 'OS_SetpointManager_WarmestTemperatureFlow'
    spm = spm.to_SetpointManagerWarmestTemperatureFlow.get
    loop_op_min_f = OpenStudio.convert(spm.minimumSetpointTemperature, 'C', 'F').get
    loop_op_max_f = OpenStudio.convert(spm.maximumSetpointTemperature, 'C', 'F').get
  when 'OS_SetpointManager_Scheduled_DualSetpoint'
    spm = spm.to_SetpointManagerScheduledDualSetpoint.get
    # Lowest setpoint is minimum of low schedule
    low_sch = spm.lowSetpointSchedule
    unless low_sch.empty?
      low_sch = low_sch.get
      min_c = nil
      if low_sch.to_ScheduleRuleset.is_initialized
        min_c = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(low_sch.to_ScheduleRuleset.get)['min']
      elsif low_sch.to_ScheduleConstant.is_initialized
        min_c = OpenstudioStandards::Schedules.schedule_constant_get_min_max(low_sch.to_ScheduleConstant.get)['min']
      else
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "Could not find min and max values for #{obj_type} Setpoint Manager.")
      end
      loop_op_min_f = OpenStudio.convert(min_c, 'C', 'F').get unless min_c.nil?
    end

    # highest setpoint it maximum of high schedule
    high_sch = spm.highSetpointSchedule
    unless high_sch.empty?
      high_sch = high_sch.get
      max_c = nil
      if high_sch.to_ScheduleRuleset.is_initialized
        max_c = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(high_sch.to_ScheduleRuleset.get)['max']
      elsif high_sch.to_ScheduleConstant.is_initialized
        max_c = OpenstudioStandards::Schedules.schedule_constant_get_min_max(high_sch.to_ScheduleConstant.get)['max']
      else
        OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "Could not find min and max values for #{obj_type} Setpoint Manager.")
      end
      loop_op_max_f = OpenStudio.convert(max_c, 'C', 'F').get unless max_c.nil?
    end
  when 'OS_SetpointManager_OutdoorAirReset'
    spm = spm.to_SetpointManagerOutdoorAirReset.get
    temp_1_f = OpenStudio.convert(spm.setpointatOutdoorHighTemperature, 'C', 'F').get
    temp_2_f = OpenStudio.convert(spm.setpointatOutdoorLowTemperature, 'C', 'F').get
    loop_op_min_f = [temp_1_f, temp_2_f].min
    loop_op_max_f = [temp_1_f, temp_2_f].max
  when 'OS_SetpointManager_FollowOutdoorAirTemperature'
    spm = spm.to_SetpointManagerFollowOutdoorAirTemperature.get
    loop_op_min_f = OpenStudio.convert(spm.minimumSetpointTemperature, 'C', 'F').get
    loop_op_max_f = OpenStudio.convert(spm.maximumSetpointTemperature, 'C', 'F').get
  else
    OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.HVAC', "Could not find min and max values for #{obj_type} Setpoint Manager.")
  end

  return { 'min_temp' => loop_op_min_f, 'max_temp' => loop_op_max_f }
end

.thermal_eff_to_afue(teff) ⇒ Double

A helper method to convert from thermal efficiency to AFUE

Parameters:

  • teff (Double)

    Thermal Efficiency

Returns:

  • (Double)

    AFUE



200
201
202
# File 'lib/openstudio-standards/hvac/conversions.rb', line 200

def self.thermal_eff_to_afue(teff)
  return teff
end

.thermal_eff_to_comb_eff(thermal_eff) ⇒ Double

A helper method to convert from thermal efficiency to combustion efficiency

Parameters:

  • thermal_eff (Double)

    Thermal efficiency

Returns:

  • (Double)

    Combustion efficiency



218
219
220
# File 'lib/openstudio-standards/hvac/conversions.rb', line 218

def self.thermal_eff_to_comb_eff(thermal_eff)
  return thermal_eff + 0.007
end

.unitary_system_min_max_temperature_value(unitary_system) ⇒ Hash

Returns the unitary system minimum and maximum design temperatures

Parameters:

  • unitary_system (<OpenStudio::Model::ModelObject>)

    OpenStudio ModelObject object

Returns:

  • (Hash)

    returns as hash with ‘min_temp’ and ‘max_temp’ in degrees Fahrenheit



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/openstudio-standards/hvac/air_loop/information.rb', line 47

def self.unitary_system_min_max_temperature_value(unitary_system)
  min_temp = nil
  max_temp = nil
  # Get the object type
  obj_type = unitary_system.iddObjectType.valueName.to_s
  case obj_type
  when 'OS_AirLoopHVAC_UnitarySystem'
    unitary_system = unitary_system.to_AirLoopHVACUnitarySystem.get
    if unitary_system.useDOASDXCoolingCoil
      min_temp = OpenStudio.convert(unitary_system.dOASDXCoolingCoilLeavingMinimumAirTemperature, 'C', 'F').get
    end
    if unitary_system.maximumSupplyAirTemperature.is_initialized
      max_temp = OpenStudio.convert(unitary_system.maximumSupplyAirTemperature.get, 'C', 'F').get
    end
  when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir'
    unitary_system = unitary_system.to_AirLoopHVACUnitaryHeatPumpAirToAir.get
    if unitary_system.maximumSupplyAirTemperaturefromSupplementalHeater.is_initialized
      max_temp = OpenStudio.convert(unitary_system.maximumSupplyAirTemperaturefromSupplementalHeater.get, 'C', 'F').get
    end
  when 'OS_AirLoopHVAC_UnitaryHeatPump_AirToAir_MultiSpeed'
    unitary_system = unitary_system.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get
    if unitary_system.maximumSupplyAirTemperaturefromSupplementalHeater.is_initialized
      max_temp = OpenStudio.convert(unitary_system.maximumSupplyAirTemperaturefromSupplementalHeater.get, 'C', 'F').get
    end
  when 'OS_AirLoopHVAC_UnitaryHeatCool_VAVChangeoverBypass'
    unitary_system = unitary_system.to_AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass.get
    min_temp = OpenStudio.convert(unitary_system.minimumOutletAirTemperatureDuringCoolingOperation, 'C', 'F').get
    max_temp = OpenStudio.convert(unitary_system.maximumOutletAirTemperatureDuringHeatingOperation, 'C', 'F').get
  end

  return { 'min_temp' => min_temp, 'max_temp' => max_temp }
end

.validate_effectiveness_hash(values_hash) ⇒ Object



110
111
112
113
114
# File 'lib/openstudio-standards/hvac/components/heat_exchanger_air_to_air.rb', line 110

def self.validate_effectiveness_hash(values_hash)
  values_hash.all? do |key, value|
    key.is_a?(Float) && value.is_a?(Float) && key.between?(0.0, 1.0) && value.between?(0.0, 1.0)
  end
end