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