Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 : #include <iostream>
51 : #include <string>
52 :
53 : // ObjexxFCL Headers
54 : #include <ObjexxFCL/Array.functions.hh>
55 : #include <ObjexxFCL/Fmath.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/Autosizing/Base.hh>
59 : #include <EnergyPlus/BranchNodeConnections.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataEnvironment.hh>
62 : #include <EnergyPlus/DataHVACGlobals.hh>
63 : #include <EnergyPlus/DataIPShortCuts.hh>
64 : #include <EnergyPlus/DataLoopNode.hh>
65 : #include <EnergyPlus/DataSizing.hh>
66 : #include <EnergyPlus/FluidProperties.hh>
67 : #include <EnergyPlus/General.hh>
68 : #include <EnergyPlus/GlobalNames.hh>
69 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
70 : #include <EnergyPlus/NodeInputManager.hh>
71 : #include <EnergyPlus/OutputProcessor.hh>
72 : #include <EnergyPlus/OutsideEnergySources.hh>
73 : #include <EnergyPlus/Plant/DataPlant.hh>
74 : #include <EnergyPlus/PlantUtilities.hh>
75 : #include <EnergyPlus/ScheduleManager.hh>
76 : #include <EnergyPlus/UtilityRoutines.hh>
77 :
78 : namespace EnergyPlus::OutsideEnergySources {
79 :
80 : // MODULE INFORMATION:
81 : // AUTHOR Dan Fisher
82 : // DATE WRITTEN Unknown
83 : // MODIFIED na
84 : // RE-ENGINEERED Brent Griffith, Sept 2010, revised plant interactions.
85 :
86 : // PURPOSE OF THIS MODULE:
87 : // Module containing the routines dealing with the OutsideEnergySources
88 :
89 278 : PlantComponent *OutsideEnergySourceSpecs::factory(EnergyPlusData &state, DataPlant::PlantEquipmentType objectType, std::string_view objectName)
90 : {
91 : // Process the input data for outside energy sources if it hasn't been done already
92 278 : if (state.dataOutsideEnergySrcs->SimOutsideEnergyGetInputFlag) {
93 159 : GetOutsideEnergySourcesInput(state);
94 159 : state.dataOutsideEnergySrcs->SimOutsideEnergyGetInputFlag = false;
95 : }
96 : // Now look for this particular pipe in the list
97 397 : for (auto &source : state.dataOutsideEnergySrcs->EnergySource) {
98 397 : if (source.EnergyType == objectType && source.Name == objectName) {
99 278 : return &source;
100 : }
101 : }
102 : // If we didn't find it, fatal
103 : ShowFatalError(state, format("OutsideEnergySourceSpecsFactory: Error getting inputs for source named: {}", objectName)); // LCOV_EXCL_LINE
104 : // Shut up the compiler
105 : return nullptr; // LCOV_EXCL_LINE
106 : }
107 :
108 7036517 : void OutsideEnergySourceSpecs::simulate(EnergyPlusData &state,
109 : [[maybe_unused]] const PlantLocation &calledFromLocation,
110 : [[maybe_unused]] bool FirstHVACIteration,
111 : Real64 &CurLoad,
112 : bool RunFlag)
113 : {
114 7036517 : this->initialize(state, CurLoad);
115 7036517 : this->calculate(state, RunFlag, CurLoad);
116 7036517 : }
117 :
118 1392 : void OutsideEnergySourceSpecs::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &)
119 : {
120 1392 : this->initialize(state, 0.0);
121 1392 : this->size(state);
122 1392 : }
123 :
124 1392 : void OutsideEnergySourceSpecs::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
125 : [[maybe_unused]] const PlantLocation &calledFromLocation,
126 : Real64 &MaxLoad,
127 : Real64 &MinLoad,
128 : Real64 &OptLoad)
129 : {
130 1392 : MinLoad = 0.0;
131 1392 : MaxLoad = this->NomCap;
132 1392 : OptLoad = this->NomCap;
133 1392 : }
134 :
135 159 : void GetOutsideEnergySourcesInput(EnergyPlusData &state)
136 : {
137 : // SUBROUTINE INFORMATION:
138 : // AUTHOR Dan Fisher
139 : // DATE WRITTEN April 1998
140 : // MODIFIED May 2010; Edwin Lee; Linda Lawrie (consolidation)
141 : // June 2022, Dareum Nam, Add DistrictHeatingSteam
142 : // RE-ENGINEERED na
143 :
144 : // PURPOSE OF THIS SUBROUTINE:
145 : // This routine obtains the input data puts it into the
146 : // component arrays. Data items in the component arrays
147 : // are initialized. Output variables are set up.
148 :
149 : // GET NUMBER OF ALL EQUIPMENT TYPES
150 159 : int const NumDistrictUnitsHeatWater = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "DistrictHeating:Water");
151 159 : int const NumDistrictUnitsCool = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "DistrictCooling");
152 159 : int const NumDistrictUnitsHeatSteam = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "DistrictHeating:Steam");
153 159 : state.dataOutsideEnergySrcs->NumDistrictUnits = NumDistrictUnitsHeatWater + NumDistrictUnitsCool + NumDistrictUnitsHeatSteam;
154 :
155 159 : if (allocated(state.dataOutsideEnergySrcs->EnergySource)) return;
156 :
157 159 : state.dataOutsideEnergySrcs->EnergySource.allocate(state.dataOutsideEnergySrcs->NumDistrictUnits);
158 159 : state.dataOutsideEnergySrcs->EnergySourceUniqueNames.reserve(static_cast<unsigned>(state.dataOutsideEnergySrcs->NumDistrictUnits));
159 :
160 159 : bool ErrorsFound(false); // If errors detected in input
161 159 : int heatWaterIndex = 0;
162 159 : int coolIndex = 0;
163 159 : int heatSteamIndex = 0;
164 :
165 437 : for (int EnergySourceNum = 1; EnergySourceNum <= state.dataOutsideEnergySrcs->NumDistrictUnits; ++EnergySourceNum) {
166 :
167 278 : std::string nodeNames;
168 : DataPlant::PlantEquipmentType EnergyType;
169 : DataLoopNode::ConnectionObjectType objType;
170 : int thisIndex;
171 278 : if (EnergySourceNum <= NumDistrictUnitsHeatWater) {
172 144 : state.dataIPShortCut->cCurrentModuleObject = "DistrictHeating:Water";
173 144 : objType = DataLoopNode::ConnectionObjectType::DistrictHeatingWater;
174 144 : nodeNames = "Hot Water Nodes";
175 144 : EnergyType = DataPlant::PlantEquipmentType::PurchHotWater;
176 144 : heatWaterIndex++;
177 144 : thisIndex = heatWaterIndex;
178 134 : } else if (EnergySourceNum > NumDistrictUnitsHeatWater && EnergySourceNum <= NumDistrictUnitsHeatWater + NumDistrictUnitsCool) {
179 133 : state.dataIPShortCut->cCurrentModuleObject = "DistrictCooling";
180 133 : objType = DataLoopNode::ConnectionObjectType::DistrictCooling;
181 133 : nodeNames = "Chilled Water Nodes";
182 133 : EnergyType = DataPlant::PlantEquipmentType::PurchChilledWater;
183 133 : coolIndex++;
184 133 : thisIndex = coolIndex;
185 : } else { // EnergySourceNum > NumDistrictUnitsHeatWater + NumDistrictUnitsCool
186 1 : state.dataIPShortCut->cCurrentModuleObject = "DistrictHeating:Steam";
187 1 : objType = DataLoopNode::ConnectionObjectType::DistrictHeatingSteam;
188 1 : nodeNames = "Steam Nodes";
189 1 : EnergyType = DataPlant::PlantEquipmentType::PurchSteam;
190 1 : heatSteamIndex++;
191 1 : thisIndex = heatSteamIndex;
192 : }
193 :
194 278 : int NumAlphas = 0, NumNums = 0, IOStat = 0;
195 834 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
196 278 : state.dataIPShortCut->cCurrentModuleObject,
197 : thisIndex,
198 278 : state.dataIPShortCut->cAlphaArgs,
199 : NumAlphas,
200 278 : state.dataIPShortCut->rNumericArgs,
201 : NumNums,
202 : IOStat,
203 : _,
204 278 : state.dataIPShortCut->lAlphaFieldBlanks,
205 278 : state.dataIPShortCut->cAlphaFieldNames);
206 :
207 278 : if (EnergySourceNum > 1) {
208 119 : GlobalNames::VerifyUniqueInterObjectName(state,
209 119 : state.dataOutsideEnergySrcs->EnergySourceUniqueNames,
210 119 : state.dataIPShortCut->cAlphaArgs(1),
211 119 : state.dataIPShortCut->cCurrentModuleObject,
212 119 : state.dataIPShortCut->cAlphaFieldNames(1),
213 : ErrorsFound);
214 : }
215 278 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).Name = state.dataIPShortCut->cAlphaArgs(1);
216 278 : if (EnergySourceNum <= NumDistrictUnitsHeatWater + NumDistrictUnitsCool) {
217 277 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).InletNodeNum =
218 554 : NodeInputManager::GetOnlySingleNode(state,
219 277 : state.dataIPShortCut->cAlphaArgs(2),
220 : ErrorsFound,
221 : objType,
222 277 : state.dataIPShortCut->cAlphaArgs(1),
223 : DataLoopNode::NodeFluidType::Water,
224 : DataLoopNode::ConnectionType::Inlet,
225 : NodeInputManager::CompFluidStream::Primary,
226 : DataLoopNode::ObjectIsNotParent);
227 277 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).OutletNodeNum =
228 831 : NodeInputManager::GetOnlySingleNode(state,
229 277 : state.dataIPShortCut->cAlphaArgs(3),
230 : ErrorsFound,
231 : objType,
232 277 : state.dataIPShortCut->cAlphaArgs(1),
233 : DataLoopNode::NodeFluidType::Water,
234 : DataLoopNode::ConnectionType::Outlet,
235 : NodeInputManager::CompFluidStream::Primary,
236 : DataLoopNode::ObjectIsNotParent);
237 : } else {
238 1 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).InletNodeNum =
239 2 : NodeInputManager::GetOnlySingleNode(state,
240 1 : state.dataIPShortCut->cAlphaArgs(2),
241 : ErrorsFound,
242 : objType,
243 1 : state.dataIPShortCut->cAlphaArgs(1),
244 : DataLoopNode::NodeFluidType::Steam,
245 : DataLoopNode::ConnectionType::Inlet,
246 : NodeInputManager::CompFluidStream::Primary,
247 : DataLoopNode::ObjectIsNotParent);
248 1 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).OutletNodeNum =
249 3 : NodeInputManager::GetOnlySingleNode(state,
250 1 : state.dataIPShortCut->cAlphaArgs(3),
251 : ErrorsFound,
252 : objType,
253 1 : state.dataIPShortCut->cAlphaArgs(1),
254 : DataLoopNode::NodeFluidType::Steam,
255 : DataLoopNode::ConnectionType::Outlet,
256 : NodeInputManager::CompFluidStream::Primary,
257 : DataLoopNode::ObjectIsNotParent);
258 : }
259 278 : BranchNodeConnections::TestCompSet(state,
260 278 : state.dataIPShortCut->cCurrentModuleObject,
261 278 : state.dataIPShortCut->cAlphaArgs(1),
262 278 : state.dataIPShortCut->cAlphaArgs(2),
263 278 : state.dataIPShortCut->cAlphaArgs(3),
264 : nodeNames);
265 278 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).NomCap = state.dataIPShortCut->rNumericArgs(1);
266 278 : if (state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).NomCap == DataSizing::AutoSize) {
267 8 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).NomCapWasAutoSized = true;
268 : }
269 278 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).EnergyTransfer = 0.0;
270 278 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).EnergyRate = 0.0;
271 278 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).EnergyType = EnergyType;
272 278 : if (!state.dataIPShortCut->lAlphaFieldBlanks(4)) {
273 0 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum =
274 0 : ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(4));
275 0 : if (state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum == 0) {
276 0 : ShowSevereError(state,
277 0 : format("{}=\"{}\", is not valid",
278 0 : state.dataIPShortCut->cCurrentModuleObject,
279 0 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).Name));
280 0 : ShowContinueError(state,
281 0 : format("{}=\"{}\" was not found.", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
282 0 : ErrorsFound = true;
283 : }
284 0 : if (!ScheduleManager::CheckScheduleValueMinMax(
285 0 : state, state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum, true, 0.0)) {
286 0 : ShowWarningError(state,
287 0 : format("{}=\"{}\", is not valid",
288 0 : state.dataIPShortCut->cCurrentModuleObject,
289 0 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).Name));
290 0 : ShowContinueError(state,
291 0 : format("{}=\"{}\" should not have negative values.",
292 0 : state.dataIPShortCut->cAlphaFieldNames(4),
293 0 : state.dataIPShortCut->cAlphaArgs(4)));
294 0 : ShowContinueError(state, "Negative values will be treated as zero, and the simulation continues.");
295 : }
296 : } else {
297 278 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum = ScheduleManager::ScheduleAlwaysOn;
298 : }
299 278 : }
300 :
301 159 : if (ErrorsFound) {
302 0 : ShowFatalError(
303 : state,
304 0 : format("Errors found in processing input for {}, Preceding condition caused termination.", state.dataIPShortCut->cCurrentModuleObject));
305 : }
306 : }
307 :
308 7037909 : void OutsideEnergySourceSpecs::initialize(EnergyPlusData &state, Real64 MyLoad)
309 : {
310 :
311 : // SUBROUTINE INFORMATION:
312 : // AUTHOR: Dan Fisher
313 : // DATE WRITTEN: October 1998
314 : // MODIFIED May 2010; Edwin Lee; Linda Lawrie (consolidation)
315 : // RE-ENGINEERED Sept 2010, Brent Griffith, plant rewrite
316 :
317 : // PURPOSE OF THIS SUBROUTINE:
318 : // This subroutine does one-time inits and sets the operating mass flow rate of this machine
319 :
320 : // METHODOLOGY EMPLOYED:
321 : // One time inits include validating source type (should happen in getinput?) and locating this
322 : // component on the PlantLoop topology.
323 : // The mass flow rate is determined based on component load, and making use of
324 : // the SetComponentFlowRate routine.
325 : // The mass flow rate could be an inter-connected-loop side trigger. This is not really the type of
326 : // interconnect that that routine was written for, but it is the clearest example of using it.
327 :
328 7037909 : auto &loop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
329 :
330 : // begin environment inits
331 7037909 : if (state.dataGlobal->BeginEnvrnFlag && this->BeginEnvrnInitFlag) {
332 : // component model has not design flow rates, using data for overall plant loop
333 1619 : PlantUtilities::InitComponentNodes(state, loop.MinMassFlowRate, loop.MaxMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
334 1619 : this->BeginEnvrnInitFlag = false;
335 : }
336 7037909 : if (!state.dataGlobal->BeginEnvrnFlag) this->BeginEnvrnInitFlag = true;
337 :
338 7037909 : Real64 TempPlantMassFlow(0.0);
339 7037909 : if (std::abs(MyLoad) > 0.0) {
340 1692056 : TempPlantMassFlow = loop.MaxMassFlowRate;
341 : }
342 :
343 : // get actual mass flow to use, hold in MassFlowRate variable
344 7037909 : PlantUtilities::SetComponentFlowRate(state, TempPlantMassFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
345 :
346 7037909 : this->InletTemp = state.dataLoopNodes->Node(this->InletNodeNum).Temp;
347 7037909 : this->MassFlowRate = TempPlantMassFlow;
348 7037909 : }
349 :
350 1392 : void OutsideEnergySourceSpecs::size(EnergyPlusData &state)
351 : {
352 : // SUBROUTINE INFORMATION:
353 : // AUTHOR Daeho Kang
354 : // DATE WRITTEN April 2014
355 : // MODIFIED June 2021, Dareum Nam, Add DistrictHeatingSteam
356 : // RE-ENGINEERED na
357 :
358 : // PURPOSE OF THIS SUBROUTINE:
359 : // This subroutine is for sizing capacities of district cooling and heating objects.
360 :
361 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
362 1392 : bool ErrorsFound(false); // If errors detected in input
363 :
364 : // Type name string variable to collapse the sizing for cooling and heating into one block
365 1392 : std::string_view typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(this->EnergyType)];
366 :
367 1392 : auto &loop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
368 1392 : int const PltSizNum = loop.PlantSizNum;
369 1392 : if (PltSizNum > 0) {
370 : Real64 NomCapDes;
371 427 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater ||
372 242 : this->EnergyType == DataPlant::PlantEquipmentType::PurchHotWater) {
373 : Real64 const rho =
374 422 : FluidProperties::GetDensityGlycol(state, loop.FluidName, Constant::InitConvTemp, loop.FluidIndex, format("Size {}", typeName));
375 : Real64 const Cp =
376 422 : FluidProperties::GetSpecificHeatGlycol(state, loop.FluidName, Constant::InitConvTemp, loop.FluidIndex, format("Size {}", typeName));
377 422 : NomCapDes = Cp * rho * state.dataSize->PlantSizData(PltSizNum).DeltaT * state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate;
378 422 : } else { // this->EnergyType == DataPlant::TypeOf_PurchSteam
379 10 : Real64 const tempSteam = FluidProperties::GetSatTemperatureRefrig(
380 15 : state, loop.FluidName, state.dataEnvrn->StdBaroPress, loop.FluidIndex, format("Size {}", typeName));
381 : Real64 const rhoSteam =
382 5 : FluidProperties::GetSatDensityRefrig(state, loop.FluidName, tempSteam, 1.0, loop.FluidIndex, format("Size {}", typeName));
383 : Real64 const EnthSteamDry =
384 5 : FluidProperties::GetSatEnthalpyRefrig(state, loop.FluidName, tempSteam, 1.0, loop.FluidIndex, format("Size {}", typeName));
385 : Real64 const EnthSteamWet =
386 5 : FluidProperties::GetSatEnthalpyRefrig(state, loop.FluidName, tempSteam, 0.0, loop.FluidIndex, format("Size {}", typeName));
387 5 : Real64 const LatentHeatSteam = EnthSteamDry - EnthSteamWet;
388 5 : NomCapDes = rhoSteam * state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate * LatentHeatSteam;
389 : }
390 427 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
391 87 : if (this->NomCapWasAutoSized) {
392 10 : this->NomCap = NomCapDes;
393 10 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
394 8 : BaseSizer::reportSizerOutput(state, typeName, this->Name, "Design Size Nominal Capacity [W]", NomCapDes);
395 : }
396 10 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
397 1 : BaseSizer::reportSizerOutput(state, typeName, this->Name, "Initial Design Size Nominal Capacity [W]", NomCapDes);
398 : }
399 : } else { // Hard-size with sizing data
400 77 : if (this->NomCap > 0.0 && NomCapDes > 0.0) {
401 75 : Real64 const NomCapUser = this->NomCap;
402 75 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
403 75 : BaseSizer::reportSizerOutput(state,
404 : typeName,
405 : this->Name,
406 : "Design Size Nominal Capacity [W]",
407 : NomCapDes,
408 : "User-Specified Nominal Capacity [W]",
409 : NomCapUser);
410 75 : if (state.dataGlobal->DisplayExtraWarnings) {
411 5 : if ((std::abs(NomCapDes - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
412 1 : ShowMessage(state, format("Size {}: Potential issue with equipment sizing for {}", typeName, this->Name));
413 1 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", NomCapUser));
414 1 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", NomCapDes));
415 1 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
416 1 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
417 : }
418 : }
419 : }
420 : }
421 : }
422 : }
423 : } else {
424 965 : if (this->NomCapWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
425 0 : ShowSevereError(state, format("Autosizing of {} nominal capacity requires a loop Sizing:Plant object", typeName));
426 0 : ShowContinueError(state, format("Occurs in {} object={}", typeName, this->Name));
427 0 : ErrorsFound = true;
428 : }
429 965 : if (!this->NomCapWasAutoSized && this->NomCap > 0.0 && state.dataPlnt->PlantFinalSizesOkayToReport) {
430 193 : BaseSizer::reportSizerOutput(state, typeName, this->Name, "User-Specified Nominal Capacity [W]", this->NomCap);
431 : }
432 : }
433 1392 : if (ErrorsFound) {
434 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
435 : }
436 1392 : }
437 :
438 7036517 : void OutsideEnergySourceSpecs::calculate(EnergyPlusData &state, bool runFlag, Real64 MyLoad)
439 : {
440 : // SUBROUTINE INFORMATION:
441 : // AUTHOR Dan Fisher
442 : // DATE WRITTEN July 1998
443 : // MODIFIED May 2010; Edwin Lee; Linda Lawrie (consolidation)
444 : // June 2021, Dareum Nam, Add DistrictHeatingSteam
445 : // RE-ENGINEERED Sept 2010, Brent Griffith, plant rewrite
446 :
447 : // SUBROUTINE PARAMETER DEFINITIONS:
448 : static constexpr std::string_view RoutineName("SimDistrictEnergy");
449 :
450 7036517 : auto &loop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
451 :
452 : // set inlet and outlet nodes
453 7036517 : int const LoopNum = this->plantLoc.loopNum;
454 7036517 : Real64 const LoopMinTemp = state.dataPlnt->PlantLoop(LoopNum).MinTemp;
455 7036517 : Real64 const LoopMaxTemp = state.dataPlnt->PlantLoop(LoopNum).MaxTemp;
456 7036517 : Real64 const LoopMinMdot = state.dataPlnt->PlantLoop(LoopNum).MinMassFlowRate;
457 7036517 : Real64 const LoopMaxMdot = state.dataPlnt->PlantLoop(LoopNum).MaxMassFlowRate;
458 :
459 : // apply power limit from input
460 7036517 : Real64 CapFraction = ScheduleManager::GetCurrentScheduleValue(state, this->CapFractionSchedNum);
461 7036517 : CapFraction = max(0.0, CapFraction); // ensure non negative
462 7036517 : Real64 const CurrentCap = this->NomCap * CapFraction;
463 7036517 : if (std::abs(MyLoad) > CurrentCap) {
464 40 : MyLoad = sign(CurrentCap, MyLoad);
465 : }
466 :
467 7036517 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater) {
468 3260166 : if (MyLoad > 0.0) MyLoad = 0.0;
469 3776351 : } else if (this->EnergyType == DataPlant::PlantEquipmentType::PurchHotWater || this->EnergyType == DataPlant::PlantEquipmentType::PurchSteam) {
470 3776351 : if (MyLoad < 0.0) MyLoad = 0.0;
471 : }
472 :
473 : // determine outlet temp based on inlet temp, cp, and MyLoad
474 7036517 : if ((this->MassFlowRate > 0.0) && runFlag) {
475 1691706 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater ||
476 1375112 : this->EnergyType == DataPlant::PlantEquipmentType::PurchHotWater) {
477 1686242 : Real64 const Cp = FluidProperties::GetSpecificHeatGlycol(
478 1686242 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, this->InletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
479 1686242 : this->OutletTemp = (MyLoad + this->MassFlowRate * Cp * this->InletTemp) / (this->MassFlowRate * Cp);
480 : // apply loop limits on temperature result to keep in check
481 1686242 : if (this->OutletTemp < LoopMinTemp) {
482 0 : this->OutletTemp = max(this->OutletTemp, LoopMinTemp);
483 0 : MyLoad = this->MassFlowRate * Cp * (this->OutletTemp - this->InletTemp);
484 : }
485 1686242 : if (this->OutletTemp > LoopMaxTemp) {
486 0 : this->OutletTemp = min(this->OutletTemp, LoopMaxTemp);
487 0 : MyLoad = this->MassFlowRate * Cp * (this->OutletTemp - this->InletTemp);
488 : }
489 1691706 : } else if (this->EnergyType == DataPlant::PlantEquipmentType::PurchSteam) { // determine mass flow rate based on inlet temp, saturate temp at
490 : // atmospheric pressure, Cp of inlet condensate, and MyLoad
491 : Real64 SatTempAtmPress =
492 5464 : FluidProperties::GetSatTemperatureRefrig(state, loop.FluidName, DataEnvironment::StdPressureSeaLevel, loop.FluidIndex, RoutineName);
493 5464 : Real64 CpCondensate = FluidProperties::GetSpecificHeatGlycol(state, loop.FluidName, this->InletTemp, loop.FluidIndex, RoutineName);
494 5464 : Real64 deltaTsensible = SatTempAtmPress - this->InletTemp;
495 5464 : Real64 EnthSteamInDry = FluidProperties::GetSatEnthalpyRefrig(state, loop.FluidName, this->InletTemp, 1.0, loop.FluidIndex, RoutineName);
496 5464 : Real64 EnthSteamOutWet = FluidProperties::GetSatEnthalpyRefrig(state, loop.FluidName, this->InletTemp, 0.0, loop.FluidIndex, RoutineName);
497 5464 : Real64 LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
498 5464 : this->MassFlowRate = MyLoad / (LatentHeatSteam + (CpCondensate * deltaTsensible));
499 5464 : PlantUtilities::SetComponentFlowRate(state, this->MassFlowRate, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
500 : // Like the assumption in Boiler:Steam, assume that it can meet the steam loop setpoint
501 5464 : this->OutletTemp = state.dataLoopNodes->Node(loop.TempSetPointNodeNum).TempSetPoint;
502 5464 : this->OutletSteamQuality = 0.0;
503 : // apply loop limits on mass flow rate result to keep in check
504 5464 : if (this->MassFlowRate < LoopMinMdot) {
505 0 : this->MassFlowRate = max(this->MassFlowRate, LoopMinMdot);
506 0 : PlantUtilities::SetComponentFlowRate(state, this->MassFlowRate, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
507 0 : MyLoad = this->MassFlowRate * LatentHeatSteam;
508 : }
509 5464 : if (this->MassFlowRate > LoopMaxMdot) {
510 0 : this->MassFlowRate = min(this->MassFlowRate, LoopMaxMdot);
511 0 : PlantUtilities::SetComponentFlowRate(state, this->MassFlowRate, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
512 0 : MyLoad = this->MassFlowRate * LatentHeatSteam;
513 : }
514 : // Like the assumption in Boiler:Steam, assume that saturated steam is leaving the district heating steam plant
515 5464 : state.dataLoopNodes->Node(this->OutletNodeNum).Quality = 1.0;
516 : }
517 1691706 : } else {
518 5344811 : this->OutletTemp = this->InletTemp;
519 5344811 : MyLoad = 0.0;
520 : }
521 7036517 : int const OutletNode = this->OutletNodeNum;
522 7036517 : state.dataLoopNodes->Node(OutletNode).Temp = this->OutletTemp;
523 7036517 : this->EnergyRate = std::abs(MyLoad);
524 7036517 : this->EnergyTransfer = this->EnergyRate * state.dataHVACGlobal->TimeStepSysSec;
525 7036517 : }
526 :
527 278 : void OutsideEnergySourceSpecs::oneTimeInit_new(EnergyPlusData &state)
528 : {
529 : // Locate the unit on the plant loops for later usage
530 278 : bool errFlag = false;
531 278 : PlantUtilities::ScanPlantLoopsForObject(state, this->Name, this->EnergyType, this->plantLoc, errFlag, _, _, _, _, _);
532 278 : if (errFlag) {
533 0 : ShowFatalError(state, "InitSimVars: Program terminated due to previous condition(s).");
534 : }
535 :
536 278 : auto &loop = state.dataPlnt->PlantLoop(this->plantLoc.loopNum);
537 : // set limits on outlet node temps to plant loop limits
538 278 : DataPlant::CompData::getPlantComponent(state, this->plantLoc).MinOutletTemp = loop.MinTemp;
539 278 : DataPlant::CompData::getPlantComponent(state, this->plantLoc).MaxOutletTemp = loop.MaxTemp;
540 : // Register design flow rate for inlet node (helps to autosize comp setpoint op scheme flows
541 278 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNodeNum, loop.MaxVolFlowRate);
542 :
543 278 : std::string reportVarPrefix = "District Heating Water ";
544 278 : OutputProcessor::EndUseCat heatingOrCooling = OutputProcessor::EndUseCat::Heating;
545 278 : Constant::eResource meterTypeKey = Constant::eResource::DistrictHeatingWater;
546 :
547 278 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater) {
548 133 : reportVarPrefix = "District Cooling Water ";
549 133 : heatingOrCooling = OutputProcessor::EndUseCat::Cooling;
550 133 : meterTypeKey = Constant::eResource::DistrictCooling;
551 145 : } else if (this->EnergyType == DataPlant::PlantEquipmentType::PurchSteam) {
552 1 : reportVarPrefix = "District Heating Steam ";
553 1 : heatingOrCooling = OutputProcessor::EndUseCat::Heating;
554 1 : meterTypeKey = Constant::eResource::DistrictHeatingSteam;
555 : }
556 834 : SetupOutputVariable(state,
557 556 : format("{}Energy", reportVarPrefix),
558 : Constant::Units::J,
559 278 : this->EnergyTransfer,
560 : OutputProcessor::TimeStepType::System,
561 : OutputProcessor::StoreType::Sum,
562 278 : this->Name,
563 : meterTypeKey,
564 : OutputProcessor::Group::Plant,
565 : heatingOrCooling);
566 834 : SetupOutputVariable(state,
567 556 : format("{}Rate", reportVarPrefix),
568 : Constant::Units::W,
569 278 : this->EnergyRate,
570 : OutputProcessor::TimeStepType::System,
571 : OutputProcessor::StoreType::Average,
572 278 : this->Name);
573 834 : SetupOutputVariable(state,
574 556 : format("{}Inlet Temperature", reportVarPrefix),
575 : Constant::Units::C,
576 278 : this->InletTemp,
577 : OutputProcessor::TimeStepType::System,
578 : OutputProcessor::StoreType::Average,
579 278 : this->Name);
580 834 : SetupOutputVariable(state,
581 556 : format("{}Outlet Temperature", reportVarPrefix),
582 : Constant::Units::C,
583 278 : this->OutletTemp,
584 : OutputProcessor::TimeStepType::System,
585 : OutputProcessor::StoreType::Average,
586 278 : this->Name);
587 834 : SetupOutputVariable(state,
588 556 : format("{}Mass Flow Rate", reportVarPrefix),
589 : Constant::Units::kg_s,
590 278 : this->MassFlowRate,
591 : OutputProcessor::TimeStepType::System,
592 : OutputProcessor::StoreType::Average,
593 278 : this->Name);
594 278 : }
595 :
596 0 : void OutsideEnergySourceSpecs::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
597 : {
598 0 : }
599 :
600 : } // namespace EnergyPlus::OutsideEnergySources
|