Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ headers
49 : #include <algorithm>
50 : #include <string>
51 : #include <utility>
52 : #include <vector>
53 :
54 : // EnergyPlus headers
55 : #include <EnergyPlus/Autosizing/Base.hh>
56 : #include <EnergyPlus/BranchNodeConnections.hh>
57 : #include <EnergyPlus/CurveManager.hh>
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataEnvironment.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/DataIPShortCuts.hh>
62 : #include <EnergyPlus/DataLoopNode.hh>
63 : #include <EnergyPlus/DataPrecisionGlobals.hh>
64 : #include <EnergyPlus/DataSizing.hh>
65 : #include <EnergyPlus/FluidProperties.hh>
66 : #include <EnergyPlus/General.hh>
67 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
68 : #include <EnergyPlus/NodeInputManager.hh>
69 : #include <EnergyPlus/OutAirNodeManager.hh>
70 : #include <EnergyPlus/OutputProcessor.hh>
71 : #include <EnergyPlus/OutputReportPredefined.hh>
72 : #include <EnergyPlus/Plant/DataPlant.hh>
73 : #include <EnergyPlus/PlantComponent.hh>
74 : #include <EnergyPlus/PlantLoopHeatPumpEIR.hh>
75 : #include <EnergyPlus/PlantUtilities.hh>
76 : #include <EnergyPlus/Psychrometrics.hh>
77 : #include <EnergyPlus/UtilityRoutines.hh>
78 :
79 : namespace EnergyPlus::EIRPlantLoopHeatPumps {
80 :
81 : constexpr Real64 Fahrenheit2Celsius(Real64 F)
82 : {
83 : return (F - 32.0) * 5.0 / 9.0;
84 : }
85 :
86 1432851 : void EIRPlantLoopHeatPump::simulate(
87 : EnergyPlusData &state, const EnergyPlus::PlantLocation &calledFromLocation, bool const FirstHVACIteration, Real64 &CurLoad, bool const RunFlag)
88 : {
89 :
90 : // Call initialize to set flow rates, run flag, and entering temperatures
91 1432851 : this->running = RunFlag;
92 :
93 1432851 : this->loadSideInletTemp = state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
94 1432851 : this->sourceSideInletTemp = state.dataLoopNodes->Node(this->sourceSideNodes.inlet).Temp;
95 1432851 : if (this->heatRecoveryAvailable) {
96 111755 : this->heatRecoveryInletTemp = state.dataLoopNodes->Node(this->heatRecoveryNodes.inlet).Temp;
97 : }
98 :
99 1432851 : if (this->waterSource) {
100 338794 : this->setOperatingFlowRatesWSHP(state, FirstHVACIteration);
101 338794 : if (calledFromLocation.loopNum == this->sourceSidePlantLoc.loopNum) { // condenser side
102 168984 : Real64 sourceQdotArg = 0.0; // pass negative if heat pump heating
103 168984 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
104 84494 : sourceQdotArg = this->sourceSideHeatTransfer * DataPrecisionGlobals::constant_minusone;
105 : } else {
106 84490 : sourceQdotArg = this->sourceSideHeatTransfer;
107 : }
108 168984 : PlantUtilities::UpdateChillerComponentCondenserSide(state,
109 : this->sourceSidePlantLoc.loopNum,
110 : this->sourceSidePlantLoc.loopSideNum,
111 : this->EIRHPType,
112 : this->sourceSideNodes.inlet,
113 : this->sourceSideNodes.outlet,
114 : this->sourceSideHeatTransfer,
115 : this->sourceSideInletTemp,
116 : this->sourceSideOutletTemp,
117 : this->sourceSideMassFlowRate,
118 : FirstHVACIteration);
119 168984 : return;
120 : }
121 1094057 : } else if (this->airSource) {
122 1094057 : this->setHeatRecoveryOperatingStatusASHP(state, FirstHVACIteration);
123 1094057 : this->setOperatingFlowRatesASHP(state, FirstHVACIteration, CurLoad);
124 :
125 1094057 : if (calledFromLocation.loopNum == this->heatRecoveryPlantLoc.loopNum) {
126 55805 : if (this->heatRecoveryAvailable) {
127 55805 : PlantUtilities::UpdateChillerComponentCondenserSide(state,
128 : this->heatRecoveryPlantLoc.loopNum,
129 : this->heatRecoveryPlantLoc.loopSideNum,
130 : this->EIRHPType,
131 : this->heatRecoveryNodes.inlet,
132 : this->heatRecoveryNodes.outlet,
133 : this->heatRecoveryRate,
134 : this->heatRecoveryInletTemp,
135 : this->heatRecoveryOutletTemp,
136 : this->heatRecoveryMassFlowRate,
137 : FirstHVACIteration);
138 : }
139 : }
140 : }
141 :
142 1263867 : if (this->running) {
143 279745 : if (this->sysControlType == ControlType::Setpoint) {
144 277327 : Real64 leavingSetpoint = state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPoint;
145 277327 : Real64 CurSpecHeat = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, loadSideInletTemp, "EIRPlantLoopHeatPump::simulate");
146 277327 : Real64 controlLoad = this->loadSideMassFlowRate * CurSpecHeat * (leavingSetpoint - loadSideInletTemp);
147 :
148 277327 : this->doPhysics(state, controlLoad);
149 : } else {
150 2418 : this->doPhysics(state, CurLoad);
151 : }
152 : } else {
153 984122 : this->resetReportingVariables();
154 : }
155 :
156 : // update report variables and nodes
157 1263867 : this->report(state);
158 : }
159 :
160 277270 : Real64 EIRPlantLoopHeatPump::getLoadSideOutletSetPointTemp(EnergyPlusData &state) const
161 : {
162 277270 : if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
163 277270 : if (this->loadSidePlantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
164 : // there will be a valid set-point on outlet
165 0 : return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPoint;
166 : } else { // use plant loop overall set-point
167 277270 : return state.dataLoopNodes->Node(this->loadSidePlantLoc.loop->TempSetPointNodeNum).TempSetPoint;
168 : }
169 0 : } else if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
170 0 : if (this->loadSidePlantLoc.comp->CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
171 : // there will be a valid set-point on outlet
172 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
173 0 : return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointHi;
174 : } else {
175 0 : return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointLo;
176 : }
177 : } else { // use plant loop overall set-point
178 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
179 0 : return state.dataLoopNodes->Node(this->loadSidePlantLoc.loop->TempSetPointNodeNum).TempSetPointHi;
180 : } else {
181 0 : return state.dataLoopNodes->Node(this->loadSidePlantLoc.loop->TempSetPointNodeNum).TempSetPointLo;
182 : }
183 : }
184 : } else {
185 : // there's no other enums for loop demand calcs, so I don't have a reasonable unit test for these
186 : // lines, they simply should not be able to get here. But a fatal is here anyway just in case,
187 : // and the lines are excluded from coverage.
188 : ShowFatalError(state, "Unsupported loop demand calculation scheme in EIR heat pump"); // LCOV_EXCL_LINE
189 : return -999; // not actually returned with Fatal Error call above // LCOV_EXCL_LINE
190 : }
191 : }
192 :
193 984951 : void EIRPlantLoopHeatPump::resetReportingVariables()
194 : {
195 984951 : this->loadSideHeatTransfer = 0.0;
196 984951 : this->loadSideEnergy = 0.0;
197 984951 : this->loadSideOutletTemp = this->loadSideInletTemp;
198 984951 : this->powerUsage = 0.0;
199 984951 : this->powerEnergy = 0.0;
200 984951 : this->sourceSideHeatTransfer = 0.0;
201 984951 : this->sourceSideOutletTemp = this->sourceSideInletTemp;
202 984951 : this->sourceSideEnergy = 0.0;
203 984951 : this->defrostEnergyRate = 0.0;
204 984951 : this->defrostEnergy = 0.0;
205 984951 : this->loadDueToDefrost = 0.0;
206 984951 : this->fractionalDefrostTime = 0.0;
207 984951 : this->partLoadRatio = 0.0;
208 984951 : this->cyclingRatio = 0.0;
209 984951 : this->defrostPowerMultiplier = 1.0;
210 984951 : this->heatRecoveryRate = 0.0;
211 984951 : this->heatRecoveryEnergy = 0.0;
212 984951 : this->heatRecoveryMassFlowRate = 0.0;
213 984951 : this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
214 984951 : this->heatRecoveryIsActive = false;
215 984951 : this->heatRecoveryOperatingStatus = 0;
216 984951 : this->thermosiphonStatus = 0;
217 984951 : }
218 :
219 338794 : void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool FirstHVACIteration)
220 : {
221 338794 : if (!this->running) {
222 336300 : this->loadSideMassFlowRate = 0.0;
223 336300 : this->sourceSideMassFlowRate = 0.0;
224 :
225 336300 : PlantUtilities::SetComponentFlowRate(
226 336300 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
227 336300 : PlantUtilities::SetComponentFlowRate(
228 336300 : state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
229 336300 : PlantUtilities::PullCompInterconnectTrigger(state,
230 336300 : this->loadSidePlantLoc,
231 336300 : this->condMassFlowRateTriggerIndex,
232 336300 : this->sourceSidePlantLoc,
233 : DataPlant::CriteriaType::MassFlowRate,
234 : this->sourceSideMassFlowRate);
235 : // Set flows if the heat pump is running
236 : } else { // the heat pump must run
237 : // apply min/max operating limits based on source side entering fluid temperature
238 2494 : if (this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) {
239 0 : this->loadSideMassFlowRate = (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->loadSideNodes.inlet).MassFlowRate : 0.0;
240 0 : this->sourceSideMassFlowRate = (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->sourceSideNodes.inlet).MassFlowRate : 0.0;
241 0 : this->running = false;
242 : } else {
243 2494 : this->loadSideMassFlowRate =
244 2494 : (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->loadSideNodes.inlet).MassFlowRate : this->loadSideDesignMassFlowRate;
245 2494 : this->sourceSideMassFlowRate = (this->heatRecoveryHeatPump) ? state.dataLoopNodes->Node(this->sourceSideNodes.inlet).MassFlowRate
246 : : this->sourceSideDesignMassFlowRate;
247 :
248 2494 : if (!FirstHVACIteration && this->flowMode == DataPlant::FlowMode::VariableSpeedPump) {
249 16 : if ((this->loadVSBranchPump || this->loadVSLoopPump) && !this->heatRecoveryHeatPump) {
250 0 : this->loadSideMassFlowRate *= std::max(this->partLoadRatio, this->minimumPLR);
251 0 : if (this->loadVSBranchPump) {
252 0 : this->loadSideMassFlowRate = std::max(this->loadSideMassFlowRate, this->loadVSPumpMinLimitMassFlow);
253 : }
254 : }
255 16 : if ((this->sourceVSBranchPump || this->sourceVSLoopPump) && !this->heatRecoveryHeatPump) {
256 0 : this->sourceSideMassFlowRate *= std::max(this->partLoadRatio, this->minimumPLR);
257 0 : if (this->sourceVSBranchPump) {
258 0 : this->sourceSideMassFlowRate = std::max(this->sourceSideMassFlowRate, this->sourceVSPumpMinLimitMassFlow);
259 : }
260 : }
261 : }
262 :
263 2494 : PlantUtilities::SetComponentFlowRate(
264 2494 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
265 2494 : PlantUtilities::SetComponentFlowRate(
266 2494 : state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
267 : }
268 :
269 : // if there's no flow in one, try to turn the entire heat pump off
270 2494 : if (this->loadSideMassFlowRate <= 0.0 || this->sourceSideMassFlowRate <= 0.0) {
271 849 : this->loadSideMassFlowRate = 0.0;
272 849 : this->sourceSideMassFlowRate = 0.0;
273 849 : this->running = false;
274 849 : PlantUtilities::SetComponentFlowRate(
275 849 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
276 849 : PlantUtilities::SetComponentFlowRate(
277 849 : state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
278 : }
279 2494 : PlantUtilities::PullCompInterconnectTrigger(state,
280 2494 : this->loadSidePlantLoc,
281 2494 : this->condMassFlowRateTriggerIndex,
282 2494 : this->sourceSidePlantLoc,
283 : DataPlant::CriteriaType::MassFlowRate,
284 : this->sourceSideMassFlowRate);
285 : }
286 338794 : }
287 :
288 1092411 : void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, Real64 const currentLoad)
289 : {
290 1092411 : if (!this->running) {
291 788665 : this->loadSideMassFlowRate = 0.0;
292 788665 : this->sourceSideMassFlowRate = 0.0;
293 788665 : PlantUtilities::SetComponentFlowRate(
294 788665 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
295 788665 : if (this->heatRecoveryAvailable) {
296 : // set the HR flow to zero if the heat pump is off
297 24485 : this->heatRecoveryMassFlowRate = 0.0;
298 24485 : PlantUtilities::SetComponentFlowRate(
299 24485 : state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
300 : }
301 : // Set flows if the heat pump is running
302 : } else { // the heat pump must run
303 : // apply min/max operating limits based on source side entering fluid temperature
304 303746 : if ((this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) &&
305 0 : !this->heatRecoveryIsActive) {
306 0 : this->loadSideMassFlowRate = 0.0;
307 0 : this->sourceSideMassFlowRate = 0.0;
308 0 : this->running = false;
309 0 : PlantUtilities::SetComponentFlowRate(
310 0 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
311 0 : if (this->heatRecoveryAvailable) {
312 0 : this->heatRecoveryMassFlowRate = 0.0;
313 0 : PlantUtilities::SetComponentFlowRate(
314 0 : state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
315 : }
316 : } else {
317 303746 : this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
318 303746 : this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
319 :
320 303746 : if (!FirstHVACIteration && this->flowMode == DataPlant::FlowMode::VariableSpeedPump) {
321 179682 : if (this->loadVSBranchPump || this->loadVSLoopPump) {
322 179682 : this->loadSideMassFlowRate *= std::max(this->partLoadRatio, this->minimumPLR);
323 179682 : if (this->loadVSBranchPump) {
324 0 : this->loadSideMassFlowRate = std::max(this->loadSideMassFlowRate, this->loadVSPumpMinLimitMassFlow);
325 : }
326 : }
327 : }
328 :
329 303746 : PlantUtilities::SetComponentFlowRate(
330 303746 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
331 :
332 303746 : if (this->heatRecoveryIsActive) {
333 87270 : this->heatRecoveryMassFlowRate = this->heatRecoveryDesignMassFlowRate;
334 87270 : PlantUtilities::SetComponentFlowRate(
335 87270 : state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
336 : }
337 : }
338 :
339 : // if there's no flow in one, try to turn the entire heat pump off
340 303746 : if (this->loadSideMassFlowRate <= 0.0) {
341 25647 : this->loadSideMassFlowRate = 0.0;
342 25647 : this->sourceSideMassFlowRate = 0.0;
343 25647 : this->running = false;
344 25647 : PlantUtilities::SetComponentFlowRate(
345 25647 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
346 : // if heat recovery is connected to plant loop
347 25647 : if (this->heatRecoveryAvailable) {
348 24310 : this->heatRecoveryMassFlowRate = 0.0;
349 24310 : PlantUtilities::SetComponentFlowRate(
350 24310 : state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
351 : }
352 : }
353 303746 : if (this->heatRecoveryAvailable) {
354 87270 : PlantUtilities::PullCompInterconnectTrigger(state,
355 87270 : this->loadSidePlantLoc,
356 87270 : this->condMassFlowRateTriggerIndex,
357 87270 : this->heatRecoveryPlantLoc,
358 : DataPlant::CriteriaType::MassFlowRate,
359 : this->heatRecoveryMassFlowRate);
360 : }
361 : }
362 1092411 : }
363 :
364 1646 : void EIRFuelFiredHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, Real64 const currentLoad)
365 : {
366 1646 : if (!this->running) {
367 836 : this->loadSideMassFlowRate = 0.0;
368 836 : this->sourceSideMassFlowRate = 0.0;
369 836 : PlantUtilities::SetComponentFlowRate(
370 836 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
371 836 : if (this->heatRecoveryAvailable) {
372 : // set the HR flow to zero if the heat pump is off
373 0 : this->heatRecoveryMassFlowRate = 0.0;
374 0 : PlantUtilities::SetComponentFlowRate(
375 0 : state, this->heatRecoveryMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet, this->heatRecoveryPlantLoc);
376 : }
377 : } else { // the heat pump must run
378 810 : this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
379 810 : this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
380 :
381 810 : if (!FirstHVACIteration && this->flowMode == DataPlant::FlowMode::LeavingSetpointModulated) {
382 0 : auto &thisInletNode = state.dataLoopNodes->Node(this->loadSideNodes.inlet);
383 0 : auto &thisOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet);
384 0 : Real64 FFHPDeltaTemp = 0.0;
385 0 : Real64 CpLoad = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, thisInletNode.Temp, "PLFFHPEIR::simulate()");
386 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating) {
387 0 : if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
388 0 : FFHPDeltaTemp = thisOutletNode.TempSetPoint - thisInletNode.Temp;
389 : } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
390 0 : FFHPDeltaTemp = thisOutletNode.TempSetPointLo - thisInletNode.Temp;
391 : }
392 0 : this->loadSideOutletTemp = FFHPDeltaTemp + thisInletNode.Temp;
393 0 : if ((FFHPDeltaTemp > 0.0) && currentLoad > 0.0) {
394 0 : this->loadSideMassFlowRate = currentLoad / (CpLoad * FFHPDeltaTemp);
395 0 : this->loadSideMassFlowRate = min(this->loadSideDesignMassFlowRate, this->loadSideMassFlowRate);
396 : } else {
397 0 : this->loadSideOutletTemp = thisInletNode.Temp;
398 0 : this->loadSideMassFlowRate = 0.0;
399 : }
400 0 : } else if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
401 0 : if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
402 0 : FFHPDeltaTemp = thisInletNode.Temp - thisOutletNode.TempSetPoint;
403 : } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
404 0 : FFHPDeltaTemp = thisInletNode.Temp - thisOutletNode.TempSetPointHi;
405 : }
406 0 : this->loadSideOutletTemp = thisInletNode.Temp - FFHPDeltaTemp;
407 0 : if ((FFHPDeltaTemp > 0.0) && std::abs(currentLoad) > 0.0) {
408 0 : this->loadSideMassFlowRate = std::abs(currentLoad) / (CpLoad * FFHPDeltaTemp);
409 0 : this->loadSideMassFlowRate = min(this->loadSideDesignMassFlowRate, this->loadSideMassFlowRate);
410 : } else {
411 0 : this->loadSideOutletTemp = thisInletNode.Temp;
412 0 : this->loadSideMassFlowRate = 0.0;
413 : }
414 : }
415 : }
416 810 : PlantUtilities::SetComponentFlowRate(
417 810 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
418 :
419 : // if there's no flow in one, try to turn the entire heat pump off
420 810 : if (this->loadSideMassFlowRate <= 0.0) {
421 0 : this->loadSideMassFlowRate = 0.0;
422 0 : this->sourceSideMassFlowRate = 0.0;
423 0 : this->running = false;
424 0 : PlantUtilities::SetComponentFlowRate(
425 0 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
426 : }
427 : }
428 1646 : }
429 :
430 278935 : void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad)
431 : {
432 : // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but
433 : // I'm not sure we can count on that so we will do one check here to make sure we don't calculate things badly
434 278935 : if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling && currentLoad >= 0.0) ||
435 277280 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating && currentLoad <= 0.0)) {
436 1665 : this->resetReportingVariables();
437 1665 : return;
438 : }
439 :
440 : // dispatch to specific physics calculations based on the heat pump type
441 277270 : if (this->waterSource) {
442 836 : this->doPhysicsWSHP(state, currentLoad);
443 276434 : } else if (this->airSource) {
444 276434 : this->doPhysicsASHP(state, currentLoad);
445 : }
446 : }
447 :
448 836 : void EIRPlantLoopHeatPump::doPhysicsWSHP(EnergyPlusData &state, Real64 currentLoad)
449 : {
450 :
451 : // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range
452 :
453 836 : Real64 availableCapacity = this->referenceCapacity;
454 836 : Real64 partLoadRatio = 0.0;
455 :
456 836 : this->calcAvailableCapacity(state, currentLoad, availableCapacity, partLoadRatio);
457 836 : this->setPartLoadAndCyclingRatio(state, partLoadRatio);
458 :
459 : // evaluate the actual current operating load side heat transfer rate
460 836 : this->calcLoadSideHeatTransfer(state, availableCapacity);
461 :
462 : // no defrost calculation for WSHP
463 : // calculate power usage from EIR curves
464 836 : this->calcPowerUsage(state);
465 :
466 : // evaluate the source side heat transfer rate
467 836 : this->calcSourceSideHeatTransferWSHP(state);
468 836 : }
469 :
470 276434 : void EIRPlantLoopHeatPump::doPhysicsASHP(EnergyPlusData &state, Real64 currentLoad)
471 : {
472 : // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range
473 :
474 276434 : Real64 availableCapacity = this->referenceCapacity;
475 276434 : Real64 partLoadRatio = 0.0;
476 :
477 276434 : this->calcAvailableCapacity(state, currentLoad, availableCapacity, partLoadRatio);
478 276434 : this->setPartLoadAndCyclingRatio(state, partLoadRatio);
479 :
480 : // do defrost calculation if applicable
481 276434 : this->doDefrost(state, availableCapacity);
482 :
483 : // evaluate the actual current operating load side heat transfer rate
484 276434 : this->calcLoadSideHeatTransfer(state, availableCapacity);
485 :
486 : // calculate power usage from EIR curves
487 276434 : this->calcPowerUsage(state);
488 :
489 276434 : if (this->heatRecoveryIsActive) {
490 : // evaluate the heat recovery side heat transfer rate
491 62960 : this->calcHeatRecoveryHeatTransferASHP(state);
492 : } else {
493 : // evaluate the source side heat transfer rate
494 213474 : this->calcSourceSideHeatTransferASHP(state);
495 : }
496 276434 : }
497 :
498 277270 : void EIRPlantLoopHeatPump::calcAvailableCapacity(EnergyPlusData &state, Real64 const currentLoad, Real64 &availableCapacity, Real64 &partLoadRatio)
499 : {
500 : // get setpoint on the load side outlet
501 277270 : Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
502 277270 : Real64 originalLoadSideOutletSPTemp = loadSideOutletSetpointTemp;
503 :
504 : // add free cooling at some point, compressor is off during free cooling, temp limits restrict free cooling range
505 :
506 277270 : Real64 capacityModifierFuncTemp = 1.0;
507 277270 : bool waterTempExceeded = false;
508 :
509 : // evaluate capacity modifier curve and determine load side heat transfer
510 : // any adjustment to outlet water temp set point requires some form of iteration
511 325350 : for (int loop = 0; loop < 2; ++loop) {
512 :
513 301310 : if (this->heatRecoveryIsActive) {
514 62960 : if (this->heatRecoveryCapFTempCurveIndex > 0) {
515 62960 : capacityModifierFuncTemp =
516 62960 : Curve::CurveValue(state, this->heatRecoveryCapFTempCurveIndex, loadSideOutletSetpointTemp, this->heatRecoveryInletTemp);
517 : } else {
518 0 : capacityModifierFuncTemp =
519 0 : Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->heatRecoveryInletTemp);
520 : }
521 62960 : availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
522 : } else {
523 238350 : capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->sourceSideInletTemp);
524 238350 : availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
525 : // apply air source HP dry air heating capacity correction
526 238350 : availableCapacity *= heatingCapacityModifierASHP(state);
527 : }
528 :
529 301310 : if (availableCapacity > 0) {
530 301310 : partLoadRatio = std::clamp(std::abs(currentLoad) / availableCapacity, 0.0, 1.0);
531 : }
532 :
533 301310 : if (this->minSupplyWaterTempCurveIndex > 0) {
534 58848 : Real64 minWaterTemp = Curve::CurveValue(state, this->minSupplyWaterTempCurveIndex, state.dataEnvrn->OutDryBulbTemp);
535 58848 : if (loadSideOutletSetpointTemp < minWaterTemp) {
536 0 : loadSideOutletSetpointTemp = originalLoadSideOutletSPTemp + (1.0 - partLoadRatio) * (minWaterTemp - originalLoadSideOutletSPTemp);
537 0 : waterTempExceeded = true;
538 : }
539 : }
540 301310 : if (this->maxSupplyWaterTempCurveIndex > 0) {
541 58848 : Real64 maxWaterTemp = Curve::CurveValue(state, this->maxSupplyWaterTempCurveIndex, state.dataEnvrn->OutDryBulbTemp);
542 58848 : if (loadSideOutletSetpointTemp > maxWaterTemp) {
543 38418 : loadSideOutletSetpointTemp = maxWaterTemp + (1.0 - partLoadRatio) * (originalLoadSideOutletSPTemp - maxWaterTemp);
544 38418 : waterTempExceeded = true;
545 : }
546 : }
547 301310 : if (this->heatRecoveryHeatPump) {
548 36 : this->calcLoadSideHeatTransfer(state, availableCapacity);
549 36 : this->calcPowerUsage(state);
550 36 : Real64 sourceSideHeatTransfer = this->calcQsource(availableCapacity * partLoadRatio, this->powerUsage);
551 : // check to see if souce side outlet temp exceeds limit and reduce PLR if necessary
552 36 : Real64 const CpSrc = this->sourceSidePlantLoc.loop->glycol->getSpecificHeat(
553 : state, this->sourceSideInletTemp, "EIRPlantLoopHeatPump::calcLoadSideHeatTransfer()");
554 36 : Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
555 36 : Real64 const tempSourceOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, sourceSideHeatTransfer / sourceMCp);
556 36 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating && tempSourceOutletTemp < this->minSourceTempLimit) {
557 36 : partLoadRatio *= (this->sourceSideInletTemp - this->minSourceTempLimit) / (this->sourceSideInletTemp - tempSourceOutletTemp);
558 0 : } else if (tempSourceOutletTemp > this->maxSourceTempLimit) {
559 0 : partLoadRatio *= (this->maxSourceTempLimit - this->sourceSideInletTemp) / (tempSourceOutletTemp - this->sourceSideInletTemp);
560 : }
561 : }
562 301310 : if (!waterTempExceeded) {
563 253230 : break;
564 : }
565 : }
566 :
567 : // check the curve values, reset to zero if negative
568 277270 : if (this->heatRecoveryIsActive && this->heatRecoveryCapFTempCurveIndex > 0) {
569 62960 : this->heatRecoveryCapModFTCurveCheck(state, loadSideOutletSetpointTemp, capacityModifierFuncTemp);
570 : } else {
571 214310 : this->capModFTCurveCheck(state, loadSideOutletSetpointTemp, capacityModifierFuncTemp);
572 : }
573 277270 : }
574 :
575 238350 : Real64 EIRPlantLoopHeatPump::heatingCapacityModifierASHP(EnergyPlusData &state) const
576 : {
577 238350 : Real64 constexpr RH90 = 90.0;
578 238350 : Real64 constexpr RH60 = 60.0;
579 238350 : Real64 constexpr rangeRH = 30.0;
580 :
581 : // apply heating mode dry outdoor (evaporator) coil correction factor for air-cooled equipment
582 238350 : if (this->capacityDryAirCurveIndex > 0 && this->airSource && state.dataEnvrn->OutRelHum < RH90) { // above 90% RH yields full capacity
583 5280 : Real64 dryCorrectionFactor = std::min(1.0, Curve::CurveValue(state, this->capacityDryAirCurveIndex, state.dataEnvrn->OutDryBulbTemp));
584 5280 : if (state.dataEnvrn->OutRelHum <= RH60) {
585 : // dry heating capacity correction factor is a function of outdoor dry-bulb temperature
586 0 : return dryCorrectionFactor;
587 : } else {
588 : // interpolation of heating capacity between wet and dry is based on outdoor relative humidity over 60%-90% range
589 5280 : Real64 semiDryFactor = dryCorrectionFactor + (1.0 - dryCorrectionFactor) * (1.0 - ((RH90 - state.dataEnvrn->OutRelHum) / rangeRH));
590 5280 : return semiDryFactor;
591 : }
592 : } else {
593 : // no correction needed, use full capacity
594 233070 : return 1.0;
595 : }
596 : }
597 :
598 277270 : void EIRPlantLoopHeatPump::setPartLoadAndCyclingRatio([[maybe_unused]] EnergyPlusData &state, Real64 &partLoadRatio)
599 : {
600 : // Initialize cycling ratio to 1.0
601 277270 : Real64 cyclingRatio = 1.0;
602 :
603 : // Check if part load ratio is below the minimum threshold
604 277270 : if (partLoadRatio < this->minimumPLR) {
605 : // Adjust cycling ratio and set part load ratio to minimum
606 124258 : cyclingRatio = partLoadRatio / this->minimumPLR;
607 124258 : partLoadRatio = this->minimumPLR;
608 : }
609 :
610 : // update class member variables
611 277270 : this->partLoadRatio = partLoadRatio;
612 277270 : this->cyclingRatio = cyclingRatio;
613 277270 : }
614 :
615 277306 : void EIRPlantLoopHeatPump::calcLoadSideHeatTransfer(EnergyPlusData &state, Real64 const availableCapacity)
616 : {
617 : // evaluate the actual current operating load side heat transfer rate
618 277306 : Real64 CpLoad = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(
619 277306 : state, state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp, "EIRPlantLoopHeatPump::calcLoadSideHeatTransfer()");
620 :
621 277306 : Real64 const operatingPLR = this->partLoadRatio * this->cyclingRatio;
622 277306 : this->loadSideHeatTransfer = availableCapacity * operatingPLR;
623 :
624 : // calculate load side outlet conditions
625 277306 : Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad;
626 277306 : this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp);
627 :
628 : // now what to do here if outlet water temp exceeds limit based on HW supply temp limit curves?
629 : // currentLoad will be met and there should? be some adjustment based on outlet water temp limit?
630 277306 : }
631 :
632 277306 : void EIRPlantLoopHeatPump::calcPowerUsage(EnergyPlusData &state)
633 : {
634 : // calculate power usage from EIR curves
635 277306 : Real64 eirModifierFuncTemp = 0.0;
636 277306 : if (this->airSource && this->heatRecoveryIsActive) {
637 62960 : if (this->heatRecoveryEIRFTempCurveIndex > 0) {
638 62960 : eirModifierFuncTemp =
639 62960 : Curve::CurveValue(state, this->heatRecoveryEIRFTempCurveIndex, this->loadSideOutletTemp, this->heatRecoveryInletTemp);
640 : // check cap func of temp curve value and reset to zero if negative
641 62960 : this->heatRecoveryEIRModCurveCheck(state, eirModifierFuncTemp);
642 : } else {
643 0 : eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp);
644 : // check cap func of temp curve value and reset to zero if negative
645 0 : this->eirModCurveCheck(state, eirModifierFuncTemp);
646 : }
647 : } else {
648 214346 : eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp);
649 : // check curves value and resets to zero if negative
650 214346 : this->eirModCurveCheck(state, eirModifierFuncTemp);
651 : }
652 277306 : Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, this->partLoadRatio);
653 : // check EIR func of PLR curve value and resets to zero if negative
654 277306 : this->eirModFPLRCurveCheck(state, eirModifierFuncPLR);
655 :
656 : // compute power usage
657 277306 : if (this->thermosiphonDisabled(state)) {
658 268836 : this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp *
659 268836 : this->defrostPowerMultiplier * this->cyclingRatio;
660 : }
661 277306 : }
662 :
663 836 : void EIRPlantLoopHeatPump::calcSourceSideHeatTransferWSHP(EnergyPlusData &state)
664 : {
665 :
666 : // energy balance on heat pump
667 836 : this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
668 :
669 : // calculate source side outlet conditions
670 836 : Real64 const CpSrc = this->sourceSidePlantLoc.loop->glycol->getSpecificHeat(
671 : state, this->sourceSideInletTemp, "EIRPlantLoopHeatPump::calcSourceSideHeatTransferWSHP()");
672 836 : Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
673 836 : this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
674 :
675 836 : if (this->waterSource && abs(this->sourceSideOutletTemp - this->sourceSideInletTemp) > 100.0) { // whoaa out of range happenings on water loop
676 : //
677 : // TODO setup recurring error warning?
678 : // lets do something different than fatal the simulation
679 0 : if ((this->sourceSideMassFlowRate / this->sourceSideDesignMassFlowRate) < 0.01) { // current source side flow is 1% of design max
680 : // just send it all to skin losses and leave the fluid temperature alone
681 0 : this->sourceSideOutletTemp = this->sourceSideInletTemp;
682 0 : } else if (this->sourceSideOutletTemp > this->sourceSideInletTemp) {
683 0 : this->sourceSideOutletTemp = this->sourceSideInletTemp + 100.0; // cap it at 100C delta
684 :
685 0 : } else if (this->sourceSideOutletTemp < this->sourceSideInletTemp) {
686 0 : this->sourceSideOutletTemp = this->sourceSideInletTemp - 100.0; // cap it at 100C delta
687 : }
688 : }
689 836 : }
690 :
691 213474 : void EIRPlantLoopHeatPump::calcSourceSideHeatTransferASHP(EnergyPlusData &state)
692 : {
693 : // energy balance on heat pump
694 213474 : this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
695 :
696 : // calculate source side outlet conditions
697 213474 : Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
698 213474 : Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
699 213474 : this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
700 213474 : if (this->heatRecoveryAvailable && !this->heatRecoveryIsActive) {
701 : // reset the HR report variables
702 0 : this->heatRecoveryRate = 0.0;
703 0 : this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
704 : }
705 213474 : }
706 :
707 62960 : void EIRPlantLoopHeatPump::calcHeatRecoveryHeatTransferASHP(EnergyPlusData &state)
708 : {
709 : // energy balance on heat pump
710 62960 : this->heatRecoveryRate = this->calcQheatRecovery(this->loadSideHeatTransfer, this->powerUsage);
711 62960 : Real64 heatRecoverRateTot = this->heatRecoveryRate;
712 :
713 : // calculate heat recovery side outlet conditions
714 62960 : Real64 const CpHR = this->heatRecoveryPlantLoc.loop->glycol->getSpecificHeat(
715 : state, this->heatRecoveryInletTemp, "EIRPlantLoopHeatPump::calcHeatRecoveryHeatTransferASHP()");
716 62960 : Real64 const hRecoveryMCp = this->heatRecoveryMassFlowRate * CpHR;
717 62960 : if (this->heatRecoveryMassFlowRate > 0.0) {
718 62960 : this->heatRecoveryOutletTemp = this->calcHROutletTemp(this->heatRecoveryInletTemp, this->heatRecoveryRate / hRecoveryMCp);
719 : } else {
720 0 : this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
721 0 : this->heatRecoveryRate = 0.0;
722 : }
723 : // limit the HR HW outlet temperature to the maximum allowed (HW Recovery)
724 62960 : if (this->heatRecoveryOutletTemp > this->maxHeatRecoveryTempLimit) {
725 62846 : if (this->heatRecoveryInletTemp < this->maxHeatRecoveryTempLimit) {
726 62842 : this->heatRecoveryOutletTemp = this->maxHeatRecoveryTempLimit;
727 62842 : this->heatRecoveryRate = hRecoveryMCp * (this->heatRecoveryOutletTemp - this->heatRecoveryInletTemp);
728 : } else {
729 4 : this->heatRecoveryRate = 0.0;
730 4 : this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
731 : }
732 : }
733 : // limit the HR CW outlet temp to the minimum allowed (CW Recovery)
734 62960 : if (this->heatRecoveryOutletTemp < this->minHeatRecoveryTempLimit) {
735 0 : if (this->heatRecoveryInletTemp > this->minHeatRecoveryTempLimit) {
736 0 : this->heatRecoveryOutletTemp = this->minHeatRecoveryTempLimit;
737 0 : this->heatRecoveryRate = hRecoveryMCp * (this->heatRecoveryInletTemp - this->heatRecoveryOutletTemp);
738 : } else {
739 0 : this->heatRecoveryRate = 0.0;
740 0 : this->heatRecoveryOutletTemp = this->heatRecoveryInletTemp;
741 : }
742 : }
743 : // report the net heat balance as source side heat transfer
744 62960 : Real64 heatReoveryRateUnused = std::max(0.0, (heatRecoverRateTot - this->heatRecoveryRate));
745 62960 : if (heatReoveryRateUnused > 0.0) {
746 62846 : this->sourceSideHeatTransfer = heatReoveryRateUnused;
747 : // calculate source side outlet conditions
748 62846 : Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
749 62846 : Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
750 62846 : this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
751 : } else {
752 : // reset the source side report variables
753 114 : this->sourceSideHeatTransfer = 0.0;
754 114 : this->sourceSideOutletTemp = this->sourceSideInletTemp;
755 : }
756 62960 : }
757 :
758 1094057 : void EIRPlantLoopHeatPump::setHeatRecoveryOperatingStatusASHP([[maybe_unused]] EnergyPlusData &state, [[maybe_unused]] bool FirstHVACIteration)
759 : {
760 1094057 : if (!this->running) {
761 789501 : if (this->heatRecoveryAvailable) {
762 : // set the HR operation off
763 24485 : this->heatRecoveryIsActive = false;
764 24485 : this->heatRecoveryOperatingStatus = 0;
765 : }
766 : } else { // the heat pump must be running
767 304556 : if (this->heatRecoveryAvailable) {
768 : // apply min/max HR operating limits based on heat recovery entering fluid temperature
769 87270 : if (this->minHeatRecoveryTempLimit > this->heatRecoveryInletTemp || this->maxHeatRecoveryTempLimit < this->heatRecoveryInletTemp) {
770 : // set the HR operation off
771 0 : this->heatRecoveryIsActive = false;
772 0 : this->heatRecoveryOperatingStatus = 0;
773 : } else {
774 : // set the HR operation on
775 87270 : this->heatRecoveryIsActive = true;
776 87270 : this->heatRecoveryOperatingStatus = 1;
777 : }
778 : }
779 : }
780 1094057 : }
781 :
782 214310 : void EIRPlantLoopHeatPump::capModFTCurveCheck(EnergyPlusData &state, const Real64 loadSideOutletSetpointTemp, Real64 &capacityModifierFuncTemp)
783 : {
784 214310 : if (capacityModifierFuncTemp < 0.0) {
785 0 : if (this->capModFTErrorIndex == 0) {
786 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
787 0 : ShowContinueError(state,
788 0 : format(" Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).", capacityModifierFuncTemp));
789 0 : ShowContinueError(state,
790 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
791 : loadSideOutletSetpointTemp,
792 0 : this->sourceSideInletTemp));
793 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
794 : }
795 0 : ShowRecurringWarningErrorAtEnd(state,
796 0 : format("{} \"{}\": Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
797 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
798 0 : this->name),
799 0 : this->capModFTErrorIndex,
800 : capacityModifierFuncTemp,
801 : capacityModifierFuncTemp);
802 0 : capacityModifierFuncTemp = 0.0;
803 : }
804 214310 : }
805 :
806 62960 : void EIRPlantLoopHeatPump::heatRecoveryCapModFTCurveCheck(EnergyPlusData &state,
807 : const Real64 loadSideOutletSetpointTemp,
808 : Real64 &capacityModifierFuncTemp)
809 : {
810 62960 : if (capacityModifierFuncTemp < 0.0) {
811 0 : if (this->heatRecCapModFTErrorIndex == 0) {
812 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
813 0 : ShowContinueError(state,
814 0 : format(" Heat Recovery mode Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).",
815 : capacityModifierFuncTemp));
816 0 : ShowContinueError(
817 : state,
818 0 : format(
819 : " Negative value occurs using a load side water temperature of {:.2T}C and heat recovery entering water temperature of {:.2T}C.",
820 : loadSideOutletSetpointTemp,
821 0 : this->heatRecoveryInletTemp));
822 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
823 : }
824 0 : ShowRecurringWarningErrorAtEnd(
825 : state,
826 0 : format("{} \"{}\": Heat Recovery mode Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
827 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
828 0 : this->name),
829 0 : this->heatRecCapModFTErrorIndex,
830 : capacityModifierFuncTemp,
831 : capacityModifierFuncTemp);
832 0 : capacityModifierFuncTemp = 0.0;
833 : }
834 62960 : }
835 :
836 214346 : void EIRPlantLoopHeatPump::eirModCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncTemp)
837 : {
838 214346 : if (eirModifierFuncTemp < 0.0) {
839 0 : if (this->eirModFTErrorIndex == 0) {
840 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
841 0 : ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
842 0 : ShowContinueError(state,
843 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
844 0 : this->loadSideOutletTemp,
845 0 : this->sourceSideInletTemp));
846 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
847 : }
848 0 : ShowRecurringWarningErrorAtEnd(state,
849 0 : format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...",
850 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
851 0 : this->name),
852 0 : this->eirModFTErrorIndex,
853 : eirModifierFuncTemp,
854 : eirModifierFuncTemp);
855 0 : eirModifierFuncTemp = 0.0;
856 : }
857 214346 : }
858 :
859 62960 : void EIRPlantLoopHeatPump::heatRecoveryEIRModCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncTemp)
860 : {
861 62960 : if (eirModifierFuncTemp < 0.0) {
862 0 : if (this->heatRecEIRModFTErrorIndex == 0 && heatRecoveryEIRFTempCurveIndex > 0) {
863 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
864 0 : ShowContinueError(
865 0 : state, format(" Heat Recovery mode EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
866 0 : ShowContinueError(
867 : state,
868 0 : format(
869 : " Negative value occurs using a load side water temperature of {:.2T}C and heat recovery entering water temperature of {:.2T}C.",
870 0 : this->loadSideOutletTemp,
871 0 : this->heatRecoveryInletTemp));
872 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
873 : }
874 0 : ShowRecurringWarningErrorAtEnd(
875 : state,
876 0 : format("{} \"{}\": Heat Recovery mode EIR Modifier curve (function of Temperatures) output is negative warning continues...",
877 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
878 0 : this->name),
879 0 : this->eirModFTErrorIndex,
880 : eirModifierFuncTemp,
881 : eirModifierFuncTemp);
882 0 : eirModifierFuncTemp = 0.0;
883 : }
884 62960 : }
885 :
886 277306 : void EIRPlantLoopHeatPump::eirModFPLRCurveCheck(EnergyPlusData &state, Real64 &eirModifierFuncPLR)
887 : {
888 277306 : if (eirModifierFuncPLR < 0.0) {
889 0 : if (this->eirModFPLRErrorIndex == 0) {
890 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
891 0 : ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR));
892 0 : ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", this->partLoadRatio));
893 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
894 : }
895 0 : ShowRecurringWarningErrorAtEnd(state,
896 0 : format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...",
897 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
898 0 : this->name),
899 0 : this->eirModFPLRErrorIndex,
900 : eirModifierFuncPLR,
901 : eirModifierFuncPLR);
902 0 : eirModifierFuncPLR = 0.0;
903 : }
904 277306 : }
905 :
906 276434 : void EIRPlantLoopHeatPump::doDefrost(EnergyPlusData &state, Real64 &availableCapacity)
907 : {
908 : // Initializing defrost adjustment factors
909 276434 : Real64 InputPowerMultiplier = 1.0;
910 276434 : Real64 HeatingCapacityMultiplier = 1.0;
911 :
912 : // Check outdoor temperature to determine of defrost is active
913 276434 : if (this->defrostAvailable && state.dataEnvrn->OutDryBulbTemp <= this->maxOutdoorTemperatureDefrost) {
914 : // Calculate defrost adjustment factors depending on defrost control type
915 : // Calculate delta w through outdoor coil by assuming a coil temp of 0.82*DBT-9.7(F) per DOE2.1E
916 24056 : Real64 OutdoorCoilT = 0.82 * state.dataEnvrn->OutDryBulbTemp - 8.589;
917 : Real64 OutdoorCoildw =
918 24056 : max(1.0e-6, (state.dataEnvrn->OutHumRat - Psychrometrics::PsyWFnTdpPb(state, OutdoorCoilT, state.dataEnvrn->OutBaroPress)));
919 24056 : if (this->defrostStrategy == DefrostControl::Timed) {
920 0 : if (this->defrostTime > 0.0) {
921 0 : this->fractionalDefrostTime = this->defrostTime; // DefrostTime in hours
922 0 : HeatingCapacityMultiplier = 0.909 - 107.33 * OutdoorCoildw;
923 0 : InputPowerMultiplier = 0.90 - 36.45 * OutdoorCoildw;
924 0 : this->loadDueToDefrost =
925 0 : (0.01 * this->fractionalDefrostTime) * (7.222 - state.dataEnvrn->OutDryBulbTemp) * (this->referenceCapacity / 1.01667);
926 0 : Real64 defrostEIRFT = 1.0 / this->referenceCOP;
927 0 : if (defrostEIRFTIndex > 0) {
928 0 : defrostEIRFT = Curve::CurveValue(
929 0 : state, this->defrostEIRFTIndex, max(15.555, state.dataEnvrn->OutWetBulbTemp), max(15.555, state.dataEnvrn->OutDryBulbTemp));
930 : }
931 0 : this->defrostEnergyRate = defrostEIRFT * (this->referenceCapacity / 1.01667) * this->fractionalDefrostTime;
932 : } else {
933 0 : this->loadDueToDefrost = 0.0;
934 0 : this->defrostEnergyRate = 0.0;
935 0 : this->fractionalDefrostTime = 0.0;
936 : }
937 24056 : } else if (this->defrostStrategy == DefrostControl::OnDemand) {
938 0 : this->fractionalDefrostTime = 1.0 / (1.0 + 0.01446 / OutdoorCoildw);
939 0 : HeatingCapacityMultiplier = 0.875 * (1.0 - this->fractionalDefrostTime);
940 0 : InputPowerMultiplier = 0.954 * (1.0 - this->fractionalDefrostTime);
941 0 : this->loadDueToDefrost =
942 0 : (0.01 * this->fractionalDefrostTime) * (7.222 - state.dataEnvrn->OutDryBulbTemp) * (this->referenceCapacity / 1.01667);
943 0 : Real64 defrostEIRFT = 0.0;
944 0 : if (defrostEIRFTIndex > 0) {
945 0 : defrostEIRFT = Curve::CurveValue(
946 0 : state, this->defrostEIRFTIndex, max(15.555, state.dataEnvrn->OutWetBulbTemp), max(15.555, state.dataEnvrn->OutDryBulbTemp));
947 : }
948 0 : this->defrostEnergyRate = defrostEIRFT * (this->referenceCapacity / 1.01667) * this->fractionalDefrostTime;
949 24056 : } else if (this->defrostStrategy == DefrostControl::TimedEmpirical) {
950 : // cycles of defrost per hour
951 24056 : Real64 thisHourDefrostCycles = Curve::CurveValue(state, this->defrostFreqCurveIndex, state.dataEnvrn->OutDryBulbTemp);
952 : // is directly proportional to the ratio of capacity used for that hour (PLR)
953 24056 : Real64 const operatingPLR = this->partLoadRatio * this->cyclingRatio;
954 24056 : thisHourDefrostCycles *= operatingPLR;
955 : // fraction of heat load per cycle of defrost
956 24056 : Real64 thisHourDefrostHeatLoad = 0.0;
957 24056 : if (this->defrostLoadCurveDims == 2) { // BiQuadratic
958 : thisHourDefrostHeatLoad =
959 0 : Curve::CurveValue(state, this->defrostHeatLoadCurveIndex, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutDryBulbTemp);
960 : } else {
961 24056 : thisHourDefrostHeatLoad = Curve::CurveValue(state, this->defrostHeatLoadCurveIndex, state.dataEnvrn->OutDryBulbTemp);
962 : }
963 : // heat load is applied to full load (not rated) and is proportional to defrost cycles per hour
964 24056 : this->loadDueToDefrost = availableCapacity * thisHourDefrostHeatLoad * thisHourDefrostCycles;
965 : // electric input fraction due to defrost
966 24056 : Real64 defrostHeatEnergyFraction = 0.0;
967 24056 : if (this->defrostEnergyCurveDims == 2) { // BiQuadratic
968 : defrostHeatEnergyFraction =
969 0 : Curve::CurveValue(state, this->defrostHeatEnergyCurveIndex, state.dataEnvrn->OutWetBulbTemp, state.dataEnvrn->OutDryBulbTemp);
970 : } else {
971 24056 : defrostHeatEnergyFraction = Curve::CurveValue(state, this->defrostHeatEnergyCurveIndex, state.dataEnvrn->OutDryBulbTemp);
972 : }
973 : // defrost energy rate is applied to rated power and is proportional to defrost cycles per hour
974 24056 : this->defrostEnergyRate = (this->referenceCapacity / this->referenceCOP) * defrostHeatEnergyFraction * thisHourDefrostCycles;
975 :
976 : // question on how these multipliers are accounted for with capacity and power (e.g., 1+ or 1-)
977 24056 : InputPowerMultiplier = 1.0 + thisHourDefrostHeatLoad;
978 24056 : HeatingCapacityMultiplier = 1.0 + (thisHourDefrostHeatLoad * thisHourDefrostCycles);
979 24056 : this->fractionalDefrostTime = thisHourDefrostCycles * this->fractionalDefrostTime;
980 : }
981 : } else {
982 252378 : this->defrostEnergyRate = 0.0;
983 252378 : this->loadDueToDefrost = 0.0;
984 252378 : this->fractionalDefrostTime = 0.0;
985 : }
986 276434 : availableCapacity *= HeatingCapacityMultiplier;
987 : // update class member variables
988 276434 : this->defrostPowerMultiplier = InputPowerMultiplier;
989 276434 : }
990 :
991 125 : void EIRPlantLoopHeatPump::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
992 : {
993 : // This function does all one-time and begin-environment initialization
994 137 : std::string static const routineName = std::string("EIRPlantLoopHeatPump :") + __FUNCTION__;
995 :
996 125 : this->oneTimeInit(state); // plant setup
997 125 : this->isPlantInletOrOutlet(state); // check location
998 :
999 125 : if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
1000 100 : this->sizeLoadSide(state);
1001 100 : if (this->waterSource) {
1002 20 : this->sizeSrcSideWSHP(state);
1003 80 : } else if (this->airSource) {
1004 80 : this->sizeSrcSideASHP(state);
1005 80 : this->sizeHeatRecoveryASHP(state);
1006 : }
1007 : }
1008 :
1009 125 : if (state.dataGlobal->BeginEnvrnFlag && this->envrnInit && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
1010 :
1011 20 : Real64 rho = this->loadSidePlantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, routineName);
1012 20 : this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
1013 20 : PlantUtilities::InitComponentNodes(state, 0.0, this->loadSideDesignMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
1014 :
1015 20 : if (this->waterSource) {
1016 4 : rho = this->sourceSidePlantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, routineName);
1017 4 : this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
1018 4 : PlantUtilities::InitComponentNodes(
1019 : state, 0.0, this->sourceSideDesignMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet);
1020 16 : } else if (this->airSource) {
1021 16 : rho = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, state.dataEnvrn->OutDryBulbTemp, 0.0, routineName);
1022 16 : this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
1023 : // heat recovery
1024 16 : if (this->heatRecoveryAvailable) {
1025 1 : rho = this->heatRecoveryPlantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, routineName);
1026 1 : this->heatRecoveryDesignMassFlowRate = rho * this->heatRecoveryDesignVolFlowRate;
1027 1 : PlantUtilities::InitComponentNodes(
1028 : state, 0.0, this->heatRecoveryDesignMassFlowRate, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet);
1029 : }
1030 : }
1031 :
1032 20 : if (this->flowMode == DataPlant::FlowMode::VariableSpeedPump) {
1033 14 : this->loadVSPumpMinLimitMassFlow =
1034 14 : PlantUtilities::MinFlowIfBranchHasVSPump(state, this->loadSidePlantLoc, this->loadVSBranchPump, this->loadVSLoopPump, true);
1035 14 : if (this->waterSource) {
1036 2 : this->sourceVSPumpMinLimitMassFlow = PlantUtilities::MinFlowIfBranchHasVSPump(
1037 2 : state, this->sourceSidePlantLoc, this->sourceVSBranchPump, this->sourceVSLoopPump, false);
1038 : }
1039 : }
1040 :
1041 20 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1042 20 : this->envrnInit = false;
1043 : }
1044 : }
1045 125 : if (!state.dataGlobal->BeginEnvrnFlag) {
1046 0 : this->envrnInit = true;
1047 : }
1048 125 : }
1049 :
1050 125 : void EIRPlantLoopHeatPump::getDesignCapacities(
1051 : [[maybe_unused]] EnergyPlusData &state, const PlantLocation &calledFromLocation, Real64 &MaxLoad, Real64 &MinLoad, Real64 &OptLoad)
1052 : {
1053 125 : if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
1054 100 : MinLoad = 0.0;
1055 100 : MaxLoad = this->referenceCapacity;
1056 100 : OptLoad = this->referenceCapacity;
1057 : } else {
1058 25 : MinLoad = 0.0;
1059 25 : MaxLoad = 0.0;
1060 25 : OptLoad = 0.0;
1061 : }
1062 125 : }
1063 :
1064 100 : void EIRPlantLoopHeatPump::sizeLoadSide(EnergyPlusData &state)
1065 : {
1066 : // Tries to size the load side flow rate and capacity, source side flow, and the rated power usage
1067 : // There are two major sections to this function, one if plant sizing is available, and one if not
1068 : // If plant sizing is available, then we can generate sizes for the equipment. This is done for not-only
1069 : // autosized fields, but also hard-sized fields so that we can report out significant deviations between
1070 : // the two values.
1071 : // If plant sizing is not available, it tries to use a companion heat pump coil to do sizing
1072 :
1073 100 : bool errorsFound = false;
1074 :
1075 : // these variables will be used throughout this function as a temporary value of that physical state
1076 100 : Real64 tmpCapacity = this->referenceCapacity;
1077 100 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
1078 100 : HeatSizingType heatingSizingMethod = this->heatSizingMethod;
1079 :
1080 100 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
1081 100 : Real64 loadSideInitTemp =
1082 100 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) ? Constant::HWInitConvTemp : Constant::CWInitConvTemp;
1083 : // I guess I can assume the plant fluids are the same for HW and CW. So only the sizing type is an issue on which to use.
1084 :
1085 100 : Real64 rho = this->loadSidePlantLoc.loop->glycol->getDensity(state, loadSideInitTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
1086 100 : Real64 Cp = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, loadSideInitTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
1087 :
1088 100 : int pltLoadSizNum = this->loadSidePlantLoc.loop->PlantSizNum;
1089 100 : if (pltLoadSizNum > 0) {
1090 : // this first IF block is really just about calculating the local tmpCapacity and tmpLoadVolFlow values
1091 : // these represent what the unit would size those to, whether it is doing auto-sizing or not
1092 70 : if (state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate > HVAC::SmallWaterVolFlow) {
1093 52 : tmpLoadVolFlow = state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate * this->sizingFactor;
1094 52 : Real64 deltaT = state.dataSize->PlantSizData(pltLoadSizNum).DeltaT;
1095 52 : if (this->companionHeatPumpCoil) {
1096 52 : if (this->companionHeatPumpCoil->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1097 28 : heatingSizingMethod = this->companionHeatPumpCoil->heatSizingMethod;
1098 : }
1099 52 : Real64 companionVolFlowRate = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
1100 52 : int compLoopNum = this->companionHeatPumpCoil->loadSidePlantLoc.loopNum;
1101 52 : if (compLoopNum > 0) {
1102 52 : companionVolFlowRate = state.dataSize->PlantSizData(compLoopNum).DesVolFlowRate * this->companionHeatPumpCoil->sizingFactor;
1103 : }
1104 52 : Real64 compRefCapacity = this->companionHeatPumpCoil->referenceCapacity;
1105 52 : Real64 compRho = rho;
1106 52 : Real64 compCp = Cp;
1107 52 : Real64 compDeltaT = deltaT;
1108 52 : if (compLoopNum > 0) {
1109 52 : compRho = state.dataPlnt->PlantLoop(compLoopNum)
1110 104 : .glycol->getDensity(state,
1111 52 : this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling ? Constant::HWInitConvTemp
1112 : : Constant::CWInitConvTemp,
1113 : "EIRPlantLoopHeatPump::sizeLoadSide()");
1114 : compCp =
1115 52 : state.dataPlnt->PlantLoop(compLoopNum)
1116 104 : .glycol->getSpecificHeat(state,
1117 52 : this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling ? Constant::HWInitConvTemp
1118 : : Constant::CWInitConvTemp,
1119 : "EIRPlantLoopHeatPump::sizeLoadSide()");
1120 52 : compDeltaT = state.dataSize->PlantSizData(compLoopNum).DeltaT;
1121 : }
1122 52 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
1123 28 : tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow;
1124 28 : if (heatingSizingMethod == HeatSizingType::Heating) {
1125 0 : tmpCapacity = (compCp * compRho * compDeltaT * companionVolFlowRate) / this->companionHeatPumpCoil->heatSizingRatio;
1126 28 : } else if (heatingSizingMethod == HeatSizingType::GreaterOfCoolingOrHeating) {
1127 24 : compRefCapacity = compCp * compRho * compDeltaT * companionVolFlowRate;
1128 24 : if (compRefCapacity > tmpCapacity) {
1129 6 : rho = compRho;
1130 6 : tmpLoadVolFlow = companionVolFlowRate;
1131 6 : tmpCapacity = compRefCapacity / this->companionHeatPumpCoil->heatSizingRatio;
1132 : }
1133 : }
1134 : } else { // size heating side based on sizing method
1135 24 : if (heatingSizingMethod == HeatSizingType::Heating) {
1136 0 : tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow;
1137 : } else {
1138 24 : compRefCapacity = compCp * compRho * compDeltaT * companionVolFlowRate;
1139 24 : if (heatingSizingMethod == HeatSizingType::Cooling) {
1140 4 : tmpCapacity = compRefCapacity * this->heatSizingRatio;
1141 4 : rho = compRho;
1142 4 : tmpLoadVolFlow = companionVolFlowRate;
1143 : } else { // else GreaterOfHeatingOrCooling
1144 20 : tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow;
1145 20 : if (compRefCapacity > tmpCapacity) {
1146 14 : tmpCapacity = compRefCapacity * this->heatSizingRatio;
1147 14 : rho = compRho;
1148 14 : tmpLoadVolFlow = companionVolFlowRate;
1149 : }
1150 : }
1151 : }
1152 : }
1153 : } else {
1154 0 : tmpCapacity = Cp * rho * deltaT * tmpLoadVolFlow * this->heatSizingRatio;
1155 : }
1156 18 : } else if (this->companionHeatPumpCoil && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
1157 4 : tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
1158 4 : if (this->companionHeatPumpCoil->referenceCapacity == DataSizing::AutoSize) {
1159 : // use reverse init temp, e.g., if this is cooling use HWInitConvTemp
1160 0 : Real64 compLoadSideInitTemp =
1161 0 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) ? Constant::HWInitConvTemp : Constant::CWInitConvTemp;
1162 0 : int compLoopNum = this->companionHeatPumpCoil->loadSidePlantLoc.loopNum;
1163 0 : if (compLoopNum > 0) {
1164 0 : Real64 const compRho = state.dataPlnt->PlantLoop(compLoopNum)
1165 0 : .glycol->getDensity(state, compLoadSideInitTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
1166 0 : Real64 const compCp = state.dataPlnt->PlantLoop(compLoopNum)
1167 0 : .glycol->getSpecificHeat(state, Constant::CWInitConvTemp, "EIRPlantLoopHeatPump::sizeLoadSide()");
1168 0 : rho = compRho;
1169 0 : Cp = compCp;
1170 : }
1171 0 : tmpCapacity = Cp * rho * state.dataSize->PlantSizData(pltLoadSizNum).DeltaT * tmpLoadVolFlow * this->heatSizingRatio;
1172 : } else {
1173 4 : tmpCapacity = this->companionHeatPumpCoil->referenceCapacity;
1174 : }
1175 4 : } else {
1176 14 : if (this->referenceCapacityWasAutoSized) {
1177 14 : tmpCapacity = 0.0;
1178 : }
1179 14 : if (this->loadSideDesignVolFlowRateWasAutoSized) {
1180 14 : tmpLoadVolFlow = 0.0;
1181 : }
1182 : }
1183 70 : if (this->heatRecoveryHeatPump) {
1184 10 : tmpLoadVolFlow = state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate;
1185 : }
1186 70 : if (this->loadSideDesignVolFlowRateWasAutoSized) {
1187 70 : this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
1188 : }
1189 70 : if (this->referenceCapacityWasAutoSized) {
1190 70 : this->referenceCapacity = tmpCapacity;
1191 : }
1192 : // now we actually need to store and report out the values
1193 70 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
1194 : // handle the auto-sizable reference capacity
1195 14 : if (this->referenceCapacityWasAutoSized) {
1196 : // if auto-sized, we just need to store the sized value and then report out the capacity when plant is ready
1197 14 : this->referenceCapacity = tmpCapacity;
1198 14 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1199 14 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
1200 : }
1201 14 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1202 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
1203 : }
1204 : } else {
1205 : // this blocks means the capacity value was hard-sized
1206 0 : if (this->referenceCapacity > 0.0 && tmpCapacity > 0.0) {
1207 : // then the capacity was hard-sized to a good value and the tmpCapacity was calculated to a good value too
1208 0 : Real64 hardSizedCapacity = this->referenceCapacity;
1209 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1210 0 : if (state.dataGlobal->DoPlantSizing) {
1211 0 : BaseSizer::reportSizerOutput(state,
1212 : typeName,
1213 : this->name,
1214 : "Design Size Nominal Capacity [W]",
1215 : tmpCapacity,
1216 : "User-Specified Nominal Capacity [W]",
1217 : hardSizedCapacity);
1218 : } else {
1219 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", hardSizedCapacity);
1220 : }
1221 : // we can warn here if there is a bit mismatch between hard- and auto-sized
1222 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1223 0 : if ((std::abs(tmpCapacity - hardSizedCapacity) / hardSizedCapacity) > state.dataSize->AutoVsHardSizingThreshold) {
1224 0 : ShowWarningMessage(state,
1225 0 : format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
1226 0 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", hardSizedCapacity));
1227 0 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpCapacity));
1228 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1229 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1230 : }
1231 : }
1232 : }
1233 : // moving forward with more calculations, we need to update the 'tmp' capacity to the hard-sized value
1234 0 : tmpCapacity = hardSizedCapacity;
1235 : }
1236 : }
1237 : // now handle the auto-sizable load side flow rate
1238 14 : if (this->loadSideDesignVolFlowRateWasAutoSized) {
1239 14 : this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
1240 14 : this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
1241 14 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1242 14 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
1243 : }
1244 14 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1245 0 : BaseSizer::reportSizerOutput(
1246 : state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
1247 : }
1248 : } else {
1249 0 : if (this->loadSideDesignVolFlowRate > 0.0 && tmpLoadVolFlow > 0.0) {
1250 0 : Real64 hardSizedLoadSideFlow = this->loadSideDesignVolFlowRate;
1251 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1252 0 : if (state.dataGlobal->DoPlantSizing) {
1253 0 : BaseSizer::reportSizerOutput(state,
1254 : typeName,
1255 : this->name,
1256 : "Design Size Load Side Volume Flow Rate [m3/s]",
1257 : tmpLoadVolFlow,
1258 : "User-Specified Load Side Volume Flow Rate [m3/s]",
1259 : hardSizedLoadSideFlow);
1260 : } else {
1261 0 : BaseSizer::reportSizerOutput(
1262 : state, typeName, this->name, "User-Specified Load Side Volume Flow Rate [m3/s]", hardSizedLoadSideFlow);
1263 : }
1264 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1265 0 : if ((std::abs(tmpLoadVolFlow - hardSizedLoadSideFlow) / hardSizedLoadSideFlow) >
1266 0 : state.dataSize->AutoVsHardSizingThreshold) {
1267 0 : ShowMessage(state, format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
1268 0 : ShowContinueError(state, format("User-Specified Load Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedLoadSideFlow));
1269 0 : ShowContinueError(state,
1270 0 : format("differs from Design Size Load Side Volume Flow Rate of {:.2R} [m3/s]", tmpLoadVolFlow));
1271 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1272 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1273 : }
1274 : }
1275 : }
1276 0 : tmpLoadVolFlow = hardSizedLoadSideFlow;
1277 : }
1278 : }
1279 : }
1280 : } else {
1281 : // no plant sizing available...try to use the companion coil
1282 30 : if (this->companionHeatPumpCoil) {
1283 30 : if (this->companionHeatPumpCoil->loadSideDesignVolFlowRateWasAutoSized && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
1284 0 : tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
1285 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
1286 0 : this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
1287 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1288 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
1289 : }
1290 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1291 0 : BaseSizer::reportSizerOutput(
1292 : state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
1293 : }
1294 : }
1295 : }
1296 30 : if (this->companionHeatPumpCoil->referenceCapacityWasAutoSized && this->companionHeatPumpCoil->referenceCapacity > 0.0) {
1297 0 : tmpCapacity = this->companionHeatPumpCoil->referenceCapacity;
1298 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
1299 0 : this->referenceCapacity = tmpCapacity;
1300 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1301 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
1302 : }
1303 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1304 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
1305 : }
1306 : }
1307 : }
1308 : } else {
1309 : // no companion coil, and no plant sizing, so can't do anything
1310 0 : if ((this->loadSideDesignVolFlowRateWasAutoSized || this->referenceCapacityWasAutoSized) &&
1311 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) {
1312 0 : ShowSevereError(state, "EIRPlantLoopHeatPump::size(): Autosizing requires a loop Sizing:Plant object.");
1313 0 : ShowContinueError(state, format("Occurs in HeatPump:PlantLoop:EquationFit:Cooling object = {}", this->name));
1314 0 : errorsFound = true;
1315 : }
1316 : }
1317 30 : if (!this->loadSideDesignVolFlowRateWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
1318 6 : BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Load Side Flow Rate [m3/s]", this->loadSideDesignVolFlowRate);
1319 : }
1320 30 : if (!this->referenceCapacityWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
1321 6 : BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", this->referenceCapacity);
1322 : }
1323 : }
1324 100 : if (errorsFound) {
1325 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
1326 : }
1327 100 : }
1328 :
1329 20 : void EIRPlantLoopHeatPump::sizeSrcSideWSHP(EnergyPlusData &state)
1330 : {
1331 : // size the source-side for the water-source HP
1332 20 : bool errorsFound = false;
1333 :
1334 : // these variables will be used throughout this function as a temporary value of that physical state
1335 20 : Real64 tmpCapacity = this->referenceCapacity;
1336 20 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
1337 : Real64 tmpSourceVolFlow;
1338 :
1339 20 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
1340 20 : Real64 sourceSideInitTemp =
1341 20 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) ? Constant::CWInitConvTemp : Constant::HWInitConvTemp;
1342 :
1343 20 : Real64 const rhoSrc = this->loadSidePlantLoc.loop->glycol->getDensity(state, sourceSideInitTemp, "EIRPlantLoopHeatPump::sizeSrcSideWSHP()");
1344 20 : Real64 const CpSrc = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, sourceSideInitTemp, "EIRPlantLoopHeatPump::sizeSrcSideWSHP()");
1345 :
1346 : // To start we need to override the calculated load side flow
1347 : // rate if it was actually hard-sized
1348 20 : if (!this->loadSideDesignVolFlowRateWasAutoSized) {
1349 10 : tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
1350 : }
1351 :
1352 : // calculate an auto-sized value for source design flow regardless of whether it was auto-sized or not
1353 20 : int plantSourceSizingIndex = this->sourceSidePlantLoc.loop->PlantSizNum;
1354 20 : if (plantSourceSizingIndex > 0) {
1355 : // to get the source flow, we first must calculate the required heat impact on the source side
1356 : // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
1357 : // Then the energy balance: Qsrc = Qload + Power
1358 : // Substituting for Power: Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
1359 10 : Real64 designSourceSideHeatTransfer = 0.0;
1360 10 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1361 5 : designSourceSideHeatTransfer = tmpCapacity * (1 - 1 / this->referenceCOP);
1362 : } else {
1363 5 : designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
1364 : }
1365 : // To get the design source flow rate, just apply the sensible heat rate equation:
1366 : // Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
1367 : // Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
1368 10 : tmpSourceVolFlow = designSourceSideHeatTransfer / (state.dataSize->PlantSizData(plantSourceSizingIndex).DeltaT * CpSrc * rhoSrc);
1369 10 : if (this->waterSource && this->heatRecoveryHeatPump) {
1370 : // If component is on plant outlet branch, use plant flow rate.
1371 10 : tmpSourceVolFlow = state.dataSize->PlantSizData(plantSourceSizingIndex).DesVolFlowRate;
1372 : }
1373 : } else {
1374 : // just assume it's the same as the load side if we don't have any sizing information
1375 10 : tmpSourceVolFlow = tmpLoadVolFlow;
1376 : }
1377 20 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
1378 10 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
1379 10 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1380 2 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
1381 : }
1382 10 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1383 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
1384 : }
1385 : } else {
1386 : // source design flow was hard-sized
1387 10 : if (this->sourceSideDesignVolFlowRate > 0.0 && tmpSourceVolFlow > 0.0) {
1388 10 : Real64 const hardSizedSourceSideFlow = this->sourceSideDesignVolFlowRate;
1389 10 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1390 2 : if (state.dataGlobal->DoPlantSizing) {
1391 0 : BaseSizer::reportSizerOutput(state,
1392 : typeName,
1393 : this->name,
1394 : "Design Size Source Side Volume Flow Rate [m3/s]",
1395 : tmpSourceVolFlow,
1396 : "User-Specified Source Side Volume Flow Rate [m3/s]",
1397 : hardSizedSourceSideFlow);
1398 : } else {
1399 2 : BaseSizer::reportSizerOutput(
1400 : state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", hardSizedSourceSideFlow);
1401 : }
1402 2 : if (state.dataGlobal->DisplayExtraWarnings) {
1403 0 : if ((std::abs(tmpSourceVolFlow - hardSizedSourceSideFlow) / hardSizedSourceSideFlow) >
1404 0 : state.dataSize->AutoVsHardSizingThreshold) {
1405 0 : ShowMessage(state, format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
1406 0 : ShowContinueError(state, format("User-Specified Source Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedSourceSideFlow));
1407 0 : ShowContinueError(state, format("differs from Design Size Source Side Volume Flow Rate of {:.2R} [m3/s]", tmpSourceVolFlow));
1408 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1409 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1410 : }
1411 : }
1412 : }
1413 10 : tmpSourceVolFlow = hardSizedSourceSideFlow;
1414 : }
1415 : }
1416 20 : if (this->companionHeatPumpCoil) {
1417 20 : tmpSourceVolFlow *= this->companionHeatPumpCoil->heatSizingRatio;
1418 : } else {
1419 0 : tmpSourceVolFlow *= this->heatSizingRatio;
1420 : }
1421 :
1422 : // skipping autosized power section
1423 :
1424 : // register the design volume flows with the plant, only doing half of source because the companion
1425 : // is generally on the same loop
1426 20 : if (!this->heatRecoveryHeatPump) {
1427 10 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->loadSideNodes.inlet, tmpLoadVolFlow);
1428 : }
1429 20 : if (!this->heatRecoveryHeatPump) {
1430 10 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->sourceSideNodes.inlet, tmpSourceVolFlow / 0.5);
1431 : }
1432 :
1433 20 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1434 : // create predefined report
1435 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, this->name, typeName);
1436 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, this->name, this->referenceCOP);
1437 4 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->name, this->referenceCapacity);
1438 : }
1439 :
1440 20 : if (errorsFound) {
1441 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
1442 : }
1443 20 : }
1444 :
1445 80 : void EIRPlantLoopHeatPump::sizeSrcSideASHP(EnergyPlusData &state)
1446 : {
1447 : // size the source-side for the air-source HP
1448 80 : bool errorsFound = false;
1449 :
1450 : // these variables will be used throughout this function as a temporary value of that physical state
1451 80 : Real64 tmpCapacity = this->referenceCapacity;
1452 80 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
1453 80 : Real64 tmpSourceVolFlow = 0.0;
1454 :
1455 : // will leave like this for now
1456 : // need to update these to better values later
1457 80 : Real64 sourceSideInitTemp = 20;
1458 80 : Real64 sourceSideHumRat = 0.0;
1459 80 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1460 : // same here; update later
1461 35 : sourceSideInitTemp = 20;
1462 : }
1463 :
1464 80 : Real64 const rhoSrc = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat);
1465 80 : Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(sourceSideHumRat);
1466 :
1467 : // set the source-side flow rate
1468 80 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
1469 : // load-side capacity should already be set, so unless the flow rate is specified, we can set
1470 : // an assumed reasonable flow rate since this doesn't affect downstream components
1471 60 : Real64 DeltaT_src = 10;
1472 : // to get the source flow, we first must calculate the required heat impact on the source side
1473 : // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
1474 : // Then the energy balance: Qsrc = Qload + Power
1475 : // Substituting for Power: Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
1476 60 : Real64 designSourceSideHeatTransfer = 0.0;
1477 60 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1478 30 : designSourceSideHeatTransfer = tmpCapacity * (1 - 1 / this->referenceCOP);
1479 : } else {
1480 30 : designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
1481 : }
1482 : // To get the design source flow rate, just apply the sensible heat rate equation:
1483 : // Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
1484 : // Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
1485 60 : tmpSourceVolFlow = designSourceSideHeatTransfer / (rhoSrc * CpSrc * DeltaT_src);
1486 20 : } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate > 0) {
1487 : // given the value by the user
1488 : // set it directly
1489 20 : tmpSourceVolFlow = this->sourceSideDesignVolFlowRate;
1490 : } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate == 0) { // LCOV_EXCL_LINE
1491 : // user gave a flow rate of 0
1492 : // protected by the input processor to be >0.0
1493 : // fatal out just in case
1494 : errorsFound = true; // LCOV_EXCL_LINE
1495 0 : ShowSevereError(state,
1496 0 : format("Invalid condenser flow rate for EIR PLHP (name={}; entered value: {}",
1497 0 : this->name,
1498 : this->sourceSideDesignVolFlowRate)); // LCOV_EXCL_LINE
1499 : } else {
1500 : // can't imagine how it would ever get to this point
1501 : // just assume it's the same as the load side if we don't have any sizing information
1502 : tmpSourceVolFlow = tmpLoadVolFlow; // LCOV_EXCL_LINE
1503 : }
1504 :
1505 80 : if (this->companionHeatPumpCoil) {
1506 80 : tmpSourceVolFlow *= this->companionHeatPumpCoil->heatSizingRatio;
1507 : } else {
1508 0 : tmpSourceVolFlow *= this->heatSizingRatio;
1509 : }
1510 80 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
1511 80 : this->sourceSideDesignMassFlowRate = rhoSrc * this->sourceSideDesignVolFlowRate;
1512 :
1513 80 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
1514 80 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
1515 60 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
1516 60 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1517 12 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
1518 : }
1519 60 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1520 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
1521 : }
1522 : } else {
1523 : // source design flow was hard-sized
1524 20 : if (this->sourceSideDesignVolFlowRate > 0.0) {
1525 20 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1526 4 : if (state.dataGlobal->DoPlantSizing) {
1527 0 : BaseSizer::reportSizerOutput(state,
1528 : typeName,
1529 : this->name,
1530 : "Design Size Source Side Volume Flow Rate [m3/s]",
1531 : tmpSourceVolFlow,
1532 : "User-Specified Source Side Volume Flow Rate [m3/s]",
1533 0 : this->sourceSideDesignVolFlowRate);
1534 : } else {
1535 4 : BaseSizer::reportSizerOutput(
1536 : state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", this->sourceSideDesignVolFlowRate);
1537 : }
1538 : }
1539 : }
1540 : }
1541 :
1542 80 : if (errorsFound) {
1543 : ShowFatalError(state, "Preceding sizing errors cause program termination"); // LCOV_EXCL_LINE
1544 : }
1545 80 : }
1546 :
1547 80 : void EIRPlantLoopHeatPump::sizeHeatRecoveryASHP(EnergyPlusData &state)
1548 : {
1549 : // size heat recovery side volume flow rate for air-source HP
1550 80 : if (!this->heatRecoveryAvailable) {
1551 75 : return;
1552 : }
1553 :
1554 : // these variables will be used throughout this function as a temporary value
1555 5 : Real64 tmpCapacity = this->referenceCapacity;
1556 5 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
1557 5 : Real64 tmpHeatRecoveryVolFlow = 0.0;
1558 : // size the heat-recovery flow rate
1559 5 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
1560 5 : Real64 heatRecoveryInitTemp =
1561 5 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) ? Constant::HWInitConvTemp : Constant::CWInitConvTemp;
1562 : Real64 const rhoHR =
1563 5 : this->heatRecoveryPlantLoc.loop->glycol->getDensity(state, heatRecoveryInitTemp, "EIRPlantLoopHeatPump::sizeHeatRecoveryASHP()");
1564 : Real64 const CpHR =
1565 5 : this->heatRecoveryPlantLoc.loop->glycol->getSpecificHeat(state, heatRecoveryInitTemp, "EIRPlantLoopHeatPump::sizeHeatRecoveryASHP()");
1566 :
1567 : // calculate an auto-sized value for heat recovery design flow regardless of whether it was auto-sized or not
1568 5 : int plantHRSizingIndex = this->heatRecoveryPlantLoc.loop->PlantSizNum;
1569 5 : if (plantHRSizingIndex > 0) {
1570 : // Definition of COP: COP = Qload/Power, therefore Power = Qload/COP
1571 : // Energy balance: Qhr = Qload + Power = Qload(1 + 1/COP), cooling mode (recovers hot water)
1572 : // Qhr = Qload - Power = Qload(1 - 1/COP), heating mode (recovers chilled water)
1573 5 : Real64 designHeatRecoveryHeatTransfer = 0.0;
1574 5 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1575 0 : designHeatRecoveryHeatTransfer = tmpCapacity * (1 - 1 / this->referenceCOP);
1576 : } else {
1577 5 : designHeatRecoveryHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
1578 : }
1579 : // calculate the design heat recovery flow rate, by applying the sensible heat rate equation:
1580 5 : tmpHeatRecoveryVolFlow = designHeatRecoveryHeatTransfer / (state.dataSize->PlantSizData(plantHRSizingIndex).DeltaT * CpHR * rhoHR);
1581 : // not sure about this
1582 : // if (this->airSource && this->heatRecoveryHeatPump) {
1583 : // // If component is on plant outlet branch, use plant flow rate
1584 : // tmpHeatRecoveryVolFlow = state.dataSize->PlantSizData(plantHRSizingIndex).DesVolFlowRate;
1585 : //}
1586 : } else {
1587 : // set it to the load side if there is plant sizing information
1588 0 : tmpHeatRecoveryVolFlow = tmpLoadVolFlow;
1589 : }
1590 : // check if the sizing ratio is based on the this->EIRHPType
1591 5 : if (this->companionHeatPumpCoil) {
1592 5 : tmpHeatRecoveryVolFlow *= this->companionHeatPumpCoil->heatSizingRatio;
1593 : } else {
1594 0 : tmpHeatRecoveryVolFlow *= this->heatSizingRatio;
1595 : }
1596 5 : this->heatRecoveryDesignMassFlowRate = rhoHR * this->heatRecoveryDesignVolFlowRate;
1597 :
1598 5 : if (this->heatRecoveryDesignVolFlowRateWasAutoSized) {
1599 5 : this->heatRecoveryDesignVolFlowRate = tmpHeatRecoveryVolFlow;
1600 5 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1601 1 : BaseSizer::reportSizerOutput(
1602 : state, typeName, this->name, "Design Size Heat Recovery Side Volume Flow Rate [m3/s]", tmpHeatRecoveryVolFlow);
1603 : }
1604 5 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
1605 0 : BaseSizer::reportSizerOutput(
1606 : state, typeName, this->name, "Initial Design Size Heat Recovery Side Volume Flow Rate [m3/s]", tmpHeatRecoveryVolFlow);
1607 : }
1608 : } else {
1609 : // heat recovery design volume flow rate was hard-sized
1610 0 : if (this->heatRecoveryDesignVolFlowRate > 0.0 && tmpHeatRecoveryVolFlow > 0.0) {
1611 0 : Real64 const hardSizedHeatRecoveryFlow = this->heatRecoveryDesignVolFlowRate;
1612 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
1613 0 : if (state.dataGlobal->DoPlantSizing) {
1614 0 : BaseSizer::reportSizerOutput(state,
1615 : typeName,
1616 : this->name,
1617 : "Design Size Heat Recovery Side Volume Flow Rate [m3/s]",
1618 : tmpHeatRecoveryVolFlow,
1619 : "User-Specified Heat Recovery Side Volume Flow Rate [m3/s]",
1620 : hardSizedHeatRecoveryFlow);
1621 : } else {
1622 0 : BaseSizer::reportSizerOutput(
1623 : state, typeName, this->name, "User-Specified Heat Recovery Side Volume Flow Rate [m3/s]", hardSizedHeatRecoveryFlow);
1624 : }
1625 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1626 0 : if ((std::abs(tmpHeatRecoveryVolFlow - hardSizedHeatRecoveryFlow) / hardSizedHeatRecoveryFlow) >
1627 0 : state.dataSize->AutoVsHardSizingThreshold) {
1628 0 : ShowMessage(state, format("EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for {}", this->name));
1629 0 : ShowContinueError(state,
1630 0 : format("User-Specified Heat Recovery Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedHeatRecoveryFlow));
1631 0 : ShowContinueError(
1632 0 : state, format("differs from Design Size Heat Recovery Side Volume Flow Rate of {:.2R} [m3/s]", tmpHeatRecoveryVolFlow));
1633 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1634 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1635 : }
1636 : }
1637 : }
1638 : }
1639 : }
1640 : }
1641 :
1642 23 : PlantComponent *EIRPlantLoopHeatPump::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType hp_type, const std::string &hp_name)
1643 : {
1644 23 : if (state.dataEIRPlantLoopHeatPump->getInputsPLHP) {
1645 5 : EIRPlantLoopHeatPump::processInputForEIRPLHP(state);
1646 5 : EIRPlantLoopHeatPump::pairUpCompanionCoils(state);
1647 5 : state.dataEIRPlantLoopHeatPump->getInputsPLHP = false;
1648 : }
1649 :
1650 60 : for (auto &plhp : state.dataEIRPlantLoopHeatPump->heatPumps) {
1651 60 : if (plhp.name == Util::makeUPPER(hp_name) && plhp.EIRHPType == hp_type) {
1652 23 : return &plhp;
1653 : }
1654 46 : }
1655 :
1656 0 : ShowFatalError(state, format("EIR Plant Loop Heat Pump factory: Error getting inputs for PLHP named: {}", hp_name));
1657 : return nullptr; // LCOV_EXCL_LINE
1658 : }
1659 :
1660 5 : void EIRPlantLoopHeatPump::pairUpCompanionCoils(EnergyPlusData &state)
1661 : {
1662 23 : for (auto &thisHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
1663 18 : if (!thisHP.companionCoilName.empty()) {
1664 18 : std::string const thisCoilName = Util::makeUPPER(thisHP.name);
1665 18 : DataPlant::PlantEquipmentType thisCoilType = thisHP.EIRHPType;
1666 18 : std::string const targetCompanionName = Util::makeUPPER(thisHP.companionCoilName);
1667 47 : for (auto &potentialCompanionCoil : state.dataEIRPlantLoopHeatPump->heatPumps) {
1668 47 : DataPlant::PlantEquipmentType potentialCompanionType = potentialCompanionCoil.EIRHPType;
1669 47 : std::string potentialCompanionName = Util::makeUPPER(potentialCompanionCoil.name);
1670 47 : if (potentialCompanionName == thisCoilName) {
1671 : // skip the current coil
1672 9 : continue;
1673 : }
1674 38 : if (potentialCompanionName == targetCompanionName) {
1675 18 : if (thisCoilType == potentialCompanionType) {
1676 0 : ShowSevereError(state, format("Invalid companion specification for EIR Plant Loop Heat Pump named \"{}\"", thisCoilName));
1677 0 : ShowContinueError(state, "For heating objects, the companion must be a cooling object, and vice-versa");
1678 0 : ShowFatalError(state, "Invalid companion object causes program termination");
1679 : }
1680 18 : thisHP.companionHeatPumpCoil = &potentialCompanionCoil;
1681 18 : break;
1682 : }
1683 65 : }
1684 18 : if (!thisHP.companionHeatPumpCoil) {
1685 0 : ShowSevereError(state, "Could not find matching companion heat pump coil.");
1686 0 : ShowContinueError(state, format("Base coil: {}", thisCoilName));
1687 0 : ShowContinueError(state, format("Looking for companion coil named: {}", targetCompanionName));
1688 0 : ShowFatalError(state, "Simulation aborts due to previous severe error");
1689 : }
1690 18 : }
1691 5 : }
1692 5 : }
1693 :
1694 5 : void EIRPlantLoopHeatPump::processInputForEIRPLHP(EnergyPlusData &state)
1695 : {
1696 :
1697 : struct ClassType
1698 : {
1699 : DataPlant::PlantEquipmentType thisType;
1700 : std::string nodesType;
1701 : std::function<Real64(Real64, Real64)> calcLoadOutletTemp;
1702 : std::function<Real64(Real64, Real64)> calcQsource;
1703 : std::function<Real64(Real64, Real64)> calcSourceOutletTemp;
1704 : std::function<Real64(Real64, Real64)> calcQheatRecovery;
1705 : std::function<Real64(Real64, Real64)> calcHROutletTemp;
1706 :
1707 10 : ClassType(DataPlant::PlantEquipmentType _thisType,
1708 : std::string _nodesType,
1709 : std::function<Real64(Real64, Real64)> _tLoadOutFunc,
1710 : std::function<Real64(Real64, Real64)> _qSrcFunc,
1711 : std::function<Real64(Real64, Real64)> _tSrcOutFunc,
1712 : std::function<Real64(Real64, Real64)> _qHeatRecovery,
1713 : std::function<Real64(Real64, Real64)> _tHROutFunc)
1714 10 : : thisType(_thisType), nodesType(std::move(_nodesType)), calcLoadOutletTemp(_tLoadOutFunc), calcQsource(_qSrcFunc),
1715 10 : calcSourceOutletTemp(_tSrcOutFunc), calcQheatRecovery(_qHeatRecovery), calcHROutletTemp(_tHROutFunc)
1716 : {
1717 10 : }
1718 : };
1719 : std::array<ClassType, 2> classesToInput = {ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRCooling,
1720 : "Chilled Water Nodes",
1721 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
1722 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
1723 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
1724 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
1725 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add},
1726 : ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRHeating,
1727 : "Hot Water Nodes",
1728 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
1729 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
1730 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
1731 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
1732 10 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract}};
1733 :
1734 5 : bool errorsFound = false;
1735 5 : std::string &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
1736 15 : for (auto const &classToInput : classesToInput) {
1737 10 : cCurrentModuleObject = DataPlant::PlantEquipTypeNames[static_cast<int>(classToInput.thisType)];
1738 : DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
1739 10 : getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(cCurrentModuleObject)));
1740 10 : int numPLHP = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
1741 10 : if (numPLHP > 0) {
1742 10 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
1743 10 : if (instances == state.dataInputProcessing->inputProcessor->epJSON.end()) {
1744 0 : continue;
1745 : }
1746 10 : auto &instancesValue = instances.value();
1747 10 : auto const &schemaProps = state.dataInputProcessing->inputProcessor->getObjectSchemaProps(state, cCurrentModuleObject);
1748 28 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
1749 18 : auto const &fields = instance.value();
1750 18 : std::string const &thisObjectName = instance.key();
1751 18 : state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
1752 :
1753 18 : EIRPlantLoopHeatPump thisPLHP;
1754 18 : thisPLHP.EIRHPType = classToInput.thisType;
1755 18 : thisPLHP.name = Util::makeUPPER(thisObjectName);
1756 36 : std::string loadSideInletNodeName = Util::makeUPPER(fields.at("load_side_inlet_node_name").get<std::string>());
1757 36 : std::string loadSideOutletNodeName = Util::makeUPPER(fields.at("load_side_outlet_node_name").get<std::string>());
1758 36 : std::string condenserType = Util::makeUPPER(fields.at("condenser_type").get<std::string>());
1759 36 : std::string sourceSideInletNodeName = Util::makeUPPER(fields.at("source_side_inlet_node_name").get<std::string>());
1760 18 : std::string sourceSideOutletNodeName = Util::makeUPPER(fields.at("source_side_outlet_node_name").get<std::string>());
1761 : thisPLHP.companionCoilName =
1762 36 : Util::makeUPPER(state.dataInputProcessing->inputProcessor->getAlphaFieldValue(fields, schemaProps, "companion_heat_pump_name"));
1763 :
1764 18 : thisPLHP.loadSideDesignVolFlowRate =
1765 36 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "load_side_reference_flow_rate");
1766 18 : if (thisPLHP.loadSideDesignVolFlowRate == DataSizing::AutoSize) {
1767 14 : thisPLHP.loadSideDesignVolFlowRateWasAutoSized = true;
1768 : }
1769 :
1770 18 : thisPLHP.sourceSideDesignVolFlowRate =
1771 36 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "source_side_reference_flow_rate");
1772 18 : if (thisPLHP.sourceSideDesignVolFlowRate == DataSizing::AutoSize) {
1773 14 : thisPLHP.sourceSideDesignVolFlowRateWasAutoSized = true;
1774 : }
1775 :
1776 36 : thisPLHP.referenceCapacity = state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "reference_capacity");
1777 18 : if (thisPLHP.referenceCapacity == DataSizing::AutoSize) {
1778 14 : thisPLHP.referenceCapacityWasAutoSized = true;
1779 : }
1780 :
1781 18 : thisPLHP.referenceCOP =
1782 36 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "reference_coefficient_of_performance");
1783 :
1784 54 : thisPLHP.sizingFactor = state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "sizing_factor");
1785 :
1786 18 : std::string const capFtName = Util::makeUPPER(fields.at("capacity_modifier_function_of_temperature_curve_name").get<std::string>());
1787 18 : thisPLHP.capFuncTempCurveIndex = Curve::GetCurveIndex(state, capFtName);
1788 18 : if (thisPLHP.capFuncTempCurveIndex == 0) {
1789 0 : ShowSevereError(state, format("Invalid curve name for EIR PLHP (name={}; entered curve name: {}", thisPLHP.name, capFtName));
1790 0 : errorsFound = true;
1791 : }
1792 :
1793 : std::string const eirFtName =
1794 18 : Util::makeUPPER(fields.at("electric_input_to_output_ratio_modifier_function_of_temperature_curve_name").get<std::string>());
1795 18 : thisPLHP.powerRatioFuncTempCurveIndex = Curve::GetCurveIndex(state, eirFtName);
1796 18 : if (thisPLHP.powerRatioFuncTempCurveIndex == 0) {
1797 0 : ShowSevereError(state, format("Invalid curve name for EIR PLHP (name={}; entered curve name: {}", thisPLHP.name, eirFtName));
1798 0 : errorsFound = true;
1799 : }
1800 :
1801 : std::string const eirFplrName =
1802 18 : Util::makeUPPER(fields.at("electric_input_to_output_ratio_modifier_function_of_part_load_ratio_curve_name").get<std::string>());
1803 18 : thisPLHP.powerRatioFuncPLRCurveIndex = Curve::GetCurveIndex(state, eirFplrName);
1804 18 : if (thisPLHP.powerRatioFuncPLRCurveIndex == 0) {
1805 0 : ShowSevereError(state, format("Invalid curve name for EIR PLHP (name={}; entered curve name: {}", thisPLHP.name, eirFplrName));
1806 0 : errorsFound = true;
1807 : }
1808 :
1809 : // inputs are past min-fields
1810 : // fields common to both objects
1811 36 : thisPLHP.minimumPLR = state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "minimum_part_load_ratio");
1812 18 : thisPLHP.minSourceTempLimit =
1813 36 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "minimum_source_inlet_temperature");
1814 18 : thisPLHP.maxSourceTempLimit =
1815 54 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "maximum_source_inlet_temperature");
1816 :
1817 18 : auto const minimumSupplyWaterTempCurveName = fields.find("minimum_supply_water_temperature_curve_name");
1818 18 : if (minimumSupplyWaterTempCurveName != fields.end()) {
1819 6 : thisPLHP.minSupplyWaterTempCurveIndex =
1820 6 : Curve::GetCurveIndex(state, Util::makeUPPER(minimumSupplyWaterTempCurveName.value().get<std::string>()));
1821 : }
1822 :
1823 18 : auto const maximumSupplyWaterTempCurveName = fields.find("maximum_supply_water_temperature_curve_name");
1824 18 : if (maximumSupplyWaterTempCurveName != fields.end()) {
1825 6 : thisPLHP.maxSupplyWaterTempCurveIndex =
1826 6 : Curve::GetCurveIndex(state, Util::makeUPPER(maximumSupplyWaterTempCurveName.value().get<std::string>()));
1827 : }
1828 : // fields only in cooling object
1829 18 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
1830 9 : auto const thermosiphonTempCurveName = fields.find("thermosiphon_capacity_fraction_curve_name");
1831 9 : if (thermosiphonTempCurveName != fields.end()) {
1832 2 : thisPLHP.thermosiphonTempCurveIndex =
1833 2 : Curve::GetCurveIndex(state, Util::makeUPPER(thermosiphonTempCurveName.value().get<std::string>()));
1834 2 : if (thisPLHP.thermosiphonTempCurveIndex == 0) {
1835 0 : ShowSevereError(state, format("{} =\"{}\"", state.dataIPShortCut->cCurrentModuleObject, thisPLHP.name));
1836 0 : ShowContinueError(state,
1837 0 : format("Invalid Thermosiphon Capacity Fraction Curve Name = {}",
1838 0 : thermosiphonTempCurveName.value().get<std::string>()));
1839 0 : errorsFound = true;
1840 : }
1841 : }
1842 18 : thisPLHP.thermosiphonMinTempDiff = state.dataInputProcessing->inputProcessor->getRealFieldValue(
1843 : fields, schemaProps, "thermosiphon_minimum_temperature_difference");
1844 9 : }
1845 :
1846 : std::string flowModeTypeName =
1847 36 : Util::makeUPPER(state.dataInputProcessing->inputProcessor->getAlphaFieldValue(fields, schemaProps, "flow_mode"));
1848 18 : thisPLHP.flowMode = static_cast<DataPlant::FlowMode>(getEnumValue(DataPlant::FlowModeNamesUC, flowModeTypeName));
1849 :
1850 : // fields only in heating object
1851 18 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1852 9 : thisPLHP.heatSizingRatio =
1853 18 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "heating_to_cooling_capacity_sizing_ratio");
1854 27 : thisPLHP.maxOutdoorTemperatureDefrost = state.dataInputProcessing->inputProcessor->getRealFieldValue(
1855 : fields, schemaProps, "maximum_outdoor_dry_bulb_temperature_for_defrost_operation");
1856 : }
1857 :
1858 18 : constexpr std::array<std::string_view, static_cast<int>(HeatSizingType::Num)> PLHPHeatSizTypeNamesUC = {
1859 : "HEATINGCAPACITY", "COOLINGCAPACITY", "GREATEROFHEATINGORCOOLING"};
1860 18 : auto const heatSizingType = fields.find("heat_pump_sizing_method");
1861 18 : if (heatSizingType != fields.end()) {
1862 7 : thisPLHP.heatSizingMethod =
1863 7 : static_cast<HeatSizingType>(getEnumValue(PLHPHeatSizTypeNamesUC, Util::makeUPPER(heatSizingType.value().get<std::string>())));
1864 : } else {
1865 : // revert to legacy sizing method, if no companion coil and this coil type is heating, set to heating
1866 11 : if (thisPLHP.companionCoilName.empty() && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
1867 0 : thisPLHP.heatSizingMethod = HeatSizingType::Heating;
1868 : } else {
1869 11 : thisPLHP.heatSizingMethod = HeatSizingType::Cooling;
1870 : }
1871 : }
1872 :
1873 18 : constexpr std::array<std::string_view, static_cast<int>(ControlType::Num)> PLHPCtrlTypeNamesUC = {"SETPOINT", "LOAD"};
1874 18 : auto const controlType = fields.find("control_type");
1875 18 : if (controlType != fields.end()) {
1876 14 : thisPLHP.sysControlType =
1877 14 : static_cast<ControlType>(getEnumValue(PLHPCtrlTypeNamesUC, Util::makeUPPER(controlType.value().get<std::string>())));
1878 : } else {
1879 4 : thisPLHP.sysControlType = ControlType::Load;
1880 : }
1881 18 : auto const capacityDryAirCurveName = fields.find("dry_outdoor_correction_factor_curve_name");
1882 18 : if (capacityDryAirCurveName != fields.end()) {
1883 6 : thisPLHP.capacityDryAirCurveIndex =
1884 6 : Curve::GetCurveIndex(state, Util::makeUPPER(capacityDryAirCurveName.value().get<std::string>()));
1885 : }
1886 :
1887 18 : constexpr std::array<std::string_view, static_cast<int>(DefrostControl::Num)> PLHPDefrostTypeNamesUC = {
1888 : "NONE", "TIMED", "ONDEMAND", "TIMEDEMPIRICAL"};
1889 18 : auto const defrostControlStrategy = fields.find("heat_pump_defrost_control");
1890 18 : if (defrostControlStrategy != fields.end()) {
1891 6 : thisPLHP.defrostStrategy = static_cast<DefrostControl>(
1892 6 : getEnumValue(PLHPDefrostTypeNamesUC, Util::makeUPPER(defrostControlStrategy.value().get<std::string>())));
1893 : } else {
1894 12 : thisPLHP.defrostStrategy = DefrostControl::None;
1895 : }
1896 :
1897 18 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating &&
1898 9 : (thisPLHP.defrostStrategy == DefrostControl::Timed || thisPLHP.defrostStrategy == DefrostControl::TimedEmpirical)) {
1899 6 : auto const timePeriod = fields.find("heat_pump_defrost_time_period_fraction");
1900 6 : if (timePeriod != fields.end()) {
1901 6 : thisPLHP.defrostTime = timePeriod.value().get<Real64>();
1902 : } else {
1903 0 : Real64 defaultVal = 0.0;
1904 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
1905 : state, cCurrentModuleObject, "heat_pump_defrost_time_period_fraction", defaultVal)) {
1906 : // excluding from coverage
1907 : ShowSevereError(state, // LCOV_EXCL_LINE
1908 0 : format("EIR PLHP \"{}\": Heat Pump Defrost Time Period Fraction not entered and default value not found.",
1909 : thisPLHP.name)); // LCOV_EXCL_LINE
1910 : errorsFound = true; // LCOV_EXCL_LINE
1911 : } else {
1912 0 : thisPLHP.defrostTime = defaultVal;
1913 : }
1914 : }
1915 6 : }
1916 :
1917 18 : if (thisPLHP.defrostStrategy == DefrostControl::TimedEmpirical) {
1918 6 : auto const timedEmpiricalDefFreqStratCurveName = fields.find("timed_empirical_defrost_frequency_curve_name");
1919 6 : if (timedEmpiricalDefFreqStratCurveName != fields.end()) {
1920 6 : thisPLHP.defrostFreqCurveIndex =
1921 6 : Curve::GetCurveIndex(state, Util::makeUPPER(timedEmpiricalDefFreqStratCurveName.value().get<std::string>()));
1922 : }
1923 6 : auto const timedEmpiricalDefHeatLoadPenaltyCurveName = fields.find("timed_empirical_defrost_heat_load_penalty_curve_name");
1924 6 : if (timedEmpiricalDefHeatLoadPenaltyCurveName != fields.end()) {
1925 6 : thisPLHP.defrostHeatLoadCurveIndex =
1926 6 : Curve::GetCurveIndex(state, Util::makeUPPER(timedEmpiricalDefHeatLoadPenaltyCurveName.value().get<std::string>()));
1927 6 : thisPLHP.defrostLoadCurveDims = state.dataCurveManager->curves(thisPLHP.defrostHeatLoadCurveIndex)->numDims;
1928 : }
1929 6 : auto const defrostHeatEnergyCurveIndexCurveName = fields.find("timed_empirical_defrost_heat_input_energy_fraction_curve_name");
1930 6 : if (defrostHeatEnergyCurveIndexCurveName != fields.end()) {
1931 6 : thisPLHP.defrostHeatEnergyCurveIndex =
1932 6 : Curve::GetCurveIndex(state, Util::makeUPPER(defrostHeatEnergyCurveIndexCurveName.value().get<std::string>()));
1933 6 : thisPLHP.defrostEnergyCurveDims = state.dataCurveManager->curves(thisPLHP.defrostHeatEnergyCurveIndex)->numDims;
1934 : }
1935 18 : } else if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) { // used for Timed or OnDemand
1936 3 : auto const defEIRFTCurveName = fields.find("defrost_energy_input_ratio_function_of_temperature_curve_name");
1937 3 : if (defEIRFTCurveName != fields.end()) {
1938 0 : thisPLHP.defrostEIRFTIndex = Curve::GetCurveIndex(state, Util::makeUPPER(defEIRFTCurveName.value().get<std::string>()));
1939 : }
1940 3 : }
1941 :
1942 18 : bool nodeErrorsFound = false;
1943 18 : thisPLHP.loadSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
1944 : loadSideInletNodeName,
1945 : nodeErrorsFound,
1946 : objType,
1947 : thisPLHP.name,
1948 : DataLoopNode::NodeFluidType::Water,
1949 : DataLoopNode::ConnectionType::Inlet,
1950 : NodeInputManager::CompFluidStream::Primary,
1951 : DataLoopNode::ObjectIsNotParent);
1952 18 : thisPLHP.loadSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
1953 : loadSideOutletNodeName,
1954 : nodeErrorsFound,
1955 : objType,
1956 : thisPLHP.name,
1957 : DataLoopNode::NodeFluidType::Water,
1958 : DataLoopNode::ConnectionType::Outlet,
1959 : NodeInputManager::CompFluidStream::Primary,
1960 : DataLoopNode::ObjectIsNotParent);
1961 18 : DataLoopNode::NodeFluidType condenserNodeType = DataLoopNode::NodeFluidType::Blank;
1962 18 : DataLoopNode::ConnectionType condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Blank;
1963 18 : DataLoopNode::ConnectionType condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Blank;
1964 18 : if (condenserType == "WATERSOURCE") {
1965 4 : thisPLHP.waterSource = true;
1966 4 : condenserNodeType = DataLoopNode::NodeFluidType::Water;
1967 4 : condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Inlet;
1968 4 : condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Outlet;
1969 14 : } else if (condenserType == "AIRSOURCE") {
1970 14 : thisPLHP.airSource = true;
1971 14 : condenserNodeType = DataLoopNode::NodeFluidType::Air;
1972 14 : condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Inlet;
1973 14 : condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Outlet;
1974 14 : if (sourceSideInletNodeName == sourceSideOutletNodeName) {
1975 0 : ShowSevereError(state, format("PlantLoopHeatPump {} has the same inlet and outlet node.", thisObjectName));
1976 0 : ShowContinueError(state, format("Node Name: {}", sourceSideInletNodeName));
1977 0 : errorsFound = true;
1978 : }
1979 : } else {
1980 : // Again, this should be protected by the input processor
1981 0 : ShowErrorMessage(
1982 : state, format("Invalid heat pump condenser type (name={}; entered type: {}", thisPLHP.name, condenserType)); // LCOV_EXCL_LINE
1983 : errorsFound = true; // LCOV_EXCL_LINE
1984 : }
1985 18 : thisPLHP.sourceSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
1986 : sourceSideInletNodeName,
1987 : nodeErrorsFound,
1988 : objType,
1989 : thisPLHP.name,
1990 : condenserNodeType,
1991 : condenserNodeConnectionType_Inlet,
1992 : NodeInputManager::CompFluidStream::Secondary,
1993 : DataLoopNode::ObjectIsNotParent);
1994 18 : thisPLHP.sourceSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
1995 : sourceSideOutletNodeName,
1996 : nodeErrorsFound,
1997 : objType,
1998 : thisPLHP.name,
1999 : condenserNodeType,
2000 : condenserNodeConnectionType_Outlet,
2001 : NodeInputManager::CompFluidStream::Secondary,
2002 : DataLoopNode::ObjectIsNotParent);
2003 :
2004 : // heat recovery inputs
2005 18 : std::string heatRecoveryInletNodeName;
2006 18 : std::string heatRecoveryOutletNodeName;
2007 36 : auto const hrInletNodeName = fields.find("heat_recovery_inlet_node_name");
2008 18 : auto const hrOutletNodeName = fields.find("heat_recovery_outlet_node_name");
2009 18 : if (hrInletNodeName != fields.end() && hrOutletNodeName != fields.end()) {
2010 2 : heatRecoveryInletNodeName = Util::makeUPPER(fields.at("heat_recovery_inlet_node_name").get<std::string>());
2011 1 : heatRecoveryOutletNodeName = Util::makeUPPER(fields.at("heat_recovery_outlet_node_name").get<std::string>());
2012 1 : thisPLHP.heatRecoveryAvailable = true;
2013 : } else {
2014 17 : thisPLHP.heatRecoveryAvailable = false;
2015 : }
2016 :
2017 18 : if (thisPLHP.airSource && thisPLHP.heatRecoveryAvailable) {
2018 1 : thisPLHP.heatRecoveryNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
2019 : heatRecoveryInletNodeName,
2020 : nodeErrorsFound,
2021 : objType,
2022 : thisPLHP.name,
2023 : DataLoopNode::NodeFluidType::Water,
2024 : DataLoopNode::ConnectionType::Inlet,
2025 : NodeInputManager::CompFluidStream::Tertiary,
2026 : DataLoopNode::ObjectIsNotParent);
2027 1 : thisPLHP.heatRecoveryNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
2028 : heatRecoveryOutletNodeName,
2029 : nodeErrorsFound,
2030 : objType,
2031 : thisPLHP.name,
2032 : DataLoopNode::NodeFluidType::Water,
2033 : DataLoopNode::ConnectionType::Outlet,
2034 : NodeInputManager::CompFluidStream::Tertiary,
2035 : DataLoopNode::ObjectIsNotParent);
2036 :
2037 1 : thisPLHP.heatRecoveryDesignVolFlowRate =
2038 2 : state.dataInputProcessing->inputProcessor->getRealFieldValue(fields, schemaProps, "heat_recovery_reference_flow_rate");
2039 1 : if (thisPLHP.heatRecoveryDesignVolFlowRate == DataSizing::AutoSize) {
2040 1 : thisPLHP.heatRecoveryDesignVolFlowRateWasAutoSized = true;
2041 : }
2042 :
2043 : // fields only in cooling object
2044 1 : if (thisPLHP.heatRecoveryAvailable && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
2045 3 : thisPLHP.maxHeatRecoveryTempLimit = state.dataInputProcessing->inputProcessor->getRealFieldValue(
2046 : fields, schemaProps, "maximum_heat_recovery_outlet_temperature");
2047 : }
2048 : // fields only in heating object
2049 1 : if (thisPLHP.heatRecoveryAvailable && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
2050 0 : thisPLHP.minHeatRecoveryTempLimit = state.dataInputProcessing->inputProcessor->getRealFieldValue(
2051 : fields, schemaProps, "minimum_heat_recovery_outlet_temperature");
2052 : }
2053 : }
2054 :
2055 18 : if (nodeErrorsFound) {
2056 0 : errorsFound = true;
2057 : }
2058 18 : BranchNodeConnections::TestCompSet(
2059 18 : state, cCurrentModuleObject, thisPLHP.name, loadSideInletNodeName, loadSideOutletNodeName, classToInput.nodesType);
2060 :
2061 18 : if (thisPLHP.waterSource) {
2062 8 : BranchNodeConnections::TestCompSet(
2063 : state, cCurrentModuleObject, thisPLHP.name, sourceSideInletNodeName, sourceSideOutletNodeName, "Condenser Water Nodes");
2064 : }
2065 :
2066 18 : if (thisPLHP.airSource && thisPLHP.heatRecoveryAvailable) {
2067 2 : BranchNodeConnections::TestCompSet(state,
2068 : cCurrentModuleObject,
2069 : thisPLHP.name,
2070 : heatRecoveryInletNodeName,
2071 : heatRecoveryOutletNodeName,
2072 : "Heat Recovery Water Nodes");
2073 :
2074 1 : auto const heatRecoveryCapFTempCurveName = fields.find("heat_recovery_capacity_modifier_function_of_temperature_curve_name");
2075 1 : if (heatRecoveryCapFTempCurveName != fields.end()) {
2076 1 : thisPLHP.heatRecoveryCapFTempCurveIndex =
2077 1 : Curve::GetCurveIndex(state, Util::makeUPPER(heatRecoveryCapFTempCurveName.value().get<std::string>()));
2078 : }
2079 : auto const heatRecoveryEIRFTempCurveName =
2080 1 : fields.find("heat_recovery_electric_input_to_output_ratio_modifier_function_of_temperature_curve_name");
2081 1 : if (heatRecoveryEIRFTempCurveName != fields.end()) {
2082 1 : thisPLHP.heatRecoveryEIRFTempCurveIndex =
2083 1 : Curve::GetCurveIndex(state, Util::makeUPPER(heatRecoveryEIRFTempCurveName.value().get<std::string>()));
2084 : }
2085 1 : }
2086 :
2087 18 : if (thisPLHP.airSource && thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating &&
2088 7 : thisPLHP.defrostStrategy != DefrostControl::None) {
2089 6 : thisPLHP.defrostAvailable = true;
2090 : }
2091 : // store the worker functions that generalized the heating/cooling sides
2092 18 : thisPLHP.calcLoadOutletTemp = classToInput.calcLoadOutletTemp;
2093 18 : thisPLHP.calcQsource = classToInput.calcQsource;
2094 18 : thisPLHP.calcSourceOutletTemp = classToInput.calcSourceOutletTemp;
2095 : // heat recovery
2096 18 : thisPLHP.calcQheatRecovery = classToInput.calcQheatRecovery;
2097 18 : thisPLHP.calcHROutletTemp = classToInput.calcHROutletTemp;
2098 :
2099 18 : if (!errorsFound) {
2100 18 : state.dataEIRPlantLoopHeatPump->heatPumps.push_back(thisPLHP);
2101 : }
2102 28 : }
2103 10 : }
2104 : }
2105 5 : if (errorsFound) {
2106 : // currently there are no straightforward unit tests possible to get here
2107 : // all curves are required and inputs are validated by the input processor
2108 : // obviously this will stay here but I don't feel like counting it against coverage
2109 : ShowFatalError(state, "Previous EIR PLHP errors cause program termination"); // LCOV_EXCL_LINE
2110 : }
2111 5 : }
2112 :
2113 763271 : void EIRPlantLoopHeatPump::checkConcurrentOperation(EnergyPlusData &state)
2114 : {
2115 : // This will do a recurring warning for concurrent companion operation.
2116 : // This function should be called at the end of the time-step to ensure any iteration-level operation
2117 : // is worked out and the results are final.
2118 : // This function does not try to be intelligent about only reporting for one of the companions. The only
2119 : // way I could think of was to have a vector, either static here or in the namespace, that would hold
2120 : // companion index values as I warn against their partner, so then I would have to add the values to the
2121 : // vector each pass, and check then each loop. This seemed really bulky and inefficient, so I chose to
2122 : // leave a tight loop here of just reporting for each coil if it and the companion are running.
2123 773657 : for (auto &thisPLHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
2124 10386 : if (!thisPLHP.companionHeatPumpCoil) {
2125 0 : continue;
2126 : }
2127 10386 : if (thisPLHP.running && thisPLHP.companionHeatPumpCoil->running && !thisPLHP.companionHeatPumpCoil->heatRecoveryAvailable) {
2128 0 : ShowRecurringWarningErrorAtEnd(state,
2129 0 : "Companion heat pump objects running concurrently, check operation. Base object name: " + thisPLHP.name,
2130 0 : thisPLHP.recurringConcurrentOperationWarningIndex);
2131 : }
2132 763271 : }
2133 763271 : }
2134 :
2135 125 : void EIRPlantLoopHeatPump::isPlantInletOrOutlet(EnergyPlusData &state)
2136 : {
2137 : // check to see if component is on a plant inlet or outlet branch to determine if flow should be registered
2138 : // only components on plant parallel component branches should be registered
2139 : // this check for the load side on a plant inlet branch and source side on a plant outlet branch
2140 : // likely will need more checking here but this works for now with existing test file
2141 125 : bool loadSideIsPlantInlet = false;
2142 125 : bool sourceSideIsPlantOutlet = false;
2143 531 : for (auto thisPlant : state.dataPlnt->PlantLoop) {
2144 1275 : for (auto thisLoopSide : thisPlant.LoopSide) {
2145 851 : if (this->loadSideNodes.inlet == thisLoopSide.NodeNumIn) {
2146 2 : loadSideIsPlantInlet = true;
2147 : }
2148 851 : if (this->sourceSideNodes.outlet == thisLoopSide.NodeNumOut) {
2149 12 : sourceSideIsPlantOutlet = true;
2150 : }
2151 851 : if (loadSideIsPlantInlet && sourceSideIsPlantOutlet) {
2152 2 : this->heatRecoveryHeatPump = true;
2153 2 : break;
2154 : }
2155 851 : }
2156 426 : if (this->heatRecoveryHeatPump) {
2157 20 : break;
2158 : }
2159 426 : }
2160 125 : }
2161 :
2162 115 : void EIRPlantLoopHeatPump::oneTimeInit(EnergyPlusData &state)
2163 : {
2164 : // This function does all the one-time initialization
2165 115 : constexpr std::string_view routineName = "EIRPlantLoopHeatPump : oneTimeInit"; // + __FUNCTION__;
2166 :
2167 115 : if (this->oneTimeInitFlag) {
2168 18 : bool errFlag = false;
2169 :
2170 : // setup output variables
2171 36 : SetupOutputVariable(state,
2172 : "Heat Pump Part Load Ratio",
2173 : Constant::Units::None,
2174 18 : this->partLoadRatio,
2175 : OutputProcessor::TimeStepType::System,
2176 : OutputProcessor::StoreType::Average,
2177 18 : this->name);
2178 36 : SetupOutputVariable(state,
2179 : "Heat Pump Cycling Ratio",
2180 : Constant::Units::None,
2181 18 : this->cyclingRatio,
2182 : OutputProcessor::TimeStepType::System,
2183 : OutputProcessor::StoreType::Average,
2184 18 : this->name);
2185 36 : SetupOutputVariable(state,
2186 : "Heat Pump Load Side Heat Transfer Rate",
2187 : Constant::Units::W,
2188 18 : this->loadSideHeatTransfer,
2189 : OutputProcessor::TimeStepType::System,
2190 : OutputProcessor::StoreType::Average,
2191 18 : this->name);
2192 36 : SetupOutputVariable(state,
2193 : "Heat Pump Load Side Heat Transfer Energy",
2194 : Constant::Units::J,
2195 18 : this->loadSideEnergy,
2196 : OutputProcessor::TimeStepType::System,
2197 : OutputProcessor::StoreType::Sum,
2198 18 : this->name,
2199 : Constant::eResource::EnergyTransfer,
2200 : OutputProcessor::Group::Plant);
2201 36 : SetupOutputVariable(state,
2202 : "Heat Pump Source Side Heat Transfer Rate",
2203 : Constant::Units::W,
2204 18 : this->sourceSideHeatTransfer,
2205 : OutputProcessor::TimeStepType::System,
2206 : OutputProcessor::StoreType::Average,
2207 18 : this->name);
2208 36 : SetupOutputVariable(state,
2209 : "Heat Pump Source Side Heat Transfer Energy",
2210 : Constant::Units::J,
2211 18 : this->sourceSideEnergy,
2212 : OutputProcessor::TimeStepType::System,
2213 : OutputProcessor::StoreType::Sum,
2214 18 : this->name);
2215 36 : SetupOutputVariable(state,
2216 : "Heat Pump Load Side Inlet Temperature",
2217 : Constant::Units::C,
2218 18 : this->loadSideInletTemp,
2219 : OutputProcessor::TimeStepType::System,
2220 : OutputProcessor::StoreType::Average,
2221 18 : this->name);
2222 36 : SetupOutputVariable(state,
2223 : "Heat Pump Load Side Outlet Temperature",
2224 : Constant::Units::C,
2225 18 : this->loadSideOutletTemp,
2226 : OutputProcessor::TimeStepType::System,
2227 : OutputProcessor::StoreType::Average,
2228 18 : this->name);
2229 36 : SetupOutputVariable(state,
2230 : "Heat Pump Source Side Inlet Temperature",
2231 : Constant::Units::C,
2232 18 : this->sourceSideInletTemp,
2233 : OutputProcessor::TimeStepType::System,
2234 : OutputProcessor::StoreType::Average,
2235 18 : this->name);
2236 36 : SetupOutputVariable(state,
2237 : "Heat Pump Source Side Outlet Temperature",
2238 : Constant::Units::C,
2239 18 : this->sourceSideOutletTemp,
2240 : OutputProcessor::TimeStepType::System,
2241 : OutputProcessor::StoreType::Average,
2242 18 : this->name);
2243 36 : SetupOutputVariable(state,
2244 : "Heat Pump Electricity Rate",
2245 : Constant::Units::W,
2246 18 : this->powerUsage,
2247 : OutputProcessor::TimeStepType::System,
2248 : OutputProcessor::StoreType::Average,
2249 18 : this->name);
2250 18 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) { // energy from HeatPump:PlantLoop:EIR:Cooling object
2251 18 : SetupOutputVariable(state,
2252 : "Heat Pump Electricity Energy",
2253 : Constant::Units::J,
2254 9 : this->powerEnergy,
2255 : OutputProcessor::TimeStepType::System,
2256 : OutputProcessor::StoreType::Sum,
2257 9 : this->name,
2258 : Constant::eResource::Electricity,
2259 : OutputProcessor::Group::Plant,
2260 : OutputProcessor::EndUseCat::Cooling,
2261 : "Heat Pump");
2262 9 : SetupOutputVariable(state,
2263 : "Thermosiphon Status",
2264 : Constant::Units::None,
2265 9 : this->thermosiphonStatus,
2266 : OutputProcessor::TimeStepType::System,
2267 : OutputProcessor::StoreType::Average,
2268 9 : this->name);
2269 9 : } else if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) { // energy from HeatPump:PlantLoop:EIR:Heating object
2270 18 : SetupOutputVariable(state,
2271 : "Heat Pump Electricity Energy",
2272 : Constant::Units::J,
2273 9 : this->powerEnergy,
2274 : OutputProcessor::TimeStepType::System,
2275 : OutputProcessor::StoreType::Sum,
2276 9 : this->name,
2277 : Constant::eResource::Electricity,
2278 : OutputProcessor::Group::Plant,
2279 : OutputProcessor::EndUseCat::Heating,
2280 : "Heat Pump");
2281 9 : if (this->defrostAvailable) {
2282 12 : SetupOutputVariable(state,
2283 : "Heat Pump Load Due To Defrost",
2284 : Constant::Units::W,
2285 6 : this->loadDueToDefrost,
2286 : OutputProcessor::TimeStepType::System,
2287 : OutputProcessor::StoreType::Average,
2288 6 : this->name);
2289 12 : SetupOutputVariable(state,
2290 : "Heat Pump Fractioal Defrost Time",
2291 : Constant::Units::W,
2292 6 : this->fractionalDefrostTime,
2293 : OutputProcessor::TimeStepType::System,
2294 : OutputProcessor::StoreType::Average,
2295 6 : this->name);
2296 12 : SetupOutputVariable(state,
2297 : "Heat Pump Defrost Electricity Rate",
2298 : Constant::Units::W,
2299 6 : this->defrostEnergyRate,
2300 : OutputProcessor::TimeStepType::System,
2301 : OutputProcessor::StoreType::Average,
2302 6 : this->name);
2303 12 : SetupOutputVariable(state,
2304 : "Heat Pump Defrost Electricity Energy",
2305 : Constant::Units::J,
2306 6 : this->defrostEnergy,
2307 : OutputProcessor::TimeStepType::System,
2308 : OutputProcessor::StoreType::Sum,
2309 6 : this->name,
2310 : Constant::eResource::Electricity,
2311 : OutputProcessor::Group::Plant,
2312 : OutputProcessor::EndUseCat::Heating,
2313 : "Heat Pump");
2314 : }
2315 : }
2316 36 : SetupOutputVariable(state,
2317 : "Heat Pump Load Side Mass Flow Rate",
2318 : Constant::Units::kg_s,
2319 18 : this->loadSideMassFlowRate,
2320 : OutputProcessor::TimeStepType::System,
2321 : OutputProcessor::StoreType::Average,
2322 18 : this->name);
2323 36 : SetupOutputVariable(state,
2324 : "Heat Pump Source Side Mass Flow Rate",
2325 : Constant::Units::kg_s,
2326 18 : this->sourceSideMassFlowRate,
2327 : OutputProcessor::TimeStepType::System,
2328 : OutputProcessor::StoreType::Average,
2329 18 : this->name);
2330 : // report variable used for debugging, System Node Specific Heat can also report the node Cp
2331 : // added spaces to SetupOutputVariable to avoid issue with variable parsing script
2332 : // Setup Output Variable(state,
2333 : // "Heat Pump Source Side Specific Heat",
2334 : // Constant::Units::J_kgK,
2335 : // this->sourceSideCp,
2336 : // OutputProcessor::TimeStepType::System,
2337 : // OutputProcessor::StoreType::Average,
2338 : // this->name);
2339 :
2340 18 : if (this->heatRecoveryAvailable) {
2341 2 : SetupOutputVariable(state,
2342 : "Heat Pump Heat Recovery Heat Transfer Rate",
2343 : Constant::Units::W,
2344 1 : this->heatRecoveryRate,
2345 : OutputProcessor::TimeStepType::System,
2346 : OutputProcessor::StoreType::Average,
2347 1 : this->name);
2348 2 : SetupOutputVariable(state,
2349 : "Heat Pump Heat Recovery Heat Transfer Energy",
2350 : Constant::Units::J,
2351 1 : this->heatRecoveryEnergy,
2352 : OutputProcessor::TimeStepType::System,
2353 : OutputProcessor::StoreType::Sum,
2354 1 : this->name);
2355 :
2356 2 : SetupOutputVariable(state,
2357 : "Heat Pump Heat Recovery Inlet Temperature",
2358 : Constant::Units::C,
2359 1 : this->heatRecoveryInletTemp,
2360 : OutputProcessor::TimeStepType::System,
2361 : OutputProcessor::StoreType::Average,
2362 1 : this->name);
2363 2 : SetupOutputVariable(state,
2364 : "Heat Pump Heat Recovery Outlet Temperature",
2365 : Constant::Units::C,
2366 1 : this->heatRecoveryOutletTemp,
2367 : OutputProcessor::TimeStepType::System,
2368 : OutputProcessor::StoreType::Average,
2369 1 : this->name);
2370 2 : SetupOutputVariable(state,
2371 : "Heat Pump Heat Recovery Mass Flow Rate",
2372 : Constant::Units::kg_s,
2373 1 : this->heatRecoveryMassFlowRate,
2374 : OutputProcessor::TimeStepType::System,
2375 : OutputProcessor::StoreType::Average,
2376 1 : this->name);
2377 1 : SetupOutputVariable(state,
2378 : "Heat Pump Heat Recovery Operation Status",
2379 : Constant::Units::None,
2380 1 : this->heatRecoveryOperatingStatus,
2381 : OutputProcessor::TimeStepType::System,
2382 : OutputProcessor::StoreType::Average,
2383 1 : this->name);
2384 : }
2385 :
2386 : // find this component on the plant
2387 18 : bool thisErrFlag = false;
2388 54 : PlantUtilities::ScanPlantLoopsForObject(
2389 36 : state, this->name, this->EIRHPType, this->loadSidePlantLoc, thisErrFlag, _, _, _, this->loadSideNodes.inlet, _);
2390 :
2391 18 : if (thisErrFlag) {
2392 0 : ShowSevereError(state,
2393 0 : format("{}: Plant topology problem for {} name = \"{}\"",
2394 : routineName,
2395 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2396 0 : this->name));
2397 0 : ShowContinueError(state, "Could not locate component's load side connections on a plant loop");
2398 0 : errFlag = true;
2399 18 : } else if (this->loadSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Supply) { // only check if !thisErrFlag
2400 0 : ShowSevereError(state,
2401 0 : format("{}: Invalid connections for {} name = \"{}\"",
2402 : routineName,
2403 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2404 0 : this->name));
2405 0 : ShowContinueError(state, "The load side connections are not on the Supply Side of a plant loop");
2406 0 : errFlag = true;
2407 : }
2408 :
2409 18 : thisErrFlag = false;
2410 18 : if (this->waterSource) {
2411 12 : PlantUtilities::ScanPlantLoopsForObject(
2412 8 : state, this->name, this->EIRHPType, this->sourceSidePlantLoc, thisErrFlag, _, _, _, this->sourceSideNodes.inlet, _);
2413 :
2414 4 : if (thisErrFlag) {
2415 0 : ShowSevereError(state,
2416 0 : format("{}: Plant topology problem for {} name = \"{}\"",
2417 : routineName,
2418 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2419 0 : this->name));
2420 0 : ShowContinueError(state, "Could not locate component's source side connections on a plant loop");
2421 0 : errFlag = true;
2422 4 : } else if (this->sourceSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
2423 0 : ShowSevereError(state,
2424 0 : format("{}: Invalid connections for {} name = \"{}\"",
2425 : routineName,
2426 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2427 0 : this->name));
2428 0 : ShowContinueError(state, "The source side connections are not on the Demand Side of a plant loop");
2429 0 : errFlag = true;
2430 : }
2431 :
2432 : // make sure it is not the same loop on both sides.
2433 4 : if (this->loadSidePlantLoc.loopNum == this->sourceSidePlantLoc.loopNum) { // user is being too tricky, don't allow
2434 0 : ShowSevereError(state,
2435 0 : format("{}: Invalid connections for {} name = \"{}\"",
2436 : routineName,
2437 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2438 0 : this->name));
2439 0 : ShowContinueError(state, "The load and source sides need to be on different loops.");
2440 0 : errFlag = true;
2441 : } else {
2442 :
2443 4 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->sourceSidePlantLoc, this->EIRHPType, true);
2444 : }
2445 14 : } else if (this->airSource) {
2446 : // nothing to do here ? not any more
2447 14 : if (this->heatRecoveryAvailable) {
2448 3 : PlantUtilities::ScanPlantLoopsForObject(
2449 2 : state, this->name, this->EIRHPType, this->heatRecoveryPlantLoc, thisErrFlag, _, _, _, this->heatRecoveryNodes.inlet, _);
2450 :
2451 1 : if (thisErrFlag) {
2452 0 : ShowSevereError(state,
2453 0 : format("{}: Plant topology problem for {} name = \"{}\"",
2454 : routineName,
2455 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2456 0 : this->name));
2457 0 : ShowContinueError(state, "Could not locate component's heat recovery side connections on a plant loop.");
2458 0 : errFlag = true;
2459 1 : } else if (this->heatRecoveryPlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
2460 0 : ShowSevereError(state,
2461 0 : format("{}: Invalid connections for {} name = \"{}\"",
2462 : routineName,
2463 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2464 0 : this->name));
2465 0 : ShowContinueError(state, "The heat recovery side connections are not on the Demand Side of a plant loop.");
2466 0 : errFlag = true;
2467 : }
2468 :
2469 : // make sure it is not the same loop on both sides.
2470 1 : if (this->loadSidePlantLoc.loopNum == this->heatRecoveryPlantLoc.loopNum) { // user is being too tricky, don't allow
2471 0 : ShowSevereError(state,
2472 0 : format("{}: Invalid connections for {} name = \"{}\"",
2473 : routineName,
2474 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2475 0 : this->name));
2476 0 : ShowContinueError(state, "The load and heat recovery sides need to be on different loops.");
2477 0 : errFlag = true;
2478 : } else {
2479 :
2480 1 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->heatRecoveryPlantLoc, this->EIRHPType, true);
2481 : }
2482 : }
2483 : }
2484 :
2485 18 : if (errFlag) {
2486 0 : ShowFatalError(state, format("{}: Program terminated due to previous condition(s).", routineName));
2487 : }
2488 18 : this->oneTimeInitFlag = false;
2489 : }
2490 115 : }
2491 :
2492 277306 : bool EIRPlantLoopHeatPump::thermosiphonDisabled(EnergyPlusData &state)
2493 : {
2494 277306 : if (this->thermosiphonTempCurveIndex > 0) {
2495 49336 : this->thermosiphonStatus = 0;
2496 49336 : Real64 dT = this->loadSideOutletTemp - this->sourceSideInletTemp;
2497 49336 : if (dT < this->thermosiphonMinTempDiff) {
2498 40866 : return true;
2499 : }
2500 8470 : Real64 thermosiphonCapFrac = Curve::CurveValue(state, this->thermosiphonTempCurveIndex, dT);
2501 8470 : Real64 capFrac = this->partLoadRatio * this->cyclingRatio;
2502 8470 : if (thermosiphonCapFrac >= capFrac) {
2503 8470 : this->thermosiphonStatus = 1;
2504 8470 : this->powerUsage = 0.0;
2505 8470 : return false;
2506 : }
2507 0 : return true;
2508 : } else {
2509 227970 : return true;
2510 : }
2511 : }
2512 :
2513 0 : Real64 EIRPlantLoopHeatPump::getDynamicMaxCapacity(EnergyPlusData &state)
2514 : {
2515 0 : Real64 sourceInletTemp = state.dataLoopNodes->Node(this->sourceSideNodes.inlet).Temp;
2516 0 : Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
2517 : // evaluate capacity modifier curve and determine load side heat transfer
2518 0 : Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, sourceInletTemp);
2519 0 : return this->referenceCapacity * capacityModifierFuncTemp * heatingCapacityModifierASHP(state);
2520 : }
2521 :
2522 1262221 : void EIRPlantLoopHeatPump::report(EnergyPlusData &state)
2523 : {
2524 :
2525 1262221 : Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec;
2526 :
2527 1262221 : this->defrostEnergy = this->defrostEnergyRate * reportingInterval;
2528 1262221 : this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval;
2529 1262221 : this->powerEnergy = this->powerUsage * reportingInterval;
2530 1262221 : this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval;
2531 1262221 : this->heatRecoveryEnergy = this->heatRecoveryRate * reportingInterval;
2532 :
2533 : // update nodes
2534 1262221 : PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
2535 1262221 : state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp;
2536 1262221 : if (this->waterSource) {
2537 169810 : PlantUtilities::SafeCopyPlantNode(state, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet);
2538 : }
2539 1262221 : state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp;
2540 1262221 : if (this->heatRecoveryAvailable) {
2541 111755 : PlantUtilities::SafeCopyPlantNode(state, this->heatRecoveryNodes.inlet, this->heatRecoveryNodes.outlet);
2542 111755 : state.dataLoopNodes->Node(this->heatRecoveryNodes.outlet).Temp = this->heatRecoveryOutletTemp;
2543 : }
2544 1262221 : }
2545 :
2546 : // From here on, the Fuel Fired Heat Pump module EIRFuelFiredHeatPump
2547 : // Enum string definitions
2548 : static constexpr std::array<std::string_view, static_cast<int>(EIRFuelFiredHeatPump::OATempCurveVar::Num)> OATempCurveVarNamesUC = {"DRYBULB",
2549 : "WETBULB"};
2550 : static constexpr std::array<std::string_view, static_cast<int>(EIRFuelFiredHeatPump::WaterTempCurveVar::Num)> WaterTempCurveVarNamesUC = {
2551 : "ENTERINGCONDENSER", "LEAVINGCONDENSER", "ENTERINGEVAPORATOR", "LEAVINGEVAPORATOR"};
2552 : static constexpr std::array<std::string_view, static_cast<int>(EIRFuelFiredHeatPump::DefrostType::Num)> DefrostTypeNamesUC = {"TIMED", "ONDEMAND"};
2553 :
2554 810 : void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad)
2555 : {
2556 810 : Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec;
2557 :
2558 : // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but
2559 : // I'm not sure we can count on that so we will do one check here to make sure we don't calculate things badly
2560 810 : if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling && currentLoad >= 0.0) ||
2561 810 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0)) {
2562 0 : this->resetReportingVariables();
2563 0 : return;
2564 : }
2565 :
2566 : // get setpoint on the load side outlet
2567 : // Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
2568 :
2569 : // Use a logic similar to that for a boilder: If the specified load is 0.0 or the boiler should not run
2570 : // then we leave this subroutine. Before leaving
2571 : // if the component control is SERIESACTIVE we set the component flow to inlet flow so that flow resolver
2572 : // will not shut down the branch
2573 810 : auto &thisInletNode = state.dataLoopNodes->Node(this->loadSideNodes.inlet);
2574 810 : auto &thisOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet);
2575 810 : auto &thisSourceSideInletNode = state.dataLoopNodes->Node(this->sourceSideNodes.inlet); // OA Intake node
2576 810 : auto &sim_component = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc);
2577 810 : if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0)) {
2578 0 : if (sim_component.FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
2579 0 : this->loadSideMassFlowRate = thisInletNode.MassFlowRate;
2580 : }
2581 0 : this->resetReportingVariables();
2582 0 : return;
2583 : }
2584 :
2585 810 : Real64 CpLoad = this->loadSidePlantLoc.loop->glycol->getSpecificHeat(state, thisInletNode.Temp, "PLFFHPEIR::simulate()");
2586 :
2587 : // Set the current load equal to the FFHP load
2588 810 : Real64 FFHPloadSideLoad = currentLoad; // this->loadSidePlantLoad = MyLoad;
2589 :
2590 : // Determine which air variable to use for GAHP:
2591 : // Source (air) side variable to use
2592 : // auto &thisloadsideinletnode = state.dataLoopNodes->Node(this->loadSideNodes.inlet);
2593 810 : Real64 oaTempforCurve = this->sourceSideInletTemp; // state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
2594 810 : if (this->oaTempCurveInputVar == OATempCurveVar::WetBulb) {
2595 0 : oaTempforCurve = Psychrometrics::PsyTwbFnTdbWPb(
2596 : state, thisSourceSideInletNode.Temp, thisSourceSideInletNode.HumRat, thisSourceSideInletNode.Press, "PLFFHPEIR::doPhysics()");
2597 : }
2598 :
2599 : // Load (water) side temperature variable
2600 810 : Real64 waterTempforCurve = this->loadSideInletTemp;
2601 810 : if (this->waterTempCurveInputVar == WaterTempCurveVar::LeavingCondenser || this->waterTempCurveInputVar == WaterTempCurveVar::LeavingEvaporator) {
2602 0 : waterTempforCurve = this->loadSideOutletTemp;
2603 : }
2604 :
2605 : // evaluate capacity modifier curve and determine load side heat transfer
2606 810 : Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, waterTempforCurve, oaTempforCurve);
2607 :
2608 810 : if (capacityModifierFuncTemp < 0.0) {
2609 0 : if (this->capModFTErrorIndex == 0) {
2610 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
2611 0 : ShowContinueError(state,
2612 0 : format(" Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).", capacityModifierFuncTemp));
2613 0 : ShowContinueError(state,
2614 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
2615 : waterTempforCurve,
2616 : oaTempforCurve));
2617 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
2618 : }
2619 0 : ShowRecurringWarningErrorAtEnd(state,
2620 0 : format("{} \"{}\": Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
2621 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2622 0 : this->name),
2623 0 : this->capModFTErrorIndex,
2624 : capacityModifierFuncTemp,
2625 : capacityModifierFuncTemp);
2626 0 : capacityModifierFuncTemp = 0.0;
2627 : }
2628 :
2629 810 : Real64 availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
2630 810 : Real64 partLoadRatio = 0.0;
2631 810 : if (availableCapacity > 0) {
2632 1620 : partLoadRatio = std::clamp(
2633 810 : std::abs(FFHPloadSideLoad) / availableCapacity, 0.0, 1.0); // max(0.0, min(std::abs(FFHPloadSideLoad) / availableCapacity, 1.0));
2634 : }
2635 :
2636 : // evaluate the actual current operating load side heat transfer rate
2637 :
2638 : // this->loadSideHeatTransfer = availableCapacity * partLoadRatio;
2639 810 : this->loadSideHeatTransfer = availableCapacity * partLoadRatio; // (partLoadRatio >= this->minPLR ? partLoadRatio : 0.0);
2640 :
2641 : // calculate load side outlet conditions
2642 810 : Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad;
2643 810 : this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp);
2644 :
2645 : // calculate power usage from EIR curves
2646 810 : Real64 eirModifierFuncTemp = Curve::CurveValue(state,
2647 : this->powerRatioFuncTempCurveIndex,
2648 : waterTempforCurve,
2649 810 : oaTempforCurve); // CurveManager::CurveValue(state, this->powerRatioFuncTempCurveIndex,
2650 : // this->loadSideOutletTemp, this->sourceSideInletTemp);
2651 :
2652 810 : if (eirModifierFuncTemp < 0.0) {
2653 0 : if (this->eirModFTErrorIndex == 0) {
2654 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
2655 0 : ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
2656 0 : ShowContinueError(state,
2657 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
2658 : waterTempforCurve,
2659 : oaTempforCurve));
2660 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
2661 : }
2662 0 : ShowRecurringWarningErrorAtEnd(state,
2663 0 : format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...",
2664 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2665 0 : this->name),
2666 0 : this->eirModFTErrorIndex,
2667 : eirModifierFuncTemp,
2668 : eirModifierFuncTemp);
2669 0 : eirModifierFuncTemp = 0.0;
2670 : }
2671 :
2672 810 : Real64 miniPLR_mod = this->minPLR;
2673 810 : Real64 PLFf = max(miniPLR_mod, partLoadRatio);
2674 :
2675 810 : Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, PLFf);
2676 : // this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp;
2677 : // this->powerEnergy = this->powerUsage * reportingInterval;
2678 :
2679 810 : if (eirModifierFuncPLR < 0.0) {
2680 0 : if (this->eirModFPLRErrorIndex == 0) {
2681 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
2682 0 : ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR));
2683 0 : ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", PLFf));
2684 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
2685 : }
2686 0 : ShowRecurringWarningErrorAtEnd(state,
2687 0 : format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...",
2688 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2689 0 : this->name),
2690 0 : this->eirModFPLRErrorIndex,
2691 : eirModifierFuncPLR,
2692 : eirModifierFuncPLR);
2693 0 : eirModifierFuncPLR = 0.0;
2694 : }
2695 :
2696 810 : constexpr Real64 minDefrostT = Fahrenheit2Celsius(16.0); // (16.0 - 32.0) * 5.0 / 9.0; // 16F
2697 810 : constexpr Real64 maxDefrostT = Fahrenheit2Celsius(38.0); // (38.0 - 32.0) * 5.0 / 9.0; // 38F
2698 :
2699 810 : Real64 oaTemp2 = std::clamp(oaTempforCurve, minDefrostT, maxDefrostT); // max(minDefrostT, min(maxDefrostT, oaTempforCurve));
2700 810 : Real64 eirDefrost = 1.0;
2701 :
2702 810 : if ((state.dataEnvrn->OutDryBulbTemp <= this->defrostMaxOADBT) && this->defrostType == DefrostType::OnDemand) {
2703 404 : if (this->defrostEIRCurveIndex > 0) {
2704 404 : eirDefrost = Curve::CurveValue(state, this->defrostEIRCurveIndex, oaTemp2);
2705 : }
2706 :
2707 404 : if (eirDefrost < 1.0) {
2708 0 : if (this->eirDefrostFTErrorIndex == 0) {
2709 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
2710 0 : ShowContinueError(state,
2711 0 : format(" EIR defrost Modifier curve (function of Temperature) output is less than 1.0 ({:.3T}).", eirDefrost));
2712 0 : ShowContinueError(state, format(" Negative value occurs using an outdoor air temperature of {:.2T}", oaTemp2));
2713 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to 1.0 and continuing simulation.");
2714 : }
2715 0 : ShowRecurringWarningErrorAtEnd(state,
2716 0 : format("{} \"{}\": EIR Modifier curve (function of PLR) output out of range warning continues...",
2717 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2718 0 : this->name),
2719 0 : this->eirDefrostFTErrorIndex,
2720 : eirDefrost,
2721 : eirDefrost);
2722 0 : eirDefrost = 1.0;
2723 : }
2724 : }
2725 :
2726 : // Cycling Ratio
2727 810 : constexpr Real64 CR_min = 0.0;
2728 810 : constexpr Real64 CR_max = 1.0;
2729 810 : Real64 CR = std::clamp(max(this->minPLR, partLoadRatio) / miniPLR_mod,
2730 : CR_min,
2731 810 : CR_max); // min(max(0.0, max(this->minPLR, partLoadRatio) / miniPLR_mod), 1.0); // partLoadRatio / this->minPLR;
2732 :
2733 810 : constexpr Real64 CRF_Slope = 0.4167;
2734 810 : constexpr Real64 CRF_Intercept = 0.5833;
2735 810 : Real64 CRF = CRF_Slope * CR + CRF_Intercept; // Use the the fixed eqn in the paper as the default curve (or maybe choose constant 1 as default)
2736 810 : if (this->cycRatioCurveIndex > 0) {
2737 810 : CRF = Curve::CurveValue(state, this->cycRatioCurveIndex, CR);
2738 : }
2739 810 : if (CRF <= Constant::rTinyValue) {
2740 0 : CRF = CRF_Intercept; // What could a proper default for too tiny CRF?
2741 : }
2742 :
2743 : // aux elec
2744 810 : Real64 eirAuxElecFuncTemp = 0.0;
2745 810 : if (this->auxElecEIRFoTempCurveIndex > 0) {
2746 810 : eirAuxElecFuncTemp = Curve::CurveValue(state, this->auxElecEIRFoTempCurveIndex, waterTempforCurve, oaTempforCurve);
2747 : }
2748 :
2749 810 : if (eirAuxElecFuncTemp < 0.0) {
2750 0 : if (this->eirAuxElecFTErrorIndex == 0) {
2751 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
2752 0 : ShowContinueError(state,
2753 0 : format(" Auxillary EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirAuxElecFuncTemp));
2754 0 : ShowContinueError(state,
2755 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
2756 : waterTempforCurve,
2757 : oaTempforCurve));
2758 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
2759 : }
2760 0 : ShowRecurringWarningErrorAtEnd(
2761 : state,
2762 0 : format("{} \"{}\": Auxillary EIR Modifier curve (function of Temperatures) output is negative warning continues...",
2763 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2764 0 : this->name),
2765 0 : this->eirAuxElecFTErrorIndex,
2766 : eirAuxElecFuncTemp,
2767 : eirAuxElecFuncTemp);
2768 0 : eirAuxElecFuncTemp = 0.0;
2769 : }
2770 :
2771 810 : Real64 eirAuxElecFuncPLR = 0.0;
2772 810 : if (this->auxElecEIRFoPLRCurveIndex > 0) {
2773 810 : eirAuxElecFuncPLR = Curve::CurveValue(state, this->auxElecEIRFoPLRCurveIndex, partLoadRatio);
2774 : }
2775 :
2776 810 : if (eirAuxElecFuncPLR < 0.0) {
2777 0 : if (this->eirAuxElecFPLRErrorIndex == 0) {
2778 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
2779 0 : ShowContinueError(state,
2780 0 : format(" Auxillary EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirAuxElecFuncPLR));
2781 0 : ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}.", partLoadRatio));
2782 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
2783 : }
2784 0 : ShowRecurringWarningErrorAtEnd(state,
2785 0 : format("{} \"{}\": Auxillary EIR Modifier curve (function of PLR) output is negative warning continues...",
2786 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
2787 0 : this->name),
2788 0 : this->eirAuxElecFPLRErrorIndex,
2789 : eirAuxElecFuncPLR,
2790 : eirAuxElecFuncPLR);
2791 0 : eirAuxElecFuncPLR = 0.0;
2792 : }
2793 :
2794 810 : this->fuelRate = this->loadSideHeatTransfer / (this->referenceCOP * CRF) * eirModifierFuncPLR * eirModifierFuncTemp * eirDefrost;
2795 :
2796 810 : this->powerUsage = this->nominalAuxElecPower * eirAuxElecFuncTemp * eirAuxElecFuncPLR;
2797 810 : if (this->defrostType == DefrostType::Timed) {
2798 0 : this->powerUsage += this->defrostResistiveHeaterCap * this->defrostOpTimeFrac;
2799 : }
2800 :
2801 810 : this->powerUsage += this->standbyElecPower;
2802 :
2803 : // energy balance on heat pump
2804 : // this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
2805 810 : this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->fuelRate + this->powerUsage - this->standbyElecPower);
2806 :
2807 : // calculate source side outlet conditions
2808 810 : Real64 CpSrc = 0.0;
2809 810 : if (this->waterSource) {
2810 0 : auto &thisSourcePlantLoop = state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum);
2811 0 : CpSrc = thisSourcePlantLoop.glycol->getSpecificHeat(state, this->sourceSideInletTemp, "PLFFHPEIR::simulate()");
2812 810 : } else if (this->airSource) {
2813 810 : CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
2814 : }
2815 : // this->sourceSideCp = CpSrc; // debuging variable
2816 : // Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
2817 810 : Real64 const sourceMCp = (this->sourceSideMassFlowRate < 1e-6 ? 1.0 : this->sourceSideMassFlowRate) * CpSrc;
2818 810 : this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
2819 : }
2820 :
2821 0 : void EIRFuelFiredHeatPump::sizeSrcSideASHP(EnergyPlusData &state)
2822 : {
2823 : // size the source-side for the air-source HP
2824 0 : bool errorsFound = false;
2825 :
2826 : // these variables will be used throughout this function as a temporary value of that physical state
2827 0 : Real64 tmpCapacity = this->referenceCapacity;
2828 0 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
2829 0 : Real64 tmpSourceVolFlow = 0.0;
2830 :
2831 : // will leave like this for now
2832 : // need to update these to better values later
2833 0 : Real64 sourceSideInitTemp = 20.0;
2834 0 : Real64 sourceSideHumRat = 0.0;
2835 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
2836 : // same here; update later
2837 0 : sourceSideInitTemp = 20.0;
2838 : }
2839 :
2840 0 : Real64 const rhoSrc = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat);
2841 0 : Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(sourceSideHumRat);
2842 :
2843 : // set the source-side flow rate
2844 0 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
2845 : // load-side capacity should already be set, so unless the flow rate is specified, we can set
2846 : // an assumed reasonable flow rate since this doesn't affect downstream components
2847 0 : Real64 DeltaT_src = 10.0;
2848 : // to get the source flow, we first must calculate the required heat impact on the source side
2849 : // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
2850 : // Then the energy balance: Qsrc = Qload + Power
2851 : // Substituting for Power: Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
2852 0 : Real64 const designSourceSideHeatTransfer = tmpCapacity * (1.0 + 1.0 / this->referenceCOP);
2853 : // To get the design source flow rate, just apply the sensible heat rate equation:
2854 : // Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
2855 : // Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
2856 0 : tmpSourceVolFlow = designSourceSideHeatTransfer / (rhoSrc * CpSrc * DeltaT_src);
2857 0 : } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate > 0.0) {
2858 : // given the value by the user
2859 : // set it directly
2860 0 : tmpSourceVolFlow = this->sourceSideDesignVolFlowRate;
2861 : } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate == 0.0) { // LCOV_EXCL_LINE
2862 : // user gave a flow rate of 0
2863 : // protected by the input processor to be >0.0
2864 : // fatal out just in case
2865 : errorsFound = true; // LCOV_EXCL_LINE
2866 0 : ShowSevereError(state,
2867 0 : format("Invalid condenser flow rate for EIR PLHP (name={}; entered value: {}",
2868 0 : this->name,
2869 : this->sourceSideDesignVolFlowRate)); // LCOV_EXCL_LINE
2870 : } else {
2871 : // can't imagine how it would ever get to this point
2872 : // just assume it's the same as the load side if we don't have any sizing information
2873 : tmpSourceVolFlow = tmpLoadVolFlow; // LCOV_EXCL_LINE
2874 : }
2875 :
2876 0 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
2877 :
2878 0 : if (errorsFound) {
2879 : ShowFatalError(state, "Preceding sizing errors cause program termination"); // LCOV_EXCL_LINE
2880 : }
2881 0 : }
2882 :
2883 836 : void EIRFuelFiredHeatPump::resetReportingVariables()
2884 : {
2885 836 : this->loadSideHeatTransfer = 0.0;
2886 836 : this->loadSideEnergy = 0.0;
2887 836 : this->loadSideOutletTemp = this->loadSideInletTemp;
2888 836 : this->fuelRate = 0.0;
2889 836 : this->fuelEnergy = 0.0;
2890 836 : this->powerUsage = 0.0;
2891 836 : this->powerEnergy = 0.0;
2892 836 : this->sourceSideHeatTransfer = 0.0;
2893 836 : this->sourceSideOutletTemp = this->sourceSideInletTemp;
2894 836 : this->sourceSideEnergy = 0.0;
2895 836 : }
2896 :
2897 2 : PlantComponent *EIRFuelFiredHeatPump::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType hp_type, const std::string &hp_name)
2898 : {
2899 2 : if (state.dataEIRFuelFiredHeatPump->getInputsFFHP) {
2900 1 : EIRFuelFiredHeatPump::processInputForEIRPLHP(state);
2901 1 : EIRFuelFiredHeatPump::pairUpCompanionCoils(state);
2902 1 : state.dataEIRFuelFiredHeatPump->getInputsFFHP = false;
2903 : }
2904 :
2905 3 : for (auto &plhp : state.dataEIRFuelFiredHeatPump->heatPumps) {
2906 3 : if (plhp.name == Util::makeUPPER(hp_name) && plhp.EIRHPType == hp_type) {
2907 2 : return &plhp;
2908 : }
2909 4 : }
2910 :
2911 0 : ShowFatalError(state, format("EIR Fuel-Fired Heat Pump factory: Error getting inputs for PLFFHP named: {}.", hp_name));
2912 : return nullptr; // LCOV_EXCL_LINE
2913 : }
2914 :
2915 1 : void EIRFuelFiredHeatPump::pairUpCompanionCoils(EnergyPlusData &state)
2916 : {
2917 3 : for (auto &thisHP : state.dataEIRFuelFiredHeatPump->heatPumps) {
2918 2 : if (!thisHP.companionCoilName.empty()) {
2919 2 : std::string thisCoilName = Util::makeUPPER(thisHP.name);
2920 2 : DataPlant::PlantEquipmentType thisCoilType = thisHP.EIRHPType;
2921 2 : std::string targetCompanionName = Util::makeUPPER(thisHP.companionCoilName);
2922 3 : for (auto &potentialCompanionCoil : state.dataEIRFuelFiredHeatPump->heatPumps) {
2923 3 : DataPlant::PlantEquipmentType potentialCompanionType = potentialCompanionCoil.EIRHPType;
2924 3 : std::string potentialCompanionName = Util::makeUPPER(potentialCompanionCoil.name);
2925 3 : if (potentialCompanionName == thisCoilName) {
2926 : // skip the current coil
2927 1 : continue;
2928 : }
2929 2 : if (potentialCompanionName == targetCompanionName) {
2930 2 : if (thisCoilType == potentialCompanionType) {
2931 0 : ShowSevereError(state,
2932 0 : format("Invalid companion specification for EIR Plant Loop Fuel-Fired Heat Pump named \"{}\"", thisCoilName));
2933 0 : ShowContinueError(state, "For heating objects, the companion must be a cooling object, and vice-versa");
2934 0 : ShowFatalError(state, "Invalid companion object causes program termination");
2935 : }
2936 2 : thisHP.companionHeatPumpCoil = &potentialCompanionCoil;
2937 2 : break;
2938 : }
2939 5 : }
2940 2 : if (!thisHP.companionHeatPumpCoil) {
2941 0 : ShowSevereError(state, "Could not find matching companion heat pump coil.");
2942 0 : ShowContinueError(state, format("Base coil: {}", thisCoilName));
2943 0 : ShowContinueError(state, format("Looking for companion coil named: {}", targetCompanionName));
2944 0 : ShowFatalError(state, "Simulation aborts due to previous severe error");
2945 : }
2946 2 : }
2947 1 : }
2948 1 : }
2949 :
2950 1 : void EIRFuelFiredHeatPump::processInputForEIRPLHP(EnergyPlusData &state)
2951 : {
2952 : struct ClassType
2953 : {
2954 : DataPlant::PlantEquipmentType thisType;
2955 : std::string nodesType;
2956 : std::function<Real64(Real64, Real64)> calcLoadOutletTemp;
2957 : std::function<Real64(Real64, Real64)> calcQsource;
2958 : std::function<Real64(Real64, Real64)> calcSourceOutletTemp;
2959 :
2960 2 : ClassType(DataPlant::PlantEquipmentType _thisType,
2961 : std::string _nodesType,
2962 : std::function<Real64(Real64, Real64)> _tLoadOutFunc,
2963 : std::function<Real64(Real64, Real64)> _qSrcFunc,
2964 : std::function<Real64(Real64, Real64)> _tSrcOutFunc)
2965 2 : : thisType(_thisType), nodesType(std::move(_nodesType)), calcLoadOutletTemp(_tLoadOutFunc), calcQsource(_qSrcFunc),
2966 2 : calcSourceOutletTemp(_tSrcOutFunc)
2967 : {
2968 2 : }
2969 : };
2970 : std::array<ClassType, 2> classesToInput = {
2971 : ClassType{DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling,
2972 : "Chilled Water Nodes",
2973 : EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::subtract,
2974 : EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::add,
2975 : EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::add},
2976 : ClassType{DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating,
2977 : "Hot Water Nodes",
2978 : EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::add,
2979 : EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::subtract,
2980 : EIRPlantLoopHeatPumps::EIRFuelFiredHeatPump::subtract},
2981 2 : };
2982 :
2983 1 : bool errorsFound = false;
2984 1 : std::string &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
2985 3 : for (auto &classToInput : classesToInput) {
2986 2 : cCurrentModuleObject = DataPlant::PlantEquipTypeNames[static_cast<int>(classToInput.thisType)];
2987 :
2988 : DataLoopNode::ConnectionObjectType objType = static_cast<DataLoopNode::ConnectionObjectType>(
2989 2 : getEnumValue(BranchNodeConnections::ConnectionObjectTypeNamesUC, Util::makeUPPER(cCurrentModuleObject)));
2990 :
2991 2 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
2992 2 : if (instances == state.dataInputProcessing->inputProcessor->epJSON.end()) {
2993 0 : continue;
2994 : }
2995 2 : auto &instancesValue = instances.value();
2996 4 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
2997 2 : auto const &fields = instance.value();
2998 2 : auto const &thisObjectName = instance.key();
2999 2 : state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
3000 :
3001 2 : EIRFuelFiredHeatPump thisPLHP;
3002 :
3003 2 : thisPLHP.EIRHPType = classToInput.thisType;
3004 4 : std::string companionCoilFieldTag = "companion_heating_heat_pump_name";
3005 2 : std::string refCapFieldTag = "nominal_cooling_capacity";
3006 2 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating) {
3007 1 : companionCoilFieldTag = "companion_cooling_heat_pump_name";
3008 1 : refCapFieldTag = "nominal_heating_capacity";
3009 : }
3010 :
3011 : // A1-A3
3012 2 : thisPLHP.name = Util::makeUPPER(thisObjectName);
3013 4 : std::string loadSideInletNodeName = Util::makeUPPER(fields.at("water_inlet_node_name").get<std::string>());
3014 2 : std::string loadSideOutletNodeName = Util::makeUPPER(fields.at("water_outlet_node_name").get<std::string>());
3015 : // Implicit
3016 : // std::string condenserType = "AIRSOURCE"; // Util::makeUPPER(fields.at("condenser_type").get<std::string>());
3017 2 : thisPLHP.airSource = true;
3018 2 : thisPLHP.waterSource = false;
3019 :
3020 : // A4
3021 2 : std::string sourceSideInletNodeName = Util::makeUPPER(fields.at("air_source_node_name").get<std::string>());
3022 : // Util::makeUPPER(fields.at("source_side_outlet_node_name").get<std::string>());
3023 2 : std::string sourceSideOutletNodeName = format("{}_SOURCE_SIDE_OUTLET_NODE", thisPLHP.name);
3024 :
3025 : // A5
3026 2 : auto compCoilFound = fields.find(companionCoilFieldTag);
3027 2 : if (compCoilFound != fields.end()) { // optional field
3028 2 : thisPLHP.companionCoilName = Util::makeUPPER(compCoilFound.value().get<std::string>());
3029 : }
3030 :
3031 : // A6 Fuel Type
3032 2 : std::string tempRsrStr = Util::makeUPPER(fields.at("fuel_type").get<std::string>());
3033 2 : thisPLHP.fuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, tempRsrStr));
3034 : // Validate fuel type input
3035 : static constexpr std::string_view RoutineName("processInputForEIRPLHP: ");
3036 2 : if (thisPLHP.fuelType == Constant::eFuel::Invalid) {
3037 0 : ShowSevereError(state, format("{}{}=\"{}\",", RoutineName, cCurrentModuleObject, thisPLHP.name));
3038 0 : ShowContinueError(state, format("Invalid Fuel Type = {}", tempRsrStr));
3039 0 : ShowContinueError(state, "Reset the Fuel Type to \"NaturalGas\".");
3040 0 : thisPLHP.fuelType = Constant::eFuel::NaturalGas;
3041 0 : errorsFound = true;
3042 : }
3043 :
3044 : // A7 End use category
3045 2 : thisPLHP.endUseSubcat = Util::makeUPPER(fields.at("end_use_subcategory").get<std::string>());
3046 2 : if (thisPLHP.endUseSubcat == "") {
3047 0 : thisPLHP.endUseSubcat = "Heat Pump Fuel Fired"; // or "General"?
3048 : }
3049 :
3050 : // N1 Nominal heating capacity
3051 2 : auto &tmpRefCapacity = fields.at(refCapFieldTag);
3052 :
3053 2 : if (tmpRefCapacity == "Autosize") {
3054 0 : thisPLHP.referenceCapacity = DataSizing::AutoSize;
3055 0 : thisPLHP.referenceCapacityWasAutoSized = true;
3056 : } else {
3057 2 : thisPLHP.referenceCapacity = tmpRefCapacity.get<Real64>();
3058 : }
3059 :
3060 : // N2 Nominal heating capacity
3061 2 : thisPLHP.referenceCOP = fields.at("nominal_cop").get<Real64>();
3062 2 : if (thisPLHP.referenceCOP <= 0.0) {
3063 0 : thisPLHP.referenceCOP = 1.0;
3064 : }
3065 :
3066 : // N3 Design flow rate
3067 2 : auto &tmpFlowRate = fields.at("design_flow_rate");
3068 2 : if (tmpFlowRate == "Autosize") {
3069 0 : thisPLHP.loadSideDesignVolFlowRate = DataSizing::AutoSize;
3070 0 : thisPLHP.loadSideDesignVolFlowRateWasAutoSized = true;
3071 : } else {
3072 2 : thisPLHP.loadSideDesignVolFlowRate = tmpFlowRate.get<Real64>();
3073 : }
3074 :
3075 : // GAHP: Add a default source side flow rate, not from input
3076 2 : Real64 defDummyASDesVolFlowRate = 1.0;
3077 2 : thisPLHP.sourceSideDesignVolFlowRate = defDummyASDesVolFlowRate;
3078 :
3079 : // N4 Design supply temperature
3080 2 : auto &tmpDesSupTemp = fields.at("design_supply_temperature");
3081 2 : if (tmpDesSupTemp == "Autosize") {
3082 : // sizing
3083 : } else {
3084 2 : thisPLHP.desSupplyTemp = tmpDesSupTemp.get<Real64>();
3085 : }
3086 :
3087 : // N5 Design temperature lift
3088 2 : auto &tmpDesTempLift = fields.at("design_temperature_lift");
3089 2 : if (tmpDesTempLift == "Autosize") {
3090 : // sizing
3091 : } else {
3092 2 : thisPLHP.desTempLift = tmpDesTempLift.get<Real64>();
3093 : }
3094 :
3095 : // N6 Sizing factor
3096 2 : auto sizeFactorFound = fields.find("sizing_factor");
3097 2 : if (sizeFactorFound != fields.end()) {
3098 2 : thisPLHP.sizingFactor = sizeFactorFound.value().get<Real64>();
3099 2 : if (thisPLHP.sizingFactor <= 0.0) {
3100 0 : thisPLHP.sizingFactor = 1.0;
3101 : }
3102 : } else {
3103 0 : Real64 defaultVal_sizeFactor = 1.0;
3104 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
3105 : state, cCurrentModuleObject, "sizing_factor", defaultVal_sizeFactor)) {
3106 0 : ShowSevereError(state, "EIR FFHP: Sizing factor not entered and could not get default value");
3107 0 : errorsFound = true;
3108 : } else {
3109 0 : thisPLHP.sizingFactor = defaultVal_sizeFactor;
3110 : }
3111 : }
3112 :
3113 : // A8 flow mode
3114 2 : thisPLHP.flowMode = static_cast<DataPlant::FlowMode>(
3115 4 : getEnumValue(DataPlant::FlowModeNamesUC, Util::makeUPPER(fields.at("flow_mode").get<std::string>())));
3116 :
3117 : // A9 outdoor_air_temperature_curve_input_variable
3118 2 : std::string oaTempCurveInputVar = Util::makeUPPER(fields.at("outdoor_air_temperature_curve_input_variable").get<std::string>());
3119 2 : thisPLHP.oaTempCurveInputVar = static_cast<OATempCurveVar>(getEnumValue(OATempCurveVarNamesUC, oaTempCurveInputVar));
3120 :
3121 : // A10 water_temperature_curve_input_variable
3122 2 : std::string waterTempCurveInputVar = Util::makeUPPER(fields.at("water_temperature_curve_input_variable").get<std::string>());
3123 2 : thisPLHP.waterTempCurveInputVar = static_cast<WaterTempCurveVar>(getEnumValue(WaterTempCurveVarNamesUC, waterTempCurveInputVar));
3124 :
3125 : // A11 normalized_capacity_function_of_temperature_curve_name
3126 2 : std::string const &capFtName = Util::makeUPPER(fields.at("normalized_capacity_function_of_temperature_curve_name").get<std::string>());
3127 :
3128 2 : thisPLHP.capFuncTempCurveIndex = Curve::GetCurveIndex(state, capFtName);
3129 2 : if (thisPLHP.capFuncTempCurveIndex == 0) {
3130 0 : ShowSevereError(state, format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {}", thisPLHP.name, capFtName));
3131 0 : errorsFound = true;
3132 : }
3133 :
3134 : // A12 fuel_energy_input_ratio_function_of_temperature_curve_name
3135 : std::string const &eirFtName =
3136 2 : Util::makeUPPER(fields.at("fuel_energy_input_ratio_function_of_temperature_curve_name").get<std::string>());
3137 2 : thisPLHP.powerRatioFuncTempCurveIndex = Curve::GetCurveIndex(state, eirFtName);
3138 2 : if (thisPLHP.capFuncTempCurveIndex == 0) {
3139 0 : ShowSevereError(state, format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {}", thisPLHP.name, eirFtName));
3140 0 : errorsFound = true;
3141 : }
3142 : // A13 fuel_energy_input_ratio_function_of_plr_curve_name
3143 2 : std::string const &eirFplrName = Util::makeUPPER(fields.at("fuel_energy_input_ratio_function_of_plr_curve_name").get<std::string>());
3144 2 : thisPLHP.powerRatioFuncPLRCurveIndex = Curve::GetCurveIndex(state, eirFplrName);
3145 2 : if (thisPLHP.capFuncTempCurveIndex == 0) {
3146 0 : ShowSevereError(state, format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {}", thisPLHP.name, eirFplrName));
3147 0 : errorsFound = true;
3148 : }
3149 :
3150 : // N7 min PLR
3151 2 : auto minPLRFound = fields.find("minimum_part_load_ratio");
3152 2 : if (minPLRFound != fields.end()) {
3153 2 : thisPLHP.minPLR = minPLRFound.value().get<Real64>();
3154 : } else {
3155 0 : Real64 defaultVal = 0.1;
3156 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "minimum_part_load_ratio", defaultVal)) {
3157 0 : ShowSevereError(state, "EIR PLFFHP: minimum PLR not entered and could not get default value.");
3158 0 : errorsFound = true;
3159 : } else {
3160 0 : thisPLHP.minPLR = defaultVal;
3161 : }
3162 : }
3163 :
3164 : // N8 max PLR
3165 2 : auto maxPLRFound = fields.find("maximum_part_load_ratio");
3166 2 : if (maxPLRFound != fields.end()) {
3167 2 : thisPLHP.maxPLR = maxPLRFound.value().get<Real64>();
3168 : } else {
3169 0 : Real64 defaultVal = 1.0;
3170 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "maximum_part_load_ratio", defaultVal)) {
3171 0 : ShowSevereError(state, "EIR PLFFHP: maximum PLR not entered and could not get default value.");
3172 0 : errorsFound = true;
3173 : } else {
3174 0 : thisPLHP.maxPLR = defaultVal;
3175 : }
3176 : }
3177 :
3178 : // A14 fuel_energy_input_ratio_defrost_adjustment_curve_name
3179 2 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
3180 1 : thisPLHP.defrostEIRCurveIndex = 0;
3181 : } else {
3182 1 : auto eirDefrostCurveFound = fields.find("fuel_energy_input_ratio_defrost_adjustment_curve_name");
3183 1 : if (eirDefrostCurveFound != fields.end()) {
3184 1 : std::string const eirDefrostCurveName = Util::makeUPPER(eirDefrostCurveFound.value().get<std::string>());
3185 1 : thisPLHP.defrostEIRCurveIndex = Curve::GetCurveIndex(state, eirDefrostCurveName);
3186 1 : if (thisPLHP.defrostEIRCurveIndex == 0) {
3187 0 : ShowSevereError(
3188 0 : state, format("Invalid curve name for EIR FFHP (name={}; entered curve name: {}", thisPLHP.name, eirDefrostCurveName));
3189 0 : errorsFound = true;
3190 : }
3191 1 : } else {
3192 0 : thisPLHP.defrostEIRCurveIndex = 0;
3193 : }
3194 1 : }
3195 :
3196 : // A15 defrost_control_type
3197 2 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
3198 1 : thisPLHP.defrostType = DefrostType::Invalid;
3199 : } else {
3200 1 : thisPLHP.defrostType =
3201 1 : static_cast<DefrostType>(getEnumValue(DefrostTypeNamesUC, Util::makeUPPER(fields.at("defrost_control_type").get<std::string>())));
3202 1 : if (thisPLHP.defrostType == DefrostType::Invalid) {
3203 0 : thisPLHP.defrostType = DefrostType::OnDemand; // set to default
3204 0 : thisPLHP.defrostOpTimeFrac = 0.0;
3205 0 : ShowWarningError(state, format("Invalid Defrost Control Type for EIR PLFFHP ({} name={})", cCurrentModuleObject, thisPLHP.name));
3206 0 : ShowContinueError(state,
3207 0 : format("The Input Variable is reset to: {}", DefrostTypeNamesUC[static_cast<int>(thisPLHP.defrostType)]));
3208 : }
3209 : }
3210 :
3211 : // N9 defrost_operation_time_fraction
3212 2 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
3213 1 : thisPLHP.defrostOpTimeFrac = 0.0;
3214 : } else {
3215 1 : auto defrostOpTimeFracFound = fields.find("defrost_operation_time_fraction");
3216 1 : if (defrostOpTimeFracFound != fields.end()) {
3217 1 : thisPLHP.defrostOpTimeFrac = defrostOpTimeFracFound.value().get<Real64>();
3218 : } else {
3219 0 : Real64 defaultVal = 0.0;
3220 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
3221 : state, cCurrentModuleObject, "defrost_operation_time_fraction", defaultVal)) {
3222 0 : ShowSevereError(state, "EIR PLFFHP: defrost time fraction not entered and could not get default value.");
3223 0 : errorsFound = true;
3224 : } else {
3225 0 : thisPLHP.defrostOpTimeFrac = defaultVal;
3226 : }
3227 : }
3228 1 : }
3229 :
3230 : // N10 Resistive Defrost Heater Capacity
3231 2 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
3232 1 : thisPLHP.defrostResistiveHeaterCap = 0.0;
3233 : } else {
3234 1 : auto resDefrostHeaterCapFound = fields.find("resistive_defrost_heater_capacity");
3235 1 : if (resDefrostHeaterCapFound != fields.end()) {
3236 1 : thisPLHP.defrostResistiveHeaterCap = resDefrostHeaterCapFound.value().get<Real64>();
3237 : } else {
3238 0 : Real64 defaultVal = 0.0;
3239 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
3240 : state, cCurrentModuleObject, "resistive_defrost_heater_capacity", defaultVal)) {
3241 0 : ShowSevereError(state, "EIR PLFFHP: Resistive Defrost Heater Capacity not entered and could not get default value.");
3242 0 : errorsFound = true;
3243 : } else {
3244 0 : thisPLHP.defrostResistiveHeaterCap = defaultVal;
3245 : }
3246 : }
3247 1 : }
3248 :
3249 : // N11 maximum_outdoor_dry_bulb_temperature_for_defrost_operation
3250 2 : if (thisPLHP.EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
3251 1 : thisPLHP.defrostMaxOADBT = -99.0;
3252 : } else {
3253 1 : auto maxOADBTFound = fields.find("maximum_outdoor_dry_bulb_temperature_for_defrost_operation");
3254 1 : if (maxOADBTFound != fields.end()) {
3255 1 : thisPLHP.defrostMaxOADBT = maxOADBTFound.value().get<Real64>();
3256 : } else {
3257 0 : Real64 defaultVal = 5.0;
3258 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
3259 : state, cCurrentModuleObject, "maximum_outdoor_dry_bulb_temperature_for_defrost_operation", defaultVal)) {
3260 0 : ShowSevereError(state, "EIR PLFFHP: max defrost operation OA temperature not entered and could not get default value.");
3261 0 : errorsFound = true;
3262 : } else {
3263 0 : thisPLHP.defrostMaxOADBT = defaultVal;
3264 : }
3265 : }
3266 1 : }
3267 :
3268 : // A16 cycling_ratio_factor_curve_name
3269 2 : auto crfCurveFound = fields.find("cycling_ratio_factor_curve_name");
3270 2 : if (crfCurveFound != fields.end()) {
3271 2 : std::string const cycRatioCurveName = Util::makeUPPER(crfCurveFound.value().get<std::string>());
3272 2 : thisPLHP.cycRatioCurveIndex = Curve::GetCurveIndex(state, cycRatioCurveName);
3273 2 : if (thisPLHP.cycRatioCurveIndex == 0) {
3274 0 : ShowSevereError(state,
3275 0 : format("Invalid curve name for EIR PLFFHP (name={}; entered curve name: {})", thisPLHP.name, cycRatioCurveName));
3276 0 : errorsFound = true;
3277 : }
3278 2 : } else {
3279 0 : thisPLHP.cycRatioCurveIndex = 0;
3280 : }
3281 :
3282 : // N12 nominal_auxiliary_electric_power
3283 2 : auto nomAuxElecPowerFound = fields.find("nominal_auxiliary_electric_power");
3284 2 : if (nomAuxElecPowerFound != fields.end()) {
3285 2 : thisPLHP.nominalAuxElecPower = nomAuxElecPowerFound.value().get<Real64>();
3286 : } else {
3287 0 : Real64 defaultVal = 0.0;
3288 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
3289 : state, cCurrentModuleObject, "nominal_auxiliary_electric_power", defaultVal)) {
3290 0 : ShowSevereError(state, "EIR PLFFHP: nominal auxiliary electric power not entered and could not get default value.");
3291 0 : errorsFound = true;
3292 : } else {
3293 0 : thisPLHP.nominalAuxElecPower = defaultVal;
3294 : }
3295 : }
3296 :
3297 : // A17 auxiliary_electric_energy_input_ratio_function_of_temperature_curve_name
3298 2 : auto auxElecEIRFTCurveFound = fields.find("auxiliary_electric_energy_input_ratio_function_of_temperature_curve_name");
3299 2 : if (auxElecEIRFTCurveFound != fields.end()) {
3300 2 : std::string const &auxEIRFTName = Util::makeUPPER(auxElecEIRFTCurveFound.value().get<std::string>());
3301 2 : thisPLHP.auxElecEIRFoTempCurveIndex = Curve::GetCurveIndex(state, auxEIRFTName);
3302 2 : if (thisPLHP.auxElecEIRFoTempCurveIndex == 0) {
3303 0 : ShowSevereError(state, format("Invalid curve name for EIR FFHP (name={}; entered curve name: {}", thisPLHP.name, auxEIRFTName));
3304 0 : errorsFound = true;
3305 : }
3306 2 : } else {
3307 0 : thisPLHP.auxElecEIRFoTempCurveIndex = 0;
3308 : }
3309 :
3310 : // A18 auxiliary_electric_energy_input_ratio_function_of_plr_curve_name
3311 2 : auto auxElecEIRFPLRCurveFound = fields.find("auxiliary_electric_energy_input_ratio_function_of_plr_curve_name");
3312 2 : if (auxElecEIRFPLRCurveFound != fields.end()) {
3313 2 : std::string const &auxEIRFPLRName = Util::makeUPPER(auxElecEIRFPLRCurveFound.value().get<std::string>());
3314 2 : thisPLHP.auxElecEIRFoPLRCurveIndex = Curve::GetCurveIndex(state, auxEIRFPLRName);
3315 2 : if (thisPLHP.auxElecEIRFoPLRCurveIndex == 0) {
3316 0 : ShowSevereError(state, format("Invalid curve name for EIR FFHP (name={}; entered curve name: {}", thisPLHP.name, auxEIRFPLRName));
3317 0 : errorsFound = true;
3318 : }
3319 2 : } else {
3320 0 : thisPLHP.auxElecEIRFoPLRCurveIndex = 0;
3321 : }
3322 :
3323 : // N13 standby_electric_power
3324 2 : auto stdElecPwrFound = fields.find("standby_electric_power");
3325 2 : if (stdElecPwrFound != fields.end()) {
3326 2 : thisPLHP.standbyElecPower = stdElecPwrFound.value().get<Real64>();
3327 : } else {
3328 0 : Real64 defaultVal = 0.0;
3329 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "standby_electric_power", defaultVal)) {
3330 0 : ShowSevereError(state, "EIR FFHP: standby electric power not entered and could not get default value.");
3331 0 : errorsFound = true;
3332 : } else {
3333 0 : thisPLHP.standbyElecPower = defaultVal;
3334 : }
3335 : }
3336 :
3337 2 : bool nodeErrorsFound = false;
3338 2 : thisPLHP.loadSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
3339 : loadSideInletNodeName,
3340 : nodeErrorsFound,
3341 : objType,
3342 : thisPLHP.name,
3343 : DataLoopNode::NodeFluidType::Water,
3344 : DataLoopNode::ConnectionType::Inlet,
3345 : NodeInputManager::CompFluidStream::Primary,
3346 : DataLoopNode::ObjectIsNotParent);
3347 2 : thisPLHP.loadSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
3348 : loadSideOutletNodeName,
3349 : nodeErrorsFound,
3350 : objType,
3351 : thisPLHP.name,
3352 : DataLoopNode::NodeFluidType::Water,
3353 : DataLoopNode::ConnectionType::Outlet,
3354 : NodeInputManager::CompFluidStream::Primary,
3355 : DataLoopNode::ObjectIsNotParent);
3356 :
3357 2 : thisPLHP.airSource = true; // this is always true, at least for now, for Fuel-Fired PlantLoop Heat Pump
3358 2 : thisPLHP.waterSource = false; // this is always false, at least for now, for Fuel-Fired PlantLoop Heat Pump
3359 2 : thisPLHP.sourceSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
3360 : sourceSideInletNodeName,
3361 : nodeErrorsFound,
3362 : objType,
3363 : thisPLHP.name,
3364 : DataLoopNode::NodeFluidType::Air,
3365 : DataLoopNode::ConnectionType::OutsideAir,
3366 : NodeInputManager::CompFluidStream::Secondary,
3367 : DataLoopNode::ObjectIsNotParent);
3368 2 : thisPLHP.sourceSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
3369 : sourceSideOutletNodeName,
3370 : nodeErrorsFound,
3371 : objType,
3372 : thisPLHP.name,
3373 : DataLoopNode::NodeFluidType::Air,
3374 : DataLoopNode::ConnectionType::OutsideAir,
3375 : NodeInputManager::CompFluidStream::Secondary,
3376 : DataLoopNode::ObjectIsNotParent);
3377 :
3378 2 : if (nodeErrorsFound) {
3379 0 : errorsFound = true;
3380 : }
3381 2 : BranchNodeConnections::TestCompSet(
3382 2 : state, cCurrentModuleObject, thisPLHP.name, loadSideInletNodeName, loadSideOutletNodeName, classToInput.nodesType);
3383 :
3384 : // store the worker functions that generalized the heating/cooling sides
3385 2 : thisPLHP.calcLoadOutletTemp = classToInput.calcLoadOutletTemp;
3386 2 : thisPLHP.calcQsource = classToInput.calcQsource;
3387 2 : thisPLHP.calcSourceOutletTemp = classToInput.calcSourceOutletTemp;
3388 :
3389 2 : if (!errorsFound) {
3390 2 : state.dataEIRFuelFiredHeatPump->heatPumps.push_back(thisPLHP);
3391 : }
3392 4 : }
3393 2 : }
3394 1 : if (errorsFound) {
3395 : ShowFatalError(state, "Previous EIR PLFFHP errors cause program termination."); // LCOV_EXCL_LINE
3396 : }
3397 1 : }
3398 :
3399 10 : void EIRFuelFiredHeatPump::oneTimeInit(EnergyPlusData &state)
3400 : {
3401 : // This function does all the one-time initialization
3402 10 : constexpr std::string_view routineName = "EIRFuelFiredHeatPump : oneTimeInit"; // + __FUNCTION__;
3403 :
3404 10 : if (this->oneTimeInitFlag) {
3405 2 : bool errFlag = false;
3406 :
3407 : // setup output variables
3408 4 : SetupOutputVariable(state,
3409 : "Fuel-fired Absorption HeatPump Load Side Heat Transfer Rate",
3410 : Constant::Units::W,
3411 2 : this->loadSideHeatTransfer,
3412 : OutputProcessor::TimeStepType::System,
3413 : OutputProcessor::StoreType::Average,
3414 2 : this->name);
3415 4 : SetupOutputVariable(state,
3416 : "Fuel-fired Absorption HeatPump Load Side Heat Transfer Energy",
3417 : Constant::Units::J,
3418 2 : this->loadSideEnergy,
3419 : OutputProcessor::TimeStepType::System,
3420 : OutputProcessor::StoreType::Sum,
3421 2 : this->name,
3422 : Constant::eResource::EnergyTransfer,
3423 : OutputProcessor::Group::Plant);
3424 : // Setup Output Variable(state,
3425 : // "Fuel-fired Absorption Heat Pump Source Side Heat Transfer Rate",
3426 : // Constant::Units::W,
3427 : // this->sourceSideHeatTransfer,
3428 : // OutputProcessor::TimeStepType::System,
3429 : // OutputProcessor::StoreType::Average,
3430 : // this->name);
3431 : // Setup Output Variable(state,
3432 : // "Fuel-fired Absorption Heat Pump Source Side Heat Transfer Energy",
3433 : // Constant::Units::J,
3434 : // this->sourceSideEnergy,
3435 : // OutputProcessor::TimeStepType::System,
3436 : // OutputProcessor::StoreType::Sum,
3437 : // this->name);
3438 4 : SetupOutputVariable(state,
3439 : "Fuel-fired Absorption HeatPump Inlet Temperature", // "Heat Pump Load Side Inlet Temperature",
3440 : Constant::Units::C,
3441 2 : this->loadSideInletTemp,
3442 : OutputProcessor::TimeStepType::System,
3443 : OutputProcessor::StoreType::Average,
3444 2 : this->name);
3445 4 : SetupOutputVariable(state,
3446 : "Fuel-fired Absorption HeatPump Outlet Temperature", // "Heat Pump Load Side Outlet Temperature",
3447 : Constant::Units::C,
3448 2 : this->loadSideOutletTemp,
3449 : OutputProcessor::TimeStepType::System,
3450 : OutputProcessor::StoreType::Average,
3451 2 : this->name);
3452 : // Setup Output Variable(state,
3453 : // "Fuel-fired Absorption Heat Pump Source Side Inlet Temperature",
3454 : // Constant::Units::C,
3455 : // this->sourceSideInletTemp,
3456 : // OutputProcessor::TimeStepType::System,
3457 : // OutputProcessor::StoreType::Average,
3458 : // this->name);
3459 : // Setup Output Variable(state,
3460 : // "Heat Pump Source Side Outlet Temperature",
3461 : // Constant::Units::C,
3462 : // this->sourceSideOutletTemp,
3463 : // OutputProcessor::TimeStepType::System,
3464 : // OutputProcessor::StoreType::Average,
3465 : // this->name);
3466 4 : SetupOutputVariable(state,
3467 : "Fuel-fired Absorption HeatPump Fuel Rate",
3468 : Constant::Units::W,
3469 2 : this->fuelRate,
3470 : OutputProcessor::TimeStepType::System,
3471 : OutputProcessor::StoreType::Average,
3472 2 : this->name);
3473 4 : SetupOutputVariable(state,
3474 : "Fuel-fired Absorption HeatPump Electricity Rate",
3475 : Constant::Units::W,
3476 2 : this->powerUsage,
3477 : OutputProcessor::TimeStepType::System,
3478 : OutputProcessor::StoreType::Average,
3479 2 : this->name);
3480 2 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) { // energy from HeatPump:AirToWater:FuelFired:Cooling object
3481 2 : SetupOutputVariable(state,
3482 : "Fuel-fired Absorption HeatPump Fuel Energy",
3483 : Constant::Units::J,
3484 1 : this->fuelEnergy,
3485 : OutputProcessor::TimeStepType::System,
3486 : OutputProcessor::StoreType::Sum,
3487 1 : this->name,
3488 1 : Constant::eFuel2eResource[(int)this->fuelType],
3489 : OutputProcessor::Group::Plant,
3490 : OutputProcessor::EndUseCat::Cooling,
3491 : this->endUseSubcat); //"Heat Pump",
3492 2 : SetupOutputVariable(state,
3493 : "Fuel-fired Absorption HeatPump Electricity Energy",
3494 : Constant::Units::J,
3495 1 : this->powerEnergy,
3496 : OutputProcessor::TimeStepType::System,
3497 : OutputProcessor::StoreType::Sum,
3498 1 : this->name,
3499 : Constant::eResource::Electricity,
3500 : OutputProcessor::Group::Plant,
3501 : OutputProcessor::EndUseCat::Cooling,
3502 : this->endUseSubcat); // "Heat Pump",
3503 1 : } else if (this->EIRHPType ==
3504 : DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating) { // energy from HeatPump:AirToWater:FuelFired:Heating object
3505 2 : SetupOutputVariable(state,
3506 : "Fuel-fired Absorption HeatPump Fuel Energy",
3507 : Constant::Units::J,
3508 1 : this->fuelEnergy,
3509 : OutputProcessor::TimeStepType::System,
3510 : OutputProcessor::StoreType::Sum,
3511 1 : this->name,
3512 1 : Constant::eFuel2eResource[(int)this->fuelType],
3513 : OutputProcessor::Group::Plant,
3514 : OutputProcessor::EndUseCat::Heating,
3515 : this->endUseSubcat); // "Heat Pump",
3516 2 : SetupOutputVariable(state,
3517 : "Fuel-fired Absorption HeatPump Electricity Energy",
3518 : Constant::Units::J,
3519 1 : this->powerEnergy,
3520 : OutputProcessor::TimeStepType::System,
3521 : OutputProcessor::StoreType::Sum,
3522 1 : this->name,
3523 : Constant::eResource::Electricity,
3524 : OutputProcessor::Group::Plant,
3525 : OutputProcessor::EndUseCat::Heating,
3526 : this->endUseSubcat); // "Heat Pump",
3527 : }
3528 4 : SetupOutputVariable(state,
3529 : "Fuel-fired Absorption HeatPump Mass Flow Rate",
3530 : Constant::Units::kg_s,
3531 2 : this->loadSideMassFlowRate,
3532 : OutputProcessor::TimeStepType::System,
3533 : OutputProcessor::StoreType::Average,
3534 2 : this->name);
3535 4 : SetupOutputVariable(state,
3536 : "Fuel-fired Absorption HeatPump Volumetric Flow Rate",
3537 : Constant::Units::m3_s,
3538 2 : this->loadSideVolumeFlowRate,
3539 : OutputProcessor::TimeStepType::System,
3540 : OutputProcessor::StoreType::Average,
3541 2 : this->name);
3542 :
3543 : // find this component on the plant
3544 2 : bool thisErrFlag = false;
3545 6 : PlantUtilities::ScanPlantLoopsForObject(
3546 4 : state, this->name, this->EIRHPType, this->loadSidePlantLoc, thisErrFlag, _, _, _, this->loadSideNodes.inlet, _);
3547 :
3548 2 : if (thisErrFlag) {
3549 0 : ShowSevereError(state,
3550 0 : format("{}: Plant topology problem for {} name = \"{}\"",
3551 : routineName,
3552 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
3553 0 : this->name));
3554 0 : ShowContinueError(state, "Could not locate component's load side connections on a plant loop");
3555 0 : errFlag = true;
3556 2 : } else if (this->loadSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Supply) { // only check if !thisErrFlag
3557 0 : ShowSevereError(state,
3558 0 : format("{}: Invalid connections for {} name = \"{}\"",
3559 : routineName,
3560 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
3561 0 : this->name));
3562 0 : ShowContinueError(state, "The load side connections are not on the Supply Side of a plant loop");
3563 0 : errFlag = true;
3564 : }
3565 :
3566 2 : thisErrFlag = false;
3567 2 : if (this->waterSource) {
3568 0 : PlantUtilities::ScanPlantLoopsForObject(
3569 0 : state, this->name, this->EIRHPType, this->sourceSidePlantLoc, thisErrFlag, _, _, _, this->sourceSideNodes.inlet, _);
3570 :
3571 0 : if (thisErrFlag) {
3572 0 : ShowSevereError(state,
3573 0 : format("{}: Plant topology problem for {} name = \"{}\"",
3574 : routineName,
3575 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
3576 0 : this->name));
3577 0 : ShowContinueError(state, "Could not locate component's source side connections on a plant loop.");
3578 0 : errFlag = true;
3579 0 : } else if (this->sourceSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
3580 0 : ShowSevereError(state,
3581 0 : format("{}: Invalid connections for {} name = \"{}\"",
3582 : routineName,
3583 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
3584 0 : this->name));
3585 0 : ShowContinueError(state, "The source side connections are not on the Demand Side of a plant loop.");
3586 0 : errFlag = true;
3587 : }
3588 :
3589 : // make sure it is not the same loop on both sides.
3590 0 : if (this->loadSidePlantLoc.loopNum == this->sourceSidePlantLoc.loopNum) { // user is being too tricky, don't allow
3591 0 : ShowSevereError(state,
3592 0 : format("{}: Invalid connections for {} name = \"{}\"",
3593 : routineName,
3594 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
3595 0 : this->name));
3596 0 : ShowContinueError(state, "The load and source sides need to be on different loops.");
3597 0 : errFlag = true;
3598 : } else {
3599 :
3600 0 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->sourceSidePlantLoc, this->EIRHPType, true);
3601 : }
3602 2 : } else if (this->airSource) {
3603 : // nothing to do here ?
3604 : }
3605 :
3606 2 : if (errFlag) {
3607 0 : ShowFatalError(state, format("{}: Program terminated due to previous condition(s).", routineName));
3608 : }
3609 2 : this->oneTimeInitFlag = false;
3610 : }
3611 10 : }
3612 :
3613 1646 : void EIRFuelFiredHeatPump::report(EnergyPlusData &state)
3614 : {
3615 1646 : Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSysSec;
3616 :
3617 1646 : this->fuelEnergy = this->fuelRate * reportingInterval;
3618 1646 : this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval;
3619 1646 : this->powerEnergy = this->powerUsage * reportingInterval;
3620 1646 : this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval;
3621 :
3622 : // update nodes
3623 1646 : PlantUtilities::SafeCopyPlantNode(state, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
3624 1646 : state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp;
3625 1646 : state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp;
3626 1646 : }
3627 :
3628 0 : Real64 EIRFuelFiredHeatPump::getDynamicMaxCapacity(EnergyPlusData &state)
3629 : {
3630 : // Source (air) side temperature variable
3631 0 : auto &thisSourceSideInletNode = state.dataLoopNodes->Node(this->sourceSideNodes.inlet);
3632 : Real64 oaTempforCurve =
3633 0 : (this->oaTempCurveInputVar == OATempCurveVar::WetBulb)
3634 0 : ? Psychrometrics::PsyTwbFnTdbWPb(
3635 : state, thisSourceSideInletNode.Temp, thisSourceSideInletNode.HumRat, thisSourceSideInletNode.Press, "PLFFHPEIR::doPhysics()")
3636 0 : : thisSourceSideInletNode.Temp;
3637 :
3638 : // Load (water) side temperature variable
3639 0 : Real64 waterTempforCurve = state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
3640 0 : if (this->waterTempCurveInputVar == WaterTempCurveVar::LeavingCondenser || this->waterTempCurveInputVar == WaterTempCurveVar::LeavingEvaporator) {
3641 0 : if (this->flowMode == DataPlant::FlowMode::LeavingSetpointModulated) {
3642 0 : auto &thisLoadSideOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet);
3643 0 : if (this->loadSidePlantLoc.loop->LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
3644 0 : waterTempforCurve = thisLoadSideOutletNode.TempSetPoint;
3645 : } else {
3646 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling) {
3647 0 : waterTempforCurve = thisLoadSideOutletNode.TempSetPointHi;
3648 : } else {
3649 0 : waterTempforCurve = thisLoadSideOutletNode.TempSetPointLo;
3650 : }
3651 : }
3652 : } else {
3653 : // If not SP modulated then use actual outlet temp from last iteration?
3654 0 : waterTempforCurve = state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp;
3655 : }
3656 : }
3657 :
3658 : // evaluate capacity modifier curve and determine load side heat transfer
3659 0 : Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, waterTempforCurve, oaTempforCurve);
3660 0 : return this->referenceCapacity * capacityModifierFuncTemp;
3661 : }
3662 :
3663 : } // namespace EnergyPlus::EIRPlantLoopHeatPumps
|