Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, 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 <string>
50 : #include <utility>
51 : #include <vector>
52 :
53 : // EnergyPlus headers
54 : #include <EnergyPlus/Autosizing/Base.hh>
55 : #include <EnergyPlus/BranchNodeConnections.hh>
56 : #include <EnergyPlus/CurveManager.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataEnvironment.hh>
59 : #include <EnergyPlus/DataHVACGlobals.hh>
60 : #include <EnergyPlus/DataIPShortCuts.hh>
61 : #include <EnergyPlus/DataLoopNode.hh>
62 : #include <EnergyPlus/DataSizing.hh>
63 : #include <EnergyPlus/FluidProperties.hh>
64 : #include <EnergyPlus/General.hh>
65 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
66 : #include <EnergyPlus/NodeInputManager.hh>
67 : #include <EnergyPlus/OutputProcessor.hh>
68 : #include <EnergyPlus/OutputReportPredefined.hh>
69 : #include <EnergyPlus/Plant/DataPlant.hh>
70 : #include <EnergyPlus/PlantComponent.hh>
71 : #include <EnergyPlus/PlantLoopHeatPumpEIR.hh>
72 : #include <EnergyPlus/PlantUtilities.hh>
73 : #include <EnergyPlus/Psychrometrics.hh>
74 : #include <EnergyPlus/UtilityRoutines.hh>
75 :
76 : namespace EnergyPlus::EIRPlantLoopHeatPumps {
77 :
78 4946 : void EIRPlantLoopHeatPump::simulate(
79 : EnergyPlusData &state, const EnergyPlus::PlantLocation &calledFromLocation, bool const FirstHVACIteration, Real64 &CurLoad, bool const RunFlag)
80 : {
81 :
82 : // Call initialize to set flow rates, run flag, and entering temperatures
83 4946 : this->running = RunFlag;
84 :
85 4946 : this->loadSideInletTemp = state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp;
86 4946 : this->sourceSideInletTemp = state.dataLoopNodes->Node(this->sourceSideNodes.inlet).Temp;
87 :
88 4946 : if (this->waterSource) {
89 3304 : this->setOperatingFlowRatesWSHP(state);
90 3304 : if (calledFromLocation.loopNum == this->sourceSidePlantLoc.loopNum) { // condenser side
91 1646 : PlantUtilities::UpdateChillerComponentCondenserSide(state,
92 : this->sourceSidePlantLoc.loopNum,
93 : this->sourceSidePlantLoc.loopSideNum,
94 : this->EIRHPType,
95 : this->sourceSideNodes.inlet,
96 : this->sourceSideNodes.outlet,
97 : this->sourceSideHeatTransfer,
98 : this->sourceSideInletTemp,
99 : this->sourceSideOutletTemp,
100 : this->sourceSideMassFlowRate,
101 : FirstHVACIteration);
102 1646 : return;
103 : }
104 1642 : } else if (this->airSource) {
105 1642 : this->setOperatingFlowRatesASHP(state);
106 : }
107 :
108 3300 : if (this->running) {
109 1608 : this->doPhysics(state, CurLoad);
110 : } else {
111 1692 : this->resetReportingVariables();
112 : }
113 :
114 : // update nodes
115 3300 : state.dataLoopNodes->Node(this->loadSideNodes.outlet).Temp = this->loadSideOutletTemp;
116 3300 : state.dataLoopNodes->Node(this->sourceSideNodes.outlet).Temp = this->sourceSideOutletTemp;
117 : }
118 :
119 1608 : Real64 EIRPlantLoopHeatPump::getLoadSideOutletSetPointTemp(EnergyPlusData &state) const
120 : {
121 1608 : auto &thisLoadPlantLoop = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum);
122 1608 : auto &thisLoadLoopSide = thisLoadPlantLoop.LoopSide(this->loadSidePlantLoc.loopSideNum);
123 1608 : auto &thisLoadBranch = thisLoadLoopSide.Branch(this->loadSidePlantLoc.branchNum);
124 1608 : auto &thisLoadComp = thisLoadBranch.Comp(this->loadSidePlantLoc.compNum);
125 1608 : if (thisLoadPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
126 1608 : if (thisLoadComp.CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
127 : // there will be a valid set-point on outlet
128 0 : return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPoint;
129 : } else { // use plant loop overall set-point
130 1608 : return state.dataLoopNodes->Node(thisLoadPlantLoop.TempSetPointNodeNum).TempSetPoint;
131 : }
132 0 : } else if (thisLoadPlantLoop.LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand) {
133 0 : if (thisLoadComp.CurOpSchemeType == DataPlant::OpScheme::CompSetPtBased) {
134 : // there will be a valid set-point on outlet
135 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
136 0 : return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointHi;
137 : } else {
138 0 : return state.dataLoopNodes->Node(this->loadSideNodes.outlet).TempSetPointLo;
139 : }
140 : } else { // use plant loop overall set-point
141 0 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) {
142 0 : return state.dataLoopNodes->Node(thisLoadPlantLoop.TempSetPointNodeNum).TempSetPointHi;
143 : } else {
144 0 : return state.dataLoopNodes->Node(thisLoadPlantLoop.TempSetPointNodeNum).TempSetPointLo;
145 : }
146 : }
147 : } else {
148 : // there's no other enums for loop demand calcs, so I don't have a reasonable unit test for these
149 : // lines, they simply should not be able to get here. But a fatal is here anyway just in case,
150 : // and the lines are excluded from coverage.
151 : ShowFatalError(state, "Unsupported loop demand calculation scheme in EIR heat pump"); // LCOV_EXCL_LINE
152 : return -999; // not actually returned with Fatal Error call above // LCOV_EXCL_LINE
153 : }
154 : }
155 :
156 1692 : void EIRPlantLoopHeatPump::resetReportingVariables()
157 : {
158 1692 : this->loadSideHeatTransfer = 0.0;
159 1692 : this->loadSideEnergy = 0.0;
160 1692 : this->loadSideOutletTemp = this->loadSideInletTemp;
161 1692 : this->powerUsage = 0.0;
162 1692 : this->powerEnergy = 0.0;
163 1692 : this->sourceSideHeatTransfer = 0.0;
164 1692 : this->sourceSideOutletTemp = this->sourceSideInletTemp;
165 1692 : this->sourceSideEnergy = 0.0;
166 1692 : }
167 :
168 3304 : void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state)
169 : {
170 3304 : if (!this->running) {
171 846 : this->loadSideMassFlowRate = 0.0;
172 846 : this->sourceSideMassFlowRate = 0.0;
173 846 : PlantUtilities::SetComponentFlowRate(
174 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
175 846 : PlantUtilities::SetComponentFlowRate(
176 : state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
177 846 : PlantUtilities::PullCompInterconnectTrigger(state,
178 : this->loadSidePlantLoc,
179 : this->condMassFlowRateTriggerIndex,
180 : this->sourceSidePlantLoc,
181 : DataPlant::CriteriaType::MassFlowRate,
182 : this->sourceSideMassFlowRate);
183 : // Set flows if the heat pump is running
184 : } else { // the heat pump must run
185 2458 : this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
186 2458 : this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
187 2458 : PlantUtilities::SetComponentFlowRate(
188 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
189 2458 : PlantUtilities::SetComponentFlowRate(
190 : state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
191 :
192 : // if there's no flow in one, try to turn the entire heat pump off
193 2458 : if (this->loadSideMassFlowRate <= 0.0 || this->sourceSideMassFlowRate <= 0.0) {
194 849 : this->loadSideMassFlowRate = 0.0;
195 849 : this->sourceSideMassFlowRate = 0.0;
196 849 : this->running = false;
197 849 : PlantUtilities::SetComponentFlowRate(
198 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
199 849 : PlantUtilities::SetComponentFlowRate(
200 : state, this->sourceSideMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet, this->sourceSidePlantLoc);
201 : }
202 2458 : PlantUtilities::PullCompInterconnectTrigger(state,
203 : this->loadSidePlantLoc,
204 : this->condMassFlowRateTriggerIndex,
205 : this->sourceSidePlantLoc,
206 : DataPlant::CriteriaType::MassFlowRate,
207 : this->sourceSideMassFlowRate);
208 : }
209 3304 : }
210 :
211 1642 : void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state)
212 : {
213 1642 : if (!this->running) {
214 834 : this->loadSideMassFlowRate = 0.0;
215 834 : this->sourceSideMassFlowRate = 0.0;
216 834 : PlantUtilities::SetComponentFlowRate(
217 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
218 : // Set flows if the heat pump is running
219 : } else { // the heat pump must run
220 808 : this->loadSideMassFlowRate = this->loadSideDesignMassFlowRate;
221 808 : this->sourceSideMassFlowRate = this->sourceSideDesignMassFlowRate;
222 808 : PlantUtilities::SetComponentFlowRate(
223 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
224 :
225 : // if there's no flow in one, try to turn the entire heat pump off
226 808 : if (this->loadSideMassFlowRate <= 0.0) {
227 0 : this->loadSideMassFlowRate = 0.0;
228 0 : this->sourceSideMassFlowRate = 0.0;
229 0 : this->running = false;
230 0 : PlantUtilities::SetComponentFlowRate(
231 : state, this->loadSideMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet, this->loadSidePlantLoc);
232 : }
233 : }
234 1642 : }
235 :
236 1608 : void EIRPlantLoopHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad)
237 : {
238 :
239 1608 : Real64 const reportingInterval = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
240 :
241 : // ideally the plant is going to ensure that we don't have a runflag=true when the load is invalid, but
242 : // 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
243 3216 : if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling && currentLoad >= 0.0) ||
244 2406 : (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating && currentLoad <= 0.0)) {
245 0 : this->resetReportingVariables();
246 0 : return;
247 : }
248 :
249 : // get setpoint on the load side outlet
250 1608 : Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state);
251 :
252 : // evaluate capacity modifier curve and determine load side heat transfer
253 1608 : Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->sourceSideInletTemp);
254 :
255 1608 : if (capacityModifierFuncTemp < 0.0) {
256 0 : if (this->capModFTErrorIndex == 0) {
257 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
258 0 : ShowContinueError(state,
259 0 : format(" Capacity Modifier curve (function of Temperatures) output is negative ({:.3T}).", capacityModifierFuncTemp));
260 0 : ShowContinueError(state,
261 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
262 : loadSideOutletSetpointTemp,
263 0 : this->sourceSideInletTemp));
264 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
265 : }
266 0 : ShowRecurringWarningErrorAtEnd(state,
267 0 : format("{} \"{}\": Capacity Modifier curve (function of Temperatures) output is negative warning continues...",
268 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
269 0 : this->name),
270 : this->capModFTErrorIndex,
271 : capacityModifierFuncTemp,
272 : capacityModifierFuncTemp);
273 0 : capacityModifierFuncTemp = 0.0;
274 : }
275 :
276 1608 : Real64 availableCapacity = this->referenceCapacity * capacityModifierFuncTemp;
277 1608 : Real64 partLoadRatio = 0.0;
278 1608 : if (availableCapacity > 0) {
279 1608 : partLoadRatio = max(0.0, min(std::abs(currentLoad) / availableCapacity, 1.0));
280 : }
281 :
282 : // evaluate the actual current operating load side heat transfer rate
283 1608 : auto &thisLoadPlantLoop = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum);
284 4824 : Real64 CpLoad = FluidProperties::GetSpecificHeatGlycol(state,
285 : thisLoadPlantLoop.FluidName,
286 1608 : state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp,
287 : thisLoadPlantLoop.FluidIndex,
288 3216 : "PLHPEIR::simulate()");
289 1608 : this->loadSideHeatTransfer = availableCapacity * partLoadRatio;
290 1608 : this->loadSideEnergy = this->loadSideHeatTransfer * reportingInterval;
291 :
292 : // calculate load side outlet conditions
293 1608 : Real64 const loadMCp = this->loadSideMassFlowRate * CpLoad;
294 1608 : this->loadSideOutletTemp = this->calcLoadOutletTemp(this->loadSideInletTemp, this->loadSideHeatTransfer / loadMCp);
295 :
296 : // calculate power usage from EIR curves
297 1608 : Real64 eirModifierFuncTemp = Curve::CurveValue(state, this->powerRatioFuncTempCurveIndex, this->loadSideOutletTemp, this->sourceSideInletTemp);
298 :
299 1608 : if (eirModifierFuncTemp < 0.0) {
300 0 : if (this->eirModFTErrorIndex == 0) {
301 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
302 0 : ShowContinueError(state, format(" EIR Modifier curve (function of Temperatures) output is negative ({:.3T}).", eirModifierFuncTemp));
303 0 : ShowContinueError(state,
304 0 : format(" Negative value occurs using a water temperature of {:.2T}C and an outdoor air temperature of {:.2T}C.",
305 : this->loadSideOutletTemp,
306 0 : this->sourceSideInletTemp));
307 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
308 : }
309 0 : ShowRecurringWarningErrorAtEnd(state,
310 0 : format("{} \"{}\": EIR Modifier curve (function of Temperatures) output is negative warning continues...",
311 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
312 0 : this->name),
313 : this->eirModFTErrorIndex,
314 : eirModifierFuncTemp,
315 : eirModifierFuncTemp);
316 0 : eirModifierFuncTemp = 0.0;
317 : }
318 :
319 1608 : Real64 eirModifierFuncPLR = Curve::CurveValue(state, this->powerRatioFuncPLRCurveIndex, partLoadRatio);
320 :
321 1608 : if (eirModifierFuncPLR < 0.0) {
322 0 : if (this->eirModFPLRErrorIndex == 0) {
323 0 : ShowSevereMessage(state, format("{} \"{}\":", DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)], this->name));
324 0 : ShowContinueError(state, format(" EIR Modifier curve (function of PLR) output is negative ({:.3T}).", eirModifierFuncPLR));
325 0 : ShowContinueError(state, format(" Negative value occurs using a Part Load Ratio of {:.2T}", partLoadRatio));
326 0 : ShowContinueErrorTimeStamp(state, " Resetting curve output to zero and continuing simulation.");
327 : }
328 0 : ShowRecurringWarningErrorAtEnd(state,
329 0 : format("{} \"{}\": EIR Modifier curve (function of PLR) output is negative warning continues...",
330 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
331 0 : this->name),
332 : this->eirModFPLRErrorIndex,
333 : eirModifierFuncPLR,
334 : eirModifierFuncPLR);
335 0 : eirModifierFuncPLR = 0.0;
336 : }
337 :
338 1608 : this->powerUsage = (this->loadSideHeatTransfer / this->referenceCOP) * eirModifierFuncPLR * eirModifierFuncTemp;
339 1608 : this->powerEnergy = this->powerUsage * reportingInterval;
340 :
341 : // energy balance on heat pump
342 1608 : this->sourceSideHeatTransfer = this->calcQsource(this->loadSideHeatTransfer, this->powerUsage);
343 1608 : this->sourceSideEnergy = this->sourceSideHeatTransfer * reportingInterval;
344 :
345 : // calculate source side outlet conditions
346 1608 : Real64 CpSrc = 0.0;
347 1608 : if (this->waterSource) {
348 1600 : CpSrc = FluidProperties::GetSpecificHeatGlycol(state,
349 : thisLoadPlantLoop.FluidName,
350 800 : state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp,
351 : thisLoadPlantLoop.FluidIndex,
352 800 : "PLHPEIR::simulate()");
353 808 : } else if (this->airSource) {
354 808 : CpSrc = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
355 : }
356 1608 : Real64 const sourceMCp = this->sourceSideMassFlowRate * CpSrc;
357 1608 : this->sourceSideOutletTemp = this->calcSourceOutletTemp(this->sourceSideInletTemp, this->sourceSideHeatTransfer / sourceMCp);
358 : }
359 :
360 30 : void EIRPlantLoopHeatPump::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
361 : {
362 : // This function does all one-time and begin-environment initialization
363 30 : std::string static const routineName = std::string("EIRPlantLoopHeatPump :") + __FUNCTION__;
364 :
365 30 : this->oneTimeInit(state); // plant setup
366 :
367 30 : if (state.dataGlobal->BeginEnvrnFlag && this->envrnInit && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
368 4 : if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
369 4 : this->sizeLoadSide(state);
370 4 : if (this->waterSource) {
371 2 : this->sizeSrcSideWSHP(state);
372 2 : } else if (this->airSource) {
373 2 : this->sizeSrcSideASHP(state);
374 : }
375 : }
376 :
377 12 : Real64 rho = FluidProperties::GetDensityGlycol(state,
378 4 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
379 : DataGlobalConstants::InitConvTemp,
380 4 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
381 4 : routineName);
382 4 : this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
383 4 : PlantUtilities::InitComponentNodes(state, 0.0, this->loadSideDesignMassFlowRate, this->loadSideNodes.inlet, this->loadSideNodes.outlet);
384 :
385 4 : if (this->waterSource) {
386 6 : rho = FluidProperties::GetDensityGlycol(state,
387 2 : state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum).FluidName,
388 : DataGlobalConstants::InitConvTemp,
389 2 : state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum).FluidIndex,
390 : routineName);
391 2 : this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
392 2 : PlantUtilities::InitComponentNodes(
393 : state, 0.0, this->sourceSideDesignMassFlowRate, this->sourceSideNodes.inlet, this->sourceSideNodes.outlet);
394 2 : } else if (this->airSource) {
395 2 : rho = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, state.dataEnvrn->OutDryBulbTemp, 0.0, routineName);
396 2 : this->sourceSideDesignMassFlowRate = rho * this->sourceSideDesignVolFlowRate;
397 : }
398 :
399 4 : this->envrnInit = false;
400 : }
401 30 : if (!state.dataGlobal->BeginEnvrnFlag) {
402 0 : this->envrnInit = true;
403 : }
404 30 : }
405 :
406 30 : void EIRPlantLoopHeatPump::getDesignCapacities(
407 : [[maybe_unused]] EnergyPlusData &state, const PlantLocation &calledFromLocation, Real64 &MaxLoad, Real64 &MinLoad, Real64 &OptLoad)
408 : {
409 30 : if (calledFromLocation.loopNum == this->loadSidePlantLoc.loopNum) {
410 20 : MinLoad = 0.0;
411 20 : MaxLoad = this->referenceCapacity;
412 20 : OptLoad = this->referenceCapacity;
413 : } else {
414 10 : MinLoad = 0.0;
415 10 : MaxLoad = 0.0;
416 10 : OptLoad = 0.0;
417 : }
418 30 : }
419 :
420 4 : void EIRPlantLoopHeatPump::sizeLoadSide(EnergyPlusData &state)
421 : {
422 : // Tries to size the load side flow rate and capacity, source side flow, and the rated power usage
423 : // There are two major sections to this function, one if plant sizing is available, and one if not
424 : // If plant sizing is available, then we can generate sizes for the equipment. This is done for not-only
425 : // autosized fields, but also hard-sized fields so that we can report out significant deviations between
426 : // the two values.
427 : // If plant sizing is not available, it tries to use a companion heat pump coil to do sizing
428 :
429 4 : bool errorsFound = false;
430 :
431 : // these variables will be used throughout this function as a temporary value of that physical state
432 4 : Real64 tmpCapacity = this->referenceCapacity;
433 4 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
434 :
435 4 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
436 4 : Real64 loadSideInitTemp = DataGlobalConstants::CWInitConvTemp;
437 4 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
438 2 : loadSideInitTemp = DataGlobalConstants::HWInitConvTemp;
439 : }
440 :
441 12 : Real64 const rho = FluidProperties::GetDensityGlycol(state,
442 4 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
443 : loadSideInitTemp,
444 4 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
445 8 : "EIRPlantLoopHeatPump::size()");
446 12 : Real64 const Cp = FluidProperties::GetSpecificHeatGlycol(state,
447 4 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
448 : loadSideInitTemp,
449 4 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
450 8 : "EIRPlantLoopHeatPump::size()");
451 :
452 4 : int pltLoadSizNum = state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).PlantSizNum;
453 4 : if (pltLoadSizNum > 0) {
454 : // this first IF block is really just about calculating the local tmpCapacity and tmpLoadVolFlow values
455 : // these represent what the unit would size those to, whether it is doing auto-sizing or not
456 0 : if (state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate > DataHVACGlobals::SmallWaterVolFlow) {
457 0 : tmpLoadVolFlow = state.dataSize->PlantSizData(pltLoadSizNum).DesVolFlowRate * this->sizingFactor;
458 0 : if (this->companionHeatPumpCoil) {
459 0 : tmpLoadVolFlow = max(tmpLoadVolFlow, this->companionHeatPumpCoil->loadSideDesignVolFlowRate);
460 0 : if (this->loadSideDesignVolFlowRateWasAutoSized) this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
461 : }
462 0 : tmpCapacity = Cp * rho * state.dataSize->PlantSizData(pltLoadSizNum).DeltaT * tmpLoadVolFlow;
463 0 : } else if (this->companionHeatPumpCoil && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
464 0 : tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
465 0 : tmpCapacity = Cp * rho * state.dataSize->PlantSizData(pltLoadSizNum).DeltaT * tmpLoadVolFlow;
466 : } else {
467 0 : if (this->referenceCapacityWasAutoSized) tmpCapacity = 0.0;
468 0 : if (this->loadSideDesignVolFlowRateWasAutoSized) tmpLoadVolFlow = 0.0;
469 : }
470 : // now we actually need to store and report out the values
471 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
472 : // handle the auto-sizable reference capacity
473 0 : if (this->referenceCapacityWasAutoSized) {
474 : // if auto-sized, we just need to store the sized value and then report out the capacity when plant is ready
475 0 : this->referenceCapacity = tmpCapacity;
476 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
477 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
478 : }
479 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
480 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
481 : }
482 : } else {
483 : // this blocks means the capacity value was hard-sized
484 0 : if (this->referenceCapacity > 0.0 && tmpCapacity > 0.0) {
485 : // then the capacity was hard-sized to a good value and the tmpCapacity was calculated to a good value too
486 0 : Real64 hardSizedCapacity = this->referenceCapacity;
487 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
488 0 : if (state.dataGlobal->DoPlantSizing) {
489 0 : BaseSizer::reportSizerOutput(state,
490 : typeName,
491 : this->name,
492 : "Design Size Nominal Capacity [W]",
493 : tmpCapacity,
494 : "User-Specified Nominal Capacity [W]",
495 0 : hardSizedCapacity);
496 : } else {
497 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", hardSizedCapacity);
498 : }
499 : // we can warn here if there is a bit mismatch between hard- and auto-sized
500 0 : if (state.dataGlobal->DisplayExtraWarnings) {
501 0 : if ((std::abs(tmpCapacity - hardSizedCapacity) / hardSizedCapacity) > state.dataSize->AutoVsHardSizingThreshold) {
502 0 : ShowWarningMessage(state, "EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for " + this->name);
503 0 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", hardSizedCapacity));
504 0 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpCapacity));
505 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
506 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
507 : }
508 : }
509 : }
510 : // moving forward with more calculations, we need to update the 'tmp' capacity to the hard-sized value
511 0 : tmpCapacity = hardSizedCapacity;
512 : }
513 : }
514 : // now handle the auto-sizable load side flow rate
515 0 : if (this->loadSideDesignVolFlowRateWasAutoSized) {
516 0 : this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
517 0 : this->loadSideDesignMassFlowRate = rho * this->loadSideDesignVolFlowRate;
518 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
519 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
520 : }
521 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
522 0 : BaseSizer::reportSizerOutput(
523 0 : state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
524 : }
525 : } else {
526 0 : if (this->loadSideDesignVolFlowRate > 0.0 && tmpLoadVolFlow > 0.0) {
527 0 : Real64 hardSizedLoadSideFlow = this->loadSideDesignVolFlowRate;
528 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
529 0 : if (state.dataGlobal->DoPlantSizing) {
530 0 : BaseSizer::reportSizerOutput(state,
531 : typeName,
532 : this->name,
533 : "Design Size Load Side Volume Flow Rate [m3/s]",
534 : tmpLoadVolFlow,
535 : "User-Specified Load Side Volume Flow Rate [m3/s]",
536 0 : hardSizedLoadSideFlow);
537 : } else {
538 0 : BaseSizer::reportSizerOutput(
539 0 : state, typeName, this->name, "User-Specified Load Side Volume Flow Rate [m3/s]", hardSizedLoadSideFlow);
540 : }
541 0 : if (state.dataGlobal->DisplayExtraWarnings) {
542 0 : if ((std::abs(tmpLoadVolFlow - hardSizedLoadSideFlow) / hardSizedLoadSideFlow) >
543 0 : state.dataSize->AutoVsHardSizingThreshold) {
544 0 : ShowMessage(state, "EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for " + this->name);
545 0 : ShowContinueError(state, format("User-Specified Load Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedLoadSideFlow));
546 0 : ShowContinueError(state,
547 0 : format("differs from Design Size Load Side Volume Flow Rate of {:.2R} [m3/s]", tmpLoadVolFlow));
548 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
549 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
550 : }
551 : }
552 : }
553 0 : tmpLoadVolFlow = hardSizedLoadSideFlow;
554 : }
555 : }
556 : }
557 : } else {
558 : // no plant sizing available...try to use the companion coil
559 4 : if (this->companionHeatPumpCoil) {
560 4 : if (this->companionHeatPumpCoil->loadSideDesignVolFlowRateWasAutoSized && this->companionHeatPumpCoil->loadSideDesignVolFlowRate > 0.0) {
561 0 : tmpLoadVolFlow = this->companionHeatPumpCoil->loadSideDesignVolFlowRate;
562 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
563 0 : this->loadSideDesignVolFlowRate = tmpLoadVolFlow;
564 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
565 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
566 : }
567 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
568 0 : BaseSizer::reportSizerOutput(
569 0 : state, typeName, this->name, "Initial Design Size Load Side Volume Flow Rate [m3/s]", tmpLoadVolFlow);
570 : }
571 : }
572 : }
573 4 : if (this->companionHeatPumpCoil->referenceCapacityWasAutoSized && this->companionHeatPumpCoil->referenceCapacity > 0.0) {
574 0 : tmpCapacity = this->companionHeatPumpCoil->referenceCapacity;
575 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
576 0 : this->referenceCapacity = tmpCapacity;
577 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
578 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Nominal Capacity [W]", tmpCapacity);
579 : }
580 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
581 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Nominal Capacity [W]", tmpCapacity);
582 : }
583 : }
584 : }
585 : } else {
586 : // no companion coil, and no plant sizing, so can't do anything
587 0 : if ((this->loadSideDesignVolFlowRateWasAutoSized || this->referenceCapacityWasAutoSized) &&
588 0 : state.dataPlnt->PlantFirstSizesOkayToFinalize) {
589 0 : ShowSevereError(state, "EIRPlantLoopHeatPump::size(): Autosizing requires a loop Sizing:Plant object.");
590 0 : ShowContinueError(state, "Occurs in HeatPump:PlantLoop:EquationFit:Cooling object = " + this->name);
591 0 : errorsFound = true;
592 : }
593 : }
594 4 : if (!this->loadSideDesignVolFlowRateWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
595 4 : BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Load Side Flow Rate [m3/s]", this->loadSideDesignVolFlowRate);
596 : }
597 4 : if (!this->referenceCapacityWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport) {
598 4 : BaseSizer::reportSizerOutput(state, typeName, this->name, "User-Specified Nominal Capacity [W]", this->referenceCapacity);
599 : }
600 : }
601 4 : if (errorsFound) {
602 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
603 : }
604 4 : }
605 :
606 2 : void EIRPlantLoopHeatPump::sizeSrcSideWSHP(EnergyPlusData &state)
607 : {
608 : // size the source-side for the water-source HP
609 2 : bool errorsFound = false;
610 :
611 : // these variables will be used throughout this function as a temporary value of that physical state
612 2 : Real64 tmpCapacity = this->referenceCapacity;
613 2 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
614 : Real64 tmpSourceVolFlow;
615 :
616 2 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
617 2 : Real64 sourceSideInitTemp = DataGlobalConstants::HWInitConvTemp;
618 2 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
619 1 : sourceSideInitTemp = DataGlobalConstants::CWInitConvTemp;
620 : }
621 :
622 6 : Real64 const rhoSrc = FluidProperties::GetDensityGlycol(state,
623 2 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
624 : sourceSideInitTemp,
625 2 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
626 4 : "EIRPlantLoopHeatPump::size()");
627 6 : Real64 const CpSrc = FluidProperties::GetSpecificHeatGlycol(state,
628 2 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidName,
629 : sourceSideInitTemp,
630 2 : state.dataPlnt->PlantLoop(this->loadSidePlantLoc.loopNum).FluidIndex,
631 4 : "EIRPlantLoopHeatPump::size()");
632 :
633 : // To start we need to override the calculated load side flow
634 : // rate if it was actually hard-sized
635 2 : if (!this->loadSideDesignVolFlowRateWasAutoSized) tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
636 :
637 : // calculate an auto-sized value for source design flow regardless of whether it was auto-sized or not
638 2 : int plantSourceSizingIndex = state.dataPlnt->PlantLoop(this->sourceSidePlantLoc.loopNum).PlantSizNum;
639 2 : if (plantSourceSizingIndex > 0) {
640 : // to get the source flow, we first must calculate the required heat impact on the source side
641 : // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
642 : // Then the energy balance: Qsrc = Qload + Power
643 : // Substituting for Power: Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
644 0 : Real64 const designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
645 : // To get the design source flow rate, just apply the sensible heat rate equation:
646 : // Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
647 : // Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
648 0 : tmpSourceVolFlow = designSourceSideHeatTransfer / (state.dataSize->PlantSizData(plantSourceSizingIndex).DeltaT * CpSrc * rhoSrc);
649 : } else {
650 : // just assume it's the same as the load side if we don't have any sizing information
651 2 : tmpSourceVolFlow = tmpLoadVolFlow;
652 : }
653 2 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
654 0 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
655 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
656 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
657 : }
658 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
659 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
660 : }
661 : } else {
662 : // source design flow was hard-sized
663 2 : if (this->sourceSideDesignVolFlowRate > 0.0 && tmpSourceVolFlow > 0.0) {
664 2 : Real64 const hardSizedSourceSideFlow = this->sourceSideDesignVolFlowRate;
665 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
666 2 : if (state.dataGlobal->DoPlantSizing) {
667 0 : BaseSizer::reportSizerOutput(state,
668 : typeName,
669 : this->name,
670 : "Design Size Source Side Volume Flow Rate [m3/s]",
671 : tmpSourceVolFlow,
672 : "User-Specified Source Side Volume Flow Rate [m3/s]",
673 0 : hardSizedSourceSideFlow);
674 : } else {
675 4 : BaseSizer::reportSizerOutput(
676 2 : state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", hardSizedSourceSideFlow);
677 : }
678 2 : if (state.dataGlobal->DisplayExtraWarnings) {
679 0 : if ((std::abs(tmpSourceVolFlow - hardSizedSourceSideFlow) / hardSizedSourceSideFlow) >
680 0 : state.dataSize->AutoVsHardSizingThreshold) {
681 0 : ShowMessage(state, "EIRPlantLoopHeatPump::size(): Potential issue with equipment sizing for " + this->name);
682 0 : ShowContinueError(state, format("User-Specified Source Side Volume Flow Rate of {:.2R} [m3/s]", hardSizedSourceSideFlow));
683 0 : ShowContinueError(state, format("differs from Design Size Source Side Volume Flow Rate of {:.2R} [m3/s]", tmpSourceVolFlow));
684 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
685 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
686 : }
687 : }
688 : }
689 2 : tmpSourceVolFlow = hardSizedSourceSideFlow;
690 : }
691 : }
692 :
693 : // skipping autosized power section
694 :
695 : // register the design volume flows with the plant, only doing half of source because the companion
696 : // is generally on the same loop
697 2 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->loadSideNodes.inlet, tmpLoadVolFlow);
698 2 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->sourceSideNodes.inlet, tmpSourceVolFlow / 0.5);
699 :
700 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
701 : // create predefined report
702 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, this->name, typeName);
703 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, this->name, this->referenceCOP);
704 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->name, this->referenceCapacity);
705 : }
706 :
707 2 : if (errorsFound) {
708 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
709 : }
710 2 : }
711 :
712 2 : void EIRPlantLoopHeatPump::sizeSrcSideASHP(EnergyPlusData &state)
713 : {
714 : // size the source-side for the air-source HP
715 2 : bool errorsFound = false;
716 :
717 : // these variables will be used throughout this function as a temporary value of that physical state
718 2 : Real64 tmpCapacity = this->referenceCapacity;
719 2 : Real64 tmpLoadVolFlow = this->loadSideDesignVolFlowRate;
720 2 : Real64 tmpSourceVolFlow = 0.0;
721 :
722 : // will leave like this for now
723 : // need to update these to better values later
724 2 : Real64 sourceSideInitTemp = 20;
725 2 : Real64 sourceSideHumRat = 0.0;
726 2 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) {
727 : // same here; update later
728 1 : sourceSideInitTemp = 20;
729 : }
730 :
731 2 : Real64 const rhoSrc = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat);
732 2 : Real64 const CpSrc = Psychrometrics::PsyCpAirFnW(sourceSideHumRat);
733 :
734 : // set the source-side flow rate
735 2 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
736 : // load-side capacity should already be set, so unless the flow rate is specified, we can set
737 : // an assumed reasonable flow rate since this doesn't affect downstream components
738 0 : Real64 DeltaT_src = 10;
739 : // to get the source flow, we first must calculate the required heat impact on the source side
740 : // First the definition of COP: COP = Qload/Power, therefore Power = Qload/COP
741 : // Then the energy balance: Qsrc = Qload + Power
742 : // Substituting for Power: Qsrc = Qload + Qload/COP, therefore Qsrc = Qload (1 + 1/COP)
743 0 : Real64 const designSourceSideHeatTransfer = tmpCapacity * (1 + 1 / this->referenceCOP);
744 : // To get the design source flow rate, just apply the sensible heat rate equation:
745 : // Qsrc = rho_src * Vdot_src * Cp_src * DeltaT_src
746 : // Vdot_src = Q_src / (rho_src * Cp_src * DeltaT_src)
747 0 : tmpSourceVolFlow = designSourceSideHeatTransfer / (rhoSrc * CpSrc * DeltaT_src);
748 2 : } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate > 0) {
749 : // given the value by the user
750 : // set it directly
751 2 : tmpSourceVolFlow = this->sourceSideDesignVolFlowRate;
752 : } else if (!this->sourceSideDesignVolFlowRateWasAutoSized && this->sourceSideDesignVolFlowRate == 0) { // LCOV_EXCL_LINE
753 : // user gave a flow rate of 0
754 : // protected by the input processor to be >0.0
755 : // fatal out just in case
756 : errorsFound = true; // LCOV_EXCL_LINE
757 0 : ShowSevereError(state,
758 0 : format("Invalid condenser flow rate for EIR PLHP (name={}; entered value: {}",
759 : this->name,
760 : this->sourceSideDesignVolFlowRate)); // LCOV_EXCL_LINE
761 : } else {
762 : // can't imagine how it would ever get to this point
763 : // just assume it's the same as the load side if we don't have any sizing information
764 : tmpSourceVolFlow = tmpLoadVolFlow; // LCOV_EXCL_LINE
765 : }
766 :
767 2 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
768 :
769 2 : std::string_view const typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)];
770 2 : if (this->sourceSideDesignVolFlowRateWasAutoSized) {
771 0 : this->sourceSideDesignVolFlowRate = tmpSourceVolFlow;
772 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
773 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
774 : }
775 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
776 0 : BaseSizer::reportSizerOutput(state, typeName, this->name, "Initial Design Size Source Side Volume Flow Rate [m3/s]", tmpSourceVolFlow);
777 : }
778 : } else {
779 : // source design flow was hard-sized
780 2 : if (this->sourceSideDesignVolFlowRate > 0.0) {
781 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
782 2 : if (state.dataGlobal->DoPlantSizing) {
783 0 : BaseSizer::reportSizerOutput(state,
784 : typeName,
785 : this->name,
786 : "Design Size Source Side Volume Flow Rate [m3/s]",
787 : tmpSourceVolFlow,
788 : "User-Specified Source Side Volume Flow Rate [m3/s]",
789 0 : this->sourceSideDesignVolFlowRate);
790 : } else {
791 4 : BaseSizer::reportSizerOutput(
792 2 : state, typeName, this->name, "User-Specified Source Side Volume Flow Rate [m3/s]", this->sourceSideDesignVolFlowRate);
793 : }
794 : }
795 : }
796 : }
797 :
798 2 : if (errorsFound) {
799 : ShowFatalError(state, "Preceding sizing errors cause program termination"); // LCOV_EXCL_LINE
800 : }
801 2 : }
802 :
803 6 : PlantComponent *EIRPlantLoopHeatPump::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType hp_type_of_num, const std::string &hp_name)
804 : {
805 6 : if (state.dataEIRPlantLoopHeatPump->getInputsPLHP) {
806 2 : EIRPlantLoopHeatPump::processInputForEIRPLHP(state);
807 2 : EIRPlantLoopHeatPump::pairUpCompanionCoils(state);
808 2 : state.dataEIRPlantLoopHeatPump->getInputsPLHP = false;
809 : }
810 :
811 9 : for (auto &plhp : state.dataEIRPlantLoopHeatPump->heatPumps) {
812 9 : if (plhp.name == UtilityRoutines::MakeUPPERCase(hp_name) && plhp.EIRHPType == hp_type_of_num) {
813 6 : return &plhp;
814 : }
815 : }
816 :
817 0 : ShowFatalError(state, "EIR Plant Loop Heat Pump factory: Error getting inputs for PLHP named: " + hp_name);
818 : return nullptr; // LCOV_EXCL_LINE
819 : }
820 :
821 2 : void EIRPlantLoopHeatPump::pairUpCompanionCoils(EnergyPlusData &state)
822 : {
823 6 : for (auto &thisHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
824 4 : if (!thisHP.companionCoilName.empty()) {
825 8 : auto thisCoilName = UtilityRoutines::MakeUPPERCase(thisHP.name);
826 4 : auto &thisCoilType = thisHP.EIRHPType;
827 8 : auto targetCompanionName = UtilityRoutines::MakeUPPERCase(thisHP.companionCoilName);
828 6 : for (auto &potentialCompanionCoil : state.dataEIRPlantLoopHeatPump->heatPumps) {
829 6 : auto &potentialCompanionType = potentialCompanionCoil.EIRHPType;
830 6 : auto potentialCompanionName = UtilityRoutines::MakeUPPERCase(potentialCompanionCoil.name);
831 6 : if (potentialCompanionName == thisCoilName) {
832 : // skip the current coil
833 2 : continue;
834 : }
835 4 : if (potentialCompanionName == targetCompanionName) {
836 4 : if (thisCoilType == potentialCompanionType) {
837 0 : ShowSevereError(state, "Invalid companion specification for EIR Plant Loop Heat Pump named \"" + thisCoilName + "\"");
838 0 : ShowContinueError(state, "For heating objects, the companion must be a cooling object, and vice-versa");
839 0 : ShowFatalError(state, "Invalid companion object causes program termination");
840 : }
841 4 : thisHP.companionHeatPumpCoil = &potentialCompanionCoil;
842 4 : break;
843 : }
844 : }
845 4 : if (!thisHP.companionHeatPumpCoil) {
846 0 : ShowSevereError(state, "Could not find matching companion heat pump coil.");
847 0 : ShowContinueError(state, "Base coil: " + thisCoilName);
848 0 : ShowContinueError(state, "Looking for companion coil named: " + targetCompanionName);
849 0 : ShowFatalError(state, "Simulation aborts due to previous severe error");
850 : }
851 : }
852 : }
853 2 : }
854 :
855 2 : void EIRPlantLoopHeatPump::processInputForEIRPLHP(EnergyPlusData &state)
856 : {
857 :
858 12 : struct ClassType
859 : {
860 : DataPlant::PlantEquipmentType thisType;
861 : std::string nodesType;
862 : std::function<Real64(Real64, Real64)> calcLoadOutletTemp;
863 : std::function<Real64(Real64, Real64)> calcQsource;
864 : std::function<Real64(Real64, Real64)> calcSourceOutletTemp;
865 :
866 4 : ClassType(DataPlant::PlantEquipmentType _thisType,
867 : std::string _nodesType,
868 : std::function<Real64(Real64, Real64)> _tLoadOutFunc,
869 : std::function<Real64(Real64, Real64)> _qSrcFunc,
870 : std::function<Real64(Real64, Real64)> _tSrcOutFunc)
871 16 : : thisType(_thisType), nodesType(std::move(_nodesType)), calcLoadOutletTemp(std::move(_tLoadOutFunc)), calcQsource(std::move(_qSrcFunc)),
872 16 : calcSourceOutletTemp(std::move(_tSrcOutFunc))
873 : {
874 4 : }
875 : };
876 : std::vector<ClassType> classesToInput = {ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRCooling,
877 : "Chilled Water Nodes",
878 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
879 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
880 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add},
881 : ClassType{DataPlant::PlantEquipmentType::HeatPumpEIRHeating,
882 : "Hot Water Nodes",
883 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::add,
884 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract,
885 4 : EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::subtract}};
886 :
887 2 : bool errorsFound = false;
888 2 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
889 6 : for (auto &classToInput : classesToInput) {
890 4 : cCurrentModuleObject = DataPlant::PlantEquipTypeNames[static_cast<int>(classToInput.thisType)];
891 8 : auto objType = (DataLoopNode::ConnectionObjectType)getEnumerationValue(BranchNodeConnections::ConnectionObjectTypeNamesUC,
892 12 : UtilityRoutines::MakeUPPERCase(cCurrentModuleObject));
893 4 : int numPLHP = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
894 4 : if (numPLHP > 0) {
895 8 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
896 4 : if (instances == state.dataInputProcessing->inputProcessor->epJSON.end()) {
897 : // Cannot imagine how you would have numPLHP > 0 and yet the instances is empty
898 : // this would indicate a major problem in the input processor, not a problem here
899 : // I'll still catch this with errorsFound but I cannot make a unit test for it so excluding the line from coverage
900 : ShowSevereError(state, // LCOV_EXCL_LINE
901 : "EIR PLHP: Somehow getNumObjectsFound was > 0 but epJSON.find found 0"); // LCOV_EXCL_LINE
902 : errorsFound = true; // LCOV_EXCL_LINE
903 : }
904 4 : auto &instancesValue = instances.value();
905 8 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
906 4 : auto const &fields = instance.value();
907 4 : auto const &thisObjectName = instance.key();
908 4 : state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
909 :
910 8 : EIRPlantLoopHeatPump thisPLHP;
911 4 : thisPLHP.EIRHPType = classToInput.thisType;
912 4 : thisPLHP.name = UtilityRoutines::MakeUPPERCase(thisObjectName);
913 8 : std::string loadSideInletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("load_side_inlet_node_name").get<std::string>());
914 8 : std::string loadSideOutletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("load_side_outlet_node_name").get<std::string>());
915 8 : std::string condenserType = UtilityRoutines::MakeUPPERCase(fields.at("condenser_type").get<std::string>());
916 8 : std::string sourceSideInletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("source_side_inlet_node_name").get<std::string>());
917 8 : std::string sourceSideOutletNodeName = UtilityRoutines::MakeUPPERCase(fields.at("source_side_outlet_node_name").get<std::string>());
918 4 : if (fields.find("companion_heat_pump_name") != fields.end()) { // optional field
919 4 : thisPLHP.companionCoilName = UtilityRoutines::MakeUPPERCase(fields.at("companion_heat_pump_name").get<std::string>());
920 : }
921 8 : auto tmpFlowRate = fields.at("load_side_reference_flow_rate");
922 4 : if (tmpFlowRate == "Autosize") {
923 0 : thisPLHP.loadSideDesignVolFlowRate = DataSizing::AutoSize;
924 0 : thisPLHP.loadSideDesignVolFlowRateWasAutoSized = true;
925 : } else {
926 4 : thisPLHP.loadSideDesignVolFlowRate = tmpFlowRate.get<Real64>();
927 : }
928 8 : auto tmpSourceFlowRate = fields.at("source_side_reference_flow_rate");
929 4 : if (tmpSourceFlowRate == "Autosize") {
930 0 : thisPLHP.sourceSideDesignVolFlowRate = DataSizing::AutoSize;
931 0 : thisPLHP.sourceSideDesignVolFlowRateWasAutoSized = true;
932 : } else {
933 4 : thisPLHP.sourceSideDesignVolFlowRate = tmpSourceFlowRate.get<Real64>();
934 : }
935 8 : auto tmpRefCapacity = fields.at("reference_capacity");
936 4 : if (tmpRefCapacity == "Autosize") {
937 0 : thisPLHP.referenceCapacity = DataSizing::AutoSize;
938 0 : thisPLHP.referenceCapacityWasAutoSized = true;
939 : } else {
940 4 : thisPLHP.referenceCapacity = tmpRefCapacity.get<Real64>();
941 : }
942 :
943 4 : if (fields.find("reference_coefficient_of_performance") != fields.end()) {
944 4 : thisPLHP.referenceCOP = fields.at("reference_coefficient_of_performance").get<Real64>();
945 : } else {
946 0 : Real64 defaultVal = 0.0;
947 0 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(
948 : state, cCurrentModuleObject, "reference_coefficient_of_performance", defaultVal)) {
949 : // this error condition would mean that someone broke the input dictionary, not their
950 : // input file. I can't really unit test it so I'll leave it here as a severe error
951 : // but excluding it from coverage
952 : ShowSevereError(state, // LCOV_EXCL_LINE
953 : "EIR PLHP: Reference COP not entered and could not get default value"); // LCOV_EXCL_LINE
954 : errorsFound = true; // LCOV_EXCL_LINE
955 : } else {
956 0 : thisPLHP.referenceCOP = defaultVal;
957 : }
958 : }
959 :
960 4 : if (fields.find("sizing_factor") != fields.end()) {
961 0 : thisPLHP.sizingFactor = fields.at("sizing_factor").get<Real64>();
962 : } else {
963 4 : Real64 defaultVal = 0.0;
964 4 : if (!state.dataInputProcessing->inputProcessor->getDefaultValue(state, cCurrentModuleObject, "sizing_factor", defaultVal)) {
965 : // this error condition would mean that someone broke the input dictionary, not their
966 : // input file. I can't really unit test it so I'll leave it here as a severe error
967 : // but excluding it from coverage
968 : ShowSevereError(state, // LCOV_EXCL_LINE
969 : "EIR PLHP: Sizing factor not entered and could not get default value"); // LCOV_EXCL_LINE
970 : errorsFound = true; // LCOV_EXCL_LINE
971 : } else {
972 4 : thisPLHP.sizingFactor = defaultVal;
973 : }
974 : }
975 :
976 4 : auto &capFtName = fields.at("capacity_modifier_function_of_temperature_curve_name");
977 4 : thisPLHP.capFuncTempCurveIndex = Curve::GetCurveIndex(state, UtilityRoutines::MakeUPPERCase(capFtName.get<std::string>()));
978 4 : if (thisPLHP.capFuncTempCurveIndex == 0) {
979 0 : ShowSevereError(
980 0 : state, "Invalid curve name for EIR PLHP (name=" + thisPLHP.name + "; entered curve name: " + capFtName.get<std::string>());
981 0 : errorsFound = true;
982 : }
983 4 : auto &eirFtName = fields.at("electric_input_to_output_ratio_modifier_function_of_temperature_curve_name");
984 4 : thisPLHP.powerRatioFuncTempCurveIndex = Curve::GetCurveIndex(state, UtilityRoutines::MakeUPPERCase(eirFtName.get<std::string>()));
985 4 : if (thisPLHP.capFuncTempCurveIndex == 0) {
986 0 : ShowSevereError(
987 0 : state, "Invalid curve name for EIR PLHP (name=" + thisPLHP.name + "; entered curve name: " + eirFtName.get<std::string>());
988 0 : errorsFound = true;
989 : }
990 4 : auto &eirFplrName = fields.at("electric_input_to_output_ratio_modifier_function_of_part_load_ratio_curve_name");
991 4 : thisPLHP.powerRatioFuncPLRCurveIndex = Curve::GetCurveIndex(state, UtilityRoutines::MakeUPPERCase(eirFplrName.get<std::string>()));
992 4 : if (thisPLHP.capFuncTempCurveIndex == 0) {
993 0 : ShowSevereError(
994 0 : state, "Invalid curve name for EIR PLHP (name=" + thisPLHP.name + "; entered curve name: " + eirFplrName.get<std::string>());
995 0 : errorsFound = true;
996 : }
997 :
998 4 : bool nodeErrorsFound = false;
999 4 : thisPLHP.loadSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
1000 : loadSideInletNodeName,
1001 : nodeErrorsFound,
1002 : objType,
1003 : thisPLHP.name,
1004 : DataLoopNode::NodeFluidType::Water,
1005 : DataLoopNode::ConnectionType::Inlet,
1006 : NodeInputManager::CompFluidStream::Primary,
1007 4 : DataLoopNode::ObjectIsNotParent);
1008 4 : thisPLHP.loadSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
1009 : loadSideOutletNodeName,
1010 : nodeErrorsFound,
1011 : objType,
1012 : thisPLHP.name,
1013 : DataLoopNode::NodeFluidType::Water,
1014 : DataLoopNode::ConnectionType::Outlet,
1015 : NodeInputManager::CompFluidStream::Primary,
1016 4 : DataLoopNode::ObjectIsNotParent);
1017 4 : DataLoopNode::NodeFluidType condenserNodeType = DataLoopNode::NodeFluidType::Blank;
1018 4 : DataLoopNode::ConnectionType condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Blank;
1019 4 : DataLoopNode::ConnectionType condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Blank;
1020 4 : if (condenserType == "WATERSOURCE") {
1021 2 : thisPLHP.waterSource = true;
1022 2 : condenserNodeType = DataLoopNode::NodeFluidType::Water;
1023 2 : condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::Inlet;
1024 2 : condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::Outlet;
1025 2 : } else if (condenserType == "AIRSOURCE") {
1026 2 : thisPLHP.airSource = true;
1027 2 : condenserNodeType = DataLoopNode::NodeFluidType::Air;
1028 2 : condenserNodeConnectionType_Inlet = DataLoopNode::ConnectionType::OutsideAir;
1029 2 : condenserNodeConnectionType_Outlet = DataLoopNode::ConnectionType::OutsideAir;
1030 : } else {
1031 : // Again, this should be protected by the input processor
1032 0 : ShowErrorMessage(state,
1033 : "Invalid heat pump condenser type (name=" + thisPLHP.name + // LCOV_EXCL_LINE
1034 : "; entered type: " + condenserType); // LCOV_EXCL_LINE
1035 : errorsFound = true; // LCOV_EXCL_LINE
1036 : }
1037 4 : thisPLHP.sourceSideNodes.inlet = NodeInputManager::GetOnlySingleNode(state,
1038 : sourceSideInletNodeName,
1039 : nodeErrorsFound,
1040 : objType,
1041 : thisPLHP.name,
1042 : condenserNodeType,
1043 : condenserNodeConnectionType_Inlet,
1044 : NodeInputManager::CompFluidStream::Secondary,
1045 4 : DataLoopNode::ObjectIsNotParent);
1046 4 : thisPLHP.sourceSideNodes.outlet = NodeInputManager::GetOnlySingleNode(state,
1047 : sourceSideOutletNodeName,
1048 : nodeErrorsFound,
1049 : objType,
1050 : thisPLHP.name,
1051 : condenserNodeType,
1052 : condenserNodeConnectionType_Outlet,
1053 : NodeInputManager::CompFluidStream::Secondary,
1054 4 : DataLoopNode::ObjectIsNotParent);
1055 4 : if (nodeErrorsFound) errorsFound = true;
1056 4 : BranchNodeConnections::TestCompSet(
1057 : state, cCurrentModuleObject, thisPLHP.name, loadSideInletNodeName, loadSideOutletNodeName, classToInput.nodesType);
1058 :
1059 4 : if (thisPLHP.waterSource) {
1060 2 : BranchNodeConnections::TestCompSet(
1061 : state, cCurrentModuleObject, thisPLHP.name, sourceSideInletNodeName, sourceSideOutletNodeName, "Condenser Water Nodes");
1062 : }
1063 :
1064 : // store the worker functions that generalized the heating/cooling sides
1065 4 : thisPLHP.calcLoadOutletTemp = classToInput.calcLoadOutletTemp;
1066 4 : thisPLHP.calcQsource = classToInput.calcQsource;
1067 4 : thisPLHP.calcSourceOutletTemp = classToInput.calcSourceOutletTemp;
1068 :
1069 4 : if (!errorsFound) {
1070 4 : state.dataEIRPlantLoopHeatPump->heatPumps.push_back(thisPLHP);
1071 : }
1072 : }
1073 : }
1074 : }
1075 2 : if (errorsFound) {
1076 : // currently there are no straightforward unit tests possible to get here
1077 : // all curves are required and inputs are validated by the input processor
1078 : // obviously this will stay here but I don't feel like counting it against coverage
1079 : ShowFatalError(state, "Previous EIR PLHP errors cause program termination"); // LCOV_EXCL_LINE
1080 : }
1081 2 : }
1082 :
1083 523087 : void EIRPlantLoopHeatPump::checkConcurrentOperation(EnergyPlusData &state)
1084 : {
1085 : // This will do a recurring warning for concurrent companion operation.
1086 : // This function should be called at the end of the time-step to ensure any iteration-level operation
1087 : // is worked out and the results are final.
1088 : // This function does not try to be intelligent about only reporting for one of the companions. The only
1089 : // way I could think of was to have a vector, either static here or in the namespace, that would hold
1090 : // companion index values as I warn against their partner, so then I would have to add the values to the
1091 : // vector each pass, and check then each loop. This seemed really bulky and inefficient, so I chose to
1092 : // leave a tight loop here of just reporting for each coil if it and the companion are running.
1093 523279 : for (auto &thisPLHP : state.dataEIRPlantLoopHeatPump->heatPumps) {
1094 192 : if (!thisPLHP.companionHeatPumpCoil) {
1095 0 : continue;
1096 : }
1097 192 : if (thisPLHP.running && thisPLHP.companionHeatPumpCoil->running) {
1098 0 : ShowRecurringWarningErrorAtEnd(state,
1099 0 : "Companion heat pump objects running concurrently, check operation. Base object name: " + thisPLHP.name,
1100 : thisPLHP.recurringConcurrentOperationWarningIndex);
1101 : }
1102 : }
1103 523087 : }
1104 30 : void EIRPlantLoopHeatPump::oneTimeInit(EnergyPlusData &state)
1105 : {
1106 : // This function does all the one-time initialization
1107 30 : std::string static const routineName = std::string("EIRPlantLoopHeatPump :") + __FUNCTION__;
1108 :
1109 30 : if (this->oneTimeInitFlag) {
1110 4 : bool errFlag = false;
1111 :
1112 : // setup output variables
1113 8 : SetupOutputVariable(state,
1114 : "Heat Pump Load Side Heat Transfer Rate",
1115 : OutputProcessor::Unit::W,
1116 : this->loadSideHeatTransfer,
1117 : OutputProcessor::SOVTimeStepType::System,
1118 : OutputProcessor::SOVStoreType::Average,
1119 4 : this->name);
1120 8 : SetupOutputVariable(state,
1121 : "Heat Pump Load Side Heat Transfer Energy",
1122 : OutputProcessor::Unit::J,
1123 : this->loadSideEnergy,
1124 : OutputProcessor::SOVTimeStepType::System,
1125 : OutputProcessor::SOVStoreType::Summed,
1126 : this->name,
1127 : _,
1128 : "ENERGYTRANSFER",
1129 : _,
1130 : _,
1131 4 : "Plant");
1132 8 : SetupOutputVariable(state,
1133 : "Heat Pump Source Side Heat Transfer Rate",
1134 : OutputProcessor::Unit::W,
1135 : this->sourceSideHeatTransfer,
1136 : OutputProcessor::SOVTimeStepType::System,
1137 : OutputProcessor::SOVStoreType::Average,
1138 4 : this->name);
1139 8 : SetupOutputVariable(state,
1140 : "Heat Pump Source Side Heat Transfer Energy",
1141 : OutputProcessor::Unit::J,
1142 : this->sourceSideEnergy,
1143 : OutputProcessor::SOVTimeStepType::System,
1144 : OutputProcessor::SOVStoreType::Summed,
1145 4 : this->name);
1146 8 : SetupOutputVariable(state,
1147 : "Heat Pump Load Side Inlet Temperature",
1148 : OutputProcessor::Unit::C,
1149 : this->loadSideInletTemp,
1150 : OutputProcessor::SOVTimeStepType::System,
1151 : OutputProcessor::SOVStoreType::Average,
1152 4 : this->name);
1153 8 : SetupOutputVariable(state,
1154 : "Heat Pump Load Side Outlet Temperature",
1155 : OutputProcessor::Unit::C,
1156 : this->loadSideOutletTemp,
1157 : OutputProcessor::SOVTimeStepType::System,
1158 : OutputProcessor::SOVStoreType::Average,
1159 4 : this->name);
1160 8 : SetupOutputVariable(state,
1161 : "Heat Pump Source Side Inlet Temperature",
1162 : OutputProcessor::Unit::C,
1163 : this->sourceSideInletTemp,
1164 : OutputProcessor::SOVTimeStepType::System,
1165 : OutputProcessor::SOVStoreType::Average,
1166 4 : this->name);
1167 8 : SetupOutputVariable(state,
1168 : "Heat Pump Source Side Outlet Temperature",
1169 : OutputProcessor::Unit::C,
1170 : this->sourceSideOutletTemp,
1171 : OutputProcessor::SOVTimeStepType::System,
1172 : OutputProcessor::SOVStoreType::Average,
1173 4 : this->name);
1174 8 : SetupOutputVariable(state,
1175 : "Heat Pump Electricity Rate",
1176 : OutputProcessor::Unit::W,
1177 : this->powerUsage,
1178 : OutputProcessor::SOVTimeStepType::System,
1179 : OutputProcessor::SOVStoreType::Average,
1180 4 : this->name);
1181 4 : if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRCooling) { // energy from HeatPump:PlantLoop:EIR:Cooling object
1182 4 : SetupOutputVariable(state,
1183 : "Heat Pump Electricity Energy",
1184 : OutputProcessor::Unit::J,
1185 : this->powerEnergy,
1186 : OutputProcessor::SOVTimeStepType::System,
1187 : OutputProcessor::SOVStoreType::Summed,
1188 : this->name,
1189 : _,
1190 : "Electricity",
1191 : "Cooling",
1192 : "Heat Pump",
1193 2 : "Plant");
1194 2 : } else if (this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpEIRHeating) { // energy from HeatPump:PlantLoop:EIR:Heating object
1195 4 : SetupOutputVariable(state,
1196 : "Heat Pump Electricity Energy",
1197 : OutputProcessor::Unit::J,
1198 : this->powerEnergy,
1199 : OutputProcessor::SOVTimeStepType::System,
1200 : OutputProcessor::SOVStoreType::Summed,
1201 : this->name,
1202 : _,
1203 : "Electricity",
1204 : "Heating",
1205 : "Heat Pump",
1206 2 : "Plant");
1207 : }
1208 8 : SetupOutputVariable(state,
1209 : "Heat Pump Load Side Mass Flow Rate",
1210 : OutputProcessor::Unit::kg_s,
1211 : this->loadSideMassFlowRate,
1212 : OutputProcessor::SOVTimeStepType::System,
1213 : OutputProcessor::SOVStoreType::Average,
1214 4 : this->name);
1215 8 : SetupOutputVariable(state,
1216 : "Heat Pump Source Side Mass Flow Rate",
1217 : OutputProcessor::Unit::kg_s,
1218 : this->sourceSideMassFlowRate,
1219 : OutputProcessor::SOVTimeStepType::System,
1220 : OutputProcessor::SOVStoreType::Average,
1221 4 : this->name);
1222 :
1223 : // find this component on the plant
1224 4 : bool thisErrFlag = false;
1225 4 : PlantUtilities::ScanPlantLoopsForObject(
1226 : state, this->name, this->EIRHPType, this->loadSidePlantLoc, thisErrFlag, _, _, _, this->loadSideNodes.inlet, _);
1227 :
1228 4 : if (thisErrFlag) {
1229 0 : ShowSevereError(state,
1230 0 : format("{}: Plant topology problem for {} name = \"{}\"",
1231 : routineName,
1232 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
1233 0 : this->name));
1234 0 : ShowContinueError(state, "Could not locate component's load side connections on a plant loop");
1235 0 : errFlag = true;
1236 4 : } else if (this->loadSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Supply) { // only check if !thisErrFlag
1237 0 : ShowSevereError(state,
1238 0 : format("{}: Invalid connections for {} name = \"{}\"",
1239 : routineName,
1240 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
1241 0 : this->name));
1242 0 : ShowContinueError(state, "The load side connections are not on the Supply Side of a plant loop");
1243 0 : errFlag = true;
1244 : }
1245 :
1246 4 : thisErrFlag = false;
1247 4 : if (this->waterSource) {
1248 2 : PlantUtilities::ScanPlantLoopsForObject(
1249 : state, this->name, this->EIRHPType, this->sourceSidePlantLoc, thisErrFlag, _, _, _, this->sourceSideNodes.inlet, _);
1250 :
1251 2 : if (thisErrFlag) {
1252 0 : ShowSevereError(state,
1253 0 : format("{}: Plant topology problem for {} name = \"{}\"",
1254 : routineName,
1255 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
1256 0 : this->name));
1257 0 : ShowContinueError(state, "Could not locate component's source side connections on a plant loop");
1258 0 : errFlag = true;
1259 2 : } else if (this->sourceSidePlantLoc.loopSideNum != DataPlant::LoopSideLocation::Demand) { // only check if !thisErrFlag
1260 0 : ShowSevereError(state,
1261 0 : format("{}: Invalid connections for {} name = \"{}\"",
1262 : routineName,
1263 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
1264 0 : this->name));
1265 0 : ShowContinueError(state, "The source side connections are not on the Demand Side of a plant loop");
1266 0 : errFlag = true;
1267 : }
1268 :
1269 : // make sure it is not the same loop on both sides.
1270 2 : if (this->loadSidePlantLoc.loopNum == this->sourceSidePlantLoc.loopNum) { // user is being too tricky, don't allow
1271 0 : ShowSevereError(state,
1272 0 : format("{}: Invalid connections for {} name = \"{}\"",
1273 : routineName,
1274 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->EIRHPType)],
1275 0 : this->name));
1276 0 : ShowContinueError(state, "The load and source sides need to be on different loops.");
1277 0 : errFlag = true;
1278 : } else {
1279 :
1280 2 : PlantUtilities::InterConnectTwoPlantLoopSides(state, this->loadSidePlantLoc, this->sourceSidePlantLoc, this->EIRHPType, true);
1281 : }
1282 2 : } else if (this->airSource) {
1283 : // nothing to do here ?
1284 : }
1285 :
1286 4 : if (errFlag) {
1287 0 : ShowFatalError(state, routineName + ": Program terminated due to previous condition(s).");
1288 : }
1289 4 : this->oneTimeInitFlag = false;
1290 : }
1291 30 : }
1292 2313 : } // namespace EnergyPlus::EIRPlantLoopHeatPumps
|