openstudio-standards 0.6.3 → 0.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/OpenStudio_Standards-ashrae_90_1.xlsx +0 -0
  3. data/data/standards/manage_OpenStudio_Standards.rb +2 -49
  4. data/data/standards/openstudio_standards_duplicates_log.csv +1 -7962
  5. data/data/standards/test_performance_expected_dd_results.csv +2005 -97
  6. data/lib/openstudio-standards/create_typical/space_type_ratios.rb +47 -57
  7. data/lib/openstudio-standards/geometry/create.rb +1 -1
  8. data/lib/openstudio-standards/geometry/create_bar.rb +6 -3
  9. data/lib/openstudio-standards/geometry/create_shape.rb +1 -1
  10. data/lib/openstudio-standards/geometry/group.rb +1 -1
  11. data/lib/openstudio-standards/geometry/information.rb +1 -1
  12. data/lib/openstudio-standards/geometry/modify.rb +53 -1
  13. data/lib/openstudio-standards/infiltration/nist_infiltration.rb +1 -1
  14. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +11 -11
  15. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +11 -11
  16. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb +2 -2
  17. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperTallBuilding.rb +44 -47
  18. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.TallBuilding.rb +43 -48
  19. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CentralAirSourceHeatPump.rb +1 -1
  20. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +44 -24
  21. data/lib/openstudio-standards/prototypes/common/objects/Prototype.refrigeration.rb +24 -24
  22. data/lib/openstudio-standards/schedules/parametric.rb +1 -1
  23. data/lib/openstudio-standards/service_water_heating/create_piping_losses.rb +152 -0
  24. data/lib/openstudio-standards/service_water_heating/create_water_heater.rb +544 -0
  25. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +303 -0
  26. data/lib/openstudio-standards/service_water_heating/create_water_use.rb +95 -0
  27. data/lib/openstudio-standards/space/space.rb +1 -1
  28. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +65 -70
  29. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +12 -14
  30. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +16 -5
  31. data/lib/openstudio-standards/standards/Standards.Model.rb +2 -2
  32. data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +10 -2
  33. data/lib/openstudio-standards/{prototypes/common/objects/Prototype.Model.swh.rb → standards/Standards.ServiceWaterHeating.rb} +209 -139
  34. data/lib/openstudio-standards/standards/Standards.Surface.rb +1 -1
  35. data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +4 -8
  36. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Model.rb +2 -2
  37. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +22251 -12963
  38. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_sets.json +91 -91
  39. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8981 -5228
  40. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8935 -5182
  41. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7281 -5391
  42. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_sets.json +91 -91
  43. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +9005 -15215
  44. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_sets.json +136 -136
  45. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +1 -1
  46. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +8717 -17168
  47. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_sets.json +136 -136
  48. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.constructions.json +1941 -651
  49. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.construction_properties.json +135 -135
  50. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.construction_properties.json +135 -135
  51. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/data/nrel_zne_ready_2017.construction_properties.json +36 -36
  52. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/data/ze_aedg_multifamily.construction_properties.json +36 -36
  53. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +377 -99
  54. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +2 -2
  55. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +3 -3
  56. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6889 -4044
  57. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_constructions.json +108 -108
  58. data/lib/openstudio-standards/standards/cbes/cbes_pre_1978/data/cbes_pre_1978.construction_properties.json +16 -16
  59. data/lib/openstudio-standards/standards/cbes/cbes_t24_1978/data/cbes_t24_1978.construction_properties.json +16 -16
  60. data/lib/openstudio-standards/standards/cbes/cbes_t24_1992/data/cbes_t24_1992.construction_properties.json +16 -16
  61. data/lib/openstudio-standards/standards/cbes/cbes_t24_2001/data/cbes_t24_2001.construction_properties.json +16 -16
  62. data/lib/openstudio-standards/standards/cbes/cbes_t24_2005/data/cbes_t24_2005.construction_properties.json +16 -16
  63. data/lib/openstudio-standards/standards/cbes/cbes_t24_2008/data/cbes_t24_2008.construction_properties.json +16 -16
  64. data/lib/openstudio-standards/standards/cbes/data/cbes.constructions.json +142 -142
  65. data/lib/openstudio-standards/standards/deer/data/deer.constructions.json +5 -1551
  66. data/lib/openstudio-standards/standards/deer/data/deer.materials.json +40 -0
  67. data/lib/openstudio-standards/standards/deer/deer_1985/data/deer_1985.motors.json +88 -8
  68. data/lib/openstudio-standards/standards/deer/deer_1996/data/deer_1996.motors.json +88 -8
  69. data/lib/openstudio-standards/standards/deer/deer_2003/data/deer_2003.motors.json +88 -8
  70. data/lib/openstudio-standards/standards/deer/deer_2007/data/deer_2007.motors.json +88 -8
  71. data/lib/openstudio-standards/standards/deer/deer_2011/data/deer_2011.motors.json +88 -8
  72. data/lib/openstudio-standards/standards/deer/deer_2014/data/deer_2014.motors.json +88 -8
  73. data/lib/openstudio-standards/standards/deer/deer_2015/data/deer_2015.motors.json +88 -8
  74. data/lib/openstudio-standards/standards/deer/deer_2017/data/deer_2017.motors.json +88 -8
  75. data/lib/openstudio-standards/standards/deer/deer_2020/data/deer_2020.motors.json +88 -8
  76. data/lib/openstudio-standards/standards/deer/deer_2025/data/deer_2025.motors.json +88 -8
  77. data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.motors.json +88 -8
  78. data/lib/openstudio-standards/standards/deer/deer_2035/data/deer_2035.motors.json +88 -8
  79. data/lib/openstudio-standards/standards/deer/deer_2040/data/deer_2040.motors.json +88 -8
  80. data/lib/openstudio-standards/standards/deer/deer_2045/data/deer_2045.motors.json +88 -8
  81. data/lib/openstudio-standards/standards/deer/deer_2050/data/deer_2050.motors.json +88 -8
  82. data/lib/openstudio-standards/standards/deer/deer_2055/data/deer_2055.motors.json +88 -8
  83. data/lib/openstudio-standards/standards/deer/deer_2060/data/deer_2060.motors.json +88 -8
  84. data/lib/openstudio-standards/standards/deer/deer_2065/data/deer_2065.motors.json +88 -8
  85. data/lib/openstudio-standards/standards/deer/deer_2070/data/deer_2070.motors.json +88 -8
  86. data/lib/openstudio-standards/standards/deer/deer_2075/data/deer_2075.motors.json +88 -8
  87. data/lib/openstudio-standards/standards/deer/deer_pre_1975/data/deer_pre_1975.motors.json +88 -8
  88. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +17 -0
  89. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_systems.rb +2 -1
  90. data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +4 -4
  91. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +61 -88
  92. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +3 -2
  93. data/lib/openstudio-standards/standards/necb/NECB2011/data/boiler_fuel_type_sets.json +54 -0
  94. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMidriseApartment.osm +1 -1
  95. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +1 -1
  96. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPPointTower.osm +1 -1
  97. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +1 -1
  98. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +4 -4
  99. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +4 -4
  100. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +32 -24
  101. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +89 -15
  102. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +5 -1
  103. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +22 -65
  104. data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +19 -0
  105. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +56 -2
  106. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +3 -1
  107. data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +2 -2
  108. data/lib/openstudio-standards/standards/necb/docs/air_system_names_method.md +127 -0
  109. data/lib/openstudio-standards/thermal_zone/thermal_zone.rb +1 -1
  110. data/lib/openstudio-standards/utilities/template_measure/resources/BTAPMeasureHelper.rb +1 -1
  111. data/lib/openstudio-standards/version.rb +1 -1
  112. data/lib/openstudio-standards/weather/information.rb +61 -5
  113. data/lib/openstudio-standards/weather/modify.rb +1 -1
  114. data/lib/openstudio-standards.rb +5 -3
  115. metadata +12 -63
  116. data/data/standards/OpenStudio_Standards-deer.xlsx +0 -0
  117. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +0 -1100
  118. data/lib/openstudio-standards/service_water_heating/component.rb +0 -189
