openstudio-standards 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/openstudio-standards.rb +1 -0
- data/lib/openstudio-standards/prototypes/Prototype.Model.elevators.rb +16 -3
- data/lib/openstudio-standards/prototypes/Prototype.Model.hvac.rb +6 -1
- data/lib/openstudio-standards/prototypes/Prototype.Model.swh.rb +16 -2
- data/lib/openstudio-standards/prototypes/Prototype.hvac_systems.rb +2034 -115
- data/lib/openstudio-standards/prototypes/Prototype.utilities.rb +175 -0
- data/lib/openstudio-standards/standards/Standards.Model.rb +3 -3
- data/lib/openstudio-standards/standards/Standards.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +5 -1
- data/lib/openstudio-standards/utilities/sqlfile.rb +174 -0
- data/lib/openstudio-standards/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9adf83fdf660388355b3cd88f23406f39a8403a1
|
4
|
+
data.tar.gz: 3af3a9d22b9a1baa7b9bebf08f8f7fd4c9d37c2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 954448813364661199669a3e35206942c7c5b39a7a54c2dfbd796dbb19c74a7420ea1f4e340929fc4f28521562847a96c18cb766dc98d7532b08ea931604432b
|
7
|
+
data.tar.gz: f49fd09c22fa987ac45994f3bc59b792702fd24f4ab6711128e6cfc9ad58a5f021b9e86cfee11a7d72645fa414070726f315ab07848d236200995e5ed68e7a49
|
data/lib/openstudio-standards.rb
CHANGED
@@ -26,6 +26,7 @@ module OpenstudioStandards
|
|
26
26
|
require_relative 'openstudio-standards/utilities/logging'
|
27
27
|
require_relative 'openstudio-standards/utilities/simulation'
|
28
28
|
require_relative 'openstudio-standards/utilities/hash'
|
29
|
+
require_relative 'openstudio-standards/utilities/sqlfile'
|
29
30
|
|
30
31
|
# Load the Openstudio Standards JSON
|
31
32
|
# and assign to a constant. This
|
@@ -137,11 +137,11 @@ class OpenStudio::Model::Model
|
|
137
137
|
end
|
138
138
|
|
139
139
|
# assign schedules
|
140
|
-
if ["Office","
|
140
|
+
if ["Office","MediumOffice","MidriseApartment","HighriseApartment","SecondarySchool"].include?(building_type)
|
141
141
|
elevator_schedule = prototype_input['elevator_schedule']
|
142
142
|
elevator_fan_schedule = prototype_input['elevator_fan_schedule']
|
143
143
|
elevator_lights_schedule = prototype_input['elevator_fan_schedule']
|
144
|
-
elsif ["LargeHotel","Hospital"].include?(building_type)
|
144
|
+
elsif ["LargeHotel","Hospital","LargeOffice"].include?(building_type)
|
145
145
|
elevator_schedule = prototype_input['exterior_fuel_equipment1_schedule']
|
146
146
|
elevator_fan_schedule = prototype_input['exterior_fuel_equipment2_schedule']
|
147
147
|
elevator_lights_schedule = prototype_input['exterior_fuel_equipment2_schedule']
|
@@ -153,7 +153,20 @@ class OpenStudio::Model::Model
|
|
153
153
|
next if not building_type.include?(hash[:stds_bldg_type])
|
154
154
|
space_type_size[space_type] = hash[:floor_area]
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
|
+
# Get the largest space type
|
158
|
+
largest_space_type = space_type_size.key(space_type_size.values.max)
|
159
|
+
|
160
|
+
# Get the occ sch, if one is specified
|
161
|
+
occ_sch = nil
|
162
|
+
if largest_space_type.defaultScheduleSet.is_initialized
|
163
|
+
if largest_space_type.defaultScheduleSet.get.numberofPeopleSchedule.is_initialized
|
164
|
+
occ_sch = largest_space_type.defaultScheduleSet.get.numberofPeopleSchedule.get
|
165
|
+
end
|
166
|
+
else
|
167
|
+
occ_sch = alwaysOffDiscreteSchedule
|
168
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "No occupancy schedule was specified for #{largest_space_type.name}, an always off schedule will be used for the elvevators and the elevators will never run.")
|
169
|
+
end
|
157
170
|
|
158
171
|
# clone and assign to elevator
|
159
172
|
elev_sch = occ_sch.clone(self)
|
@@ -79,7 +79,7 @@ class OpenStudio::Model::Model
|
|
79
79
|
prototype_input['vav_fan_motor_efficiency'],
|
80
80
|
prototype_input['vav_fan_pressure_rise'],
|
81
81
|
return_plenum,
|
82
|
-
|
82
|
+
reheat_type = 'Water',
|
83
83
|
building_type)
|
84
84
|
|
85
85
|
when 'CAV'
|
@@ -240,6 +240,11 @@ class OpenStudio::Model::Model
|
|
240
240
|
prototype_input['doas_economizer_control_type'],
|
241
241
|
building_type)
|
242
242
|
|
243
|
+
add_four_pipe_fan_coil(template,
|
244
|
+
hot_water_loop,
|
245
|
+
chilled_water_loop,
|
246
|
+
thermal_zones)
|
247
|
+
|
243
248
|
when 'DC' # Data Center
|
244
249
|
|
245
250
|
# Retrieve the existing hot water loop
|
@@ -323,6 +323,13 @@ class OpenStudio::Model::Model
|
|
323
323
|
# next if no service water heating demand
|
324
324
|
next if not (gal_hr_per_area.to_f > 0.0 || gal_hr_peak_flow_rate.to_f > 0.0)
|
325
325
|
|
326
|
+
# If there is no SWH schedule specified, assume
|
327
|
+
# that there should be no SWH consumption for this space type.
|
328
|
+
unless flow_rate_fraction_schedule
|
329
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "No service water heating schedule was specified for #{space_type.name}, an always off schedule will be used and no water will be used.")
|
330
|
+
flow_rate_fraction_schedule = alwaysOffDiscreteSchedule
|
331
|
+
end
|
332
|
+
|
326
333
|
if (stds_bldg_type == "MidriseApartment" && stds_space_type.include?("Apartment")) || stds_bldg_type == "StripMall"
|
327
334
|
num_units = space_type_hash[space_type][:num_units].round
|
328
335
|
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding dedicated water heating fpr #{num_units} #{space_type.name} units, each with max flow rate of #{gal_hr_peak_flow_rate} gal/hr per.")
|
@@ -735,8 +742,15 @@ class OpenStudio::Model::Model
|
|
735
742
|
max_flow_rate_array = [] # gallons per hour
|
736
743
|
water_use_equipment_array.each do |water_use_equip|
|
737
744
|
water_use_equip_sch = water_use_equip.flowRateFractionSchedule
|
738
|
-
next if
|
739
|
-
water_use_equip_sch = water_use_equip_sch.get
|
745
|
+
next if water_use_equip_sch.empty?
|
746
|
+
water_use_equip_sch = water_use_equip_sch.get
|
747
|
+
if water_use_equip_sch.to_ScheduleRuleset.is_initialized
|
748
|
+
water_use_equip_sch = water_use_equip_sch.to_ScheduleRuleset.get
|
749
|
+
elsif water_use_equip_sch.to_ScheduleConstant.is_initialized
|
750
|
+
water_use_equip_sch = water_use_equip_sch.to_ScheduleConstant.get
|
751
|
+
elsif water_use_equip_sch.to_ScheduleCompact.is_initialized
|
752
|
+
water_use_equip_sch = water_use_equip_sch.to_ScheduleCompact.get
|
753
|
+
end
|
740
754
|
max_sch_value = water_use_equip_sch.annual_min_max_value['max']
|
741
755
|
|
742
756
|
# get water_use_equip_def to get max flow rate
|
@@ -3,12 +3,14 @@
|
|
3
3
|
class OpenStudio::Model::Model
|
4
4
|
require_relative 'Prototype.AirTerminalSingleDuctVAVReheat'
|
5
5
|
|
6
|
-
# Creates a hot water loop with
|
7
|
-
# and
|
6
|
+
# Creates a hot water loop with a boiler, district heating, or a
|
7
|
+
# water-to-water heat pump and adds it to the model.
|
8
8
|
#
|
9
|
-
# @param boiler_fuel_type [String] valid choices are Electricity, NaturalGas, PropaneGas, FuelOil#1, FuelOil#2, DistrictHeating
|
9
|
+
# @param boiler_fuel_type [String] valid choices are Electricity, NaturalGas, PropaneGas, FuelOil#1, FuelOil#2, DistrictHeating, HeatPump
|
10
|
+
# @param ambient_loop [OpenStudio::Model::PlantLoop] The condenser loop for the heat pump.
|
11
|
+
# Only used when boiler_fuel_type is HeatPump.
|
10
12
|
# @return [OpenStudio::Model::PlantLoop] the resulting hot water loop
|
11
|
-
def add_hw_loop(boiler_fuel_type, building_type =
|
13
|
+
def add_hw_loop(boiler_fuel_type, building_type=nil, ambient_loop=nil)
|
12
14
|
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', 'Adding hot water loop.')
|
13
15
|
|
14
16
|
# hot water loop
|
@@ -53,14 +55,24 @@ class OpenStudio::Model::Model
|
|
53
55
|
hw_pump.setPumpControlType('Intermittent')
|
54
56
|
hw_pump.addToNode(hot_water_loop.supplyInletNode)
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
+
case boiler_fuel_type
|
59
|
+
# District Heating
|
60
|
+
when 'DistrictHeating'
|
58
61
|
dist_ht = OpenStudio::Model::DistrictHeating.new(self)
|
59
62
|
dist_ht.setName('Purchased Heating')
|
60
63
|
dist_ht.autosizeNominalCapacity
|
61
64
|
hot_water_loop.addSupplyBranchForComponent(dist_ht)
|
65
|
+
# Ambient Loop
|
66
|
+
when 'HeatPump'
|
67
|
+
water_to_water_hp = OpenStudio::Model::HeatPumpWaterToWaterEquationFitHeating.new(self)
|
68
|
+
hot_water_loop.addSupplyBranchForComponent(water_to_water_hp)
|
69
|
+
# Get or add an ambient loop
|
70
|
+
if ambient_loop.nil?
|
71
|
+
ambient_loop = get_or_add_ambient_water_loop
|
72
|
+
end
|
73
|
+
ambient_loop.addDemandBranchForComponent(water_to_water_hp)
|
62
74
|
# Boiler
|
63
|
-
|
75
|
+
when 'Electricity', 'NaturalGas', 'PropaneGas', 'FuelOil#1', 'FuelOil#2'
|
64
76
|
boiler_max_t_f = 203
|
65
77
|
boiler_max_t_c = OpenStudio.convert(boiler_max_t_f, 'F', 'C').get
|
66
78
|
boiler = OpenStudio::Model::BoilerHotWater.new(self)
|
@@ -87,6 +99,8 @@ class OpenStudio::Model::Model
|
|
87
99
|
# boiler_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(self,hw_temp_sch)
|
88
100
|
# boiler_stpt_manager.setName("Boiler outlet setpoint manager")
|
89
101
|
# boiler_stpt_manager.addToNode(boiler.outletModelObject.get.to_Node.get)
|
102
|
+
else
|
103
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "Boiler fuel type #{boiler_fuel_type} is not valid, no boiler will be added.")
|
90
104
|
end
|
91
105
|
|
92
106
|
# hot water loop pipes
|
@@ -101,7 +115,7 @@ class OpenStudio::Model::Model
|
|
101
115
|
demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(self)
|
102
116
|
demand_outlet_pipe.addToNode(hot_water_loop.demandOutletNode)
|
103
117
|
|
104
|
-
return hot_water_loop
|
118
|
+
return hot_water_loop
|
105
119
|
end
|
106
120
|
|
107
121
|
# Creates a chilled water loop and adds it to the model.
|
@@ -395,7 +409,7 @@ class OpenStudio::Model::Model
|
|
395
409
|
# for supplemental heating/cooling and adds it to the model.
|
396
410
|
#
|
397
411
|
# @return [OpenStudio::Model::PlantLoop] the resulting plant loop
|
398
|
-
# @todo replace cooling tower with fluid cooler
|
412
|
+
# @todo replace cooling tower with fluid cooler after fixing sizing inputs
|
399
413
|
def add_hp_loop(building_type = nil)
|
400
414
|
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', 'Adding heat pump loop.')
|
401
415
|
|
@@ -513,6 +527,200 @@ class OpenStudio::Model::Model
|
|
513
527
|
return heat_pump_water_loop
|
514
528
|
end
|
515
529
|
|
530
|
+
# Creates loop that roughly mimics a properly sized ground heat exchanger.
|
531
|
+
#
|
532
|
+
# for supplemental heating/cooling and adds it to the model.
|
533
|
+
#
|
534
|
+
# @return [OpenStudio::Model::PlantLoop] the resulting plant loop
|
535
|
+
def add_ground_hx_loop
|
536
|
+
|
537
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', 'Adding ground source loop.')
|
538
|
+
|
539
|
+
# Ground source loop
|
540
|
+
ground_hx_loop = OpenStudio::Model::PlantLoop.new(self)
|
541
|
+
ground_hx_loop.setName('Ground HX Loop')
|
542
|
+
ground_hx_loop.setMaximumLoopTemperature(80)
|
543
|
+
ground_hx_loop.setMinimumLoopTemperature(5)
|
544
|
+
|
545
|
+
# Loop controls
|
546
|
+
max_delta_t_r = 12 # temp change at high and low entering condition
|
547
|
+
min_inlet_f = 30 # low entering condition.
|
548
|
+
max_inlet_f = 90 # high entering condition
|
549
|
+
|
550
|
+
delta_t_k = OpenStudio.convert(max_delta_t_r,'R','K').get
|
551
|
+
min_inlet_c = OpenStudio.convert(min_inlet_f,'F','C').get
|
552
|
+
max_inlet_c = OpenStudio.convert(max_inlet_f,'F','C').get
|
553
|
+
|
554
|
+
# Calculate the linear formula that defines outlet
|
555
|
+
# temperature based on inlet temperature of the ground hx.
|
556
|
+
min_outlet_c = min_inlet_c + delta_t_k
|
557
|
+
max_outlet_c = max_inlet_c - delta_t_k
|
558
|
+
slope_c_per_c = (max_outlet_c - min_outlet_c)/(max_inlet_c - min_inlet_c)
|
559
|
+
intercept_c = min_outlet_c - (slope_c_per_c * min_inlet_c)
|
560
|
+
|
561
|
+
sizing_plant = ground_hx_loop.sizingPlant
|
562
|
+
sizing_plant.setLoopType('Heating')
|
563
|
+
sizing_plant.setDesignLoopExitTemperature(max_outlet_c)
|
564
|
+
sizing_plant.setLoopDesignTemperatureDifference(delta_t_k)
|
565
|
+
|
566
|
+
# Pump
|
567
|
+
pump = OpenStudio::Model::PumpConstantSpeed.new(self)
|
568
|
+
pump.setName("#{ground_hx_loop.name} Pump")
|
569
|
+
pump_head_ft_h2o = 60
|
570
|
+
pump_head_press_pa = OpenStudio.convert(pump_head_ft_h2o, 'ftH_{2}O', 'Pa').get
|
571
|
+
pump.setRatedPumpHead(pump_head_press_pa)
|
572
|
+
pump.setPumpControlType('Intermittent')
|
573
|
+
pump.addToNode(ground_hx_loop.supplyInletNode)
|
574
|
+
|
575
|
+
# Use EMS and a PlantComponentTemperatureSource to mimic the operation
|
576
|
+
# of the ground heat exchanger.
|
577
|
+
|
578
|
+
# Schedule to actuate ground HX outlet temperature
|
579
|
+
hx_temp_sch = OpenStudio::Model::ScheduleConstant.new(self)
|
580
|
+
hx_temp_sch.setName('Ground HX Temp Sch')
|
581
|
+
hx_temp_sch.setValue(24) # TODO
|
582
|
+
|
583
|
+
hx = OpenStudio::Model::PlantComponentTemperatureSource.new(self)
|
584
|
+
hx.setName('Ground HX')
|
585
|
+
hx.setTemperatureSpecificationType('Scheduled')
|
586
|
+
hx.setSourceTemperatureSchedule(hx_temp_sch)
|
587
|
+
ground_hx_loop.addSupplyBranchForComponent(hx)
|
588
|
+
|
589
|
+
hx_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(self, hx_temp_sch)
|
590
|
+
hx_stpt_manager.setName("#{hx.name} Supply Outlet Setpoint")
|
591
|
+
hx_stpt_manager.addToNode(hx.outletModelObject.get.to_Node.get)
|
592
|
+
|
593
|
+
loop_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(self, hx_temp_sch)
|
594
|
+
loop_stpt_manager.setName("#{ground_hx_loop.name} Supply Outlet Setpoint")
|
595
|
+
loop_stpt_manager.addToNode(ground_hx_loop.supplyOutletNode)
|
596
|
+
|
597
|
+
# Sensor to read supply inlet temperature
|
598
|
+
supply_inlet_node = ground_hx_loop.supplyInletNode
|
599
|
+
|
600
|
+
inlet_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(self, 'System Node Temperature')
|
601
|
+
inlet_temp_sensor.setName("#{hx.name} Inlet Temp Sensor")
|
602
|
+
inlet_temp_sensor.setKeyName("#{supply_inlet_node.handle}")
|
603
|
+
|
604
|
+
# Actuator to set supply outlet temperature
|
605
|
+
outlet_temp_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hx_temp_sch, 'Schedule:Constant', 'Schedule Value')
|
606
|
+
outlet_temp_actuator.setName("#{hx.name} Outlet Temp Actuator")
|
607
|
+
|
608
|
+
# Actuator to set supply outlet temperature
|
609
|
+
outlet_temp_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hx_temp_sch, 'Schedule:Constant', 'Schedule Value')
|
610
|
+
outlet_temp_actuator.setName("#{hx.name} Outlet Temp Actuator")
|
611
|
+
|
612
|
+
# Program to control outlet temperature
|
613
|
+
# Adjusts delta-t based on calculation of
|
614
|
+
# slope and intercept from control temperatures
|
615
|
+
program = OpenStudio::Model::EnergyManagementSystemProgram.new(self)
|
616
|
+
program.setName("#{hx.name} Temp Control")
|
617
|
+
program_body = <<-EMS
|
618
|
+
SET Tin = #{inlet_temp_sensor.handle}
|
619
|
+
SET Tout = #{slope_c_per_c.round(2)} * Tin + #{intercept_c.round(1)}
|
620
|
+
SET #{outlet_temp_actuator.handle} = Tout
|
621
|
+
EMS
|
622
|
+
program.setBody(program_body)
|
623
|
+
|
624
|
+
# Program calling manager
|
625
|
+
pcm = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(self)
|
626
|
+
pcm.setName("#{program.name} Calling Mgr")
|
627
|
+
pcm.setCallingPoint('InsideHVACSystemIterationLoop')
|
628
|
+
pcm.addProgram(program)
|
629
|
+
|
630
|
+
return ground_hx_loop
|
631
|
+
|
632
|
+
end
|
633
|
+
|
634
|
+
# Adds an ambient condenser water loop that will be used in a district
|
635
|
+
# to connect buildings as a shared sink/source for heat pumps.
|
636
|
+
#
|
637
|
+
# @return [OpenStudio::Model::PlantLoop] the ambient loop
|
638
|
+
# @todo handle ground and heat pump with this; make heating/cooling source options (boiler, fluid cooler, district)
|
639
|
+
def add_district_ambient_loop
|
640
|
+
|
641
|
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding district ambient loop.")
|
642
|
+
|
643
|
+
# Ambient loop
|
644
|
+
loop = OpenStudio::Model::PlantLoop.new(self)
|
645
|
+
loop.setName('Ambient Loop')
|
646
|
+
loop.setMaximumLoopTemperature(80)
|
647
|
+
loop.setMinimumLoopTemperature(5)
|
648
|
+
|
649
|
+
# Ambient loop controls
|
650
|
+
amb_high_temp_f = 90 # Supplemental cooling below 65F
|
651
|
+
amb_low_temp_f = 41 # Supplemental heat below 41F
|
652
|
+
amb_temp_sizing_f = 102.2 #CW sized to deliver 102.2F
|
653
|
+
amb_delta_t_r = 19.8 #19.8F delta-T
|
654
|
+
|
655
|
+
amb_high_temp_c = OpenStudio.convert(amb_high_temp_f,'F','C').get
|
656
|
+
amb_low_temp_c = OpenStudio.convert(amb_low_temp_f,'F','C').get
|
657
|
+
amb_temp_sizing_c = OpenStudio.convert(amb_temp_sizing_f,'F','C').get
|
658
|
+
amb_delta_t_k = OpenStudio.convert(amb_delta_t_r,'R','K').get
|
659
|
+
|
660
|
+
amb_high_temp_sch = OpenStudio::Model::ScheduleRuleset.new(self)
|
661
|
+
amb_high_temp_sch.setName("Ambient Loop High Temp - #{amb_high_temp_f}F")
|
662
|
+
amb_high_temp_sch.defaultDaySchedule.setName("Ambient Loop High Temp - #{amb_high_temp_f}F Default")
|
663
|
+
amb_high_temp_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0,24,0,0),amb_high_temp_c)
|
664
|
+
|
665
|
+
amb_low_temp_sch = OpenStudio::Model::ScheduleRuleset.new(self)
|
666
|
+
amb_low_temp_sch.setName("Ambient Loop Low Temp - #{amb_low_temp_f}F")
|
667
|
+
amb_low_temp_sch.defaultDaySchedule.setName("Ambient Loop Low Temp - #{amb_low_temp_f}F Default")
|
668
|
+
amb_low_temp_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0,24,0,0),amb_low_temp_c)
|
669
|
+
|
670
|
+
amb_stpt_manager = OpenStudio::Model::SetpointManagerScheduledDualSetpoint.new(self)
|
671
|
+
amb_stpt_manager.setHighSetpointSchedule(amb_high_temp_sch)
|
672
|
+
amb_stpt_manager.setLowSetpointSchedule(amb_low_temp_sch)
|
673
|
+
amb_stpt_manager.addToNode(loop.supplyOutletNode)
|
674
|
+
|
675
|
+
sizing_plant = loop.sizingPlant
|
676
|
+
sizing_plant.setLoopType('Heating')
|
677
|
+
sizing_plant.setDesignLoopExitTemperature(amb_temp_sizing_c)
|
678
|
+
sizing_plant.setLoopDesignTemperatureDifference(amb_delta_t_k)
|
679
|
+
|
680
|
+
# Ambient loop pump
|
681
|
+
amb_pump = OpenStudio::Model::PumpVariableSpeed.new(self)
|
682
|
+
amb_pump.setName('Ambient Loop Pump')
|
683
|
+
amb_pump_head_ft_h2o = 60
|
684
|
+
amb_pump_head_press_pa = OpenStudio.convert(amb_pump_head_ft_h2o, 'ftH_{2}O','Pa').get
|
685
|
+
amb_pump.setRatedPumpHead(amb_pump_head_press_pa)
|
686
|
+
amb_pump.setPumpControlType('Intermittent')
|
687
|
+
amb_pump.addToNode(loop.supplyInletNode)
|
688
|
+
|
689
|
+
# Cooling
|
690
|
+
district_cooling = OpenStudio::Model::DistrictCooling.new(self)
|
691
|
+
district_cooling.setNominalCapacity(1000000000000) # large number; no autosizing
|
692
|
+
loop.addSupplyBranchForComponent(district_cooling)
|
693
|
+
|
694
|
+
# Heating
|
695
|
+
district_heating = OpenStudio::Model::DistrictHeating.new(self)
|
696
|
+
district_heating.setNominalCapacity(1000000000000) # large number; no autosizing
|
697
|
+
loop.addSupplyBranchForComponent(district_heating)
|
698
|
+
|
699
|
+
# Ambient water loop pipes
|
700
|
+
supply_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(self)
|
701
|
+
supply_bypass_pipe.setName("#{loop.name} Supply Bypass")
|
702
|
+
loop.addSupplyBranchForComponent(supply_bypass_pipe)
|
703
|
+
|
704
|
+
demand_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(self)
|
705
|
+
demand_bypass_pipe.setName("#{loop.name} Demand Bypass")
|
706
|
+
loop.addDemandBranchForComponent(demand_bypass_pipe)
|
707
|
+
|
708
|
+
supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(self)
|
709
|
+
supply_outlet_pipe.setName("#{loop.name} Supply Outlet")
|
710
|
+
supply_outlet_pipe.addToNode(loop.supplyOutletNode)
|
711
|
+
|
712
|
+
demand_inlet_pipe = OpenStudio::Model::PipeAdiabatic.new(self)
|
713
|
+
demand_inlet_pipe.setName("#{loop.name} Demand Inlet")
|
714
|
+
demand_inlet_pipe.addToNode(loop.demandInletNode)
|
715
|
+
|
716
|
+
demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(self)
|
717
|
+
demand_outlet_pipe.setName("#{loop.name} Demand Outlet")
|
718
|
+
demand_outlet_pipe.addToNode(loop.demandOutletNode)
|
719
|
+
|
720
|
+
return loop
|
721
|
+
|
722
|
+
end
|
723
|
+
|
516
724
|
# Creates a VAV system and adds it to the model.
|
517
725
|
#
|
518
726
|
# @param template [String] Valid choices are 90.1-2004,
|
@@ -530,8 +738,7 @@ class OpenStudio::Model::Model
|
|
530
738
|
# @param vav_fan_pressure_rise [Double] fan pressure rise, in Pa
|
531
739
|
# @param return_plenum [OpenStudio::Model::ThermalZone] the zone to attach as
|
532
740
|
# the supply plenum, or nil, in which case no return plenum will be used.
|
533
|
-
# @param
|
534
|
-
# but if false, the reheat coils will be served by the hot_water_loop.
|
741
|
+
# @param reheat_type [String] valid options are NaturalGas, Electricity, Water, nil (no heat)
|
535
742
|
# @param building_type [String] the building type
|
536
743
|
# @return [OpenStudio::Model::AirLoopHVAC] the resulting VAV air loop
|
537
744
|
def add_vav_reheat(template,
|
@@ -545,7 +752,7 @@ class OpenStudio::Model::Model
|
|
545
752
|
vav_fan_motor_efficiency,
|
546
753
|
vav_fan_pressure_rise,
|
547
754
|
return_plenum,
|
548
|
-
|
755
|
+
reheat_type = 'Water',
|
549
756
|
building_type = nil)
|
550
757
|
|
551
758
|
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding VAV system for #{thermal_zones.size} zones.")
|
@@ -657,21 +864,27 @@ class OpenStudio::Model::Model
|
|
657
864
|
fan.setEndUseSubcategory('VAV system Fans')
|
658
865
|
|
659
866
|
# heating coil
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
htg_coil.controllerWaterCoil.get.setName("#{air_loop.name} Main Htg Coil Controller")
|
665
|
-
htg_coil.setRatedInletWaterTemperature(hw_temp_c)
|
666
|
-
htg_coil.setRatedInletAirTemperature(prehtg_sa_temp_c)
|
667
|
-
htg_coil.setRatedOutletWaterTemperature(hw_temp_c - hw_delta_t_k)
|
668
|
-
htg_coil.setRatedOutletAirTemperature(htg_sa_temp_c)
|
669
|
-
if building_type == 'LargeHotel'
|
670
|
-
htg_coil.setRatedInletAirTemperature(htg_sa_temp_c)
|
671
|
-
htg_coil.setRatedOutletAirTemperature(rht_sa_temp_c)
|
867
|
+
if hot_water_loop.nil?
|
868
|
+
htg_coil = OpenStudio::Model::CoilHeatingGas.new(self, alwaysOnDiscreteSchedule)
|
869
|
+
htg_coil.setName("#{air_loop.name} Main Htg Coil")
|
870
|
+
htg_coil.addToNode(air_loop.supplyInletNode)
|
672
871
|
else
|
872
|
+
htg_coil = OpenStudio::Model::CoilHeatingWater.new(self, alwaysOnDiscreteSchedule)
|
873
|
+
htg_coil.addToNode(air_loop.supplyInletNode)
|
874
|
+
hot_water_loop.addDemandBranchForComponent(htg_coil)
|
875
|
+
htg_coil.setName("#{air_loop.name} Main Htg Coil")
|
876
|
+
htg_coil.controllerWaterCoil.get.setName("#{air_loop.name} Main Htg Coil Controller")
|
877
|
+
htg_coil.setRatedInletWaterTemperature(hw_temp_c)
|
673
878
|
htg_coil.setRatedInletAirTemperature(prehtg_sa_temp_c)
|
879
|
+
htg_coil.setRatedOutletWaterTemperature(hw_temp_c - hw_delta_t_k)
|
674
880
|
htg_coil.setRatedOutletAirTemperature(htg_sa_temp_c)
|
881
|
+
if building_type == 'LargeHotel'
|
882
|
+
htg_coil.setRatedInletAirTemperature(htg_sa_temp_c)
|
883
|
+
htg_coil.setRatedOutletAirTemperature(rht_sa_temp_c)
|
884
|
+
else
|
885
|
+
htg_coil.setRatedInletAirTemperature(prehtg_sa_temp_c)
|
886
|
+
htg_coil.setRatedOutletAirTemperature(htg_sa_temp_c)
|
887
|
+
end
|
675
888
|
end
|
676
889
|
|
677
890
|
# cooling coil
|
@@ -711,10 +924,14 @@ class OpenStudio::Model::Model
|
|
711
924
|
thermal_zones.each do |zone|
|
712
925
|
# reheat coil
|
713
926
|
rht_coil = nil
|
714
|
-
|
927
|
+
case reheat_type
|
928
|
+
when 'NaturalGas'
|
929
|
+
rht_coil = OpenStudio::Model::CoilHeatingGas.new(self, alwaysOnDiscreteSchedule)
|
930
|
+
rht_coil.setName("#{zone.name} Rht Coil")
|
931
|
+
when 'Electricity'
|
715
932
|
rht_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOnDiscreteSchedule)
|
716
933
|
rht_coil.setName("#{zone.name} Rht Coil")
|
717
|
-
|
934
|
+
when 'Water'
|
718
935
|
rht_coil = OpenStudio::Model::CoilHeatingWater.new(self, alwaysOnDiscreteSchedule)
|
719
936
|
rht_coil.setName("#{zone.name} Rht Coil")
|
720
937
|
rht_coil.setRatedInletWaterTemperature(hw_temp_c)
|
@@ -722,6 +939,11 @@ class OpenStudio::Model::Model
|
|
722
939
|
rht_coil.setRatedOutletWaterTemperature(hw_temp_c - hw_delta_t_k)
|
723
940
|
rht_coil.setRatedOutletAirTemperature(rht_sa_temp_c)
|
724
941
|
hot_water_loop.addDemandBranchForComponent(rht_coil)
|
942
|
+
when nil
|
943
|
+
# Zero-capacity, always-off electric heating coil
|
944
|
+
rht_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOffDiscreteSchedule)
|
945
|
+
rht_coil.setName("#{zone.name} No Reheat")
|
946
|
+
rht_coil.setNominalCapacity(0)
|
725
947
|
end
|
726
948
|
|
727
949
|
# vav terminal
|
@@ -1543,9 +1765,9 @@ class OpenStudio::Model::Model
|
|
1543
1765
|
# or nil in which case will be defaulted to always open
|
1544
1766
|
# @param fan_location [Double] valid choices are BlowThrough, DrawThrough
|
1545
1767
|
# @param fan_type [Double] valid choices are ConstantVolume, Cycling
|
1546
|
-
# @param heating_type [Double] valid choices are
|
1768
|
+
# @param heating_type [Double] valid choices are NaturalGas, Electricity, Water, nil (no heat)
|
1547
1769
|
# Single Speed Heat Pump, Water To Air Heat Pump
|
1548
|
-
# @param supplemental_heating_type [Double] valid choices are
|
1770
|
+
# @param supplemental_heating_type [Double] valid choices are Electricity, NaturalGas, nil (no heat)
|
1549
1771
|
# @param cooling_type [String] valid choices are Water, Two Speed DX AC,
|
1550
1772
|
# Single Speed DX AC, Single Speed Heat Pump, Water To Air Heat Pump
|
1551
1773
|
# @param building_type [String] the building type
|
@@ -1566,23 +1788,21 @@ class OpenStudio::Model::Model
|
|
1566
1788
|
cooling_type,
|
1567
1789
|
building_type = nil)
|
1568
1790
|
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
hw_delta_t_k = OpenStudio.convert(hw_delta_t_r, 'R', 'K').get
|
1791
|
+
hw_temp_f = 180 # HW setpoint 180F
|
1792
|
+
hw_delta_t_r = 20 # 20F delta-T
|
1793
|
+
hw_temp_c = OpenStudio.convert(hw_temp_f, 'F', 'C').get
|
1794
|
+
hw_delta_t_k = OpenStudio.convert(hw_delta_t_r, 'R', 'K').get
|
1574
1795
|
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1796
|
+
# control temps used across all air handlers
|
1797
|
+
clg_sa_temp_f = 55 # Central deck clg temp 55F
|
1798
|
+
prehtg_sa_temp_f = 44.6 # Preheat to 44.6F
|
1799
|
+
htg_sa_temp_f = 55 # Central deck htg temp 55F
|
1800
|
+
rht_sa_temp_f = 104 # VAV box reheat to 104F
|
1580
1801
|
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
end
|
1802
|
+
clg_sa_temp_c = OpenStudio.convert(clg_sa_temp_f, 'F', 'C').get
|
1803
|
+
prehtg_sa_temp_c = OpenStudio.convert(prehtg_sa_temp_f, 'F', 'C').get
|
1804
|
+
htg_sa_temp_c = OpenStudio.convert(htg_sa_temp_f, 'F', 'C').get
|
1805
|
+
rht_sa_temp_c = OpenStudio.convert(rht_sa_temp_f, 'F', 'C').get
|
1586
1806
|
|
1587
1807
|
# hvac operation schedule
|
1588
1808
|
hvac_op_sch = if hvac_op_sch.nil?
|
@@ -1680,15 +1900,20 @@ class OpenStudio::Model::Model
|
|
1680
1900
|
end
|
1681
1901
|
|
1682
1902
|
htg_coil = nil
|
1683
|
-
|
1903
|
+
case heating_type
|
1904
|
+
when 'NaturalGas', 'Gas'
|
1684
1905
|
htg_coil = OpenStudio::Model::CoilHeatingGas.new(self, alwaysOnDiscreteSchedule)
|
1685
1906
|
htg_coil.setName("#{air_loop.name} Gas Htg Coil")
|
1686
1907
|
|
1687
1908
|
if template == 'DOE Ref Pre-1980'
|
1688
1909
|
htg_coil.setGasBurnerEfficiency(0.78)
|
1689
1910
|
end
|
1690
|
-
|
1691
|
-
|
1911
|
+
when nil
|
1912
|
+
# Zero-capacity, always-off electric heating coil
|
1913
|
+
htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOffDiscreteSchedule)
|
1914
|
+
htg_coil.setName("#{air_loop.name} No Heat")
|
1915
|
+
htg_coil.setNominalCapacity(0)
|
1916
|
+
when 'Water'
|
1692
1917
|
if hot_water_loop.nil?
|
1693
1918
|
OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'No hot water plant loop supplied')
|
1694
1919
|
return false
|
@@ -1700,7 +1925,7 @@ class OpenStudio::Model::Model
|
|
1700
1925
|
htg_coil.setRatedOutletWaterTemperature(hw_temp_c - hw_delta_t_k)
|
1701
1926
|
htg_coil.setRatedOutletAirTemperature(htg_sa_temp_c)
|
1702
1927
|
hot_water_loop.addDemandBranchForComponent(htg_coil)
|
1703
|
-
|
1928
|
+
when 'Single Speed Heat Pump'
|
1704
1929
|
htg_cap_f_of_temp = OpenStudio::Model::CurveCubic.new(self)
|
1705
1930
|
htg_cap_f_of_temp.setCoefficient1Constant(0.758746)
|
1706
1931
|
htg_cap_f_of_temp.setCoefficient2x(0.027626)
|
@@ -1770,7 +1995,7 @@ class OpenStudio::Model::Model
|
|
1770
1995
|
def_eir_f_of_temp.setMaximumValueofy(46.11111)
|
1771
1996
|
|
1772
1997
|
htg_coil.setDefrostEnergyInputRatioFunctionofTemperatureCurve(def_eir_f_of_temp)
|
1773
|
-
|
1998
|
+
when 'Water To Air Heat Pump'
|
1774
1999
|
if hot_water_loop.nil?
|
1775
2000
|
OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'No hot water plant loop supplied')
|
1776
2001
|
return false
|
@@ -1790,15 +2015,24 @@ class OpenStudio::Model::Model
|
|
1790
2015
|
htg_coil.setHeatingPowerConsumptionCoefficient5(-0.103079864171839)
|
1791
2016
|
|
1792
2017
|
hot_water_loop.addDemandBranchForComponent(htg_coil)
|
2018
|
+
when 'Electricity', 'Electric'
|
2019
|
+
htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOnDiscreteSchedule)
|
2020
|
+
htg_coil.setName("#{air_loop.name} Electric Htg Coil")
|
1793
2021
|
end
|
1794
2022
|
|
1795
2023
|
supplemental_htg_coil = nil
|
1796
|
-
|
2024
|
+
case supplemental_heating_type
|
2025
|
+
when 'Electricity', 'Electric' # TODO change spreadsheet to Electricity
|
1797
2026
|
supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOnDiscreteSchedule)
|
1798
2027
|
supplemental_htg_coil.setName("#{air_loop.name} Electric Backup Htg Coil")
|
1799
|
-
|
2028
|
+
when 'NaturalGas', 'Gas'
|
1800
2029
|
supplemental_htg_coil = OpenStudio::Model::CoilHeatingGas.new(self, alwaysOnDiscreteSchedule)
|
1801
2030
|
supplemental_htg_coil.setName("#{air_loop.name} Gas Backup Htg Coil")
|
2031
|
+
when nil
|
2032
|
+
# Zero-capacity, always-off electric heating coil
|
2033
|
+
supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOffDiscreteSchedule)
|
2034
|
+
supplemental_htg_coil.setName("#{air_loop.name} No Backup Heat")
|
2035
|
+
supplemental_htg_coil.setNominalCapacity(0)
|
1802
2036
|
end
|
1803
2037
|
|
1804
2038
|
clg_coil = nil
|
@@ -2902,7 +3136,7 @@ class OpenStudio::Model::Model
|
|
2902
3136
|
# @param thermal_zones [String] zones to connect to this system
|
2903
3137
|
# @param fan_type [Double] valid choices are ConstantVolume, Cycling
|
2904
3138
|
# @param heating_type [Double] valid choices are
|
2905
|
-
#
|
3139
|
+
# NaturalGas, Electricity, Water, nil (no heat)
|
2906
3140
|
# @param cooling_type [String] valid choices are
|
2907
3141
|
# Two Speed DX AC, Single Speed DX AC
|
2908
3142
|
# @param building_type [String] the building type
|
@@ -2963,13 +3197,18 @@ class OpenStudio::Model::Model
|
|
2963
3197
|
|
2964
3198
|
# add heating coil
|
2965
3199
|
htg_coil = nil
|
2966
|
-
|
3200
|
+
case heating_type
|
3201
|
+
when 'NaturalGas', 'Gas'
|
2967
3202
|
htg_coil = OpenStudio::Model::CoilHeatingGas.new(self, alwaysOnDiscreteSchedule)
|
2968
3203
|
htg_coil.setName("#{zone.name} PTAC Gas Htg Coil")
|
2969
|
-
|
3204
|
+
when 'Electricity', 'Electric'
|
2970
3205
|
htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOnDiscreteSchedule)
|
2971
3206
|
htg_coil.setName("#{zone.name} PTAC Electric Htg Coil")
|
2972
|
-
|
3207
|
+
when nil
|
3208
|
+
htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOffDiscreteSchedule)
|
3209
|
+
htg_coil.setName("#{zone.name} PTAC No Heat")
|
3210
|
+
htg_coil.setNominalCapacity(0)
|
3211
|
+
when 'Water'
|
2973
3212
|
if hot_water_loop.nil?
|
2974
3213
|
OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'No hot water plant loop supplied')
|
2975
3214
|
return false
|
@@ -3502,6 +3741,180 @@ class OpenStudio::Model::Model
|
|
3502
3741
|
return rad_heaters
|
3503
3742
|
end
|
3504
3743
|
|
3744
|
+
# Creates an evaporative cooler for each zone and adds it to the model.
|
3745
|
+
#
|
3746
|
+
# @param template [String] Valid choices are 90.1-2004,
|
3747
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
3748
|
+
# @param sys_name [String] the name of the system, or nil in which case it will be defaulted
|
3749
|
+
# @param thermal_zones [String] zones to connect to this system
|
3750
|
+
# @param building_type [String] the building type
|
3751
|
+
# @return [Array<OpenStudio::Model::AirLoopHVAC>] the resulting evaporative coolers
|
3752
|
+
def add_evap_cooler(template,
|
3753
|
+
thermal_zones,
|
3754
|
+
building_type = nil)
|
3755
|
+
|
3756
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding evaporative coolers for #{thermal_zones.size} zones.")
|
3757
|
+
thermal_zones.each do |zone|
|
3758
|
+
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.Model.Model', "---#{zone.name}")
|
3759
|
+
end
|
3760
|
+
|
3761
|
+
# Evap cooler control temperatures
|
3762
|
+
min_sa_temp_f = 55
|
3763
|
+
clg_sa_temp_f = 70
|
3764
|
+
max_sa_temp_f = 78
|
3765
|
+
htg_sa_temp_f = 122 # Not used
|
3766
|
+
|
3767
|
+
min_sa_temp_c = OpenStudio.convert(min_sa_temp_f, 'F', 'C').get
|
3768
|
+
clg_sa_temp_c = OpenStudio.convert(clg_sa_temp_f, 'F', 'C').get
|
3769
|
+
max_sa_temp_c = OpenStudio.convert(max_sa_temp_f, 'F', 'C').get
|
3770
|
+
htg_sa_temp_c = OpenStudio.convert(htg_sa_temp_f, 'F', 'C').get
|
3771
|
+
|
3772
|
+
approach_r = 3 # WetBulb approach
|
3773
|
+
approach_k = OpenStudio.convert(approach_r, 'R', 'K').get
|
3774
|
+
|
3775
|
+
fan_static_pressure_in_h2o = 0.25
|
3776
|
+
fan_static_pressure_pa = OpenStudio.convert(fan_static_pressure_in_h2o, "inH_{2}O","Pa").get
|
3777
|
+
|
3778
|
+
# EMS programs
|
3779
|
+
programs = []
|
3780
|
+
|
3781
|
+
# Make an evap cooler for each zone
|
3782
|
+
evap_coolers = []
|
3783
|
+
thermal_zones.each do |zone|
|
3784
|
+
|
3785
|
+
zone_name_clean = zone.name.get.gsub(':', '')
|
3786
|
+
|
3787
|
+
# Air loop
|
3788
|
+
air_loop = OpenStudio::Model::AirLoopHVAC.new(self)
|
3789
|
+
air_loop.setName("#{zone_name_clean} Evap Cooler")
|
3790
|
+
|
3791
|
+
# Schedule to control the airloop availability
|
3792
|
+
air_loop_avail_sch = OpenStudio::Model::ScheduleConstant.new(self)
|
3793
|
+
air_loop_avail_sch.setName("#{air_loop.name} Availability Sch")
|
3794
|
+
air_loop_avail_sch.setValue(1)
|
3795
|
+
air_loop.setAvailabilitySchedule(air_loop_avail_sch)
|
3796
|
+
|
3797
|
+
# EMS to turn on Evap Cooler if
|
3798
|
+
# there is a cooling load in the target zone.
|
3799
|
+
# Without this EMS, the airloop runs 24/7-365,
|
3800
|
+
# even when there is no load in the zone.
|
3801
|
+
|
3802
|
+
# Create a sensor to read the zone load
|
3803
|
+
zn_load_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(self, 'Zone Predicted Sensible Load to Cooling Setpoint Heat Transfer Rate')
|
3804
|
+
zn_load_sensor.setName("#{zone_name_clean} Clg Load Sensor")
|
3805
|
+
zn_load_sensor.setKeyName("#{zone.handle}")
|
3806
|
+
|
3807
|
+
# Create an actuator to set the airloop availability
|
3808
|
+
air_loop_avail_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(air_loop_avail_sch, 'Schedule:Constant', 'Schedule Value')
|
3809
|
+
air_loop_avail_actuator.setName("#{air_loop.name} Availability Actuator")
|
3810
|
+
|
3811
|
+
# Create a program to turn on Evap Cooler if
|
3812
|
+
# there is a cooling load in the target zone.
|
3813
|
+
# Load < 0.0 is a cooling load.
|
3814
|
+
avail_program = OpenStudio::Model::EnergyManagementSystemProgram.new(self)
|
3815
|
+
avail_program.setName("#{air_loop.name} Availability Control")
|
3816
|
+
avail_program_body = <<-EMS
|
3817
|
+
IF #{zn_load_sensor.handle} < 0.0
|
3818
|
+
SET #{air_loop_avail_actuator.handle} = 1
|
3819
|
+
ELSE
|
3820
|
+
SET #{air_loop_avail_actuator.handle} = 0
|
3821
|
+
ENDIF
|
3822
|
+
EMS
|
3823
|
+
avail_program.setBody(avail_program_body)
|
3824
|
+
|
3825
|
+
programs << avail_program
|
3826
|
+
|
3827
|
+
# Setpoint follows OAT WetBulb
|
3828
|
+
evap_stpt_manager = OpenStudio::Model::SetpointManagerFollowOutdoorAirTemperature.new(self)
|
3829
|
+
evap_stpt_manager.setName("#{approach_r} F above OATwb")
|
3830
|
+
evap_stpt_manager.setReferenceTemperatureType('OutdoorAirWetBulb')
|
3831
|
+
evap_stpt_manager.setMaximumSetpointTemperature(max_sa_temp_c)
|
3832
|
+
evap_stpt_manager.setMinimumSetpointTemperature(min_sa_temp_c)
|
3833
|
+
evap_stpt_manager.setOffsetTemperatureDifference(approach_k)
|
3834
|
+
evap_stpt_manager.addToNode(air_loop.supplyOutletNode)
|
3835
|
+
|
3836
|
+
# Air handler sizing
|
3837
|
+
sizing_system = air_loop.sizingSystem
|
3838
|
+
sizing_system.setCentralCoolingDesignSupplyAirTemperature(clg_sa_temp_c)
|
3839
|
+
sizing_system.setCentralHeatingDesignSupplyAirTemperature(htg_sa_temp_c)
|
3840
|
+
sizing_system.setAllOutdoorAirinCooling(true)
|
3841
|
+
sizing_system.setAllOutdoorAirinHeating(true)
|
3842
|
+
sizing_system.setSystemOutdoorAirMethod('ZoneSum')
|
3843
|
+
|
3844
|
+
# Direct Evap Cooler
|
3845
|
+
# TODO better assumptions for evap cooler performance
|
3846
|
+
# and fan pressure rise
|
3847
|
+
evap = OpenStudio::Model::EvaporativeCoolerDirectResearchSpecial.new(self, alwaysOnDiscreteSchedule)
|
3848
|
+
evap.setName("#{zone.name} Evap Media")
|
3849
|
+
evap.autosizePrimaryAirDesignFlowRate
|
3850
|
+
evap.addToNode(air_loop.supplyInletNode)
|
3851
|
+
|
3852
|
+
# Fan (cycling), must be inside unitary system to cycle on airloop
|
3853
|
+
fan = OpenStudio::Model::FanOnOff.new(self, alwaysOnDiscreteSchedule)
|
3854
|
+
fan.setName("#{zone.name} Evap Cooler Supply Fan")
|
3855
|
+
fan.setFanEfficiency(0.55)
|
3856
|
+
fan.setPressureRise(fan_static_pressure_pa)
|
3857
|
+
|
3858
|
+
# Dummy zero-capacity cooling coil
|
3859
|
+
clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(self)
|
3860
|
+
clg_coil.setName("Zero-capacity DX Coil")
|
3861
|
+
clg_coil.setAvailabilitySchedule(alwaysOffDiscreteSchedule)
|
3862
|
+
|
3863
|
+
unitary_system = OpenStudio::Model::AirLoopHVACUnitarySystem.new(self)
|
3864
|
+
unitary_system.setName("#{zone.name} Evap Cooler Cycling Fan")
|
3865
|
+
unitary_system.setSupplyFan(fan)
|
3866
|
+
unitary_system.setCoolingCoil(clg_coil)
|
3867
|
+
unitary_system.setControllingZoneorThermostatLocation(zone)
|
3868
|
+
unitary_system.setMaximumSupplyAirTemperature(50)
|
3869
|
+
unitary_system.setFanPlacement('BlowThrough')
|
3870
|
+
unitary_system.setSupplyAirFlowRateMethodDuringCoolingOperation('SupplyAirFlowRate')
|
3871
|
+
unitary_system.setSupplyAirFlowRateMethodDuringHeatingOperation('SupplyAirFlowRate')
|
3872
|
+
unitary_system.setSupplyAirFlowRateMethodWhenNoCoolingorHeatingisRequired('SupplyAirFlowRate')
|
3873
|
+
unitary_system.setSupplyAirFanOperatingModeSchedule(alwaysOffDiscreteSchedule)
|
3874
|
+
unitary_system.addToNode(air_loop.supplyInletNode)
|
3875
|
+
|
3876
|
+
# Outdoor air intake system
|
3877
|
+
oa_intake_controller = OpenStudio::Model::ControllerOutdoorAir.new(self)
|
3878
|
+
oa_intake_controller.setName("#{air_loop.name} OA Controller")
|
3879
|
+
oa_intake_controller.setMinimumLimitType('FixedMinimum')
|
3880
|
+
oa_intake_controller.setMinimumFractionofOutdoorAirSchedule(alwaysOnDiscreteSchedule)
|
3881
|
+
|
3882
|
+
controller_mv = oa_intake_controller.controllerMechanicalVentilation
|
3883
|
+
controller_mv.setName("#{air_loop.name} Vent Controller")
|
3884
|
+
controller_mv.setSystemOutdoorAirMethod('ZoneSum')
|
3885
|
+
|
3886
|
+
oa_intake = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(self, oa_intake_controller)
|
3887
|
+
oa_intake.setName("#{air_loop.name} OA Sys")
|
3888
|
+
oa_intake.addToNode(air_loop.supplyInletNode)
|
3889
|
+
|
3890
|
+
# make an air terminal for the zone
|
3891
|
+
air_terminal = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(self, alwaysOnDiscreteSchedule)
|
3892
|
+
air_terminal.setName("#{zone.name} Air Terminal")
|
3893
|
+
|
3894
|
+
# attach new terminal to the zone and to the airloop
|
3895
|
+
air_loop.addBranchForZone(zone, air_terminal.to_StraightComponent)
|
3896
|
+
|
3897
|
+
sizing_zone = zone.sizingZone
|
3898
|
+
sizing_zone.setCoolingDesignAirFlowMethod('DesignDay')
|
3899
|
+
sizing_zone.setHeatingDesignAirFlowMethod('DesignDay')
|
3900
|
+
sizing_zone.setZoneCoolingDesignSupplyAirTemperature(clg_sa_temp_c)
|
3901
|
+
sizing_zone.setZoneHeatingDesignSupplyAirTemperature(htg_sa_temp_c)
|
3902
|
+
|
3903
|
+
evap_coolers << air_loop
|
3904
|
+
|
3905
|
+
end
|
3906
|
+
|
3907
|
+
# Create a programcallingmanager
|
3908
|
+
avail_pcm = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(self)
|
3909
|
+
avail_pcm.setName('Evap Cooler Availability Program Calling Manager')
|
3910
|
+
avail_pcm.setCallingPoint('AfterPredictorAfterHVACManagers')
|
3911
|
+
programs.each do |program|
|
3912
|
+
avail_pcm.addProgram(program)
|
3913
|
+
end
|
3914
|
+
|
3915
|
+
return evap_coolers
|
3916
|
+
end
|
3917
|
+
|
3505
3918
|
# Creates a service water heating loop.
|
3506
3919
|
#
|
3507
3920
|
# @param template [String] Valid choices are 90.1-2004,
|
@@ -4010,7 +4423,8 @@ class OpenStudio::Model::Model
|
|
4010
4423
|
}
|
4011
4424
|
data = find_object($os_standards['space_types'], search_criteria)
|
4012
4425
|
if data.nil?
|
4013
|
-
|
4426
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "Could not find space type for: #{search_criteria}.")
|
4427
|
+
return nil
|
4014
4428
|
end
|
4015
4429
|
space = getSpaceByName(space_name)
|
4016
4430
|
space = space.get
|
@@ -4104,7 +4518,6 @@ class OpenStudio::Model::Model
|
|
4104
4518
|
mixed_water_temp_f = OpenStudio.convert(water_use_temperature, 'F', 'C').get
|
4105
4519
|
mixed_water_temp_sch = OpenStudio::Model::ScheduleRuleset.new(self)
|
4106
4520
|
mixed_water_temp_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), OpenStudio.convert(mixed_water_temp_f, 'F', 'C').get)
|
4107
|
-
puts mixed_water_temp_sch
|
4108
4521
|
water_fixture_def.setTargetTemperatureSchedule(mixed_water_temp_sch)
|
4109
4522
|
|
4110
4523
|
# Water use equipment
|
@@ -4138,6 +4551,8 @@ class OpenStudio::Model::Model
|
|
4138
4551
|
# @param economizer_control_type [String] valid choices are
|
4139
4552
|
# FixedDryBulb,
|
4140
4553
|
# @param building_type [String] the building type
|
4554
|
+
# @param energy_recovery [Bool] if true, an ERV will be added to the
|
4555
|
+
# DOAS system.
|
4141
4556
|
# @return [OpenStudio::Model::AirLoopHVAC] the resulting DOAS air loop
|
4142
4557
|
def add_doas(template,
|
4143
4558
|
sys_name,
|
@@ -4148,13 +4563,36 @@ class OpenStudio::Model::Model
|
|
4148
4563
|
oa_damper_sch,
|
4149
4564
|
fan_max_flow_rate,
|
4150
4565
|
economizer_control_type,
|
4151
|
-
building_type = nil
|
4566
|
+
building_type = nil,
|
4567
|
+
energy_recovery=false)
|
4152
4568
|
|
4153
4569
|
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding DOAS system for #{thermal_zones.size} zones.")
|
4154
4570
|
thermal_zones.each do |zone|
|
4155
4571
|
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.Model.Model', "---#{zone.name}")
|
4156
4572
|
end
|
4157
4573
|
|
4574
|
+
# DOAS Controls
|
4575
|
+
|
4576
|
+
# Reset SAT down to 55F during hotter outdoor
|
4577
|
+
# conditions for humidity management
|
4578
|
+
lo_oat_f = 60
|
4579
|
+
sat_at_lo_oat_f = 60
|
4580
|
+
hi_oat_f = 70
|
4581
|
+
sat_at_hi_oat_f = 55
|
4582
|
+
lo_oat_c = OpenStudio.convert(lo_oat_f, 'F', 'C').get
|
4583
|
+
hi_oat_c = OpenStudio.convert(hi_oat_f, 'F', 'C').get
|
4584
|
+
sat_at_lo_oat_c = OpenStudio.convert(sat_at_lo_oat_f, 'F', 'C').get
|
4585
|
+
sat_at_hi_oat_c = OpenStudio.convert(sat_at_hi_oat_f, 'F', 'C').get
|
4586
|
+
|
4587
|
+
# Create a setpoint manager
|
4588
|
+
sat_oa_reset = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(self)
|
4589
|
+
sat_oa_reset.setName("DOAS SAT Reset")
|
4590
|
+
sat_oa_reset.setControlVariable('Temperature')
|
4591
|
+
sat_oa_reset.setSetpointatOutdoorLowTemperature(sat_at_lo_oat_c)
|
4592
|
+
sat_oa_reset.setOutdoorLowTemperature(lo_oat_c)
|
4593
|
+
sat_oa_reset.setSetpointatOutdoorHighTemperature(sat_at_hi_oat_c)
|
4594
|
+
sat_oa_reset.setOutdoorHighTemperature(hi_oat_c)
|
4595
|
+
|
4158
4596
|
# hvac operation schedule
|
4159
4597
|
hvac_op_sch = if hvac_op_sch.nil?
|
4160
4598
|
alwaysOnDiscreteSchedule
|
@@ -4172,38 +4610,41 @@ class OpenStudio::Model::Model
|
|
4172
4610
|
# DOAS
|
4173
4611
|
air_loop = OpenStudio::Model::AirLoopHVAC.new(self)
|
4174
4612
|
if sys_name.nil?
|
4175
|
-
air_loop.setName("#{thermal_zones.size} DOAS
|
4613
|
+
air_loop.setName("#{thermal_zones.size} Zone DOAS")
|
4176
4614
|
else
|
4177
|
-
air_loop.setName(
|
4615
|
+
air_loop.setName(sys_name)
|
4178
4616
|
end
|
4179
4617
|
air_loop.setNightCycleControlType('CycleOnAny')
|
4180
4618
|
# modify system sizing properties
|
4181
4619
|
sizing_system = air_loop.sizingSystem
|
4182
4620
|
# set central heating and cooling temperatures for sizing
|
4183
|
-
sizing_system.setCentralCoolingDesignSupplyAirTemperature(
|
4184
|
-
sizing_system.setCentralHeatingDesignSupplyAirTemperature(
|
4621
|
+
sizing_system.setCentralCoolingDesignSupplyAirTemperature(sat_at_hi_oat_c)
|
4622
|
+
sizing_system.setCentralHeatingDesignSupplyAirTemperature(sat_at_lo_oat_c)
|
4185
4623
|
sizing_system.setSizingOption('Coincident')
|
4186
4624
|
# load specification
|
4187
|
-
sizing_system.
|
4188
|
-
sizing_system.
|
4189
|
-
sizing_system.
|
4190
|
-
sizing_system.
|
4191
|
-
sizing_system.setMinimumSystemAirFlowRatio(0.3) # No DCV
|
4625
|
+
sizing_system.setTypeofLoadtoSizeOn('VentilationRequirement')
|
4626
|
+
sizing_system.setAllOutdoorAirinCooling(true)
|
4627
|
+
sizing_system.setAllOutdoorAirinHeating(true)
|
4628
|
+
sizing_system.setMinimumSystemAirFlowRatio(0.3)
|
4192
4629
|
|
4193
4630
|
# set availability schedule
|
4194
4631
|
air_loop.setAvailabilitySchedule(hvac_op_sch)
|
4632
|
+
|
4633
|
+
# get the supply air inlet node
|
4195
4634
|
airloop_supply_inlet = air_loop.supplyInletNode
|
4196
4635
|
|
4197
4636
|
# create air loop fan
|
4198
4637
|
# constant speed fan
|
4638
|
+
fan_static_pressure_in_h2o = 2.5
|
4639
|
+
fan_static_pressure_pa = OpenStudio.convert(fan_static_pressure_in_h2o, "inH_{2}O","Pa").get
|
4199
4640
|
fan = OpenStudio::Model::FanConstantVolume.new(self, alwaysOnDiscreteSchedule)
|
4200
|
-
fan.setName('DOAS
|
4641
|
+
fan.setName('DOAS Fan')
|
4201
4642
|
fan.setFanEfficiency(0.58175)
|
4202
|
-
fan.setPressureRise(
|
4203
|
-
if
|
4204
|
-
fan.setMaximumFlowRate(OpenStudio.convert(fan_max_flow_rate, 'cfm', 'm^3/s').get) # unit of fan_max_flow_rate is cfm
|
4205
|
-
else
|
4643
|
+
fan.setPressureRise(fan_static_pressure_pa)
|
4644
|
+
if fan_max_flow_rate.nil?
|
4206
4645
|
fan.autosizeMaximumFlowRate
|
4646
|
+
else
|
4647
|
+
fan.setMaximumFlowRate(OpenStudio.convert(fan_max_flow_rate, 'cfm', 'm^3/s').get) # unit of fan_max_flow_rate is cfm
|
4207
4648
|
end
|
4208
4649
|
fan.setMotorEfficiency(0.895)
|
4209
4650
|
fan.setMotorInAirstreamFraction(1.0)
|
@@ -4247,62 +4688,63 @@ class OpenStudio::Model::Model
|
|
4247
4688
|
system_oa = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(self, controller_oa)
|
4248
4689
|
system_oa.addToNode(airloop_supply_inlet)
|
4249
4690
|
|
4250
|
-
# create scheduled setpoint manager for airloop
|
4251
|
-
# DOAS or VAV for cooling and not ventilation
|
4252
|
-
setpoint_manager = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(self)
|
4253
|
-
setpoint_manager.setControlVariable('Temperature')
|
4254
|
-
setpoint_manager.setSetpointatOutdoorLowTemperature(15.5)
|
4255
|
-
setpoint_manager.setOutdoorLowTemperature(15.5)
|
4256
|
-
setpoint_manager.setSetpointatOutdoorHighTemperature(12.8)
|
4257
|
-
setpoint_manager.setOutdoorHighTemperature(21)
|
4258
|
-
|
4259
|
-
# connect components to airloop
|
4260
|
-
# find the supply inlet node of the airloop
|
4261
|
-
|
4262
4691
|
# add setpoint manager to supply equipment outlet node
|
4263
|
-
|
4692
|
+
sat_oa_reset.addToNode(air_loop.supplyOutletNode)
|
4693
|
+
|
4694
|
+
# ERV, if requested
|
4695
|
+
if energy_recovery
|
4696
|
+
# Get the OA system and its outboard OA node
|
4697
|
+
oa_system = air_loop.airLoopHVACOutdoorAirSystem.get
|
4698
|
+
oa_node = oa_system.outboardOANode.get
|
4699
|
+
|
4700
|
+
# Create the ERV and set its properties
|
4701
|
+
erv = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(self)
|
4702
|
+
erv.addToNode(oa_node)
|
4703
|
+
erv.setHeatExchangerType("Rotary")
|
4704
|
+
# TODO Come up with scheme for estimating power of ERV motor wheel
|
4705
|
+
# which might require knowing airlow (like prototype buildings do).
|
4706
|
+
# erv.setNominalElectricPower(value_new)
|
4707
|
+
erv.setEconomizerLockout(true)
|
4708
|
+
erv.setSupplyAirOutletTemperatureControl(false)
|
4709
|
+
|
4710
|
+
erv.setSensibleEffectivenessat100HeatingAirFlow(0.76)
|
4711
|
+
erv.setSensibleEffectivenessat75HeatingAirFlow(0.81)
|
4712
|
+
erv.setLatentEffectivenessat100HeatingAirFlow(0.68)
|
4713
|
+
erv.setLatentEffectivenessat75HeatingAirFlow(0.73)
|
4714
|
+
|
4715
|
+
erv.setSensibleEffectivenessat100CoolingAirFlow(0.76)
|
4716
|
+
erv.setSensibleEffectivenessat75CoolingAirFlow(0.81)
|
4717
|
+
erv.setLatentEffectivenessat100CoolingAirFlow(0.68)
|
4718
|
+
erv.setLatentEffectivenessat75CoolingAirFlow(0.73)
|
4719
|
+
|
4720
|
+
# Increase fan pressure caused by the ERV
|
4721
|
+
fans = []
|
4722
|
+
fans += air_loop.supplyComponents("OS:Fan:VariableVolume".to_IddObjectType)
|
4723
|
+
fans += air_loop.supplyComponents("OS:Fan:ConstantVolume".to_IddObjectType)
|
4724
|
+
if fans.size > 0
|
4725
|
+
if fans[0].to_FanConstantVolume.is_initialized
|
4726
|
+
fans[0].to_FanConstantVolume.get.setPressureRise(OpenStudio.convert(1.0,"inH_{2}O","Pa").get)
|
4727
|
+
elsif fans[0].to_FanVariableVolume.is_initialized
|
4728
|
+
fans[0].to_FanVariableVolume.get.setPressureRise(OpenStudio.convert(1.0,"inH_{2}O","Pa").get)
|
4729
|
+
end
|
4730
|
+
end
|
4731
|
+
end
|
4264
4732
|
|
4265
4733
|
# add thermal zones to airloop
|
4266
4734
|
thermal_zones.each do |zone|
|
4267
4735
|
zone_name = zone.name.to_s
|
4268
4736
|
|
4737
|
+
# Ensure that zone sizing accounts for DOAS
|
4269
4738
|
zone_sizing = zone.sizingZone
|
4270
|
-
zone_sizing.
|
4271
|
-
zone_sizing.
|
4272
|
-
zone_sizing.
|
4273
|
-
zone_sizing.
|
4739
|
+
zone_sizing.setAccountforDedicatedOutdoorAirSystem(true)
|
4740
|
+
zone_sizing.setDedicatedOutdoorAirSystemControlStrategy('ColdSupplyAir')
|
4741
|
+
zone_sizing.setDedicatedOutdoorAirLowSetpointTemperatureforDesign(sat_at_hi_oat_c)
|
4742
|
+
zone_sizing.setDedicatedOutdoorAirHighSetpointTemperatureforDesign(sat_at_lo_oat_c)
|
4274
4743
|
|
4275
4744
|
# make an air terminal for the zone
|
4276
4745
|
air_terminal = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(self, alwaysOnDiscreteSchedule)
|
4277
4746
|
air_terminal.setName(zone_name + 'Air Terminal')
|
4278
4747
|
|
4279
|
-
fan_coil_cooling_coil = OpenStudio::Model::CoilCoolingWater.new(self, alwaysOnDiscreteSchedule)
|
4280
|
-
fan_coil_cooling_coil.setName(zone_name + 'FCU Cooling Coil')
|
4281
|
-
chilled_water_loop.addDemandBranchForComponent(fan_coil_cooling_coil)
|
4282
|
-
fan_coil_cooling_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0)
|
4283
|
-
|
4284
|
-
fan_coil_heating_coil = OpenStudio::Model::CoilHeatingWater.new(self, alwaysOnDiscreteSchedule)
|
4285
|
-
fan_coil_heating_coil.setName(zone_name + 'FCU Heating Coil')
|
4286
|
-
hot_water_loop.addDemandBranchForComponent(fan_coil_heating_coil)
|
4287
|
-
fan_coil_heating_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0)
|
4288
|
-
|
4289
|
-
fan_coil_fan = OpenStudio::Model::FanOnOff.new(self, alwaysOnDiscreteSchedule)
|
4290
|
-
fan_coil_fan.setName(zone_name + ' Fan Coil fan')
|
4291
|
-
fan_coil_fan.setFanEfficiency(0.16)
|
4292
|
-
fan_coil_fan.setPressureRise(270.9) # Pa
|
4293
|
-
fan_coil_fan.autosizeMaximumFlowRate
|
4294
|
-
fan_coil_fan.setMotorEfficiency(0.29)
|
4295
|
-
fan_coil_fan.setMotorInAirstreamFraction(1.0)
|
4296
|
-
fan_coil_fan.setEndUseSubcategory('FCU Fans')
|
4297
|
-
|
4298
|
-
fan_coil = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(self, alwaysOnDiscreteSchedule,
|
4299
|
-
fan_coil_fan, fan_coil_cooling_coil, fan_coil_heating_coil)
|
4300
|
-
fan_coil.setName(zone_name + 'FCU')
|
4301
|
-
fan_coil.setCapacityControlMethod('CyclingFan')
|
4302
|
-
fan_coil.autosizeMaximumSupplyAirFlowRate
|
4303
|
-
fan_coil.setMaximumOutdoorAirFlowRate(0)
|
4304
|
-
fan_coil.addToThermalZone(zone)
|
4305
|
-
|
4306
4748
|
# attach new terminal to the zone and to the airloop
|
4307
4749
|
air_loop.addBranchForZone(zone, air_terminal.to_StraightComponent)
|
4308
4750
|
end
|
@@ -4310,6 +4752,742 @@ class OpenStudio::Model::Model
|
|
4310
4752
|
return air_loop
|
4311
4753
|
end
|
4312
4754
|
|
4755
|
+
# Adds hydronic or electric baseboard heating to each zone.
|
4756
|
+
#
|
4757
|
+
# @param template [String] Valid choices are 90.1-2004,
|
4758
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
4759
|
+
# @param hot_water_loop [OpenStudio::Model::PlantLoop]
|
4760
|
+
# the hot water loop that serves the baseboards. If nil, baseboards are electric.
|
4761
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add baseboards to.
|
4762
|
+
# @return [Array<OpenStudio::Model::ZoneHVACBaseboardConvectiveElectric, OpenStudio::Model::ZoneHVACBaseboardConvectiveWater>]
|
4763
|
+
# array of baseboard heaters.
|
4764
|
+
def add_baseboard(template,
|
4765
|
+
hot_water_loop,
|
4766
|
+
thermal_zones)
|
4767
|
+
|
4768
|
+
# Make a baseboard heater for each zone
|
4769
|
+
baseboards = []
|
4770
|
+
thermal_zones.each do |zone|
|
4771
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding baseboard heat for #{zone.name}.")
|
4772
|
+
|
4773
|
+
if hot_water_loop.nil?
|
4774
|
+
baseboard = OpenStudio::Model::ZoneHVACBaseboardConvectiveElectric.new(self)
|
4775
|
+
baseboard.setName("#{zone.name} Electric Baseboard")
|
4776
|
+
baseboard.addToThermalZone(zone)
|
4777
|
+
baseboards << baseboard
|
4778
|
+
else
|
4779
|
+
htg_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(self)
|
4780
|
+
htg_coil.setName("#{zone.name} Hydronic Baseboard Coil")
|
4781
|
+
hot_water_loop.addDemandBranchForComponent(htg_coil)
|
4782
|
+
baseboard = OpenStudio::Model::ZoneHVACBaseboardConvectiveWater.new(self, alwaysOnDiscreteSchedule, htg_coil)
|
4783
|
+
baseboard.setName("#{zone.name} Hydronic Baseboard")
|
4784
|
+
baseboard.addToThermalZone(zone)
|
4785
|
+
baseboards << baseboard
|
4786
|
+
end
|
4787
|
+
|
4788
|
+
end
|
4789
|
+
|
4790
|
+
return baseboards
|
4791
|
+
|
4792
|
+
end
|
4793
|
+
|
4794
|
+
# Adds four pipe fan coil units to each zone.
|
4795
|
+
#
|
4796
|
+
# @param template [String] Valid choices are 90.1-2004,
|
4797
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
4798
|
+
# @param hot_water_loop [OpenStudio::Model::PlantLoop]
|
4799
|
+
# the hot water loop that serves the fan coils. If nil, a zero-capacity,
|
4800
|
+
# electric heating coil set to Always-Off will be included in the unit.
|
4801
|
+
# @param chilled_water_loop [OpenStudio::Model::PlantLoop]
|
4802
|
+
# the chilled water loop that serves the fan coils.
|
4803
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add fan coil units to.
|
4804
|
+
# @return [Array<OpenStudio::Model::ZoneHVACFourPipeFanCoil>]
|
4805
|
+
# array of fan coil units.
|
4806
|
+
def add_four_pipe_fan_coil(template,
|
4807
|
+
hot_water_loop,
|
4808
|
+
chilled_water_loop,
|
4809
|
+
thermal_zones)
|
4810
|
+
|
4811
|
+
# Supply temps used across all zones
|
4812
|
+
zn_dsn_clg_sa_temp_f = 55
|
4813
|
+
zn_dsn_htg_sa_temp_f = 104
|
4814
|
+
|
4815
|
+
zn_dsn_clg_sa_temp_c = OpenStudio.convert(zn_dsn_clg_sa_temp_f, 'F', 'C').get
|
4816
|
+
zn_dsn_htg_sa_temp_c = OpenStudio.convert(zn_dsn_htg_sa_temp_f, 'F', 'C').get
|
4817
|
+
|
4818
|
+
# Make a fan coil unit for each zone
|
4819
|
+
fcus = []
|
4820
|
+
thermal_zones.each do |zone|
|
4821
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding fan coil for #{zone.name}.")
|
4822
|
+
|
4823
|
+
zone_sizing = zone.sizingZone
|
4824
|
+
zone_sizing.setZoneCoolingDesignSupplyAirTemperature(zn_dsn_clg_sa_temp_c)
|
4825
|
+
zone_sizing.setZoneHeatingDesignSupplyAirTemperature(zn_dsn_htg_sa_temp_c)
|
4826
|
+
|
4827
|
+
fcu_clg_coil = nil
|
4828
|
+
if chilled_water_loop
|
4829
|
+
fcu_clg_coil = OpenStudio::Model::CoilCoolingWater.new(self, alwaysOnDiscreteSchedule)
|
4830
|
+
fcu_clg_coil.setName("#{zone.name} 'FCU Cooling Coil")
|
4831
|
+
chilled_water_loop.addDemandBranchForComponent(fcu_clg_coil)
|
4832
|
+
fcu_clg_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0)
|
4833
|
+
else
|
4834
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "Fan coil units require a chilled water loop, but none was provided.")
|
4835
|
+
return fcus
|
4836
|
+
end
|
4837
|
+
|
4838
|
+
fcu_htg_coil = nil
|
4839
|
+
if hot_water_loop
|
4840
|
+
fcu_htg_coil = OpenStudio::Model::CoilHeatingWater.new(self, alwaysOnDiscreteSchedule)
|
4841
|
+
fcu_htg_coil.setName("#{zone.name} FCU Heating Coil")
|
4842
|
+
hot_water_loop.addDemandBranchForComponent(fcu_htg_coil)
|
4843
|
+
fcu_htg_coil.controllerWaterCoil.get.setMinimumActuatedFlow(0)
|
4844
|
+
else
|
4845
|
+
# Zero-capacity, always-off electric heating coil
|
4846
|
+
fcu_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOffDiscreteSchedule)
|
4847
|
+
fcu_htg_coil.setName("#{zone.name} No Heat")
|
4848
|
+
fcu_htg_coil.setNominalCapacity(0)
|
4849
|
+
end
|
4850
|
+
|
4851
|
+
fcu_fan = OpenStudio::Model::FanOnOff.new(self, alwaysOnDiscreteSchedule)
|
4852
|
+
fcu_fan.setName("#{zone.name} Fan Coil fan")
|
4853
|
+
fcu_fan.setFanEfficiency(0.16)
|
4854
|
+
fcu_fan.setPressureRise(270.9) # Pa
|
4855
|
+
fcu_fan.autosizeMaximumFlowRate
|
4856
|
+
fcu_fan.setMotorEfficiency(0.29)
|
4857
|
+
fcu_fan.setMotorInAirstreamFraction(1.0)
|
4858
|
+
fcu_fan.setEndUseSubcategory('FCU Fans')
|
4859
|
+
|
4860
|
+
fcu = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(self,
|
4861
|
+
alwaysOnDiscreteSchedule,
|
4862
|
+
fcu_fan,
|
4863
|
+
fcu_clg_coil,
|
4864
|
+
fcu_htg_coil)
|
4865
|
+
fcu.setName("#{zone.name} FCU")
|
4866
|
+
fcu.setCapacityControlMethod('CyclingFan')
|
4867
|
+
fcu.autosizeMaximumSupplyAirFlowRate
|
4868
|
+
fcu.setMaximumOutdoorAirFlowRate(0)
|
4869
|
+
fcu.addToThermalZone(zone)
|
4870
|
+
fcus << fcu
|
4871
|
+
|
4872
|
+
end
|
4873
|
+
|
4874
|
+
return fcus
|
4875
|
+
end
|
4876
|
+
|
4877
|
+
# Adds a window air conditioner to each zone.
|
4878
|
+
# Code adapted from:
|
4879
|
+
# https://github.com/NREL/OpenStudio-BEopt/blob/master/measures/ResidentialHVACRoomAirConditioner/measure.rb
|
4880
|
+
#
|
4881
|
+
# @param template [String] Valid choices are 90.1-2004,
|
4882
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
4883
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add fan coil units to.
|
4884
|
+
# @return [Array<OpenStudio::Model::ZoneHVACPackagedTerminalAirConditioner>] and array of PTACs used as window AC units
|
4885
|
+
def add_window_ac(template,
|
4886
|
+
thermal_zones)
|
4887
|
+
|
4888
|
+
# Defaults
|
4889
|
+
eer = 8.5 # Btu/W-h
|
4890
|
+
shr = 0.65 # The sensible heat ratio (ratio of the sensible portion of the load to the total load) at the nominal rated capacity
|
4891
|
+
airflow_cfm_per_ton = 350.0 # cfm/ton
|
4892
|
+
|
4893
|
+
# Performance curves
|
4894
|
+
# From Frigidaire 10.7 EER unit in Winkler et. al. Lab Testing of Window ACs (2013)
|
4895
|
+
# NOTE: These coefficients are in SI UNITS
|
4896
|
+
cool_cap_ft_coeffs_si = [0.6405, 0.01568, 0.0004531, 0.001615, -0.0001825, 0.00006614]
|
4897
|
+
cool_eir_ft_coeffs_si = [2.287, -0.1732, 0.004745, 0.01662, 0.000484, -0.001306]
|
4898
|
+
cool_cap_fflow_coeffs = [0.887, 0.1128, 0]
|
4899
|
+
cool_eir_fflow_coeffs = [1.763, -0.6081, 0]
|
4900
|
+
cool_plf_fplr_coeffs = [0.78, 0.22, 0]
|
4901
|
+
|
4902
|
+
# Make the curves
|
4903
|
+
roomac_cap_ft = create_curve_biquadratic(cool_cap_ft_coeffs_si, "RoomAC-Cap-fT", 0, 100, 0, 100, nil, nil)
|
4904
|
+
roomac_cap_fff = create_curve_quadratic(cool_cap_fflow_coeffs, "RoomAC-Cap-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
4905
|
+
roomac_eir_ft = create_curve_biquadratic(cool_eir_ft_coeffs_si, "RoomAC-EIR-fT", 0, 100, 0, 100, nil, nil)
|
4906
|
+
roomac_eir_fff = create_curve_quadratic(cool_eir_fflow_coeffs, "RoomAC-EIR-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
4907
|
+
roomac_plf_fplr = create_curve_quadratic(cool_plf_fplr_coeffs, "RoomAC-PLF-fPLR", 0, 1, 0, 1, is_dimensionless=true)
|
4908
|
+
|
4909
|
+
acs = []
|
4910
|
+
thermal_zones.each do |zone|
|
4911
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding window AC for #{zone.name}.")
|
4912
|
+
|
4913
|
+
clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(self,
|
4914
|
+
alwaysOnDiscreteSchedule,
|
4915
|
+
roomac_cap_ft,
|
4916
|
+
roomac_cap_fff,
|
4917
|
+
roomac_eir_ft,
|
4918
|
+
roomac_eir_fff,
|
4919
|
+
roomac_plf_fplr)
|
4920
|
+
clg_coil.setName("Window AC Clg Coil")
|
4921
|
+
clg_coil.setRatedSensibleHeatRatio(shr)
|
4922
|
+
clg_coil.setRatedCOP(OpenStudio::OptionalDouble.new(OpenStudio.convert(eer, "Btu/h", "W").get))
|
4923
|
+
clg_coil.setRatedEvaporatorFanPowerPerVolumeFlowRate(OpenStudio::OptionalDouble.new(773.3))
|
4924
|
+
clg_coil.setEvaporativeCondenserEffectiveness(OpenStudio::OptionalDouble.new(0.9))
|
4925
|
+
clg_coil.setMaximumOutdoorDryBulbTemperatureForCrankcaseHeaterOperation(OpenStudio::OptionalDouble.new(10))
|
4926
|
+
clg_coil.setBasinHeaterSetpointTemperature(OpenStudio::OptionalDouble.new(2))
|
4927
|
+
|
4928
|
+
fan = OpenStudio::Model::FanOnOff.new(self, alwaysOnDiscreteSchedule)
|
4929
|
+
fan.setName("Window AC Supply Fan")
|
4930
|
+
fan.setFanEfficiency(1)
|
4931
|
+
fan.setPressureRise(0)
|
4932
|
+
fan.setMotorEfficiency(1)
|
4933
|
+
fan.setMotorInAirstreamFraction(0)
|
4934
|
+
|
4935
|
+
htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOffDiscreteSchedule)
|
4936
|
+
htg_coil.setName("Window AC Always Off Htg Coil")
|
4937
|
+
|
4938
|
+
ptac = OpenStudio::Model::ZoneHVACPackagedTerminalAirConditioner.new(self,
|
4939
|
+
alwaysOnDiscreteSchedule,
|
4940
|
+
fan,
|
4941
|
+
htg_coil,
|
4942
|
+
clg_coil)
|
4943
|
+
ptac.setName("#{zone.name} Window AC")
|
4944
|
+
ptac.setSupplyAirFanOperatingModeSchedule(alwaysOffDiscreteSchedule)
|
4945
|
+
ptac.addToThermalZone(zone)
|
4946
|
+
|
4947
|
+
acs << ptac
|
4948
|
+
|
4949
|
+
end
|
4950
|
+
|
4951
|
+
return acs
|
4952
|
+
|
4953
|
+
end
|
4954
|
+
|
4955
|
+
# Adds a forced air furnace or central AC to each zone.
|
4956
|
+
# Code adapted from:
|
4957
|
+
# https://github.com/NREL/OpenStudio-BEopt/blob/master/measures/ResidentialHVACFurnaceFuel/measure.rb
|
4958
|
+
#
|
4959
|
+
# @param template [String] Valid choices are 90.1-2004,
|
4960
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
4961
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add fan coil units to.
|
4962
|
+
# @param heating [Bool] if true, the unit will include a NaturalGas heating coil
|
4963
|
+
# @param cooling [Bool] if true, the unit will include a DX cooling coil
|
4964
|
+
# @param ventilation [Bool] if true, the unit will include an OA intake
|
4965
|
+
# @return [Array<OpenStudio::Model::AirLoopHVAC>] and array of air loops representing the furnaces
|
4966
|
+
def add_furnace_central_ac(template,
|
4967
|
+
thermal_zones,
|
4968
|
+
heating,
|
4969
|
+
cooling,
|
4970
|
+
ventilation)
|
4971
|
+
|
4972
|
+
equip_name = nil
|
4973
|
+
if heating && cooling
|
4974
|
+
equip_name = "Central Heating and AC"
|
4975
|
+
elsif heating && !cooling
|
4976
|
+
equip_name = "Furnace"
|
4977
|
+
elsif cooling && !heating
|
4978
|
+
equip_name = "Central AC"
|
4979
|
+
else
|
4980
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', "Heating and cooling both disabled, not a valid Furnace or Central AC selection, no equipment was added.")
|
4981
|
+
return []
|
4982
|
+
end
|
4983
|
+
|
4984
|
+
# Defaults
|
4985
|
+
fan_pressure_rise_in = 0.5 # 0.5 in W.C.
|
4986
|
+
afue = 0.78
|
4987
|
+
seer = 13
|
4988
|
+
eer = 11.1
|
4989
|
+
shr = 0.73
|
4990
|
+
ac_w_per_cfm = 0.365
|
4991
|
+
sat_htg_f = 120
|
4992
|
+
sat_clg_f = 55
|
4993
|
+
crank_case_heat_w = 0
|
4994
|
+
crank_case_max_temp_f = 55
|
4995
|
+
|
4996
|
+
# Performance curves
|
4997
|
+
# These coefficients are in IP UNITS
|
4998
|
+
cool_cap_ft_coeffs_ip = [3.670270705, -0.098652414, 0.000955906, 0.006552414, -0.0000156, -0.000131877]
|
4999
|
+
cool_eir_ft_coeffs_ip = [-3.302695861, 0.137871531, -0.001056996, -0.012573945, 0.000214638, -0.000145054]
|
5000
|
+
cool_cap_fflow_coeffs = [0.718605468, 0.410099989, -0.128705457]
|
5001
|
+
cool_eir_fflow_coeffs = [1.32299905, -0.477711207, 0.154712157]
|
5002
|
+
cool_plf_fplr_coeffs = [0.8, 0.2, 0]
|
5003
|
+
|
5004
|
+
# Convert coefficients from IP to SI
|
5005
|
+
cool_cap_ft_coeffs_si = convert_curve_biquadratic(cool_cap_ft_coeffs_ip)
|
5006
|
+
cool_eir_ft_coeffs_si = convert_curve_biquadratic(cool_eir_ft_coeffs_ip)
|
5007
|
+
|
5008
|
+
# Make the curves
|
5009
|
+
ac_cap_ft = create_curve_biquadratic(cool_cap_ft_coeffs_si, "AC-Cap-fT", 0, 100, 0, 100, nil, nil)
|
5010
|
+
ac_cap_fff = create_curve_quadratic(cool_cap_fflow_coeffs, "AC-Cap-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
5011
|
+
ac_eir_ft = create_curve_biquadratic(cool_eir_ft_coeffs_si, "AC-EIR-fT", 0, 100, 0, 100, nil, nil)
|
5012
|
+
ac_eir_fff = create_curve_quadratic(cool_eir_fflow_coeffs, "AC-EIR-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
5013
|
+
ac_plf_fplr = create_curve_quadratic(cool_plf_fplr_coeffs, "AC-PLF-fPLR", 0, 1, 0, 1, is_dimensionless=true)
|
5014
|
+
|
5015
|
+
# Unit conversion
|
5016
|
+
fan_pressure_rise_pa = OpenStudio.convert(fan_pressure_rise_in, 'inH_{2}O', 'Pa').get
|
5017
|
+
|
5018
|
+
furnaces = []
|
5019
|
+
thermal_zones.each do |zone|
|
5020
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding furnace AC for #{zone.name}.")
|
5021
|
+
|
5022
|
+
air_loop_name = "#{zone.name} #{equip_name}"
|
5023
|
+
air_loop = OpenStudio::Model::AirLoopHVAC.new(self)
|
5024
|
+
air_loop.setName("#{air_loop_name}")
|
5025
|
+
|
5026
|
+
# Heating Coil
|
5027
|
+
htg_coil = nil
|
5028
|
+
if heating
|
5029
|
+
htg_coil = OpenStudio::Model::CoilHeatingGas.new(self)
|
5030
|
+
htg_coil.setName("#{air_loop_name} htg coil")
|
5031
|
+
htg_coil.setGasBurnerEfficiency(afue_to_thermal_eff(afue))
|
5032
|
+
htg_coil.setParasiticElectricLoad(0)
|
5033
|
+
htg_coil.setParasiticGasLoad(0)
|
5034
|
+
end
|
5035
|
+
|
5036
|
+
# Cooling Coil
|
5037
|
+
clg_coil = nil
|
5038
|
+
if cooling
|
5039
|
+
clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(self,
|
5040
|
+
alwaysOnDiscreteSchedule,
|
5041
|
+
ac_cap_ft,
|
5042
|
+
ac_cap_fff,
|
5043
|
+
ac_eir_ft,
|
5044
|
+
ac_eir_fff,
|
5045
|
+
ac_plf_fplr)
|
5046
|
+
clg_coil.setName("#{air_loop_name} cooling coil")
|
5047
|
+
clg_coil.setRatedSensibleHeatRatio(shr)
|
5048
|
+
clg_coil.setRatedCOP(OpenStudio::OptionalDouble.new(eer_to_cop(eer)))
|
5049
|
+
clg_coil.setRatedEvaporatorFanPowerPerVolumeFlowRate(OpenStudio::OptionalDouble.new(ac_w_per_cfm / OpenStudio::convert(1.0,"cfm","m^3/s").get))
|
5050
|
+
clg_coil.setNominalTimeForCondensateRemovalToBegin(OpenStudio::OptionalDouble.new(1000.0))
|
5051
|
+
clg_coil.setRatioOfInitialMoistureEvaporationRateAndSteadyStateLatentCapacity(OpenStudio::OptionalDouble.new(1.5))
|
5052
|
+
clg_coil.setMaximumCyclingRate(OpenStudio::OptionalDouble.new(3.0))
|
5053
|
+
clg_coil.setLatentCapacityTimeConstant(OpenStudio::OptionalDouble.new(45.0))
|
5054
|
+
clg_coil.setCondenserType("AirCooled")
|
5055
|
+
clg_coil.setCrankcaseHeaterCapacity(OpenStudio::OptionalDouble.new(crank_case_heat_w))
|
5056
|
+
clg_coil.setMaximumOutdoorDryBulbTemperatureForCrankcaseHeaterOperation(OpenStudio::OptionalDouble.new(OpenStudio::convert(crank_case_max_temp_f,"F","C").get))
|
5057
|
+
end
|
5058
|
+
|
5059
|
+
# Fan
|
5060
|
+
fan = OpenStudio::Model::FanOnOff.new(self, alwaysOnDiscreteSchedule)
|
5061
|
+
fan.setName("#{air_loop_name} supply fan")
|
5062
|
+
fan.setEndUseSubcategory('residential hvac fan')
|
5063
|
+
fan.setFanEfficiency(0.6) # Overall Efficiency of the Supply Fan, Motor and Drive
|
5064
|
+
fan.setPressureRise(fan_pressure_rise_pa)
|
5065
|
+
fan.setMotorEfficiency(1)
|
5066
|
+
fan.setMotorInAirstreamFraction(1)
|
5067
|
+
|
5068
|
+
# Outdoor Air Intake
|
5069
|
+
oa_intake_controller = OpenStudio::Model::ControllerOutdoorAir.new(self)
|
5070
|
+
oa_intake_controller.setName("#{air_loop.name} OA Controller")
|
5071
|
+
oa_intake = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(self, oa_intake_controller)
|
5072
|
+
oa_intake.setName("#{air_loop.name} OA Sys")
|
5073
|
+
oa_intake.addToNode(air_loop.supplyInletNode)
|
5074
|
+
if !ventilation
|
5075
|
+
# Disable the OA
|
5076
|
+
oa_intake_controller.setMinimumOutdoorAirSchedule(alwaysOffDiscreteSchedule)
|
5077
|
+
end
|
5078
|
+
|
5079
|
+
# Unitary System (holds the coils and fan)
|
5080
|
+
unitary = OpenStudio::Model::AirLoopHVACUnitarySystem.new(self)
|
5081
|
+
unitary.setName("#{air_loop_name} zoneunitary system")
|
5082
|
+
unitary.setAvailabilitySchedule(alwaysOnDiscreteSchedule)
|
5083
|
+
unitary.setMaximumSupplyAirTemperature(OpenStudio::convert(120.0,"F","C").get)
|
5084
|
+
unitary.setControllingZoneorThermostatLocation(zone)
|
5085
|
+
unitary.addToNode(air_loop.supplyInletNode)
|
5086
|
+
|
5087
|
+
# Set flow rates during different conditions
|
5088
|
+
unitary.setSupplyAirFlowRateDuringHeatingOperation(0) unless heating
|
5089
|
+
unitary.setSupplyAirFlowRateDuringCoolingOperation(0) unless cooling
|
5090
|
+
unitary.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(0) unless ventilation
|
5091
|
+
|
5092
|
+
# Attach the coils and fan
|
5093
|
+
unitary.setHeatingCoil(htg_coil) if htg_coil
|
5094
|
+
unitary.setCoolingCoil(clg_coil) if clg_coil
|
5095
|
+
unitary.setSupplyFan(fan)
|
5096
|
+
unitary.setFanPlacement("BlowThrough")
|
5097
|
+
unitary.setSupplyAirFanOperatingModeSchedule(alwaysOffDiscreteSchedule)
|
5098
|
+
|
5099
|
+
# Diffuser
|
5100
|
+
diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(self, alwaysOnDiscreteSchedule)
|
5101
|
+
diffuser.setName(" #{zone.name} direct air")
|
5102
|
+
air_loop.addBranchForZone(zone, diffuser)
|
5103
|
+
|
5104
|
+
furnaces << air_loop
|
5105
|
+
end
|
5106
|
+
|
5107
|
+
return furnaces
|
5108
|
+
end
|
5109
|
+
|
5110
|
+
# Adds an air source heat pump to each zone.
|
5111
|
+
# Code adapted from:
|
5112
|
+
# https://github.com/NREL/OpenStudio-BEopt/blob/master/measures/ResidentialHVACAirSourceHeatPumpSingleSpeed/measure.rb
|
5113
|
+
#
|
5114
|
+
# @param template [String] Valid choices are 90.1-2004,
|
5115
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
5116
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add fan coil units to.
|
5117
|
+
# @param heating [Bool] if true, the unit will include a NaturalGas heating coil
|
5118
|
+
# @param cooling [Bool] if true, the unit will include a DX cooling coil
|
5119
|
+
# @param ventilation [Bool] if true, the unit will include an OA intake
|
5120
|
+
# @return [Array<OpenStudio::Model::AirLoopHVAC>] and array of air loops representing the heat pumps
|
5121
|
+
def add_central_air_source_heat_pump(template,
|
5122
|
+
thermal_zones,
|
5123
|
+
heating,
|
5124
|
+
cooling,
|
5125
|
+
ventilation)
|
5126
|
+
|
5127
|
+
equip_name = "Central Air Source HP"
|
5128
|
+
|
5129
|
+
# Defaults
|
5130
|
+
fan_pressure_rise_in = 0.5 # 0.5 in W.C.
|
5131
|
+
hspf = 7.7
|
5132
|
+
seer = 13
|
5133
|
+
eer = 11.4
|
5134
|
+
cop = 3.05
|
5135
|
+
shr = 0.73
|
5136
|
+
ac_w_per_cfm = 0.365
|
5137
|
+
min_hp_oat_f = 0
|
5138
|
+
sat_htg_f = 120
|
5139
|
+
sat_clg_f = 55
|
5140
|
+
crank_case_heat_w = 0
|
5141
|
+
crank_case_max_temp_f = 55
|
5142
|
+
|
5143
|
+
# Unit conversion
|
5144
|
+
fan_pressure_rise_pa = OpenStudio.convert(fan_pressure_rise_in, 'inH_{2}O', 'Pa').get
|
5145
|
+
|
5146
|
+
# Performance curves
|
5147
|
+
# These coefficients are in IP UNITS
|
5148
|
+
cool_cap_ft_coeffs_ip = [3.68637657, -0.098352478, 0.000956357, 0.005838141, -0.0000127, -0.000131702]
|
5149
|
+
cool_eir_ft_coeffs_ip = [-3.437356399, 0.136656369, -0.001049231, -0.0079378, 0.000185435, -0.0001441]
|
5150
|
+
cool_cap_fflow_coeffs = [0.718664047, 0.41797409, -0.136638137]
|
5151
|
+
cool_eir_fflow_coeffs = [1.143487507, -0.13943972, -0.004047787]
|
5152
|
+
cool_plf_fplr_coeffs = [0.8, 0.2, 0]
|
5153
|
+
|
5154
|
+
heat_cap_ft_coeffs_ip = [0.566333415, -0.000744164, -0.0000103, 0.009414634, 0.0000506, -0.00000675]
|
5155
|
+
heat_eir_ft_coeffs_ip = [0.718398423, 0.003498178, 0.000142202, -0.005724331, 0.00014085, -0.000215321]
|
5156
|
+
heat_cap_fflow_coeffs = [0.694045465, 0.474207981, -0.168253446]
|
5157
|
+
heat_eir_fflow_coeffs = [2.185418751, -1.942827919, 0.757409168]
|
5158
|
+
heat_plf_fplr_coeffs = [0.8, 0.2, 0]
|
5159
|
+
|
5160
|
+
defrost_eir_coeffs = [0.1528, 0, 0, 0, 0, 0]
|
5161
|
+
|
5162
|
+
# Convert coefficients from IP to SI
|
5163
|
+
cool_cap_ft_coeffs_si = convert_curve_biquadratic(cool_cap_ft_coeffs_ip)
|
5164
|
+
cool_eir_ft_coeffs_si = convert_curve_biquadratic(cool_eir_ft_coeffs_ip)
|
5165
|
+
heat_cap_ft_coeffs_si = convert_curve_biquadratic(heat_cap_ft_coeffs_ip)
|
5166
|
+
heat_eir_ft_coeffs_si = convert_curve_biquadratic(heat_eir_ft_coeffs_ip)
|
5167
|
+
|
5168
|
+
# Make the curves
|
5169
|
+
cool_cap_ft = create_curve_biquadratic(cool_cap_ft_coeffs_si, "Cool-Cap-fT", 0, 100, 0, 100, nil, nil)
|
5170
|
+
cool_cap_fff = create_curve_quadratic(cool_cap_fflow_coeffs, "Cool-Cap-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
5171
|
+
cool_eir_ft = create_curve_biquadratic(cool_eir_ft_coeffs_si, "Cool-EIR-fT", 0, 100, 0, 100, nil, nil)
|
5172
|
+
cool_eir_fff = create_curve_quadratic(cool_eir_fflow_coeffs, "Cool-EIR-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
5173
|
+
cool_plf_fplr = create_curve_quadratic(cool_plf_fplr_coeffs, "Cool-PLF-fPLR", 0, 1, 0, 1, is_dimensionless=true)
|
5174
|
+
|
5175
|
+
heat_cap_ft = create_curve_biquadratic(heat_cap_ft_coeffs_si, "Heat-Cap-fT", 0, 100, 0, 100, nil, nil)
|
5176
|
+
heat_cap_fff = create_curve_quadratic(heat_cap_fflow_coeffs, "Heat-Cap-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
5177
|
+
heat_eir_ft = create_curve_biquadratic(heat_eir_ft_coeffs_si, "Heat-EIR-fT", 0, 100, 0, 100, nil, nil)
|
5178
|
+
heat_eir_fff = create_curve_quadratic(heat_eir_fflow_coeffs, "Heat-EIR-fFF", 0, 2, 0, 2, is_dimensionless=true)
|
5179
|
+
heat_plf_fplr = create_curve_quadratic(heat_plf_fplr_coeffs, "Heat-PLF-fPLR", 0, 1, 0, 1, is_dimensionless=true)
|
5180
|
+
|
5181
|
+
# Heating defrost curve for reverse cycle
|
5182
|
+
defrost_eir_curve = create_curve_biquadratic(defrost_eir_coeffs, "DefrostEIR", -100, 100, -100, 100, nil, nil)
|
5183
|
+
|
5184
|
+
# Unit conversion
|
5185
|
+
fan_pressure_rise_pa = OpenStudio.convert(fan_pressure_rise_in, 'inH_{2}O', 'Pa').get
|
5186
|
+
|
5187
|
+
hps = []
|
5188
|
+
thermal_zones.each do |zone|
|
5189
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding furnace AC for #{zone.name}.")
|
5190
|
+
|
5191
|
+
air_loop_name = "#{zone.name} #{equip_name}"
|
5192
|
+
air_loop = OpenStudio::Model::AirLoopHVAC.new(self)
|
5193
|
+
air_loop.setName("#{air_loop_name}")
|
5194
|
+
|
5195
|
+
# Heating Coil
|
5196
|
+
htg_coil = nil
|
5197
|
+
supp_htg_coil = nil
|
5198
|
+
if heating
|
5199
|
+
htg_coil = OpenStudio::Model::CoilHeatingDXSingleSpeed.new(self,
|
5200
|
+
alwaysOnDiscreteSchedule,
|
5201
|
+
heat_cap_ft,
|
5202
|
+
heat_cap_fff,
|
5203
|
+
heat_eir_ft,
|
5204
|
+
heat_eir_fff,
|
5205
|
+
heat_plf_fplr)
|
5206
|
+
htg_coil.setName("#{air_loop_name} heating coil")
|
5207
|
+
htg_coil.setRatedCOP(hspf_to_cop_heating_no_fan(hspf))
|
5208
|
+
htg_coil.setRatedSupplyFanPowerPerVolumeFlowRate(ac_w_per_cfm / OpenStudio::convert(1.0,"cfm","m^3/s").get)
|
5209
|
+
htg_coil.setDefrostEnergyInputRatioFunctionofTemperatureCurve(defrost_eir_curve)
|
5210
|
+
htg_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(OpenStudio::convert(min_hp_oat_f,"F","C").get)
|
5211
|
+
htg_coil.setMaximumOutdoorDryBulbTemperatureforDefrostOperation(OpenStudio::convert(40.0,"F","C").get)
|
5212
|
+
htg_coil.setCrankcaseHeaterCapacity(crank_case_heat_w)
|
5213
|
+
htg_coil.setMaximumOutdoorDryBulbTemperatureforCrankcaseHeaterOperation(OpenStudio::convert(crank_case_max_temp_f,"F","C").get)
|
5214
|
+
htg_coil.setDefrostStrategy("ReverseCycle")
|
5215
|
+
htg_coil.setDefrostControl("OnDemand")
|
5216
|
+
|
5217
|
+
# Supplemental Heating Coil
|
5218
|
+
supp_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, alwaysOnDiscreteSchedule)
|
5219
|
+
supp_htg_coil.setName("#{air_loop_name} supp htg coil")
|
5220
|
+
supp_htg_coil.setEfficiency(1)
|
5221
|
+
end
|
5222
|
+
|
5223
|
+
# Cooling Coil
|
5224
|
+
clg_coil = nil
|
5225
|
+
if cooling
|
5226
|
+
clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(self,
|
5227
|
+
alwaysOnDiscreteSchedule,
|
5228
|
+
cool_cap_ft,
|
5229
|
+
cool_cap_fff,
|
5230
|
+
cool_eir_ft,
|
5231
|
+
cool_eir_fff,
|
5232
|
+
cool_plf_fplr)
|
5233
|
+
clg_coil.setName("#{air_loop_name} cooling coil")
|
5234
|
+
clg_coil.setRatedSensibleHeatRatio(shr)
|
5235
|
+
clg_coil.setRatedCOP(OpenStudio::OptionalDouble.new(cop))
|
5236
|
+
clg_coil.setRatedEvaporatorFanPowerPerVolumeFlowRate(OpenStudio::OptionalDouble.new(ac_w_per_cfm / OpenStudio::convert(1.0,"cfm","m^3/s").get))
|
5237
|
+
clg_coil.setNominalTimeForCondensateRemovalToBegin(OpenStudio::OptionalDouble.new(1000.0))
|
5238
|
+
clg_coil.setRatioOfInitialMoistureEvaporationRateAndSteadyStateLatentCapacity(OpenStudio::OptionalDouble.new(1.5))
|
5239
|
+
clg_coil.setMaximumCyclingRate(OpenStudio::OptionalDouble.new(3.0))
|
5240
|
+
clg_coil.setLatentCapacityTimeConstant(OpenStudio::OptionalDouble.new(45.0))
|
5241
|
+
clg_coil.setCondenserType("AirCooled")
|
5242
|
+
clg_coil.setCrankcaseHeaterCapacity(OpenStudio::OptionalDouble.new(crank_case_heat_w))
|
5243
|
+
clg_coil.setMaximumOutdoorDryBulbTemperatureForCrankcaseHeaterOperation(OpenStudio::OptionalDouble.new(OpenStudio::convert(crank_case_max_temp_f,"F","C").get))
|
5244
|
+
end
|
5245
|
+
|
5246
|
+
# Fan
|
5247
|
+
fan = OpenStudio::Model::FanOnOff.new(self, alwaysOnDiscreteSchedule)
|
5248
|
+
fan.setName("#{air_loop_name} supply fan")
|
5249
|
+
fan.setEndUseSubcategory('residential hvac fan')
|
5250
|
+
fan.setFanEfficiency(0.6) # Overall Efficiency of the Supply Fan, Motor and Drive
|
5251
|
+
fan.setPressureRise(fan_pressure_rise_pa)
|
5252
|
+
fan.setMotorEfficiency(1)
|
5253
|
+
fan.setMotorInAirstreamFraction(1)
|
5254
|
+
|
5255
|
+
# Outdoor Air Intake
|
5256
|
+
oa_intake_controller = OpenStudio::Model::ControllerOutdoorAir.new(self)
|
5257
|
+
oa_intake_controller.setName("#{air_loop.name} OA Controller")
|
5258
|
+
oa_intake = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(self, oa_intake_controller)
|
5259
|
+
oa_intake.setName("#{air_loop.name} OA Sys")
|
5260
|
+
oa_intake.addToNode(air_loop.supplyInletNode)
|
5261
|
+
if !ventilation
|
5262
|
+
# Disable the OA
|
5263
|
+
oa_intake_controller.setMinimumOutdoorAirSchedule(alwaysOffDiscreteSchedule)
|
5264
|
+
end
|
5265
|
+
|
5266
|
+
# Unitary System (holds the coils and fan)
|
5267
|
+
unitary = OpenStudio::Model::AirLoopHVACUnitarySystem.new(self)
|
5268
|
+
unitary.setName("#{air_loop_name} zoneunitary system")
|
5269
|
+
unitary.setAvailabilitySchedule(alwaysOnDiscreteSchedule)
|
5270
|
+
unitary.setMaximumSupplyAirTemperature(OpenStudio::convert(170.0,"F","C").get) # higher temp for supplemental heat as to not severely limit its use, resulting in unmet hours.
|
5271
|
+
unitary.setMaximumOutdoorDryBulbTemperatureforSupplementalHeaterOperation(OpenStudio::convert(40.0,"F","C").get)
|
5272
|
+
unitary.setControllingZoneorThermostatLocation(zone)
|
5273
|
+
unitary.addToNode(air_loop.supplyInletNode)
|
5274
|
+
|
5275
|
+
# Set flow rates during different conditions
|
5276
|
+
unitary.setSupplyAirFlowRateWhenNoCoolingorHeatingisRequired(0) unless ventilation
|
5277
|
+
|
5278
|
+
# Attach the coils and fan
|
5279
|
+
unitary.setHeatingCoil(htg_coil) if htg_coil
|
5280
|
+
unitary.setCoolingCoil(clg_coil) if clg_coil
|
5281
|
+
unitary.setSupplementalHeatingCoil(supp_htg_coil) if supp_htg_coil
|
5282
|
+
unitary.setSupplyFan(fan)
|
5283
|
+
unitary.setFanPlacement("BlowThrough")
|
5284
|
+
unitary.setSupplyAirFanOperatingModeSchedule(alwaysOffDiscreteSchedule)
|
5285
|
+
|
5286
|
+
# Diffuser
|
5287
|
+
diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(self, alwaysOnDiscreteSchedule)
|
5288
|
+
diffuser.setName(" #{zone.name} direct air")
|
5289
|
+
air_loop.addBranchForZone(zone, diffuser)
|
5290
|
+
|
5291
|
+
hps << air_loop
|
5292
|
+
end
|
5293
|
+
|
5294
|
+
return hps
|
5295
|
+
end
|
5296
|
+
|
5297
|
+
# Adds zone level water-to-air heat pumps for each zone.
|
5298
|
+
#
|
5299
|
+
# @param condenser_loop [OpenStudio::Model::PlantLoop] the condenser loop for the heat pumps
|
5300
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add heat pumps to.
|
5301
|
+
# @param ventilation [Bool] if true, ventilation will be supplied through the unit. If false,
|
5302
|
+
# no ventilation will be supplied through the unit, with the expectation that it will be provided
|
5303
|
+
# by a DOAS or separate system.
|
5304
|
+
# @return [Array<OpenStudio::Model::ZoneHVACWaterToAirHeatPump>] an array of heat pumps
|
5305
|
+
def add_water_source_hp(condenser_loop,
|
5306
|
+
thermal_zones,
|
5307
|
+
ventilation=true)
|
5308
|
+
|
5309
|
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding zone water-to-air heat pump.")
|
5310
|
+
|
5311
|
+
water_to_air_hp_systems = []
|
5312
|
+
thermal_zones.each do |zone|
|
5313
|
+
|
5314
|
+
supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(self, self.alwaysOnDiscreteSchedule)
|
5315
|
+
|
5316
|
+
htg_coil = OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.new(self)
|
5317
|
+
htg_coil.setName("WSHP Htg Coil")
|
5318
|
+
htg_coil.setRatedHeatingCoefficientofPerformance(4.2)
|
5319
|
+
htg_coil.setHeatingCapacityCoefficient1(0.237847462869254)
|
5320
|
+
htg_coil.setHeatingCapacityCoefficient2(-3.35823796081626)
|
5321
|
+
htg_coil.setHeatingCapacityCoefficient3(3.80640467406376)
|
5322
|
+
htg_coil.setHeatingCapacityCoefficient4(0.179200417311554)
|
5323
|
+
htg_coil.setHeatingCapacityCoefficient5(0.12860719846082)
|
5324
|
+
htg_coil.setHeatingPowerConsumptionCoefficient1(-3.79175529243238)
|
5325
|
+
htg_coil.setHeatingPowerConsumptionCoefficient2(3.38799239505527)
|
5326
|
+
htg_coil.setHeatingPowerConsumptionCoefficient3(1.5022612076303)
|
5327
|
+
htg_coil.setHeatingPowerConsumptionCoefficient4(-0.177653510577989)
|
5328
|
+
htg_coil.setHeatingPowerConsumptionCoefficient5(-0.103079864171839)
|
5329
|
+
|
5330
|
+
condenser_loop.addDemandBranchForComponent(htg_coil)
|
5331
|
+
|
5332
|
+
clg_coil = OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.new(self)
|
5333
|
+
clg_coil.setName("WSHP Clg Coil")
|
5334
|
+
clg_coil.setRatedCoolingCoefficientofPerformance(3.4)
|
5335
|
+
clg_coil.setTotalCoolingCapacityCoefficient1(-4.30266987344639)
|
5336
|
+
clg_coil.setTotalCoolingCapacityCoefficient2(7.18536990534372)
|
5337
|
+
clg_coil.setTotalCoolingCapacityCoefficient3(-2.23946714486189)
|
5338
|
+
clg_coil.setTotalCoolingCapacityCoefficient4(0.139995928440879)
|
5339
|
+
clg_coil.setTotalCoolingCapacityCoefficient5(0.102660179888915)
|
5340
|
+
clg_coil.setSensibleCoolingCapacityCoefficient1(6.0019444814887)
|
5341
|
+
clg_coil.setSensibleCoolingCapacityCoefficient2(22.6300677244073)
|
5342
|
+
clg_coil.setSensibleCoolingCapacityCoefficient3(-26.7960783730934)
|
5343
|
+
clg_coil.setSensibleCoolingCapacityCoefficient4(-1.72374720346819)
|
5344
|
+
clg_coil.setSensibleCoolingCapacityCoefficient5(0.490644802367817)
|
5345
|
+
clg_coil.setSensibleCoolingCapacityCoefficient6(0.0693119353468141)
|
5346
|
+
clg_coil.setCoolingPowerConsumptionCoefficient1(-5.67775976415698)
|
5347
|
+
clg_coil.setCoolingPowerConsumptionCoefficient2(0.438988156976704)
|
5348
|
+
clg_coil.setCoolingPowerConsumptionCoefficient3(5.845277342193)
|
5349
|
+
clg_coil.setCoolingPowerConsumptionCoefficient4(0.141605667000125)
|
5350
|
+
clg_coil.setCoolingPowerConsumptionCoefficient5(-0.168727936032429)
|
5351
|
+
|
5352
|
+
condenser_loop.addDemandBranchForComponent(clg_coil)
|
5353
|
+
|
5354
|
+
# add fan
|
5355
|
+
fan = OpenStudio::Model::FanOnOff.new(self, self.alwaysOnDiscreteSchedule)
|
5356
|
+
fan.setName("#{zone.name} WSHP Fan")
|
5357
|
+
fan_static_pressure_in_h2o = 1.33
|
5358
|
+
fan_static_pressure_pa = OpenStudio.convert(fan_static_pressure_in_h2o, "inH_{2}O","Pa").get
|
5359
|
+
fan.setPressureRise(fan_static_pressure_pa)
|
5360
|
+
fan.setFanEfficiency(0.52)
|
5361
|
+
fan.setMotorEfficiency(0.8)
|
5362
|
+
|
5363
|
+
water_to_air_hp_system = OpenStudio::Model::ZoneHVACWaterToAirHeatPump.new(self, self.alwaysOnDiscreteSchedule, fan, htg_coil, clg_coil, supplemental_htg_coil)
|
5364
|
+
water_to_air_hp_system.setName("#{zone.name} WSHP")
|
5365
|
+
unless ventilation
|
5366
|
+
water_to_air_hp_system.setOutdoorAirFlowRateDuringHeatingOperation(OpenStudio::OptionalDouble.new(0))
|
5367
|
+
water_to_air_hp_system.setOutdoorAirFlowRateDuringCoolingOperation(OpenStudio::OptionalDouble.new(0))
|
5368
|
+
water_to_air_hp_system.setOutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded(OpenStudio::OptionalDouble.new(0))
|
5369
|
+
end
|
5370
|
+
water_to_air_hp_system.addToThermalZone(zone)
|
5371
|
+
|
5372
|
+
water_to_air_hp_systems << water_to_air_hp_system
|
5373
|
+
|
5374
|
+
end
|
5375
|
+
|
5376
|
+
return water_to_air_hp_systems
|
5377
|
+
end
|
5378
|
+
|
5379
|
+
# Adds zone level ERVs for each zone.
|
5380
|
+
#
|
5381
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add heat pumps to.
|
5382
|
+
# @return [Array<OpenStudio::Model::ZoneHVACEnergyRecoveryVentilator>] an array of zone ERVs
|
5383
|
+
# @todo review the static pressure rise for the ERV
|
5384
|
+
def add_zone_erv(template, thermal_zones)
|
5385
|
+
|
5386
|
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding zone ERV for #{thermal_zones.size} zones.")
|
5387
|
+
thermal_zones.each do |zone|
|
5388
|
+
OpenStudio::logFree(OpenStudio::Debug, 'openstudio.Model.Model', "---#{zone.name}")
|
5389
|
+
end
|
5390
|
+
|
5391
|
+
# ERV properties
|
5392
|
+
fan_static_pressure_in_h2o = 0.25
|
5393
|
+
fan_static_pressure_pa = OpenStudio.convert(fan_static_pressure_in_h2o, 'inH_{2}O', 'Pa').get
|
5394
|
+
fan_motor_efficiency = 0.8
|
5395
|
+
|
5396
|
+
erv_systems = []
|
5397
|
+
thermal_zones.each do |zone|
|
5398
|
+
|
5399
|
+
# Determine the OA requirement for this zone
|
5400
|
+
min_oa_flow_m3_per_s_per_m2 = zone.outdoor_airflow_rate_per_area
|
5401
|
+
|
5402
|
+
supply_fan = OpenStudio::Model::FanOnOff.new(self)
|
5403
|
+
supply_fan.setName("#{zone.name} ERV Supply Fan")
|
5404
|
+
supply_fan.setMotorEfficiency(fan_motor_efficiency)
|
5405
|
+
impeller_eff = supply_fan.baseline_impeller_efficiency(template)
|
5406
|
+
supply_fan.change_impeller_efficiency(impeller_eff)
|
5407
|
+
supply_fan.setPressureRise(fan_static_pressure_pa)
|
5408
|
+
supply_fan.setMotorInAirstreamFraction(1)
|
5409
|
+
|
5410
|
+
exhaust_fan = OpenStudio::Model::FanOnOff.new(self)
|
5411
|
+
exhaust_fan.setName("#{zone.name} ERV Exhaust Fan")
|
5412
|
+
exhaust_fan.setMotorEfficiency(fan_motor_efficiency)
|
5413
|
+
exhaust_fan.change_impeller_efficiency(impeller_eff)
|
5414
|
+
exhaust_fan.setPressureRise(fan_static_pressure_pa)
|
5415
|
+
exhaust_fan.setMotorInAirstreamFraction(1)
|
5416
|
+
|
5417
|
+
erv_controller = OpenStudio::Model::ZoneHVACEnergyRecoveryVentilatorController.new(self)
|
5418
|
+
# erv_controller.setExhaustAirTemperatureLimit("NoExhaustAirTemperatureLimit")
|
5419
|
+
# erv_controller.setExhaustAirEnthalpyLimit("NoExhaustAirEnthalpyLimit")
|
5420
|
+
# erv_controller.setTimeofDayEconomizerFlowControlSchedule(self.alwaysOnDiscreteSchedule)
|
5421
|
+
# erv_controller.setHighHumidityControlFlag(false)
|
5422
|
+
|
5423
|
+
heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(self)
|
5424
|
+
# heat_exchanger.setHeatExchangerType("Plate")
|
5425
|
+
# heat_exchanger.setEconomizerLockout(true)
|
5426
|
+
# heat_exchanger.setSupplyAirOutletTemperatureControl(false)
|
5427
|
+
# heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(0.76)
|
5428
|
+
# heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(0.81)
|
5429
|
+
# heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(0.68)
|
5430
|
+
# heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(0.73)
|
5431
|
+
# heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(0.76)
|
5432
|
+
# heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(0.81)
|
5433
|
+
# heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(0.68)
|
5434
|
+
# heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(0.73)
|
5435
|
+
|
5436
|
+
zone_hvac = OpenStudio::Model::ZoneHVACEnergyRecoveryVentilator.new(self, heat_exchanger, supply_fan, exhaust_fan)
|
5437
|
+
zone_hvac.setName("#{zone.name} ERV")
|
5438
|
+
zone_hvac.setVentilationRateperUnitFloorArea(min_oa_flow_m3_per_s_per_m2)
|
5439
|
+
zone_hvac.setController(erv_controller)
|
5440
|
+
zone_hvac.addToThermalZone(zone)
|
5441
|
+
|
5442
|
+
# Calculate ERV SAT during sizing periods
|
5443
|
+
# Heating design day
|
5444
|
+
oat_f = 0
|
5445
|
+
return_air_f = 68
|
5446
|
+
eff = heat_exchanger.sensibleEffectivenessat100HeatingAirFlow
|
5447
|
+
coldest_erv_supply_f = oat_f - (eff * (oat_f - return_air_f))
|
5448
|
+
coldest_erv_supply_c = OpenStudio.convert(coldest_erv_supply_f, 'F', 'C').get
|
5449
|
+
|
5450
|
+
# Cooling design day
|
5451
|
+
oat_f = 110
|
5452
|
+
return_air_f = 75
|
5453
|
+
eff = heat_exchanger.sensibleEffectivenessat100CoolingAirFlow
|
5454
|
+
hottest_erv_supply_f = oat_f - (eff * (oat_f - return_air_f))
|
5455
|
+
hottest_erv_supply_c = OpenStudio.convert(hottest_erv_supply_f, 'F', 'C').get
|
5456
|
+
|
5457
|
+
# Ensure that zone sizing accounts for OA from ERV
|
5458
|
+
zone_sizing = zone.sizingZone
|
5459
|
+
zone_sizing.setAccountforDedicatedOutdoorAirSystem(true)
|
5460
|
+
zone_sizing.setDedicatedOutdoorAirSystemControlStrategy('ColdSupplyAir')
|
5461
|
+
zone_sizing.setDedicatedOutdoorAirLowSetpointTemperatureforDesign(coldest_erv_supply_c)
|
5462
|
+
zone_sizing.setDedicatedOutdoorAirHighSetpointTemperatureforDesign(hottest_erv_supply_c)
|
5463
|
+
|
5464
|
+
erv_systems << zone_hvac
|
5465
|
+
|
5466
|
+
end
|
5467
|
+
|
5468
|
+
return erv_systems
|
5469
|
+
end
|
5470
|
+
|
5471
|
+
# Adds ideal air loads systems for each zone.
|
5472
|
+
#
|
5473
|
+
# @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] array of zones to add heat pumps to.
|
5474
|
+
# @return [Array<OpenStudio::Model::ZoneHVACIdealLoadsAirSystem>] an array of ideal air loads systems
|
5475
|
+
# @todo review the default ventilation settings
|
5476
|
+
def add_ideal_air_loads(template, thermal_zones)
|
5477
|
+
|
5478
|
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding ideal air loads for #{thermal_zones.size} zones.")
|
5479
|
+
|
5480
|
+
ideal_systems = []
|
5481
|
+
thermal_zones.each do |zone|
|
5482
|
+
ideal_loads = OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(self)
|
5483
|
+
ideal_loads.addToThermalZone(zone)
|
5484
|
+
ideal_systems << ideal_loads
|
5485
|
+
end
|
5486
|
+
|
5487
|
+
return ideal_systems
|
5488
|
+
|
5489
|
+
end
|
5490
|
+
|
4313
5491
|
# Add an elevator the the specified space
|
4314
5492
|
#
|
4315
5493
|
# @param template [String] Valid choices are
|
@@ -4686,4 +5864,745 @@ class OpenStudio::Model::Model
|
|
4686
5864
|
|
4687
5865
|
return true
|
4688
5866
|
end
|
5867
|
+
|
5868
|
+
# Either get the existing chilled water loop in the model or
|
5869
|
+
# add a new one if there isn't one already.
|
5870
|
+
#
|
5871
|
+
# @param cool_fuel [String] the cooling fuel. Valid choices are
|
5872
|
+
# Electricity, DistrictCooling, and HeatPump.
|
5873
|
+
# @param air_cooled [Bool] if true, the chiller will be air-cooled.
|
5874
|
+
# if false, it will be water-cooled.
|
5875
|
+
def get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=true)
|
5876
|
+
# Retrieve the existing chilled water loop
|
5877
|
+
# or add a new one if necessary.
|
5878
|
+
chilled_water_loop = nil
|
5879
|
+
if getPlantLoopByName('Chilled Water Loop').is_initialized
|
5880
|
+
chilled_water_loop = getPlantLoopByName('Chilled Water Loop').get
|
5881
|
+
else
|
5882
|
+
case cool_fuel
|
5883
|
+
when 'DistrictCooling'
|
5884
|
+
chilled_water_loop = add_chw_loop(template,
|
5885
|
+
'const_pri',
|
5886
|
+
chiller_cooling_type = nil,
|
5887
|
+
chiller_condenser_type = nil,
|
5888
|
+
chiller_compressor_type = nil,
|
5889
|
+
cool_fuel,
|
5890
|
+
condenser_water_loop = nil,
|
5891
|
+
building_type = nil)
|
5892
|
+
when 'HeatPump'
|
5893
|
+
condenser_water_loop = get_or_add_ambient_water_loop
|
5894
|
+
chilled_water_loop = add_chw_loop(template,
|
5895
|
+
'const_pri_var_sec',
|
5896
|
+
'WaterCooled',
|
5897
|
+
chiller_condenser_type = nil,
|
5898
|
+
'Rotary Screw',
|
5899
|
+
cooling_fuel = nil,
|
5900
|
+
condenser_water_loop,
|
5901
|
+
building_type = nil)
|
5902
|
+
when 'Electricity'
|
5903
|
+
if air_cooled
|
5904
|
+
chilled_water_loop = add_chw_loop(template,
|
5905
|
+
'const_pri',
|
5906
|
+
chiller_cooling_type = nil,
|
5907
|
+
chiller_condenser_type = nil,
|
5908
|
+
chiller_compressor_type = nil,
|
5909
|
+
cool_fuel,
|
5910
|
+
condenser_water_loop = nil,
|
5911
|
+
building_type = nil)
|
5912
|
+
else
|
5913
|
+
fan_type = 'TwoSpeed Fan'
|
5914
|
+
if template == '90.1-2013'
|
5915
|
+
fan_type = 'Variable Speed Fan'
|
5916
|
+
end
|
5917
|
+
condenser_water_loop = add_cw_loop(template,
|
5918
|
+
'Open Cooling Tower',
|
5919
|
+
'Propeller or Axial',
|
5920
|
+
fan_type,
|
5921
|
+
1,
|
5922
|
+
1,
|
5923
|
+
nil)
|
5924
|
+
chilled_water_loop = add_chw_loop(template,
|
5925
|
+
'const_pri_var_sec',
|
5926
|
+
'WaterCooled',
|
5927
|
+
chiller_condenser_type = nil,
|
5928
|
+
'Rotary Screw',
|
5929
|
+
cooling_fuel = nil,
|
5930
|
+
condenser_water_loop,
|
5931
|
+
building_type = nil)
|
5932
|
+
end
|
5933
|
+
end
|
5934
|
+
end
|
5935
|
+
|
5936
|
+
return chilled_water_loop
|
5937
|
+
end
|
5938
|
+
|
5939
|
+
# Either get the existing hot water loop in the model or
|
5940
|
+
# add a new one if there isn't one already.
|
5941
|
+
#
|
5942
|
+
# @param heat_fuel [String] the heating fuel.
|
5943
|
+
# Valid choices are NaturalGas, Electricity, DistrictHeating
|
5944
|
+
def get_or_add_hot_water_loop(heat_fuel)
|
5945
|
+
|
5946
|
+
# Retrieve the existing hot water loop
|
5947
|
+
# or add a new one if necessary.
|
5948
|
+
hot_water_loop = nil
|
5949
|
+
hot_water_loop = if getPlantLoopByName('Hot Water Loop').is_initialized
|
5950
|
+
getPlantLoopByName('Hot Water Loop').get
|
5951
|
+
else
|
5952
|
+
add_hw_loop(heat_fuel)
|
5953
|
+
end
|
5954
|
+
|
5955
|
+
return hot_water_loop
|
5956
|
+
end
|
5957
|
+
|
5958
|
+
# Either get the existing ambient water loop in the model or
|
5959
|
+
# add a new one if there isn't one already.
|
5960
|
+
#
|
5961
|
+
def get_or_add_ambient_water_loop
|
5962
|
+
|
5963
|
+
# Retrieve the existing hot water loop
|
5964
|
+
# or add a new one if necessary.
|
5965
|
+
ambient_water_loop = nil
|
5966
|
+
ambient_water_loop = if getPlantLoopByName('Ambient Loop').is_initialized
|
5967
|
+
getPlantLoopByName('Ambient Loop').get
|
5968
|
+
else
|
5969
|
+
add_district_ambient_loop
|
5970
|
+
end
|
5971
|
+
|
5972
|
+
return ambient_water_loop
|
5973
|
+
end
|
5974
|
+
|
5975
|
+
# Either get the existing ground heat exchanger loop in the model or
|
5976
|
+
# add a new one if there isn't one already.
|
5977
|
+
#
|
5978
|
+
def get_or_add_ground_hx_loop
|
5979
|
+
|
5980
|
+
# Retrieve the existing ground HX loop
|
5981
|
+
# or add a new one if necessary.
|
5982
|
+
ground_hx_loop = nil
|
5983
|
+
ground_hx_loop = if getPlantLoopByName('Ground HX Loop').is_initialized
|
5984
|
+
getPlantLoopByName('Ground HX Loop').get
|
5985
|
+
else
|
5986
|
+
add_ground_hx_loop
|
5987
|
+
end
|
5988
|
+
|
5989
|
+
return ground_hx_loop
|
5990
|
+
end
|
5991
|
+
|
5992
|
+
# Either get the existing heat pump loop in the model or
|
5993
|
+
# add a new one if there isn't one already.
|
5994
|
+
#
|
5995
|
+
def get_or_add_heat_pump_loop
|
5996
|
+
|
5997
|
+
# Retrieve the existing heat pump loop
|
5998
|
+
# or add a new one if necessary.
|
5999
|
+
heat_pump_loop = nil
|
6000
|
+
heat_pump_loop = if getPlantLoopByName('Heat Pump Loop').is_initialized
|
6001
|
+
getPlantLoopByName('Heat Pump Loop').get
|
6002
|
+
else
|
6003
|
+
add_hp_loop
|
6004
|
+
end
|
6005
|
+
|
6006
|
+
return heat_pump_loop
|
6007
|
+
end
|
6008
|
+
|
6009
|
+
# Add the specified system type to the
|
6010
|
+
# specified zones based on the specified template.
|
6011
|
+
# For multi-zone system types, add one system per story.
|
6012
|
+
#
|
6013
|
+
# @param template [String] Valid choices are 90.1-2004,
|
6014
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
6015
|
+
# @param system_type [String] The system type. Valid choices are
|
6016
|
+
# TODO enumerate the valid strings
|
6017
|
+
# @return [Bool] returns true if successful, false if not
|
6018
|
+
def add_hvac_system(template, system_type, main_heat_fuel, zone_heat_fuel, cool_fuel, zones)
|
6019
|
+
|
6020
|
+
# Don't do anything if there are no zones
|
6021
|
+
return true if zones.empty?
|
6022
|
+
|
6023
|
+
case system_type
|
6024
|
+
when 'PTAC'
|
6025
|
+
case main_heat_fuel
|
6026
|
+
when 'NaturalGas', 'DistrictHeating'
|
6027
|
+
heating_type = 'Water'
|
6028
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6029
|
+
when 'Electricity'
|
6030
|
+
heating_type = main_heat_fuel
|
6031
|
+
hot_water_loop = nil
|
6032
|
+
when nil
|
6033
|
+
heating_type = zone_heat_fuel
|
6034
|
+
hot_water_loop = nil
|
6035
|
+
end
|
6036
|
+
|
6037
|
+
add_ptac(template,
|
6038
|
+
sys_name=nil,
|
6039
|
+
hot_water_loop,
|
6040
|
+
zones,
|
6041
|
+
fan_type='ConstantVolume',
|
6042
|
+
heating_type,
|
6043
|
+
cooling_type='Single Speed DX AC')
|
6044
|
+
|
6045
|
+
when 'PTHP'
|
6046
|
+
add_pthp(template,
|
6047
|
+
sys_name=nil,
|
6048
|
+
zones,
|
6049
|
+
fan_type='ConstantVolume')
|
6050
|
+
|
6051
|
+
when 'PSZ-AC'
|
6052
|
+
case main_heat_fuel
|
6053
|
+
when 'NaturalGas'
|
6054
|
+
heating_type = main_heat_fuel
|
6055
|
+
supplemental_heating_type = 'Electricity'
|
6056
|
+
hot_water_loop = nil
|
6057
|
+
when 'DistrictHeating'
|
6058
|
+
heating_type = 'Water'
|
6059
|
+
supplemental_heating_type = 'Electricity'
|
6060
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6061
|
+
when nil
|
6062
|
+
heating_type = nil
|
6063
|
+
supplemental_heating_type = nil
|
6064
|
+
hot_water_loop = nil
|
6065
|
+
when 'Electricity'
|
6066
|
+
heating_type = main_heat_fuel
|
6067
|
+
supplemental_heating_type = 'Electricity'
|
6068
|
+
end
|
6069
|
+
|
6070
|
+
case cool_fuel
|
6071
|
+
when 'DistrictCooling'
|
6072
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel)
|
6073
|
+
cooling_type='Water'
|
6074
|
+
else
|
6075
|
+
chilled_water_loop = nil
|
6076
|
+
cooling_type='Single Speed DX AC'
|
6077
|
+
end
|
6078
|
+
|
6079
|
+
add_psz_ac(template,
|
6080
|
+
sys_name = nil,
|
6081
|
+
hot_water_loop,
|
6082
|
+
chilled_water_loop,
|
6083
|
+
zones,
|
6084
|
+
hvac_op_sch=nil,
|
6085
|
+
oa_damper_sch=nil,
|
6086
|
+
fan_location='DrawThrough',
|
6087
|
+
fan_type='ConstantVolume',
|
6088
|
+
heating_type,
|
6089
|
+
supplemental_heating_type,
|
6090
|
+
cooling_type)
|
6091
|
+
|
6092
|
+
when 'PSZ-HP'
|
6093
|
+
add_psz_ac(template,
|
6094
|
+
sys_name = 'PSZ-HP',
|
6095
|
+
hot_water_loop=nil,
|
6096
|
+
chilled_water_loop=nil,
|
6097
|
+
zones,
|
6098
|
+
hvac_op_sch=nil,
|
6099
|
+
oa_damper_sch=nil,
|
6100
|
+
fan_location='DrawThrough',
|
6101
|
+
fan_type='ConstantVolume',
|
6102
|
+
heating_type='Single Speed Heat Pump',
|
6103
|
+
supplemental_heating_type='Electricity',
|
6104
|
+
cooling_type='Single Speed Heat Pump')
|
6105
|
+
|
6106
|
+
when 'Fan Coil'
|
6107
|
+
case main_heat_fuel
|
6108
|
+
when 'NaturalGas', 'DistrictHeating', 'Electricity'
|
6109
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6110
|
+
when nil
|
6111
|
+
hot_water_loop = nil
|
6112
|
+
end
|
6113
|
+
|
6114
|
+
case cool_fuel
|
6115
|
+
when 'Electricity', 'DistrictCooling'
|
6116
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=true)
|
6117
|
+
when nil
|
6118
|
+
chilled_water_loop = nil
|
6119
|
+
end
|
6120
|
+
|
6121
|
+
add_four_pipe_fan_coil(template,
|
6122
|
+
hot_water_loop,
|
6123
|
+
chilled_water_loop,
|
6124
|
+
zones)
|
6125
|
+
|
6126
|
+
when 'Baseboards'
|
6127
|
+
case main_heat_fuel
|
6128
|
+
when 'NaturalGas', 'DistrictHeating'
|
6129
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6130
|
+
when 'Electricity'
|
6131
|
+
hot_water_loop = nil
|
6132
|
+
when nil
|
6133
|
+
# TODO Error, Baseboard systems must have a main_heat_fuel
|
6134
|
+
# return ??
|
6135
|
+
end
|
6136
|
+
|
6137
|
+
add_baseboard(template,
|
6138
|
+
hot_water_loop,
|
6139
|
+
zones)
|
6140
|
+
|
6141
|
+
when 'Unit Heaters'
|
6142
|
+
add_unitheater(template,
|
6143
|
+
sys_name=nil,
|
6144
|
+
zones,
|
6145
|
+
hvac_op_sch=nil,
|
6146
|
+
fan_control_type='ConstantVolume',
|
6147
|
+
fan_pressure_rise=OpenStudio.convert(0.2, 'inH_{2}O', 'Pa').get,
|
6148
|
+
main_heat_fuel,
|
6149
|
+
hot_water_loop=nil)
|
6150
|
+
|
6151
|
+
when 'Window AC'
|
6152
|
+
add_window_ac(template,
|
6153
|
+
zones)
|
6154
|
+
|
6155
|
+
when 'Residential AC'
|
6156
|
+
add_furnace_central_ac(template,
|
6157
|
+
zones,
|
6158
|
+
heating=false,
|
6159
|
+
cooling=true,
|
6160
|
+
ventilation=false)
|
6161
|
+
|
6162
|
+
when 'Forced Air Furnace'
|
6163
|
+
add_furnace_central_ac(template,
|
6164
|
+
zones,
|
6165
|
+
heating=true,
|
6166
|
+
cooling=false,
|
6167
|
+
ventilation=true)
|
6168
|
+
|
6169
|
+
when 'Residential Forced Air Furnace'
|
6170
|
+
add_furnace_central_ac(template,
|
6171
|
+
zones,
|
6172
|
+
heating=true,
|
6173
|
+
cooling=false,
|
6174
|
+
ventilation=false)
|
6175
|
+
|
6176
|
+
|
6177
|
+
when 'Residential Air Source Heat Pump'
|
6178
|
+
heating = true unless main_heat_fuel.nil?
|
6179
|
+
cooling = true unless cool_fuel.nil?
|
6180
|
+
|
6181
|
+
add_central_air_source_heat_pump(template,
|
6182
|
+
zones,
|
6183
|
+
heating,
|
6184
|
+
cooling,
|
6185
|
+
ventilation=false)
|
6186
|
+
|
6187
|
+
when 'VAV Reheat'
|
6188
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6189
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=false)
|
6190
|
+
|
6191
|
+
reheat_type = 'Water'
|
6192
|
+
if zone_heat_fuel == 'Electricity'
|
6193
|
+
reheat_type = 'Electricity'
|
6194
|
+
end
|
6195
|
+
|
6196
|
+
add_vav_reheat(template,
|
6197
|
+
sys_name=nil,
|
6198
|
+
hot_water_loop,
|
6199
|
+
chilled_water_loop,
|
6200
|
+
zones,
|
6201
|
+
hvac_op_sch=nil,
|
6202
|
+
oa_damper_sch=nil,
|
6203
|
+
vav_fan_efficiency=0.62,
|
6204
|
+
vav_fan_motor_efficiency=0.9,
|
6205
|
+
vav_fan_pressure_rise=OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
|
6206
|
+
return_plenum=nil,
|
6207
|
+
reheat_type)
|
6208
|
+
|
6209
|
+
when 'VAV No Reheat'
|
6210
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=false)
|
6211
|
+
|
6212
|
+
add_vav_reheat(template,
|
6213
|
+
sys_name=nil,
|
6214
|
+
hot_water_loop,
|
6215
|
+
chilled_water_loop,
|
6216
|
+
zones,
|
6217
|
+
hvac_op_sch=nil,
|
6218
|
+
oa_damper_sch=nil,
|
6219
|
+
vav_fan_efficiency=0.62,
|
6220
|
+
vav_fan_motor_efficiency=0.9,
|
6221
|
+
vav_fan_pressure_rise=OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
|
6222
|
+
return_plenum=nil,
|
6223
|
+
reheat_type=nil)
|
6224
|
+
|
6225
|
+
when 'VAV Gas Reheat'
|
6226
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=false)
|
6227
|
+
|
6228
|
+
add_vav_reheat(template,
|
6229
|
+
sys_name=nil,
|
6230
|
+
hot_water_loop,
|
6231
|
+
chilled_water_loop,
|
6232
|
+
zones,
|
6233
|
+
hvac_op_sch=nil,
|
6234
|
+
oa_damper_sch=nil,
|
6235
|
+
vav_fan_efficiency=0.62,
|
6236
|
+
vav_fan_motor_efficiency=0.9,
|
6237
|
+
vav_fan_pressure_rise=OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
|
6238
|
+
return_plenum=nil,
|
6239
|
+
reheat_type='NaturalGas')
|
6240
|
+
|
6241
|
+
when 'PVAV Reheat'
|
6242
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6243
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=false)
|
6244
|
+
|
6245
|
+
electric_reheat = false
|
6246
|
+
if zone_heat_fuel == 'Electricity'
|
6247
|
+
electric_reheat = true
|
6248
|
+
end
|
6249
|
+
|
6250
|
+
add_pvav(template,
|
6251
|
+
sys_name=nil,
|
6252
|
+
zones,
|
6253
|
+
hvac_op_sch=nil,
|
6254
|
+
oa_damper_sch=nil,
|
6255
|
+
electric_reheat,
|
6256
|
+
hot_water_loop,
|
6257
|
+
chilled_water_loop,
|
6258
|
+
return_plenum=nil)
|
6259
|
+
|
6260
|
+
when 'PVAV PFP Boxes'
|
6261
|
+
case cool_fuel
|
6262
|
+
when 'DistrictCooling'
|
6263
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel)
|
6264
|
+
else
|
6265
|
+
chilled_water_loop = nil
|
6266
|
+
end
|
6267
|
+
|
6268
|
+
add_pvav_pfp_boxes(template,
|
6269
|
+
sys_name=nil,
|
6270
|
+
zones,
|
6271
|
+
hvac_op_sch=nil,
|
6272
|
+
oa_damper_sch=nil,
|
6273
|
+
vav_fan_efficiency=0.62,
|
6274
|
+
vav_fan_motor_efficiency=0.9,
|
6275
|
+
vav_fan_pressure_rise=OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
|
6276
|
+
chilled_water_loop)
|
6277
|
+
when 'VAV PFP Boxes'
|
6278
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=false)
|
6279
|
+
|
6280
|
+
add_pvav_pfp_boxes(template,
|
6281
|
+
sys_name=nil,
|
6282
|
+
zones,
|
6283
|
+
hvac_op_sch=nil,
|
6284
|
+
oa_damper_sch=nil,
|
6285
|
+
vav_fan_efficiency=0.62,
|
6286
|
+
vav_fan_motor_efficiency=0.9,
|
6287
|
+
vav_fan_pressure_rise=OpenStudio.convert(4.0, 'inH_{2}O', 'Pa').get,
|
6288
|
+
chilled_water_loop)
|
6289
|
+
|
6290
|
+
when 'Water Source Heat Pumps'
|
6291
|
+
case 'main_heat_fuel'
|
6292
|
+
when 'NaturalGas'
|
6293
|
+
condenser_loop = get_or_add_ambient_water_loop
|
6294
|
+
else
|
6295
|
+
condenser_loop = get_or_add_heat_pump_loop
|
6296
|
+
end
|
6297
|
+
|
6298
|
+
add_water_source_hp(condenser_loop,
|
6299
|
+
zones,
|
6300
|
+
ventilation=false)
|
6301
|
+
|
6302
|
+
when 'Ground Source Heat Pumps'
|
6303
|
+
# TODO replace condenser loop w/ ground HX model
|
6304
|
+
# that does not involve district objects
|
6305
|
+
condenser_loop = get_or_add_ground_hx_loop
|
6306
|
+
add_water_source_hp(condenser_loop,
|
6307
|
+
zones,
|
6308
|
+
ventilation=false)
|
6309
|
+
|
6310
|
+
when 'DOAS'
|
6311
|
+
hot_water_loop = get_or_add_hot_water_loop(main_heat_fuel)
|
6312
|
+
chilled_water_loop = get_or_add_chilled_water_loop(template, cool_fuel, air_cooled=false)
|
6313
|
+
|
6314
|
+
add_doas(template,
|
6315
|
+
sys_name=nil,
|
6316
|
+
hot_water_loop,
|
6317
|
+
chilled_water_loop,
|
6318
|
+
zones,
|
6319
|
+
hvac_op_sch=nil,
|
6320
|
+
oa_damper_sch=nil,
|
6321
|
+
fan_max_flow_rate=nil,
|
6322
|
+
economizer_control_type='FixedDryBulb',
|
6323
|
+
building_type = nil)
|
6324
|
+
|
6325
|
+
when 'ERVs'
|
6326
|
+
add_zone_erv(template, zones)
|
6327
|
+
|
6328
|
+
when 'Evaporative Cooler'
|
6329
|
+
add_evap_cooler(template,
|
6330
|
+
zones)
|
6331
|
+
|
6332
|
+
when 'Ideal Air Loads'
|
6333
|
+
add_ideal_air_loads(template,
|
6334
|
+
zones)
|
6335
|
+
|
6336
|
+
### Combination Systems ###
|
6337
|
+
when 'Water Source Heat Pumps with ERVs'
|
6338
|
+
add_hvac_system(template,
|
6339
|
+
system_type='Water Source Heat Pumps',
|
6340
|
+
main_heat_fuel,
|
6341
|
+
zone_heat_fuel,
|
6342
|
+
cool_fuel,
|
6343
|
+
zones)
|
6344
|
+
|
6345
|
+
add_hvac_system(template,
|
6346
|
+
system_type='ERVs',
|
6347
|
+
main_heat_fuel,
|
6348
|
+
zone_heat_fuel,
|
6349
|
+
cool_fuel,
|
6350
|
+
zones)
|
6351
|
+
|
6352
|
+
when 'Water Source Heat Pumps with DOAS'
|
6353
|
+
add_hvac_system(template,
|
6354
|
+
system_type='Water Source Heat Pumps',
|
6355
|
+
main_heat_fuel,
|
6356
|
+
zone_heat_fuel,
|
6357
|
+
cool_fuel,
|
6358
|
+
zones)
|
6359
|
+
|
6360
|
+
add_hvac_system(template,
|
6361
|
+
system_type='DOAS',
|
6362
|
+
main_heat_fuel,
|
6363
|
+
zone_heat_fuel,
|
6364
|
+
cool_fuel,
|
6365
|
+
zones)
|
6366
|
+
|
6367
|
+
when 'Ground Source Heat Pumps with ERVs'
|
6368
|
+
add_hvac_system(template,
|
6369
|
+
system_type='Ground Source Heat Pumps',
|
6370
|
+
main_heat_fuel,
|
6371
|
+
zone_heat_fuel,
|
6372
|
+
cool_fuel,
|
6373
|
+
zones)
|
6374
|
+
|
6375
|
+
add_hvac_system(template,
|
6376
|
+
system_type='ERVs',
|
6377
|
+
main_heat_fuel,
|
6378
|
+
zone_heat_fuel,
|
6379
|
+
cool_fuel,
|
6380
|
+
zones)
|
6381
|
+
|
6382
|
+
when 'Ground Source Heat Pumps with DOAS'
|
6383
|
+
add_hvac_system(template,
|
6384
|
+
system_type='Ground Source Heat Pumps',
|
6385
|
+
main_heat_fuel,
|
6386
|
+
zone_heat_fuel,
|
6387
|
+
cool_fuel,
|
6388
|
+
zones)
|
6389
|
+
|
6390
|
+
add_hvac_system(template,
|
6391
|
+
system_type='DOAS',
|
6392
|
+
main_heat_fuel,
|
6393
|
+
zone_heat_fuel,
|
6394
|
+
cool_fuel,
|
6395
|
+
zones)
|
6396
|
+
|
6397
|
+
when 'Fan Coil with DOAS'
|
6398
|
+
add_hvac_system(template,
|
6399
|
+
system_type='Fan Coil',
|
6400
|
+
main_heat_fuel,
|
6401
|
+
zone_heat_fuel,
|
6402
|
+
cool_fuel,
|
6403
|
+
zones)
|
6404
|
+
|
6405
|
+
add_hvac_system(template,
|
6406
|
+
system_type='DOAS',
|
6407
|
+
main_heat_fuel,
|
6408
|
+
zone_heat_fuel,
|
6409
|
+
cool_fuel,
|
6410
|
+
zones)
|
6411
|
+
|
6412
|
+
when 'Fan Coil with ERVs'
|
6413
|
+
add_hvac_system(template,
|
6414
|
+
system_type='Fan Coil',
|
6415
|
+
main_heat_fuel,
|
6416
|
+
zone_heat_fuel,
|
6417
|
+
cool_fuel,
|
6418
|
+
zones)
|
6419
|
+
|
6420
|
+
add_hvac_system(template,
|
6421
|
+
system_type='ERVs',
|
6422
|
+
main_heat_fuel,
|
6423
|
+
zone_heat_fuel,
|
6424
|
+
cool_fuel,
|
6425
|
+
zones)
|
6426
|
+
|
6427
|
+
else
|
6428
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "HVAC system type '#{system_type}' not recognized")
|
6429
|
+
return false
|
6430
|
+
end
|
6431
|
+
|
6432
|
+
end
|
6433
|
+
|
6434
|
+
# Determine the typical system type given the inputs.
|
6435
|
+
#
|
6436
|
+
# @param template [String] Valid choices are 90.1-2004,
|
6437
|
+
# 90.1-2007, 90.1-2010, 90.1-2013
|
6438
|
+
# @param area_type [String] Valid choices are residential
|
6439
|
+
# and nonresidential
|
6440
|
+
# @param delivery_type [String] Conditioning delivery type.
|
6441
|
+
# Valid choices are air and hydronic
|
6442
|
+
# @param heating_source [String] Valid choices are
|
6443
|
+
# Electricity, NaturalGas, DistrictHeating, DistrictAmbient
|
6444
|
+
# @param cooling_source [String] Valid choices are
|
6445
|
+
# Electricity, DistrictCooling, DistrictAmbient
|
6446
|
+
# @param area_m2 [Double] Area in m^2
|
6447
|
+
# @param num_stories [Integer] Number of stories
|
6448
|
+
# @return [String] The system type. Possibilities are
|
6449
|
+
# PTHP, PTAC, PSZ_AC, PSZ_HP, PVAV_Reheat, PVAV_PFP_Boxes,
|
6450
|
+
# VAV_Reheat, VAV_PFP_Boxes, Gas_Furnace, Electric_Furnace
|
6451
|
+
def typical_hvac_system_type(template,
|
6452
|
+
climate_zone,
|
6453
|
+
area_type,
|
6454
|
+
delivery_type,
|
6455
|
+
heating_source,
|
6456
|
+
cooling_source,
|
6457
|
+
area_m2,
|
6458
|
+
num_stories)
|
6459
|
+
# [type, central_heating_fuel, zone_heating_fuel, cooling_fuel]
|
6460
|
+
system_type = [nil, nil, nil, nil]
|
6461
|
+
|
6462
|
+
# Convert area to ft^2
|
6463
|
+
area_ft2 = OpenStudio.convert(area_m2, 'm^2', 'ft^2').get
|
6464
|
+
|
6465
|
+
# categorize building by type & size
|
6466
|
+
size_category = nil
|
6467
|
+
case area_type
|
6468
|
+
when 'residential'
|
6469
|
+
# residential and less than 4 stories
|
6470
|
+
if num_stories <= 3
|
6471
|
+
size_category = 'res_small'
|
6472
|
+
# residential and more than 4 stories
|
6473
|
+
else
|
6474
|
+
size_category = 'res_med'
|
6475
|
+
end
|
6476
|
+
when 'nonresidential'
|
6477
|
+
# nonresidential and 3 floors or less and < 75,000 ft2
|
6478
|
+
if num_stories <= 3 && area_ft2 < 75_000
|
6479
|
+
size_category = 'nonres_small'
|
6480
|
+
# nonresidential and 4 or 5 floors OR 5 floors or less and 75,000 ft2 to 150,000 ft2
|
6481
|
+
elsif ((num_stories == 4 || num_stories == 5) && area_ft2 < 75_000) || (num_stories <= 5 && (area_ft2 >= 75_000 && area_ft2 <= 150_000))
|
6482
|
+
size_category = 'nonres_med'
|
6483
|
+
# nonresidential and more than 5 floors or >150,000 ft2
|
6484
|
+
elsif num_stories >= 5 || area_ft2 > 150_000
|
6485
|
+
size_category = 'nonres_lg'
|
6486
|
+
end
|
6487
|
+
end
|
6488
|
+
|
6489
|
+
# Define the lookup by row and by fuel type
|
6490
|
+
syts = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
|
6491
|
+
# [heating_source][cooling_source][delivery_type][size_category]
|
6492
|
+
# = [type, central_heating_fuel, zone_heating_fuel, cooling_fuel]
|
6493
|
+
|
6494
|
+
## Forced Air ##
|
6495
|
+
|
6496
|
+
# Gas, Electric, forced air
|
6497
|
+
syts['NaturalGas']['Electricity']['air']['res_small'] = ['PTAC', 'NaturalGas', nil, 'Electricity']
|
6498
|
+
syts['NaturalGas']['Electricity']['air']['res_med'] = ['PTAC', 'NaturalGas', nil, 'Electricity']
|
6499
|
+
syts['NaturalGas']['Electricity']['air']['nonres_small'] = ['PSZ-AC', 'NaturalGas', nil, 'Electricity']
|
6500
|
+
syts['NaturalGas']['Electricity']['air']['nonres_med'] = ['PVAV Reheat', 'NaturalGas', 'NaturalGas', 'Electricity']
|
6501
|
+
syts['NaturalGas']['Electricity']['air']['nonres_lg'] = ['VAV Reheat', 'NaturalGas', 'NaturalGas', 'Electricity']
|
6502
|
+
|
6503
|
+
# Electric, Electric, forced air
|
6504
|
+
syts['Electricity']['Electricity']['air']['res_small'] = ['PTHP', 'Electricity', nil, 'Electricity']
|
6505
|
+
syts['Electricity']['Electricity']['air']['res_med'] = ['PTHP', 'Electricity', nil, 'Electricity']
|
6506
|
+
syts['Electricity']['Electricity']['air']['nonres_small'] = ['PSZ-HP', 'Electricity', nil, 'Electricity']
|
6507
|
+
syts['Electricity']['Electricity']['air']['nonres_med'] = ['PVAV PFP Boxes', 'Electricity', 'Electricity', 'Electricity']
|
6508
|
+
syts['Electricity']['Electricity']['air']['nonres_lg'] = ['VAV PFP Boxes', 'Electricity', 'Electricity', 'Electricity']
|
6509
|
+
|
6510
|
+
# District Hot Water, Electric, forced air
|
6511
|
+
syts['DistrictHeating']['Electricity']['air']['res_small'] = ['PTAC', 'DistrictHeating', nil, 'Electricity']
|
6512
|
+
syts['DistrictHeating']['Electricity']['air']['res_med'] = ['PTAC', 'DistrictHeating', nil, 'Electricity']
|
6513
|
+
syts['DistrictHeating']['Electricity']['air']['nonres_small'] = ['PVAV Reheat', 'DistrictHeating', 'DistrictHeating', 'Electricity']
|
6514
|
+
syts['DistrictHeating']['Electricity']['air']['nonres_med'] = ['PVAV Reheat', 'DistrictHeating', 'DistrictHeating', 'Electricity']
|
6515
|
+
syts['DistrictHeating']['Electricity']['air']['nonres_lg'] = ['VAV Reheat', 'DistrictHeating', 'DistrictHeating', 'Electricity']
|
6516
|
+
|
6517
|
+
# Ambient Loop, Ambient Loop, forced air
|
6518
|
+
syts['DistrictAmbient']['DistrictAmbient']['air']['res_small'] = ['Water Source Heat Pumps with ERVs', 'HeatPump', nil, 'HeatPump']
|
6519
|
+
syts['DistrictAmbient']['DistrictAmbient']['air']['res_med'] = ['Water Source Heat Pumps with DOAS', 'HeatPump', nil, 'HeatPump']
|
6520
|
+
syts['DistrictAmbient']['DistrictAmbient']['air']['nonres_small'] = ['PVAV Reheat', 'HeatPump', 'HeatPump', 'HeatPump']
|
6521
|
+
syts['DistrictAmbient']['DistrictAmbient']['air']['nonres_med'] = ['PVAV Reheat', 'HeatPump', 'HeatPump', 'HeatPump']
|
6522
|
+
syts['DistrictAmbient']['DistrictAmbient']['air']['nonres_lg'] = ['VAV Reheat', 'HeatPump', 'HeatPump', 'HeatPump']
|
6523
|
+
|
6524
|
+
# Gas, District Chilled Water, forced air
|
6525
|
+
syts['NaturalGas']['DistrictCooling']['air']['res_small'] = ['PSZ-AC', 'NaturalGas', nil, 'DistrictCooling']
|
6526
|
+
syts['NaturalGas']['DistrictCooling']['air']['res_med'] = ['PSZ-AC', 'NaturalGas', nil, 'DistrictCooling']
|
6527
|
+
syts['NaturalGas']['DistrictCooling']['air']['nonres_small'] = ['PSZ-AC', 'NaturalGas', nil, 'DistrictCooling']
|
6528
|
+
syts['NaturalGas']['DistrictCooling']['air']['nonres_med'] = ['PVAV Reheat', 'NaturalGas', 'NaturalGas', 'DistrictCooling']
|
6529
|
+
syts['NaturalGas']['DistrictCooling']['air']['nonres_lg'] = ['VAV Reheat', 'NaturalGas', 'NaturalGas', 'DistrictCooling']
|
6530
|
+
|
6531
|
+
# Electric, District Chilled Water, forced air
|
6532
|
+
syts['Electricity']['DistrictCooling']['air']['res_small'] = ['PSZ-AC', 'Electricity', nil, 'DistrictCooling']
|
6533
|
+
syts['Electricity']['DistrictCooling']['air']['res_med'] = ['PSZ-AC', 'Electricity', nil, 'DistrictCooling']
|
6534
|
+
syts['Electricity']['DistrictCooling']['air']['nonres_small'] = ['PSZ-AC', 'Electricity', nil, 'DistrictCooling']
|
6535
|
+
syts['Electricity']['DistrictCooling']['air']['nonres_med'] = ['PVAV Reheat', 'Electricity', 'Electricity', 'DistrictCooling']
|
6536
|
+
syts['Electricity']['DistrictCooling']['air']['nonres_lg'] = ['VAV Reheat', 'Electricity', 'Electricity', 'DistrictCooling']
|
6537
|
+
|
6538
|
+
# District Hot Water, District Chilled Water, forced air
|
6539
|
+
syts['DistrictHeating']['DistrictCooling']['air']['res_small'] = ['PSZ-AC', 'DistrictHeating', nil, 'DistrictCooling']
|
6540
|
+
syts['DistrictHeating']['DistrictCooling']['air']['res_med'] = ['PSZ-AC', 'DistrictHeating', nil, 'DistrictCooling']
|
6541
|
+
syts['DistrictHeating']['DistrictCooling']['air']['nonres_small'] = ['PVAV Reheat', 'DistrictHeating', 'DistrictHeating', 'DistrictCooling']
|
6542
|
+
syts['DistrictHeating']['DistrictCooling']['air']['nonres_med'] = ['PVAV Reheat', 'DistrictHeating', 'DistrictHeating', 'DistrictCooling']
|
6543
|
+
syts['DistrictHeating']['DistrictCooling']['air']['nonres_lg'] = ['VAV Reheat', 'DistrictHeating', 'DistrictHeating', 'DistrictCooling']
|
6544
|
+
|
6545
|
+
## Hydronic ##
|
6546
|
+
|
6547
|
+
# Gas, Electric, hydronic
|
6548
|
+
syts['NaturalGas']['Electricity']['hydronic']['res_med'] = ['Fan Coil with DOAS', 'NaturalGas', nil, 'Electricity']
|
6549
|
+
syts['NaturalGas']['Electricity']['hydronic']['nonres_small'] = ['Water Source Heat Pumps with DOAS', 'NaturalGas', nil, 'Electricity']
|
6550
|
+
syts['NaturalGas']['Electricity']['hydronic']['nonres_med'] = ['Fan Coil with DOAS', 'NaturalGas', 'NaturalGas', 'Electricity']
|
6551
|
+
syts['NaturalGas']['Electricity']['hydronic']['nonres_lg'] = ['Fan Coil with DOAS', 'NaturalGas', 'NaturalGas', 'Electricity']
|
6552
|
+
|
6553
|
+
# Electric, Electric, hydronic
|
6554
|
+
syts['Electricity']['Electricity']['hydronic']['res_small'] = ['Ground Source Heat Pumps with ERVs', 'Electricity', nil, 'Electricity']
|
6555
|
+
syts['Electricity']['Electricity']['hydronic']['res_med'] = ['Ground Source Heat Pumps with DOAS', 'Electricity', nil, 'Electricity']
|
6556
|
+
syts['Electricity']['Electricity']['hydronic']['nonres_small'] = ['Ground Source Heat Pumps with DOAS', 'Electricity', nil, 'Electricity']
|
6557
|
+
syts['Electricity']['Electricity']['hydronic']['nonres_med'] = ['Ground Source Heat Pumps with DOAS', 'Electricity', 'Electricity', 'Electricity']
|
6558
|
+
syts['Electricity']['Electricity']['hydronic']['nonres_lg'] = ['Ground Source Heat Pumps with DOAS', 'Electricity', 'Electricity', 'Electricity']
|
6559
|
+
|
6560
|
+
# District Hot Water, Electric, hydronic
|
6561
|
+
syts['DistrictHeating']['Electricity']['hydronic']['res_small'] = [] # TODO decide if there is anything reasonable for this
|
6562
|
+
syts['DistrictHeating']['Electricity']['hydronic']['res_med'] = ['Fan Coil with DOAS', 'DistrictHeating', nil, 'Electricity']
|
6563
|
+
syts['DistrictHeating']['Electricity']['hydronic']['nonres_small'] = ['Water Source Heat Pumps with DOAS', 'DistrictHeating', 'DistrictHeating', 'Electricity']
|
6564
|
+
syts['DistrictHeating']['Electricity']['hydronic']['nonres_med'] = ['Fan Coil with DOAS', 'DistrictHeating', 'DistrictHeating', 'Electricity']
|
6565
|
+
syts['DistrictHeating']['Electricity']['hydronic']['nonres_lg'] = ['Fan Coil with DOAS', 'DistrictHeating', 'DistrictHeating', 'Electricity']
|
6566
|
+
|
6567
|
+
# Ambient Loop, Ambient Loop, hydronic
|
6568
|
+
syts['DistrictAmbient']['DistrictAmbient']['hydronic']['res_small'] = ['Water Source Heat Pumps with ERVs', 'HeatPump', nil, 'HeatPump']
|
6569
|
+
syts['DistrictAmbient']['DistrictAmbient']['hydronic']['res_med'] = ['Water Source Heat Pumps with DOAS', 'HeatPump', nil, 'HeatPump']
|
6570
|
+
syts['DistrictAmbient']['DistrictAmbient']['hydronic']['nonres_small'] = ['Water Source Heat Pumps with DOAS', 'HeatPump', 'HeatPump', 'HeatPump']
|
6571
|
+
syts['DistrictAmbient']['DistrictAmbient']['hydronic']['nonres_med'] = ['Water Source Heat Pumps with DOAS', 'HeatPump', 'HeatPump', 'HeatPump']
|
6572
|
+
syts['DistrictAmbient']['DistrictAmbient']['hydronic']['nonres_lg'] = ['Fan Coil with DOAS', 'DistrictHeating', nil, 'Electricity'] # TODO is this reasonable?
|
6573
|
+
|
6574
|
+
# Gas, District Chilled Water, hydronic
|
6575
|
+
syts['NaturalGas']['DistrictCooling']['hydronic']['res_med'] = ['Fan Coil with DOAS', 'NaturalGas', nil, 'DistrictCooling']
|
6576
|
+
syts['NaturalGas']['DistrictCooling']['hydronic']['nonres_small'] = ['Fan Coil with DOAS', 'NaturalGas', nil, 'DistrictCooling']
|
6577
|
+
syts['NaturalGas']['DistrictCooling']['hydronic']['nonres_med'] = ['Fan Coil with DOAS', 'NaturalGas', 'NaturalGas', 'DistrictCooling']
|
6578
|
+
syts['NaturalGas']['DistrictCooling']['hydronic']['nonres_lg'] = ['Fan Coil with DOAS', 'NaturalGas', 'NaturalGas', 'DistrictCooling']
|
6579
|
+
|
6580
|
+
# Electric, District Chilled Water, hydronic
|
6581
|
+
syts['Electricity']['DistrictCooling']['hydronic']['res_med'] = ['Fan Coil with ERVs', 'Electricity', nil, 'DistrictCooling']
|
6582
|
+
syts['Electricity']['DistrictCooling']['hydronic']['nonres_small'] = ['Fan Coil with DOAS', 'Electricity', nil, 'DistrictCooling']
|
6583
|
+
syts['Electricity']['DistrictCooling']['hydronic']['nonres_med'] = ['Fan Coil with DOAS', 'Electricity', 'Electricity', 'DistrictCooling']
|
6584
|
+
syts['Electricity']['DistrictCooling']['hydronic']['nonres_lg'] = ['Fan Coil with DOAS', 'Electricity', 'Electricity', 'DistrictCooling']
|
6585
|
+
|
6586
|
+
# District Hot Water, District Chilled Water, hydronic
|
6587
|
+
syts['DistrictHeating']['DistrictCooling']['hydronic']['res_small'] = ['Fan Coil with ERVs', 'DistrictHeating', nil, 'DistrictCooling']
|
6588
|
+
syts['DistrictHeating']['DistrictCooling']['hydronic']['res_med'] = ['Fan Coil with DOAS', 'DistrictHeating', nil, 'DistrictCooling']
|
6589
|
+
syts['DistrictHeating']['DistrictCooling']['hydronic']['nonres_small'] = ['Fan Coil with DOAS', 'DistrictHeating', 'DistrictHeating', 'DistrictCooling']
|
6590
|
+
syts['DistrictHeating']['DistrictCooling']['hydronic']['nonres_med'] = ['Fan Coil with DOAS', 'DistrictHeating', 'DistrictHeating', 'DistrictCooling']
|
6591
|
+
syts['DistrictHeating']['DistrictCooling']['hydronic']['nonres_lg'] = ['Fan Coil with DOAS', 'DistrictHeating', 'DistrictHeating', 'DistrictCooling']
|
6592
|
+
|
6593
|
+
# Get the system type
|
6594
|
+
system_type = syts[heating_source][cooling_source][delivery_type][size_category]
|
6595
|
+
|
6596
|
+
if system_type.nil?
|
6597
|
+
system_type = [nil, nil, nil, nil]
|
6598
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', "Could not determine system type for #{template}, #{area_type}, #{heating_source} heating, #{cooling_source} cooling, #{delivery_type} delivery, #{area_ft2.round} ft^2, #{num_stories} stories.")
|
6599
|
+
else
|
6600
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "System type is #{system_type[0]} for #{template}, #{area_type}, #{heating_source} heating, #{cooling_source} cooling, #{delivery_type} delivery, #{area_ft2.round} ft^2, #{num_stories} stories.")
|
6601
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "--- #{system_type[1]} for main heating") unless system_type[1].nil?
|
6602
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "--- #{system_type[2]} for zone heat/reheat") unless system_type[2].nil?
|
6603
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "--- #{system_type[3]} for cooling") unless system_type[3].nil?
|
6604
|
+
end
|
6605
|
+
|
6606
|
+
return system_type
|
6607
|
+
end
|
4689
6608
|
end
|