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