@@ -1,1100 +0,0 @@
1
- class Standard
2
- # @!group ServiceWaterHeating
3
-
4
- # Creates a service water heating loop.
5
- #
6
- # @param model [OpenStudio::Model::Model] OpenStudio model object
7
- # @param system_name [String] the name of the system, or nil in which case it will be defaulted
8
- # @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone]
9
- # zones to place water heater in. If nil, will be assumed in 70F air for heat loss.
10
- # @param service_water_temperature [Double] service water temperature, in C
11
- # @param service_water_pump_head [Double] service water pump head, in Pa
12
- # @param service_water_pump_motor_efficiency [Double] service water pump motor efficiency, as decimal.
13
- # @param water_heater_capacity [Double] water heater heating capacity, in W
14
- # @param water_heater_volume [Double] water heater volume, in m^3
15
- # @param water_heater_fuel [String] water heater fuel. Valid choices are NaturalGas, Electricity
16
- # @param parasitic_fuel_consumption_rate [Double] the parasitic fuel consumption rate of the water heater, in W
17
- # @param add_pipe_losses [Boolean] if true, add piping and associated heat losses to system. If false, add no pipe heat losses
18
- # @param floor_area_served [Double] area served by the SWH loop, in m^2. Used for pipe loss piping length estimation
19
- # @param number_of_stories [Integer] number of stories served by the SWH loop. Used for pipe loss piping length estimation
20
- # @param pipe_insulation_thickness [Double] thickness of the fiberglass batt pipe insulation, in m. Use 0 for uninsulated pipes
21
- # @param number_water_heaters [Double] the number of water heaters represented by the capacity and volume inputs.
22
- # Used to modify efficiencies for water heaters based on individual component size while avoiding having to model
23
- # lots of individual water heaters (for runtime sake).
24
- # @return [OpenStudio::Model::PlantLoop]
25
- # the resulting service water loop.
26
- def model_add_swh_loop(model,
27
- system_name,
28
- water_heater_thermal_zone,
29
- service_water_temperature,
30
- service_water_pump_head,
31
- service_water_pump_motor_efficiency,
32
- water_heater_capacity,
33
- water_heater_volume,
34
- water_heater_fuel,
35
- parasitic_fuel_consumption_rate,
36
- add_pipe_losses = false,
37
- floor_area_served = 465,
38
- number_of_stories = 1,
39
- pipe_insulation_thickness = 0.0127, # 1/2in
40
- number_water_heaters = 1)
41
- # Service water heating loop
42
- service_water_loop = OpenStudio::Model::PlantLoop.new(model)
43
- service_water_loop.setMinimumLoopTemperature(10.0)
44
- service_water_loop.setMaximumLoopTemperature(60.0)
45
-
46
- if system_name.nil?
47
- service_water_loop.setName('Service Water Loop')
48
- else
49
- service_water_loop.setName(system_name)
50
- end
51
-
52
- # Temperature schedule type limits
53
- temp_sch_type_limits = OpenstudioStandards::Schedules.create_schedule_type_limits(model,
54
- name: 'Temperature Schedule Type Limits',
55
- lower_limit_value: 0.0,
56
- upper_limit_value: 100.0,
57
- numeric_type: 'Continuous',
58
- unit_type: 'Temperature')
59
-
60
- # Service water heating loop controls
61
- swh_temp_c = service_water_temperature
62
- swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
63
- swh_delta_t_r = 9.0 # 9F delta-T
64
- swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get
65
- swh_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
66
- swh_temp_c,
67
- name: "Service Water Loop Temp - #{swh_temp_f.round}F",
68
- schedule_type_limit: 'Temperature')
69
- swh_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
70
- swh_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, swh_temp_sch)
71
- swh_stpt_manager.setName('Service hot water setpoint manager')
72
- swh_stpt_manager.addToNode(service_water_loop.supplyOutletNode)
73
- sizing_plant = service_water_loop.sizingPlant
74
- sizing_plant.setLoopType('Heating')
75
- sizing_plant.setDesignLoopExitTemperature(swh_temp_c)
76
- sizing_plant.setLoopDesignTemperatureDifference(swh_delta_t_k)
77
-
78
- # Determine if circulating or non-circulating based on supplied head pressure
79
- swh_pump_head_press_pa = service_water_pump_head
80
- circulating = true
81
- if swh_pump_head_press_pa.nil? || swh_pump_head_press_pa <= 1
82
- # As if there is no circulation pump
83
- swh_pump_head_press_pa = 0.001
84
- service_water_pump_motor_efficiency = 1
85
- circulating = false
86
- end
87
-
88
- # Service water heating pump
89
- if circulating
90
- swh_pump = OpenStudio::Model::PumpConstantSpeed.new(model)
91
- swh_pump.setName("#{service_water_loop.name} Circulator Pump")
92
- swh_pump.setPumpControlType('Intermittent')
93
- else
94
- swh_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
95
- swh_pump.setName("#{service_water_loop.name} Water Mains Pressure Driven")
96
- swh_pump.setPumpControlType('Continuous')
97
- end
98
- swh_pump.setRatedPumpHead(swh_pump_head_press_pa.to_f)
99
- swh_pump.setMotorEfficiency(service_water_pump_motor_efficiency)
100
- swh_pump.addToNode(service_water_loop.supplyInletNode)
101
-
102
- water_heater = OpenstudioStandards::ServiceWaterHeating.model_add_water_heater(model,
103
- water_heater_capacity: water_heater_capacity,
104
- water_heater_volume: water_heater_volume,
105
- water_heater_fuel: water_heater_fuel,
106
- on_cycle_parasitic_fuel_consumption_rate: parasitic_fuel_consumption_rate,
107
- off_cycle_parasitic_fuel_consumption_rate: parasitic_fuel_consumption_rate,
108
- service_water_temperature: service_water_temperature,
109
- service_water_temperature_schedule: swh_temp_sch,
110
- set_peak_use_flowrate: false,
111
- peak_flowrate: 0.0,
112
- flowrate_schedule: nil,
113
- water_heater_thermal_zone: water_heater_thermal_zone,
114
- number_water_heaters: number_water_heaters)
115
- service_water_loop.addSupplyBranchForComponent(water_heater)
116
-
117
- # Pipe losses
118
- if add_pipe_losses
119
- model_add_piping_losses_to_swh_system(model,
120
- service_water_loop,
121
- circulating,
122
- pipe_insulation_thickness: pipe_insulation_thickness,
123
- floor_area_served: floor_area_served,
124
- number_of_stories: number_of_stories)
125
- end
126
-
127
- # Service water heating loop bypass pipes
128
- water_heater_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
129
- service_water_loop.addSupplyBranchForComponent(water_heater_bypass_pipe)
130
- coil_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
131
- service_water_loop.addDemandBranchForComponent(coil_bypass_pipe)
132
- supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
133
- supply_outlet_pipe.addToNode(service_water_loop.supplyOutletNode)
134
- demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
135
- demand_outlet_pipe.addToNode(service_water_loop.demandOutletNode)
136
-
137
- if circulating
138
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Added circulating SWH loop called #{service_water_loop.name}")
139
- else
140
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Added non-circulating SWH loop called #{service_water_loop.name}")
141
- end
142
-
143
- return service_water_loop
144
- end
145
-
146
- # Creates a heatpump water heater and attaches it to the supplied service water heating loop.
147
- #
148
- # @param model [OpenStudio::Model::Model] OpenStudio model object
149
- # @param type [String] valid option are 'WrappedCondenser' or 'PumpedCondenser' (default).
150
- # The 'WrappedCondenser' uses a WaterHeaterStratified tank, 'PumpedCondenser' uses a WaterHeaterMixed tank.
151
- # @param water_heater_capacity [Double] water heater capacity, in W
152
- # @param water_heater_volume [Double] water heater volume, in m^3
153
- # @param service_water_temperature [Double] water heater temperature, in C
154
- # @param parasitic_fuel_consumption_rate [Double] water heater parasitic fuel consumption rate, in W
155
- # @param swh_temp_sch [OpenStudio::Model::Schedule] the service water heating schedule. If nil, will be defaulted.
156
- # @param set_peak_use_flowrate [Boolean] if true, the peak flow rate and flow rate schedule will be set.
157
- # @param peak_flowrate [Double] in m^3/s
158
- # @param flowrate_schedule [String] name of the flow rate schedule
159
- # @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone] zone to place water heater in.
160
- # If nil, will be assumed in 70F air for heat loss.
161
- # @param use_ems_control [Boolean] if true, use ems control logic if using a 'WrappedCondenser' style HPWH.
162
- # @return [OpenStudio::Model::WaterHeaterMixed] the resulting water heater
163
- def model_add_heatpump_water_heater(model,
164
- type: 'PumpedCondenser',
165
- water_heater_capacity: 500,
166
- electric_backup_capacity: 4500,
167
- water_heater_volume: OpenStudio.convert(80.0, 'gal', 'm^3').get,
168
- service_water_temperature: OpenStudio.convert(125.0, 'F', 'C').get,
169
- parasitic_fuel_consumption_rate: 3.0,
170
- swh_temp_sch: nil,
171
- cop: 2.8,
172
- shr: 0.88,
173
- tank_ua: 3.9,
174
- set_peak_use_flowrate: false,
175
- peak_flowrate: 0.0,
176
- flowrate_schedule: nil,
177
- water_heater_thermal_zone: nil,
178
- use_ems_control: false)
179
-
180
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', 'Adding heat pump water heater')
181
-
182
- # create heat pump water heater
183
- if type == 'WrappedCondenser'
184
- hpwh = OpenStudio::Model::WaterHeaterHeatPumpWrappedCondenser.new(model)
185
- elsif type == 'PumpedCondenser'
186
- hpwh = OpenStudio::Model::WaterHeaterHeatPump.new(model)
187
- end
188
-
189
- # calculate tank height and radius
190
- water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get
191
- hpwh_vol_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get
192
- tank_height = (0.0188 * hpwh_vol_gal) + 0.0935 # linear relationship that gets GE height at 50 gal and AO Smith height at 80 gal
193
- tank_radius = (0.9 * water_heater_volume / (Math::PI * tank_height))**0.5
194
- tank_surface_area = 2.0 * Math::PI * tank_radius * (tank_radius + tank_height)
195
- u_tank = (5.678 * tank_ua) / OpenStudio.convert(tank_surface_area, 'm^2', 'ft^2').get
196
- hpwh.setName("#{hpwh_vol_gal.round}gal Heat Pump Water Heater - #{water_heater_capacity_kbtu_per_hr.round(0)}kBtu/hr")
197
-
198
- # set min/max HPWH operating temperature limit
199
- hpwh_op_min_temp_c = OpenStudio.convert(45.0, 'F', 'C').get
200
- hpwh_op_max_temp_c = OpenStudio.convert(120.0, 'F', 'C').get
201
-
202
- if type == 'WrappedCondenser'
203
- hpwh.setMinimumInletAirTemperatureforCompressorOperation(hpwh_op_min_temp_c)
204
- hpwh.setMaximumInletAirTemperatureforCompressorOperation(hpwh_op_max_temp_c)
205
- # set sensor heights
206
- if hpwh_vol_gal <= 50.0
207
- hpwh.setDeadBandTemperatureDifference(0.5)
208
- h_ue = (1 - (3.5 / 12.0)) * tank_height # in the 4th node of the tank (counting from top)
209
- h_le = (1 - (10.5 / 12.0)) * tank_height # in the 11th node of the tank (counting from top)
210
- h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top)
211
- h_condbot = (1 - (10.99 / 12.0)) * tank_height # in the 11th node of the tank
212
- h_hpctrl = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank
213
- hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl)
214
- hpwh.setControlSensor1Weight(1.0)
215
- hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl)
216
- else
217
- hpwh.setDeadBandTemperatureDifference(3.89)
218
- h_ue = (1 - (3.5 / 12.0)) * tank_height # in the 3rd node of the tank (counting from top)
219
- h_le = (1 - (9.5 / 12.0)) * tank_height # in the 10th node of the tank (counting from top)
220
- h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top)
221
- h_condbot = 0.01 # bottom node
222
- h_hpctrl_up = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank
223
- h_hpctrl_low = (1 - (8.5 / 12.0)) * tank_height # in the 9th node of the tank
224
- hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl_up)
225
- hpwh.setControlSensor1Weight(0.75)
226
- hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl_low)
227
- end
228
- hpwh.setCondenserBottomLocation(h_condbot)
229
- hpwh.setCondenserTopLocation(h_condtop)
230
- hpwh.setTankElementControlLogic('MutuallyExclusive')
231
- hpwh.autocalculateEvaporatorAirFlowRate
232
- elsif type == 'PumpedCondenser'
233
- hpwh.setDeadBandTemperatureDifference(3.89)
234
- hpwh.autosizeEvaporatorAirFlowRate
235
- end
236
-
237
- # set heat pump water heater properties
238
- hpwh.setFanPlacement('DrawThrough')
239
- hpwh.setOnCycleParasiticElectricLoad(0.0)
240
- hpwh.setOffCycleParasiticElectricLoad(0.0)
241
- hpwh.setParasiticHeatRejectionLocation('Outdoors')
242
-
243
- # set temperature setpoint schedule
244
- if swh_temp_sch.nil?
245
- # temperature schedule type limits
246
- temp_sch_type_limits = OpenstudioStandards::Schedules.create_schedule_type_limits(model,
247
- name: 'Temperature Schedule Type Limits',
248
- lower_limit_value: 0.0,
249
- upper_limit_value: 100.0,
250
- numeric_type: 'Continuous',
251
- unit_type: 'Temperature')
252
- # service water heating loop controls
253
- swh_temp_c = service_water_temperature
254
- swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
255
- swh_delta_t_r = 9.0 # 9F delta-T
256
- swh_temp_c = OpenStudio.convert(swh_temp_f, 'F', 'C').get
257
- swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get
258
- swh_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
259
- swh_temp_c,
260
- name: "Heat Pump Water Heater Temp - #{swh_temp_f.round}F",
261
- schedule_type_limit: 'Temperature')
262
- swh_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
263
- end
264
- hpwh.setCompressorSetpointTemperatureSchedule(swh_temp_sch)
265
-
266
- # coil curves
267
- hpwh_cap = OpenStudio::Model::CurveBiquadratic.new(model)
268
- hpwh_cap.setName('HPWH-Cap-fT')
269
- hpwh_cap.setCoefficient1Constant(0.563)
270
- hpwh_cap.setCoefficient2x(0.0437)
271
- hpwh_cap.setCoefficient3xPOW2(0.000039)
272
- hpwh_cap.setCoefficient4y(0.0055)
273
- hpwh_cap.setCoefficient5yPOW2(-0.000148)
274
- hpwh_cap.setCoefficient6xTIMESY(-0.000145)
275
- hpwh_cap.setMinimumValueofx(0.0)
276
- hpwh_cap.setMaximumValueofx(100.0)
277
- hpwh_cap.setMinimumValueofy(0.0)
278
- hpwh_cap.setMaximumValueofy(100.0)
279
-
280
- hpwh_cop = OpenStudio::Model::CurveBiquadratic.new(model)
281
- hpwh_cop.setName('HPWH-COP-fT')
282
- hpwh_cop.setCoefficient1Constant(1.1332)
283
- hpwh_cop.setCoefficient2x(0.063)
284
- hpwh_cop.setCoefficient3xPOW2(-0.0000979)
285
- hpwh_cop.setCoefficient4y(-0.00972)
286
- hpwh_cop.setCoefficient5yPOW2(-0.0000214)
287
- hpwh_cop.setCoefficient6xTIMESY(-0.000686)
288
- hpwh_cop.setMinimumValueofx(0.0)
289
- hpwh_cop.setMaximumValueofx(100.0)
290
- hpwh_cop.setMinimumValueofy(0.0)
291
- hpwh_cop.setMaximumValueofy(100.0)
292
-
293
- # create DX coil object
294
- if type == 'WrappedCondenser'
295
- coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPumpWrapped.get
296
- coil.setRatedCondenserWaterTemperature(48.89)
297
- coil.autocalculateRatedEvaporatorAirFlowRate
298
- elsif type == 'PumpedCondenser'
299
- coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPump.get
300
- coil.autosizeRatedEvaporatorAirFlowRate
301
- end
302
-
303
- # set coil properties
304
- coil.setName("#{hpwh.name} Coil")
305
- coil.setRatedHeatingCapacity(water_heater_capacity)
306
- coil.setRatedCOP(cop)
307
- coil.setRatedSensibleHeatRatio(shr)
308
- coil.setRatedEvaporatorInletAirDryBulbTemperature(OpenStudio.convert(67.5, 'F', 'C').get)
309
- coil.setRatedEvaporatorInletAirWetBulbTemperature(OpenStudio.convert(56.426, 'F', 'C').get)
310
- coil.setEvaporatorFanPowerIncludedinRatedCOP(true)
311
- coil.setEvaporatorAirTemperatureTypeforCurveObjects('WetBulbTemperature')
312
- coil.setHeatingCapacityFunctionofTemperatureCurve(hpwh_cap)
313
- coil.setHeatingCOPFunctionofTemperatureCurve(hpwh_cop)
314
- coil.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(0.0)
315
-
316
- # set tank properties
317
- if type == 'WrappedCondenser'
318
- tank = hpwh.tank.to_WaterHeaterStratified.get
319
- tank.setTankHeight(tank_height)
320
- tank.setHeaterPriorityControl('MasterSlave')
321
- if hpwh_vol_gal <= 50.0
322
- tank.setHeater1DeadbandTemperatureDifference(25.0)
323
- tank.setHeater2DeadbandTemperatureDifference(30.0)
324
- else
325
- tank.setHeater1DeadbandTemperatureDifference(18.5)
326
- tank.setHeater2DeadbandTemperatureDifference(3.89)
327
- end
328
- hpwh_bottom_element_sp = OpenStudio::Model::ScheduleConstant.new(model)
329
- hpwh_bottom_element_sp.setName("#{hpwh.name} BottomElementSetpoint")
330
- hpwh_top_element_sp = OpenStudio::Model::ScheduleConstant.new(model)
331
- hpwh_top_element_sp.setName("#{hpwh.name} TopElementSetpoint")
332
- tank.setHeater1Capacity(electric_backup_capacity)
333
- tank.setHeater1Height(h_ue)
334
- tank.setHeater1SetpointTemperatureSchedule(hpwh_top_element_sp) # Overwritten later by EMS
335
- tank.setHeater2Capacity(electric_backup_capacity)
336
- tank.setHeater2Height(h_le)
337
- tank.setHeater2SetpointTemperatureSchedule(hpwh_bottom_element_sp)
338
- tank.setUniformSkinLossCoefficientperUnitAreatoAmbientTemperature(u_tank)
339
- tank.setNumberofNodes(12)
340
- tank.setAdditionalDestratificationConductivity(0)
341
- tank.setNode1AdditionalLossCoefficient(0)
342
- tank.setNode2AdditionalLossCoefficient(0)
343
- tank.setNode3AdditionalLossCoefficient(0)
344
- tank.setNode4AdditionalLossCoefficient(0)
345
- tank.setNode5AdditionalLossCoefficient(0)
346
- tank.setNode6AdditionalLossCoefficient(0)
347
- tank.setNode7AdditionalLossCoefficient(0)
348
- tank.setNode8AdditionalLossCoefficient(0)
349
- tank.setNode9AdditionalLossCoefficient(0)
350
- tank.setNode10AdditionalLossCoefficient(0)
351
- tank.setNode11AdditionalLossCoefficient(0)
352
- tank.setNode12AdditionalLossCoefficient(0)
353
- tank.setUseSideDesignFlowRate(0.9 * water_heater_volume / 60.1)
354
- tank.setSourceSideDesignFlowRate(0)
355
- tank.setSourceSideFlowControlMode('')
356
- tank.setSourceSideInletHeight(0)
357
- tank.setSourceSideOutletHeight(0)
358
- elsif type == 'PumpedCondenser'
359
- tank = hpwh.tank.to_WaterHeaterMixed.get
360
- tank.setDeadbandTemperatureDifference(3.89)
361
- tank.setHeaterControlType('Cycle')
362
- tank.setHeaterMaximumCapacity(electric_backup_capacity)
363
- end
364
- tank.setName("#{hpwh.name} Tank")
365
- tank.setEndUseSubcategory('Service Hot Water')
366
- tank.setTankVolume(0.9 * water_heater_volume)
367
- tank.setMaximumTemperatureLimit(90.0)
368
- tank.setHeaterFuelType('Electricity')
369
- tank.setHeaterThermalEfficiency(1.0)
370
- tank.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
371
- tank.setOffCycleParasiticFuelType('Electricity')
372
- tank.setOnCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
373
- tank.setOnCycleParasiticFuelType('Electricity')
374
-
375
- # set fan properties
376
- fan = hpwh.fan.to_FanOnOff.get
377
- fan.setName("#{hpwh.name} Fan")
378
- fan_power = 0.0462 # watts per cfm
379
- if hpwh_vol_gal <= 50.0
380
- fan.setFanEfficiency(23.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get)
381
- fan.setPressureRise(23.0)
382
- else
383
- fan.setFanEfficiency(65.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get)
384
- fan.setPressureRise(65.0)
385
- end
386
- # determine maximum flow rate from water heater capacity
387
- # use 5.035E-5 m^3/s/W from EnergyPlus used to autocalculate the evaporator air flow rate in WaterHeater:HeatPump:PumpedCondenser and Coil:WaterHeating:AirToWaterHeatPump:Pumped
388
- fan_flow_rate_m3_per_s = water_heater_capacity * 5.035e-5
389
- fan.setMaximumFlowRate(fan_flow_rate_m3_per_s)
390
- fan.setMotorEfficiency(1.0)
391
- fan.setMotorInAirstreamFraction(1.0)
392
- fan.setEndUseSubcategory('Service Hot Water')
393
-
394
- if water_heater_thermal_zone.nil?
395
- # add in schedules for Tamb, RHamb, and the compressor
396
- # assume the water heater is indoors at 70F for now
397
- default_water_heater_ambient_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
398
- OpenStudio.convert(70.0, 'F', 'C').get,
399
- name: 'Water Heater Ambient Temp Schedule 70F',
400
- schedule_type_limit: 'Temperature')
401
- if temp_sch_type_limits.nil?
402
- temp_sch_type_limits = OpenstudioStandards::Schedules.create_schedule_type_limits(model,
403
- name: 'Temperature Schedule Type Limits',
404
- lower_limit_value: 0.0,
405
- upper_limit_value: 100.0,
406
- numeric_type: 'Continuous',
407
- unit_type: 'Temperature')
408
- end
409
- default_water_heater_ambient_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
410
- tank.setAmbientTemperatureIndicator('Schedule')
411
- tank.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
412
- tank.resetAmbientTemperatureThermalZone
413
- hpwh_rhamb = OpenStudio::Model::ScheduleConstant.new(model)
414
- hpwh_rhamb.setName("#{hpwh.name} Ambient Humidity Schedule")
415
- hpwh_rhamb.setValue(0.5)
416
- hpwh.setInletAirConfiguration('Schedule')
417
- hpwh.setInletAirTemperatureSchedule(default_water_heater_ambient_temp_sch)
418
- hpwh.setInletAirHumiditySchedule(hpwh_rhamb)
419
- hpwh.setCompressorLocation('Schedule')
420
- hpwh.setCompressorAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
421
- else
422
- hpwh.addToThermalZone(water_heater_thermal_zone)
423
- hpwh.setInletAirConfiguration('ZoneAirOnly')
424
- hpwh.setCompressorLocation('Zone')
425
- tank.setAmbientTemperatureIndicator('ThermalZone')
426
- tank.setAmbientTemperatureThermalZone(water_heater_thermal_zone)
427
- tank.resetAmbientTemperatureSchedule
428
- end
429
-
430
- if set_peak_use_flowrate
431
- rated_flow_rate_m3_per_s = peak_flowrate
432
- rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
433
- tank.setPeakUseFlowRate(rated_flow_rate_m3_per_s)
434
- schedule = model_add_schedule(model, flowrate_schedule)
435
- tank.setUseFlowRateFractionSchedule(schedule)
436
- end
437
-
438
- # add EMS for overriding HPWH setpoints schedules (for upper/lower heating element in water tank and compressor in heat pump)
439
- if type == 'WrappedCondenser' && use_ems_control
440
- hpwh_name_ems_friendly = ems_friendly_name(hpwh.name)
441
-
442
- # create an ambient temperature sensor for the air that blows through the HPWH evaporator
443
- if water_heater_thermal_zone.nil?
444
- # assume the condenser is outside
445
- amb_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Drybulb Temperature')
446
- amb_temp_sensor.setName("#{hpwh_name_ems_friendly}_amb_temp")
447
- amb_temp_sensor.setKeyName('Environment')
448
- else
449
- amb_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Temperature')
450
- amb_temp_sensor.setName("#{hpwh_name_ems_friendly}_amb_temp")
451
- amb_temp_sensor.setKeyName(water_heater_thermal_zone.name.to_s)
452
- end
453
-
454
- # create actuator for heat pump compressor
455
- if swh_temp_sch.to_ScheduleConstant.is_initialized
456
- swh_temp_sch = swh_temp_sch.to_ScheduleConstant.get
457
- schedule_type = 'Schedule:Constant'
458
- elsif swh_temp_sch.to_ScheduleCompact.is_initialized
459
- swh_temp_sch = swh_temp_sch.to_ScheduleCompact.get
460
- schedule_type = 'Schedule:Compact'
461
- elsif swh_temp_sch.to_ScheduleRuleset.is_initialized
462
- swh_temp_sch = swh_temp_sch.to_ScheduleRuleset.get
463
- schedule_type = 'Schedule:Year'
464
- else
465
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.Prototype.ServiceWaterHeating', "Unsupported schedule type for HPWH setpoint schedule #{swh_temp_sch.name}.")
466
- return false
467
- end
468
- hpwhschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(swh_temp_sch, schedule_type, 'Schedule Value')
469
- hpwhschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_HPWHSchedOverride")
470
-
471
- # create actuator for lower heating element in water tank
472
- leschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hpwh_bottom_element_sp, 'Schedule:Constant', 'Schedule Value')
473
- leschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_LESchedOverride")
474
-
475
- # create actuator for upper heating element in water tank
476
- ueschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hpwh_top_element_sp, 'Schedule:Constant', 'Schedule Value')
477
- ueschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_UESchedOverride")
478
-
479
- # create sensor for heat pump compressor
480
- t_set_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
481
- t_set_sensor.setName("#{hpwh_name_ems_friendly}_T_set")
482
- t_set_sensor.setKeyName(swh_temp_sch.name.to_s)
483
-
484
- # define control configuration
485
- t_offset = 9.0 # deg-C
486
-
487
- # get tank specifications
488
- upper_element_db = tank.heater1DeadbandTemperatureDifference
489
-
490
- # define control logic
491
- hpwh_ctrl_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
492
- hpwh_ctrl_program.setName("#{hpwh_name_ems_friendly}_Control")
493
- hpwh_ctrl_program.addLine("SET #{hpwhschedoverride_actuator.name} = #{t_set_sensor.name}")
494
- # lockout hp when ambient temperature is either too high or too low
495
- hpwh_ctrl_program.addLine("IF (#{amb_temp_sensor.name}<#{hpwh_op_min_temp_c}) || (#{amb_temp_sensor.name}>#{hpwh_op_max_temp_c})")
496
- hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name} = #{t_set_sensor.name}")
497
- hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name} = #{t_set_sensor.name}")
498
- hpwh_ctrl_program.addLine('ELSE')
499
- # upper element setpoint temperature
500
- hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name} = #{t_set_sensor.name} - #{t_offset}")
501
- # upper element cut-in temperature
502
- hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name}_cut_in = #{ueschedoverride_actuator.name} - #{upper_element_db}")
503
- # lower element disabled
504
- hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name} = 0")
505
- # lower element disabled
506
- hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name}_cut_in = 0")
507
- hpwh_ctrl_program.addLine('ENDIF')
508
-
509
- # create a program calling manager
510
- program_calling_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
511
- program_calling_manager.setName("#{hpwh_name_ems_friendly}_ProgramManager")
512
- program_calling_manager.setCallingPoint('InsideHVACSystemIterationLoop')
513
- program_calling_manager.addProgram(hpwh_ctrl_program)
514
- end
515
-
516
- return hpwh
517
- end
518
-
519
- # Creates a booster water heater and attaches it
520
- # to the supplied service water heating loop.
521
- #
522
- # @param model [OpenStudio::Model::Model] OpenStudio model object
523
- # @param main_service_water_loop [OpenStudio::Model::PlantLoop]
524
- # the main service water loop that this booster assists.
525
- # @param water_heater_capacity [Double] water heater capacity, in W
526
- # @param water_heater_volume [Double] water heater volume, in m^3
527
- # @param water_heater_fuel [Double] valid choices are
528
- # Gas, Electric
529
- # @param booster_water_temperature [Double] water heater temperature, in C
530
- # @param parasitic_fuel_consumption_rate [Double] water heater parasitic
531
- # fuel consumption rate, in W
532
- # @param booster_water_heater_thermal_zone [OpenStudio::Model::ThermalZone]
533
- # zones to place water heater in. If nil, will be assumed in 70F air for heat loss.
534
- # @return [OpenStudio::Model::PlantLoop]
535
- # the resulting booster water loop.
536
- def model_add_swh_booster(model,
537
- main_service_water_loop,
538
- water_heater_capacity,
539
- water_heater_volume,
540
- water_heater_fuel,
541
- booster_water_temperature,
542
- parasitic_fuel_consumption_rate,
543
- booster_water_heater_thermal_zone)
544
-
545
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding booster water heater to #{main_service_water_loop.name}")
546
-
547
- # Booster water heating loop
548
- booster_service_water_loop = OpenStudio::Model::PlantLoop.new(model)
549
- booster_service_water_loop.setName('Service Water Loop')
550
-
551
- # Temperature schedule type limits
552
- temp_sch_type_limits = OpenstudioStandards::Schedules.create_schedule_type_limits(model,
553
- name: 'Temperature Schedule Type Limits',
554
- lower_limit_value: 0.0,
555
- upper_limit_value: 100.0,
556
- numeric_type: 'Continuous',
557
- unit_type: 'Temperature')
558
-
559
- # Service water heating loop controls
560
- swh_temp_c = booster_water_temperature
561
- swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
562
- swh_delta_t_r = 9 # 9F delta-T
563
- swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get
564
- swh_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
565
- swh_temp_c,
566
- name: "Service Water Booster Temp - #{swh_temp_f}F",
567
- schedule_type_limit: 'Temperature')
568
- swh_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
569
- swh_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, swh_temp_sch)
570
- swh_stpt_manager.setName('Hot water booster setpoint manager')
571
- swh_stpt_manager.addToNode(booster_service_water_loop.supplyOutletNode)
572
- sizing_plant = booster_service_water_loop.sizingPlant
573
- sizing_plant.setLoopType('Heating')
574
- sizing_plant.setDesignLoopExitTemperature(swh_temp_c)
575
- sizing_plant.setLoopDesignTemperatureDifference(swh_delta_t_k)
576
-
577
- # Booster water heating pump
578
- swh_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
579
- swh_pump.setName('Booster Water Loop Pump')
580
- swh_pump.setRatedPumpHead(0.0) # As if there is no circulation pump
581
- swh_pump.setRatedPowerConsumption(0.0) # As if there is no circulation pump
582
- swh_pump.setMotorEfficiency(1)
583
- swh_pump.setPumpControlType('Continuous')
584
- swh_pump.setMinimumFlowRate(0.0)
585
- swh_pump.addToNode(booster_service_water_loop.supplyInletNode)
586
-
587
- # Water heater
588
- # @todo Standards - Change water heater methodology to follow
589
- # 'Model Enhancements Appendix A.'
590
- water_heater_capacity_btu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'Btu/hr').get
591
- water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'kBtu/hr').get
592
- water_heater_vol_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get
593
-
594
- # Water heater depends on the fuel type
595
- water_heater = OpenStudio::Model::WaterHeaterMixed.new(model)
596
- water_heater.setName("#{water_heater_vol_gal}gal #{water_heater_fuel} Booster Water Heater - #{water_heater_capacity_kbtu_per_hr.round}kBtu/hr")
597
- water_heater.setTankVolume(OpenStudio.convert(water_heater_vol_gal, 'gal', 'm^3').get)
598
- water_heater.setSetpointTemperatureSchedule(swh_temp_sch)
599
- water_heater.setDeadbandTemperatureDifference(2.0)
600
- water_heater.setEndUseSubcategory('Booster')
601
-
602
- if booster_water_heater_thermal_zone.nil?
603
- # Assume the water heater is indoors at 70F or 72F
604
- case template
605
- when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013', '90.1-2016', '90.1-2019'
606
- indoor_temp = 71.6
607
- else
608
- indoor_temp = 70.0
609
- end
610
- default_water_heater_ambient_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
611
- OpenStudio.convert(indoor_temp, 'F', 'C').get,
612
- name: "Water Heater Ambient Temp Schedule #{indoor_temp}F",
613
- schedule_type_limit: 'Temperature')
614
- default_water_heater_ambient_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
615
- water_heater.setAmbientTemperatureIndicator('Schedule')
616
- water_heater.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
617
- water_heater.resetAmbientTemperatureThermalZone
618
- else
619
- water_heater.setAmbientTemperatureIndicator('ThermalZone')
620
- water_heater.setAmbientTemperatureThermalZone(booster_water_heater_thermal_zone)
621
- water_heater.resetAmbientTemperatureSchedule
622
- end
623
-
624
- water_heater.setMaximumTemperatureLimit(swh_temp_c)
625
- water_heater.setDeadbandTemperatureDifference(OpenStudio.convert(3.6, 'R', 'K').get)
626
- water_heater.setHeaterControlType('Cycle')
627
- water_heater.setHeaterMaximumCapacity(OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'W').get)
628
- water_heater.setOffCycleParasiticHeatFractiontoTank(0.8)
629
- water_heater.setIndirectWaterHeatingRecoveryTime(1.5) # 1.5hrs
630
- if water_heater_fuel == 'Electricity'
631
- water_heater.setHeaterFuelType('Electricity')
632
- water_heater.setHeaterThermalEfficiency(1.0)
633
- water_heater.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
634
- water_heater.setOnCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
635
- water_heater.setOffCycleParasiticFuelType('Electricity')
636
- water_heater.setOnCycleParasiticFuelType('Electricity')
637
- water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053)
638
- water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053)
639
- elsif water_heater_fuel == 'Natural Gas' || water_heater_fuel == 'NaturalGas'
640
- water_heater.setHeaterFuelType('Gas')
641
- water_heater.setHeaterThermalEfficiency(0.8)
642
- water_heater.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
643
- water_heater.setOnCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
644
- water_heater.setOffCycleParasiticFuelType('Gas')
645
- water_heater.setOnCycleParasiticFuelType('Gas')
646
- water_heater.setOffCycleLossCoefficienttoAmbientTemperature(6.0)
647
- water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0)
648
- end
649
-
650
- booster_service_water_loop.addSupplyBranchForComponent(water_heater)
651
-
652
- # Service water heating loop bypass pipes
653
- water_heater_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
654
- booster_service_water_loop.addSupplyBranchForComponent(water_heater_bypass_pipe)
655
- coil_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
656
- booster_service_water_loop.addDemandBranchForComponent(coil_bypass_pipe)
657
- supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
658
- supply_outlet_pipe.addToNode(booster_service_water_loop.supplyOutletNode)
659
- demand_inlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
660
- demand_inlet_pipe.addToNode(booster_service_water_loop.demandInletNode)
661
- demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
662
- demand_outlet_pipe.addToNode(booster_service_water_loop.demandOutletNode)
663
-
664
- # Heat exchanger to supply the booster water heater
665
- # with normal hot water from the main service water loop.
666
- hx = OpenStudio::Model::HeatExchangerFluidToFluid.new(model)
667
- hx.setName('HX for Booster Water Heating')
668
- hx.setHeatExchangeModelType('Ideal')
669
- hx.setControlType('UncontrolledOn')
670
- hx.setHeatTransferMeteringEndUseType('LoopToLoop')
671
-
672
- # Add the HX to the supply side of the booster loop
673
- hx.addToNode(booster_service_water_loop.supplyInletNode)
674
-
675
- # Add the HX to the demand side of
676
- # the main service water loop.
677
- main_service_water_loop.addDemandBranchForComponent(hx)
678
-
679
- # Add a plant component temperature source to the demand outlet
680
- # of the HX to represent the fact that the water used by the booster
681
- # would in reality be at the mains temperature.
682
- mains_src = OpenStudio::Model::PlantComponentTemperatureSource.new(model)
683
- mains_src.setName('Mains Water Makeup for SWH Booster')
684
- mains_src.addToNode(hx.demandOutletModelObject.get.to_Node.get)
685
-
686
- # Mains water temperature sensor
687
- mains_water_temp_sen = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Mains Water Temperature')
688
- mains_water_temp_sen.setName('Mains_Water_Temp_Sen')
689
- mains_water_temp_sen.setKeyName('Environment')
690
-
691
- # Schedule to actuate
692
- water_mains_temp_sch = OpenStudio::Model::ScheduleConstant.new(model)
693
- water_mains_temp_sch.setName('Mains Water Temperature')
694
- water_mains_temp_sch.setValue(OpenStudio.convert(50, 'F', 'C').get)
695
-
696
- # Actuator for mains water temperature schedule
697
- mains_water_temp_sch_act = OpenStudio::Model::EnergyManagementSystemActuator.new(water_mains_temp_sch, 'Schedule:Constant', 'Schedule Value')
698
- mains_water_temp_sch_act.setName('Mains_Water_Temp_Act')
699
-
700
- # Program
701
- mains_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
702
- mains_prg.setName('Mains_Water_Prg')
703
- mains_prg_body = "SET #{mains_water_temp_sch_act.handle} = #{mains_water_temp_sen.handle}"
704
- mains_prg.setBody(mains_prg_body)
705
-
706
- # Program Calling Manager
707
- mains_mgr = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
708
- mains_mgr.setName('Mains_Water_Prg_Mgr')
709
- mains_mgr.setCallingPoint('BeginTimestepBeforePredictor')
710
- mains_mgr.addProgram(mains_prg)
711
-
712
- # Make the plant component use the actuated schedule
713
- mains_src.setTemperatureSpecificationType('Scheduled')
714
- mains_src.setSourceTemperatureSchedule(water_mains_temp_sch)
715
-
716
- return booster_service_water_loop
717
- end
718
-
719
- # Creates water fixtures and attaches them
720
- # to the supplied service water loop.
721
- #
722
- # @param model [OpenStudio::Model::Model] OpenStudio model object
723
- # @param use_name [String] The name that will be assigned
724
- # to the newly created fixture.
725
- # @param swh_loop [OpenStudio::Model::PlantLoop]
726
- # the main service water loop to add water fixtures to.
727
- # @param peak_flowrate [Double] in m^3/s
728
- # @param flowrate_schedule [String] name of the flow rate schedule
729
- # @param water_use_temperature [Double] mixed water use temperature, in C
730
- # @param space_name [String] the name of the space to add the water fixture to,
731
- # or nil, in which case it will not be assigned to any particular space.
732
- # @return [OpenStudio::Model::WaterUseEquipment]
733
- # the resulting water fixture.
734
- def model_add_swh_end_uses(model,
735
- use_name,
736
- swh_loop,
737
- peak_flowrate,
738
- flowrate_schedule,
739
- water_use_temperature,
740
- space_name,
741
- frac_sensible: 0.2,
742
- frac_latent: 0.05)
743
- # Water use connection
744
- swh_connection = OpenStudio::Model::WaterUseConnections.new(model)
745
-
746
- # Water fixture definition
747
- water_fixture_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
748
- rated_flow_rate_m3_per_s = peak_flowrate
749
- rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
750
-
751
- water_use_sensible_frac_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
752
- frac_sensible,
753
- name: "Fraction Sensible - #{frac_sensible}",
754
- schedule_type_limit: 'Fractional')
755
- water_use_latent_frac_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
756
- frac_latent,
757
- name: "Fraction Latent - #{frac_latent}",
758
- schedule_type_limit: 'Fractional')
759
- water_fixture_def.setSensibleFractionSchedule(water_use_sensible_frac_sch)
760
- water_fixture_def.setLatentFractionSchedule(water_use_latent_frac_sch)
761
- water_fixture_def.setPeakFlowRate(rated_flow_rate_m3_per_s)
762
- water_fixture_def.setName("#{use_name} Service Water Use Def #{rated_flow_rate_gal_per_min.round(2)}gpm")
763
- # Target mixed water temperature
764
- mixed_water_temp_f = OpenStudio.convert(water_use_temperature, 'C', 'F').get
765
- mixed_water_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
766
- OpenStudio.convert(mixed_water_temp_f, 'F', 'C').get,
767
- name: "Mixed Water At Faucet Temp - #{mixed_water_temp_f.round}F",
768
- schedule_type_limit: 'Temperature')
769
- water_fixture_def.setTargetTemperatureSchedule(mixed_water_temp_sch)
770
-
771
- # Water use equipment
772
- water_fixture = OpenStudio::Model::WaterUseEquipment.new(water_fixture_def)
773
- schedule = model_add_schedule(model, flowrate_schedule)
774
- water_fixture.setFlowRateFractionSchedule(schedule)
775
-
776
- if space_name.nil?
777
- water_fixture.setName("#{use_name} Service Water Use #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
778
- swh_connection.setName("#{use_name} WUC #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
779
- else
780
- water_fixture.setName("#{space_name} Service Water Use #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
781
- swh_connection.setName("#{space_name} WUC #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
782
- end
783
-
784
- unless space_name.nil?
785
- space = model.getSpaceByName(space_name)
786
- space = space.get
787
- water_fixture.setSpace(space)
788
- end
789
-
790
- swh_connection.addWaterUseEquipment(water_fixture)
791
-
792
- # Connect the water use connection to the SWH loop
793
- unless swh_loop.nil?
794
- swh_loop.addDemandBranchForComponent(swh_connection)
795
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding water fixture to #{swh_loop.name}.")
796
- end
797
-
798
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Added #{water_fixture.name}.")
799
-
800
- return water_fixture
801
- end
802
-
803
- # This method will add a swh water fixture to the model for the space.
804
- # It will return a water fixture object, or NIL if there is no water load at all.
805
- #
806
- # Adds a WaterUseEquipment object representing the SWH loads of the supplied Space.
807
- # Attaches this WaterUseEquipment to the supplied PlantLoop via a new WaterUseConnections object.
808
- #
809
- # @param model [OpenStudio::Model::Model] OpenStudio model object
810
- # @param swh_loop [OpenStudio::Model::PlantLoop] the SWH loop to connect the WaterUseEquipment to
811
- # @param space [OpenStudio::Model::Space] the Space to add a WaterUseEquipment for
812
- # @param space_multiplier [Double] the multiplier to use if the supplied Space actually represents
813
- # more area than is shown in the model.
814
- # @param is_flow_per_area [Boolean] if true, use the value in the 'service_water_heating_peak_flow_per_area'
815
- # field of the space_types JSON. If false, use the value in the 'service_water_heating_peak_flow_rate' field.
816
- # @return [OpenStudio::Model::WaterUseEquipment] the WaterUseEquipment for the
817
- def model_add_swh_end_uses_by_space(model,
818
- swh_loop,
819
- space,
820
- space_multiplier = 1.0,
821
- is_flow_per_area = true)
822
- # SpaceType
823
- if space.spaceType.empty?
824
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Space #{space.name} does not have a Space Type assigned, cannot add SWH end uses.")
825
- return nil
826
- end
827
- space_type = space.spaceType.get
828
-
829
- # Standards Building Type
830
- if space_type.standardsBuildingType.empty?
831
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Space #{space.name}'s Space Type does not have a Standards Building Type assigned, cannot add SWH end uses.")
832
- return nil
833
- end
834
- stds_bldg_type = space_type.standardsBuildingType.get
835
- building_type = model_get_lookup_name(stds_bldg_type)
836
-
837
- # Standards Space Type
838
- if space_type.standardsSpaceType.empty?
839
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Space #{space.name}'s Space Type does not have a Standards Space Type assigned, cannot add SWH end uses.")
840
- return nil
841
- end
842
- stds_spc_type = space_type.standardsSpaceType.get
843
-
844
- # find the specific space_type properties from standard.json
845
- search_criteria = {
846
- 'template' => template,
847
- 'building_type' => building_type,
848
- 'space_type' => stds_spc_type
849
- }
850
- data = standards_lookup_table_first(table_name: 'space_types', search_criteria: search_criteria)
851
- if data.nil?
852
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "Could not find space type for: #{search_criteria}.")
853
- return nil
854
- end
855
- space_area = OpenStudio.convert(space.floorArea, 'm^2', 'ft^2').get # ft2
856
-
857
- # If there is no service hot water load.. Don't bother adding anything.
858
- if data['service_water_heating_peak_flow_per_area'].to_f < 0.00001 && data['service_water_heating_peak_flow_rate'].to_f < 0.00001
859
- return nil
860
- end
861
-
862
- # Water use connection
863
- swh_connection = OpenStudio::Model::WaterUseConnections.new(model)
864
-
865
- # Water fixture definition
866
- water_fixture_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
867
- rated_flow_rate_per_area = data['service_water_heating_peak_flow_per_area'].to_f # gal/h.ft2
868
- rated_flow_rate_gal_per_hour = if is_flow_per_area
869
- rated_flow_rate_per_area * space_area * space_multiplier # gal/h
870
- else
871
- data['service_water_heating_peak_flow_rate'].to_f
872
- end
873
- rated_flow_rate_gal_per_min = rated_flow_rate_gal_per_hour / 60 # gal/h to gal/min
874
- rated_flow_rate_m3_per_s = OpenStudio.convert(rated_flow_rate_gal_per_min, 'gal/min', 'm^3/s').get
875
- water_use_sensible_frac_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
876
- 0.2,
877
- name: 'Fraction Sensible - 0.2',
878
- schedule_type_limit: 'Fractional')
879
- water_use_latent_frac_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
880
- 0.05,
881
- name: 'Fraction Latent - 0.05',
882
- schedule_type_limit: 'Fractional')
883
- water_fixture_def.setSensibleFractionSchedule(water_use_sensible_frac_sch)
884
- water_fixture_def.setLatentFractionSchedule(water_use_latent_frac_sch)
885
- water_fixture_def.setPeakFlowRate(rated_flow_rate_m3_per_s)
886
- water_fixture_def.setName("#{space.name.get} Service Water Use Def #{rated_flow_rate_gal_per_min.round(2)}gpm")
887
- # Target mixed water temperature
888
- mixed_water_temp_f = data['service_water_heating_target_temperature']
889
- mixed_water_temp_c = OpenStudio.convert(mixed_water_temp_f, 'F', 'C').get
890
- mixed_water_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
891
- mixed_water_temp_c,
892
- name: "Mixed Water At Faucet Temp - #{mixed_water_temp_f.round}F",
893
- schedule_type_limit: 'Temperature')
894
- water_fixture_def.setTargetTemperatureSchedule(mixed_water_temp_sch)
895
-
896
- # Water use equipment
897
- water_fixture = OpenStudio::Model::WaterUseEquipment.new(water_fixture_def)
898
- schedule = model_add_schedule(model, data['service_water_heating_schedule'])
899
- water_fixture.setFlowRateFractionSchedule(schedule)
900
- water_fixture.setName("#{space.name.get} Service Water Use #{rated_flow_rate_gal_per_min.round(2)}gpm")
901
- swh_connection.addWaterUseEquipment(water_fixture)
902
- # Assign water fixture to a space
903
- water_fixture.setSpace(space) if model_attach_water_fixtures_to_spaces?(model)
904
-
905
- # Connect the water use connection to the SWH loop
906
- swh_loop.addDemandBranchForComponent(swh_connection)
907
- return water_fixture
908
- end
909
-
910
- # Determine whether or not water fixtures are attached to spaces
911
- # @todo For hotels and apartments, add the water fixture at the space level
912
- # @param model [OpenStudio::Model::Model] OpenStudio model object
913
- # @return [Boolean] returns true if successful, false if not
914
- def model_attach_water_fixtures_to_spaces?(model)
915
- # if building_type!=nil && ((building_type.downcase.include?"hotel") || (building_type.downcase.include?"apartment"))
916
- # return true
917
- # end
918
- return false
919
- end
920
-
921
- # Creates water fixtures and attaches them to the supplied booster water loop.
922
- #
923
- # @param model [OpenStudio::Model::Model] OpenStudio model object
924
- # @param swh_booster_loop [OpenStudio::Model::PlantLoop]
925
- # the booster water loop to add water fixtures to.
926
- # @param peak_flowrate [Double] in m^3/s
927
- # @param flowrate_schedule [String] name of the flow rate schedule
928
- # @param water_use_temperature [Double] mixed water use temperature, in C
929
- # @return [OpenStudio::Model::WaterUseEquipment] the resulting water fixture
930
- def model_add_booster_swh_end_uses(model,
931
- swh_booster_loop,
932
- peak_flowrate,
933
- flowrate_schedule,
934
- water_use_temperature)
935
-
936
- # Water use connection
937
- swh_connection = OpenStudio::Model::WaterUseConnections.new(model)
938
-
939
- # Water fixture definition
940
- water_fixture_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
941
- rated_flow_rate_m3_per_s = peak_flowrate
942
- rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
943
- water_fixture_def.setName("Booster Water Fixture Def - #{rated_flow_rate_gal_per_min.round(2)} gpm")
944
- water_fixture_def.setPeakFlowRate(rated_flow_rate_m3_per_s)
945
- # Target mixed water temperature
946
- mixed_water_temp_f = OpenStudio.convert(water_use_temperature, 'C', 'F').get
947
- mixed_water_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
948
- OpenStudio.convert(mixed_water_temp_f, 'F', 'C').get,
949
- name: "Mixed Water At Faucet Temp - #{mixed_water_temp_f.round}F",
950
- schedule_type_limit: 'Temperature')
951
- water_fixture_def.setTargetTemperatureSchedule(mixed_water_temp_sch)
952
-
953
- # Water use equipment
954
- water_fixture = OpenStudio::Model::WaterUseEquipment.new(water_fixture_def)
955
- water_fixture.setName("Booster Water Fixture - #{rated_flow_rate_gal_per_min.round(2)} gpm at #{mixed_water_temp_f.round}F")
956
- schedule = model_add_schedule(model, flowrate_schedule)
957
- water_fixture.setFlowRateFractionSchedule(schedule)
958
- swh_connection.addWaterUseEquipment(water_fixture)
959
-
960
- # Connect the water use connection to the SWH loop
961
- unless swh_booster_loop.nil?
962
- swh_booster_loop.addDemandBranchForComponent(swh_connection)
963
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding water fixture to #{swh_booster_loop.name}.")
964
- end
965
-
966
- return water_fixture
967
- end
968
-
969
- # Adds insulated 0.75in copper piping to the model.
970
- # For circulating systems, assume length of piping is proportional
971
- # to the area and number of stories in the building.
972
- # For non-circulating systems, assume that the water heaters
973
- # are close to the point of use.
974
- # Assume that piping is located in a zone
975
- #
976
- # @param model [OpenStudio::Model::Model] OpenStudio model object
977
- # @param swh_loop [OpenStudio::Model::PlantLoop] the service water heating loop
978
- # @param floor_area_served [Double] the area of building served by the service water heating loop, in m^2
979
- # @param number_of_stories [Integer] the number of stories served by the service water heating loop
980
- # @param pipe_insulation_thickness [Double] the thickness of the pipe insulation, in m. Use 0 for no insulation
981
- # @param circulating [Boolean] use true for circulating systems, false for non-circulating systems
982
- # @param air_temp_surrounding_piping [Double] the temperature of the air surrounding the piping, in C.
983
- # @return [Boolean] returns true if successful, false if not
984
- def model_add_piping_losses_to_swh_system(model,
985
- swh_loop,
986
- circulating,
987
- pipe_insulation_thickness: 0,
988
- floor_area_served: 465,
989
- number_of_stories: 1,
990
- air_temp_surrounding_piping: 21.1111)
991
-
992
- # Estimate pipe length
993
- if circulating
994
- # For circulating systems, get pipe length based on the size of the building.
995
- # Formula from A.3.1 PrototypeModelEnhancements_2014_0.pdf
996
- floor_area_ft2 = OpenStudio.convert(floor_area_served, 'm^2', 'ft^2').get
997
- pipe_length_ft = 2.0 * (Math.sqrt(floor_area_ft2 / number_of_stories) + (10.0 * (number_of_stories - 1.0)))
998
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Pipe length #{pipe_length_ft.round}ft = 2.0 * ( (#{floor_area_ft2.round}ft2 / #{number_of_stories} stories)^0.5 + (10.0ft * (#{number_of_stories} stories - 1.0) ) )")
999
- else
1000
- # For non-circulating systems, assume water heater is close to point of use
1001
- pipe_length_ft = 20.0
1002
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Pipe length #{pipe_length_ft.round}ft. For non-circulating systems, assume water heater is close to point of use.")
1003
- end
1004
-
1005
- # For systems whose water heater object represents multiple pieces
1006
- # of equipment, multiply the piping length by the number of pieces of equipment.
1007
- swh_loop.supplyComponents('OS_WaterHeater_Mixed'.to_IddObjectType).each do |sc|
1008
- next unless sc.to_WaterHeaterMixed.is_initialized
1009
-
1010
- water_heater = sc.to_WaterHeaterMixed.get
1011
-
1012
- # get number of water heaters
1013
- if water_heater.additionalProperties.getFeatureAsInteger('component_quantity').is_initialized
1014
- comp_qty = water_heater.additionalProperties.getFeatureAsInteger('component_quantity').get
1015
- else
1016
- comp_qty = 1
1017
- end
1018
-
1019
- if comp_qty > 1
1020
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Piping length has been multiplied by #{comp_qty}X because #{water_heater.name} represents #{comp_qty} pieces of equipment.")
1021
- pipe_length_ft *= comp_qty
1022
- break
1023
- end
1024
- end
1025
-
1026
- # Service water heating piping heat loss scheduled air temperature
1027
- swh_piping_air_temp_c = air_temp_surrounding_piping
1028
- swh_piping_air_temp_f = OpenStudio.convert(swh_piping_air_temp_c, 'C', 'F').get
1029
- swh_piping_air_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
1030
- swh_piping_air_temp_c,
1031
- name: "#{swh_loop.name} Piping Air Temp - #{swh_piping_air_temp_f.round}F",
1032
- schedule_type_limit: 'Temperature')
1033
-
1034
- # Service water heating piping heat loss scheduled air velocity
1035
- swh_piping_air_velocity_m_per_s = 0.3
1036
- swh_piping_air_velocity_mph = OpenStudio.convert(swh_piping_air_velocity_m_per_s, 'm/s', 'mile/hr').get
1037
- swh_piping_air_velocity_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
1038
- swh_piping_air_velocity_m_per_s,
1039
- name: "#{swh_loop.name} Piping Air Velocity - #{swh_piping_air_velocity_mph.round(2)}mph",
1040
- schedule_type_limit: 'Dimensionless')
1041
-
1042
- # Material for 3/4in type L (heavy duty) copper pipe
1043
- copper_pipe = OpenStudio::Model::StandardOpaqueMaterial.new(model)
1044
- copper_pipe.setName('Copper pipe 0.75in type L')
1045
- copper_pipe.setRoughness('Smooth')
1046
- copper_pipe.setThickness(OpenStudio.convert(0.045, 'in', 'm').get)
1047
- copper_pipe.setThermalConductivity(386.0)
1048
- copper_pipe.setDensity(OpenStudio.convert(556, 'lb/ft^3', 'kg/m^3').get)
1049
- copper_pipe.setSpecificHeat(OpenStudio.convert(0.092, 'Btu/lb*R', 'J/kg*K').get)
1050
- copper_pipe.setThermalAbsorptance(0.9) # @todo find reference for property
1051
- copper_pipe.setSolarAbsorptance(0.7) # @todo find reference for property
1052
- copper_pipe.setVisibleAbsorptance(0.7) # @todo find reference for property
1053
-
1054
- # Construction for pipe
1055
- pipe_construction = OpenStudio::Model::Construction.new(model)
1056
-
1057
- # Add insulation material to insulated pipe
1058
- if pipe_insulation_thickness > 0
1059
- # Material for fiberglass insulation
1060
- # R-value from Owens-Corning 1/2in fiberglass pipe insulation
1061
- # https://www.grainger.com/product/OWENS-CORNING-1-2-Thick-40PP22
1062
- # but modified until simulated heat loss = 17.7 Btu/hr/ft of pipe with 140F water and 70F air
1063
- pipe_insulation_thickness_in = OpenStudio.convert(pipe_insulation_thickness, 'm', 'in').get
1064
- insulation = OpenStudio::Model::StandardOpaqueMaterial.new(model)
1065
- insulation.setName("Fiberglass batt #{pipe_insulation_thickness_in.round(2)}in")
1066
- insulation.setRoughness('Smooth')
1067
- insulation.setThickness(OpenStudio.convert(pipe_insulation_thickness_in, 'in', 'm').get)
1068
- insulation.setThermalConductivity(OpenStudio.convert(0.46, 'Btu*in/hr*ft^2*R', 'W/m*K').get)
1069
- insulation.setDensity(OpenStudio.convert(0.7, 'lb/ft^3', 'kg/m^3').get)
1070
- insulation.setSpecificHeat(OpenStudio.convert(0.2, 'Btu/lb*R', 'J/kg*K').get)
1071
- insulation.setThermalAbsorptance(0.9) # Irrelevant for Pipe:Indoor; no radiation model is used
1072
- insulation.setSolarAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used
1073
- insulation.setVisibleAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used
1074
-
1075
- pipe_construction.setName("Copper pipe 0.75in type L with #{pipe_insulation_thickness_in.round(2)}in fiberglass batt")
1076
- pipe_construction.setLayers([insulation, copper_pipe])
1077
- else
1078
- pipe_construction.setName('Uninsulated copper pipe 0.75in type L')
1079
- pipe_construction.setLayers([copper_pipe])
1080
- end
1081
-
1082
- heat_loss_pipe = OpenStudio::Model::PipeIndoor.new(model)
1083
- heat_loss_pipe.setName("#{swh_loop.name} Pipe #{pipe_length_ft}ft")
1084
- heat_loss_pipe.setEnvironmentType('Schedule')
1085
- # @todoschedule type registry error for this setter
1086
- # heat_loss_pipe.setAmbientTemperatureSchedule(swh_piping_air_temp_sch)
1087
- heat_loss_pipe.setPointer(7, swh_piping_air_temp_sch.handle)
1088
- # @todo schedule type registry error for this setter
1089
- # heat_loss_pipe.setAmbientAirVelocitySchedule(model.alwaysOffDiscreteSchedule)
1090
- heat_loss_pipe.setPointer(8, swh_piping_air_velocity_sch.handle)
1091
- heat_loss_pipe.setConstruction(pipe_construction)
1092
- heat_loss_pipe.setPipeInsideDiameter(OpenStudio.convert(0.785, 'in', 'm').get)
1093
- heat_loss_pipe.setPipeLength(OpenStudio.convert(pipe_length_ft, 'ft', 'm').get)
1094
-
1095
- heat_loss_pipe.addToNode(swh_loop.demandInletNode)
1096
-
1097
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Added #{pipe_length_ft.round}ft of #{pipe_construction.name} losing heat to #{swh_piping_air_temp_f.round}F air to #{swh_loop.name}.")
1098
- return true
1099
- end
1100
- end