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 <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 275 : 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 275 : if (state.dataOutsideEnergySrcs->SimOutsideEnergyGetInputFlag) {
93 157 : GetOutsideEnergySourcesInput(state);
94 157 : state.dataOutsideEnergySrcs->SimOutsideEnergyGetInputFlag = false;
95 : }
96 : // Now look for this particular pipe in the list
97 393 : for (auto &source : state.dataOutsideEnergySrcs->EnergySource) {
98 393 : if (source.EnergyType == objectType && source.Name == objectName) {
99 275 : 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 7025029 : void OutsideEnergySourceSpecs::simulate(EnergyPlusData &state,
109 : [[maybe_unused]] const PlantLocation &calledFromLocation,
110 : [[maybe_unused]] bool FirstHVACIteration,
111 : Real64 &CurLoad,
112 : bool RunFlag)
113 : {
114 7025029 : this->initialize(state, CurLoad);
115 7025029 : this->calculate(state, RunFlag, CurLoad);
116 7025029 : }
117 :
118 1375 : void OutsideEnergySourceSpecs::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &)
119 : {
120 1375 : this->initialize(state, 0.0);
121 1375 : this->size(state);
122 1375 : }
123 :
124 1375 : void OutsideEnergySourceSpecs::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
125 : [[maybe_unused]] const PlantLocation &calledFromLocation,
126 : Real64 &MaxLoad,
127 : Real64 &MinLoad,
128 : Real64 &OptLoad)
129 : {
130 1375 : MinLoad = 0.0;
131 1375 : MaxLoad = this->NomCap;
132 1375 : OptLoad = this->NomCap;
133 1375 : }
134 :
135 157 : 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 : // RE-ENGINEERED na
142 :
143 : // PURPOSE OF THIS SUBROUTINE:
144 : // This routine obtains the input data puts it into the
145 : // component arrays. Data items in the component arrays
146 : // are initialized. Output variables are set up.
147 :
148 : // GET NUMBER OF ALL EQUIPMENT TYPES
149 157 : int const NumDistrictUnitsHeat = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "DistrictHeating");
150 157 : int const NumDistrictUnitsCool = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "DistrictCooling");
151 157 : state.dataOutsideEnergySrcs->NumDistrictUnits = NumDistrictUnitsHeat + NumDistrictUnitsCool;
152 :
153 157 : if (allocated(state.dataOutsideEnergySrcs->EnergySource)) return;
154 :
155 157 : state.dataOutsideEnergySrcs->EnergySource.allocate(state.dataOutsideEnergySrcs->NumDistrictUnits);
156 157 : state.dataOutsideEnergySrcs->EnergySourceUniqueNames.reserve(static_cast<unsigned>(state.dataOutsideEnergySrcs->NumDistrictUnits));
157 :
158 157 : bool ErrorsFound(false); // If errors detected in input
159 157 : int heatIndex = 0;
160 157 : int coolIndex = 0;
161 :
162 432 : for (int EnergySourceNum = 1; EnergySourceNum <= state.dataOutsideEnergySrcs->NumDistrictUnits; ++EnergySourceNum) {
163 :
164 550 : std::string nodeNames;
165 : DataPlant::PlantEquipmentType EnergyType;
166 : DataLoopNode::ConnectionObjectType objType;
167 : int thisIndex;
168 275 : if (EnergySourceNum <= NumDistrictUnitsHeat) {
169 143 : state.dataIPShortCut->cCurrentModuleObject = "DistrictHeating";
170 143 : objType = DataLoopNode::ConnectionObjectType::DistrictHeating;
171 143 : nodeNames = "Hot Water Nodes";
172 143 : EnergyType = DataPlant::PlantEquipmentType::PurchHotWater;
173 143 : heatIndex++;
174 143 : thisIndex = heatIndex;
175 : } else {
176 132 : state.dataIPShortCut->cCurrentModuleObject = "DistrictCooling";
177 132 : objType = DataLoopNode::ConnectionObjectType::DistrictCooling;
178 132 : nodeNames = "Chilled Water Nodes";
179 132 : EnergyType = DataPlant::PlantEquipmentType::PurchChilledWater;
180 132 : coolIndex++;
181 132 : thisIndex = coolIndex;
182 : }
183 :
184 275 : int NumAlphas = 0, NumNums = 0, IOStat = 0;
185 1650 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
186 275 : state.dataIPShortCut->cCurrentModuleObject,
187 : thisIndex,
188 275 : state.dataIPShortCut->cAlphaArgs,
189 : NumAlphas,
190 275 : state.dataIPShortCut->rNumericArgs,
191 : NumNums,
192 : IOStat,
193 : _,
194 275 : state.dataIPShortCut->lAlphaFieldBlanks,
195 275 : state.dataIPShortCut->cAlphaFieldNames);
196 :
197 275 : if (EnergySourceNum > 1) {
198 354 : GlobalNames::VerifyUniqueInterObjectName(state,
199 118 : state.dataOutsideEnergySrcs->EnergySourceUniqueNames,
200 118 : state.dataIPShortCut->cAlphaArgs(1),
201 118 : state.dataIPShortCut->cCurrentModuleObject,
202 118 : state.dataIPShortCut->cAlphaFieldNames(1),
203 : ErrorsFound);
204 : }
205 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).Name = state.dataIPShortCut->cAlphaArgs(1);
206 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).InletNodeNum =
207 550 : NodeInputManager::GetOnlySingleNode(state,
208 275 : state.dataIPShortCut->cAlphaArgs(2),
209 : ErrorsFound,
210 : objType,
211 275 : state.dataIPShortCut->cAlphaArgs(1),
212 : DataLoopNode::NodeFluidType::Water,
213 : DataLoopNode::ConnectionType::Inlet,
214 : NodeInputManager::CompFluidStream::Primary,
215 275 : DataLoopNode::ObjectIsNotParent);
216 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).OutletNodeNum =
217 550 : NodeInputManager::GetOnlySingleNode(state,
218 275 : state.dataIPShortCut->cAlphaArgs(3),
219 : ErrorsFound,
220 : objType,
221 275 : state.dataIPShortCut->cAlphaArgs(1),
222 : DataLoopNode::NodeFluidType::Water,
223 : DataLoopNode::ConnectionType::Outlet,
224 : NodeInputManager::CompFluidStream::Primary,
225 275 : DataLoopNode::ObjectIsNotParent);
226 550 : BranchNodeConnections::TestCompSet(state,
227 275 : state.dataIPShortCut->cCurrentModuleObject,
228 275 : state.dataIPShortCut->cAlphaArgs(1),
229 275 : state.dataIPShortCut->cAlphaArgs(2),
230 275 : state.dataIPShortCut->cAlphaArgs(3),
231 : nodeNames);
232 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).NomCap = state.dataIPShortCut->rNumericArgs(1);
233 275 : if (state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).NomCap == DataSizing::AutoSize) {
234 7 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).NomCapWasAutoSized = true;
235 : }
236 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).EnergyTransfer = 0.0;
237 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).EnergyRate = 0.0;
238 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).EnergyType = EnergyType;
239 275 : if (!state.dataIPShortCut->lAlphaFieldBlanks(4)) {
240 0 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum =
241 0 : ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(4));
242 0 : if (state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum == 0) {
243 0 : ShowSevereError(state,
244 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" + state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).Name +
245 : "\", is not valid");
246 0 : ShowContinueError(state,
247 0 : state.dataIPShortCut->cAlphaFieldNames(4) + "=\"" + state.dataIPShortCut->cAlphaArgs(4) + "\" was not found.");
248 0 : ErrorsFound = true;
249 : }
250 0 : if (!ScheduleManager::CheckScheduleValueMinMax(
251 0 : state, state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum, ">=", 0.0)) {
252 0 : ShowWarningError(state,
253 0 : state.dataIPShortCut->cCurrentModuleObject + "=\"" +
254 0 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).Name + "\", is not valid");
255 0 : ShowContinueError(state,
256 0 : state.dataIPShortCut->cAlphaFieldNames(4) + "=\"" + state.dataIPShortCut->cAlphaArgs(4) +
257 : "\" should not have negative values.");
258 0 : ShowContinueError(state, "Negative values will be treated as zero, and the simulation continues.");
259 : }
260 : } else {
261 275 : state.dataOutsideEnergySrcs->EnergySource(EnergySourceNum).CapFractionSchedNum = DataGlobalConstants::ScheduleAlwaysOn;
262 : }
263 : }
264 :
265 157 : if (ErrorsFound) {
266 0 : ShowFatalError(state,
267 0 : "Errors found in processing input for " + state.dataIPShortCut->cCurrentModuleObject +
268 : ", Preceding condition caused termination.");
269 : }
270 : }
271 :
272 7026404 : void OutsideEnergySourceSpecs::initialize(EnergyPlusData &state, Real64 MyLoad)
273 : {
274 :
275 : // SUBROUTINE INFORMATION:
276 : // AUTHOR: Dan Fisher
277 : // DATE WRITTEN: October 1998
278 : // MODIFIED May 2010; Edwin Lee; Linda Lawrie (consolidation)
279 : // RE-ENGINEERED Sept 2010, Brent Griffith, plant rewrite
280 :
281 : // PURPOSE OF THIS SUBROUTINE:
282 : // This subroutine does one-time inits and sets the operating mass flow rate of this machine
283 :
284 : // METHODOLOGY EMPLOYED:
285 : // One time inits include validating source type (should happen in getinput?) and locating this
286 : // component on the PlantLoop topology.
287 : // The mass flow rate is determined based on component load, and making use of
288 : // the SetComponentFlowRate routine.
289 : // The mass flow rate could be an inter-connected-loop side trigger. This is not really the type of
290 : // interconnect that that routine was written for, but it is the clearest example of using it.
291 :
292 : // begin environment inits
293 7026404 : if (state.dataGlobal->BeginEnvrnFlag && this->BeginEnvrnInitFlag) {
294 : // component model has not design flow rates, using data for overall plant loop
295 4770 : PlantUtilities::InitComponentNodes(state,
296 1590 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).MinMassFlowRate,
297 1590 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).MaxMassFlowRate,
298 : this->InletNodeNum,
299 : this->OutletNodeNum);
300 1590 : this->BeginEnvrnInitFlag = false;
301 : }
302 7026404 : if (!state.dataGlobal->BeginEnvrnFlag) this->BeginEnvrnInitFlag = true;
303 :
304 7026404 : Real64 TempPlantMassFlow(0.0);
305 7026404 : if (std::abs(MyLoad) > 0.0) {
306 1631132 : TempPlantMassFlow = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).MaxMassFlowRate;
307 : }
308 :
309 : // get actual mass flow to use, hold in MassFlowRate variable
310 7026404 : PlantUtilities::SetComponentFlowRate(state, TempPlantMassFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
311 :
312 7026404 : this->InletTemp = state.dataLoopNodes->Node(this->InletNodeNum).Temp;
313 7026404 : this->MassFlowRate = TempPlantMassFlow;
314 7026404 : }
315 :
316 1375 : void OutsideEnergySourceSpecs::size(EnergyPlusData &state)
317 : {
318 : // SUBROUTINE INFORMATION:
319 : // AUTHOR Daeho Kang
320 : // DATE WRITTEN April 2014
321 : // MODIFIED
322 : // RE-ENGINEERED na
323 :
324 : // PURPOSE OF THIS SUBROUTINE:
325 : // This subroutine is for sizing capacities of district cooling and heating objects.
326 :
327 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
328 1375 : bool ErrorsFound(false); // If errors detected in input
329 :
330 : // Type name string variable to collapse the sizing for cooling and heating into one block
331 2750 : std::string typeName;
332 1375 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater) {
333 660 : typeName = "Cooling";
334 : } else { // Heating
335 715 : typeName = "Heating";
336 : }
337 :
338 1375 : int const PltSizNum = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).PlantSizNum;
339 1375 : if (PltSizNum > 0) {
340 1230 : Real64 const rho = FluidProperties::GetDensityGlycol(state,
341 410 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
342 : DataGlobalConstants::InitConvTemp,
343 410 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
344 820 : "SizeDistrict" + typeName);
345 1230 : Real64 const Cp = FluidProperties::GetSpecificHeatGlycol(state,
346 410 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
347 : DataGlobalConstants::InitConvTemp,
348 410 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
349 820 : "SizeDistrict" + typeName);
350 410 : Real64 const NomCapDes = Cp * rho * state.dataSize->PlantSizData(PltSizNum).DeltaT * state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate;
351 410 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
352 82 : if (this->NomCapWasAutoSized) {
353 7 : this->NomCap = NomCapDes;
354 7 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
355 7 : BaseSizer::reportSizerOutput(state, "District" + typeName, this->Name, "Design Size Nominal Capacity [W]", NomCapDes);
356 : }
357 7 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
358 0 : BaseSizer::reportSizerOutput(state, "District" + typeName, this->Name, "Initial Design Size Nominal Capacity [W]", NomCapDes);
359 : }
360 : } else { // Hard-size with sizing data
361 75 : if (this->NomCap > 0.0 && NomCapDes > 0.0) {
362 73 : Real64 const NomCapUser = this->NomCap;
363 73 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
364 292 : BaseSizer::reportSizerOutput(state,
365 146 : "District" + typeName,
366 : this->Name,
367 : "Design Size Nominal Capacity [W]",
368 : NomCapDes,
369 : "User-Specified Nominal Capacity [W]",
370 73 : NomCapUser);
371 73 : if (state.dataGlobal->DisplayExtraWarnings) {
372 5 : if ((std::abs(NomCapDes - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
373 1 : ShowMessage(state, "SizeDistrict" + typeName + ": Potential issue with equipment sizing for " + this->Name);
374 1 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", NomCapUser));
375 1 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", NomCapDes));
376 1 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
377 1 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
378 : }
379 : }
380 : }
381 : }
382 : }
383 : }
384 : } else {
385 965 : if (this->NomCapWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
386 0 : ShowSevereError(state, "Autosizing of District " + typeName + " nominal capacity requires a loop Sizing:Plant object");
387 0 : ShowContinueError(state, "Occurs in District" + typeName + " object=" + this->Name);
388 0 : ErrorsFound = true;
389 : }
390 965 : if (!this->NomCapWasAutoSized && this->NomCap > 0.0 && state.dataPlnt->PlantFinalSizesOkayToReport) {
391 193 : BaseSizer::reportSizerOutput(state, "District" + typeName, this->Name, "User-Specified Nominal Capacity [W]", this->NomCap);
392 : }
393 : }
394 1375 : if (ErrorsFound) {
395 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
396 : }
397 1375 : }
398 :
399 7025029 : void OutsideEnergySourceSpecs::calculate(EnergyPlusData &state, bool runFlag, Real64 MyLoad)
400 : {
401 : // SUBROUTINE INFORMATION:
402 : // AUTHOR Dan Fisher
403 : // DATE WRITTEN July 1998
404 : // MODIFIED May 2010; Edwin Lee; Linda Lawrie (consolidation)
405 : // RE-ENGINEERED Sept 2010, Brent Griffith, plant rewrite
406 :
407 : // SUBROUTINE PARAMETER DEFINITIONS:
408 : static constexpr std::string_view RoutineName("SimDistrictEnergy");
409 :
410 : // set inlet and outlet nodes
411 7025029 : int const LoopNum = this->plantLoc.loopNum;
412 7025029 : Real64 const LoopMinTemp = state.dataPlnt->PlantLoop(LoopNum).MinTemp;
413 7025029 : Real64 const LoopMaxTemp = state.dataPlnt->PlantLoop(LoopNum).MaxTemp;
414 :
415 14050058 : Real64 const Cp = FluidProperties::GetSpecificHeatGlycol(
416 21075087 : state, state.dataPlnt->PlantLoop(LoopNum).FluidName, this->InletTemp, state.dataPlnt->PlantLoop(LoopNum).FluidIndex, RoutineName);
417 :
418 : // apply power limit from input
419 7025029 : Real64 CapFraction = ScheduleManager::GetCurrentScheduleValue(state, this->CapFractionSchedNum);
420 7025029 : CapFraction = max(0.0, CapFraction); // ensure non negative
421 7025029 : Real64 const CurrentCap = this->NomCap * CapFraction;
422 7025029 : if (std::abs(MyLoad) > CurrentCap) {
423 35 : MyLoad = sign(CurrentCap, MyLoad);
424 : }
425 :
426 7025029 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater) {
427 3332692 : if (MyLoad > 0.0) MyLoad = 0.0;
428 3692337 : } else if (this->EnergyType == DataPlant::PlantEquipmentType::PurchHotWater) {
429 3692337 : if (MyLoad < 0.0) MyLoad = 0.0;
430 : }
431 :
432 : // determine outlet temp based on inlet temp, cp, and MyLoad
433 7025029 : if ((this->MassFlowRate > 0.0) && runFlag) {
434 1630782 : this->OutletTemp = (MyLoad + this->MassFlowRate * Cp * this->InletTemp) / (this->MassFlowRate * Cp);
435 : // apply loop limits on temperature result to keep in check
436 1630782 : if (this->OutletTemp < LoopMinTemp) {
437 1 : this->OutletTemp = max(this->OutletTemp, LoopMinTemp);
438 1 : MyLoad = this->MassFlowRate * Cp * (this->OutletTemp - this->InletTemp);
439 : }
440 3261564 : if (this->OutletTemp > LoopMaxTemp) {
441 0 : this->OutletTemp = min(this->OutletTemp, LoopMaxTemp);
442 0 : MyLoad = this->MassFlowRate * Cp * (this->OutletTemp - this->InletTemp);
443 : }
444 : } else {
445 5394247 : this->OutletTemp = this->InletTemp;
446 5394247 : MyLoad = 0.0;
447 : }
448 7025029 : int const OutletNode = this->OutletNodeNum;
449 7025029 : state.dataLoopNodes->Node(OutletNode).Temp = this->OutletTemp;
450 7025029 : this->EnergyRate = std::abs(MyLoad);
451 7025029 : this->EnergyTransfer = this->EnergyRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
452 7025029 : }
453 :
454 275 : void OutsideEnergySourceSpecs::oneTimeInit_new(EnergyPlusData &state)
455 : {
456 :
457 : // Locate the unit on the plant loops for later usage
458 275 : bool errFlag = false;
459 275 : PlantUtilities::ScanPlantLoopsForObject(state, this->Name, this->EnergyType, this->plantLoc, errFlag, _, _, _, _, _);
460 275 : if (errFlag) {
461 0 : ShowFatalError(state, "InitSimVars: Program terminated due to previous condition(s).");
462 : }
463 : // set limits on outlet node temps to plant loop limits
464 275 : DataPlant::CompData::getPlantComponent(state, this->plantLoc).MinOutletTemp = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).MinTemp;
465 275 : DataPlant::CompData::getPlantComponent(state, this->plantLoc).MaxOutletTemp = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).MaxTemp;
466 : // Register design flow rate for inlet node (helps to autosize comp setpoint op scheme flows
467 275 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNodeNum, state.dataPlnt->PlantLoop(this->plantLoc.loopNum).MaxVolFlowRate);
468 :
469 : // this may need some help, if the objects change location later, due to a push_back,
470 : // then the pointers to these output variables will be bad
471 : // for (int EnergySourceNum = 1; EnergySourceNum <= NumDistrictUnits; ++EnergySourceNum) {
472 550 : std::string hotOrChilled = "Hot ";
473 550 : std::string reportVarPrefix = "District Heating ";
474 550 : std::string heatingOrCooling = "Heating";
475 275 : std::string_view typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(DataPlant::PlantEquipmentType::PurchHotWater)];
476 275 : if (this->EnergyType == DataPlant::PlantEquipmentType::PurchChilledWater) {
477 132 : hotOrChilled = "Chilled ";
478 132 : reportVarPrefix = "District Cooling ";
479 132 : heatingOrCooling = "Cooling";
480 132 : typeName = DataPlant::PlantEquipTypeNames[static_cast<int>(DataPlant::PlantEquipmentType::PurchChilledWater)];
481 : }
482 825 : SetupOutputVariable(state,
483 550 : reportVarPrefix + hotOrChilled + "Water Energy",
484 : OutputProcessor::Unit::J,
485 : this->EnergyTransfer,
486 : OutputProcessor::SOVTimeStepType::System,
487 : OutputProcessor::SOVStoreType::Summed,
488 : this->Name,
489 : _,
490 : typeName,
491 : heatingOrCooling,
492 : _,
493 : "Plant");
494 825 : SetupOutputVariable(state,
495 550 : reportVarPrefix + hotOrChilled + "Water Rate",
496 : OutputProcessor::Unit::W,
497 : this->EnergyRate,
498 : OutputProcessor::SOVTimeStepType::System,
499 : OutputProcessor::SOVStoreType::Average,
500 : this->Name);
501 :
502 825 : SetupOutputVariable(state,
503 550 : reportVarPrefix + "Rate",
504 : OutputProcessor::Unit::W,
505 : this->EnergyRate,
506 : OutputProcessor::SOVTimeStepType::System,
507 : OutputProcessor::SOVStoreType::Average,
508 : this->Name);
509 825 : SetupOutputVariable(state,
510 550 : reportVarPrefix + "Inlet Temperature",
511 : OutputProcessor::Unit::C,
512 : this->InletTemp,
513 : OutputProcessor::SOVTimeStepType::System,
514 : OutputProcessor::SOVStoreType::Average,
515 : this->Name);
516 825 : SetupOutputVariable(state,
517 550 : reportVarPrefix + "Outlet Temperature",
518 : OutputProcessor::Unit::C,
519 : this->OutletTemp,
520 : OutputProcessor::SOVTimeStepType::System,
521 : OutputProcessor::SOVStoreType::Average,
522 : this->Name);
523 825 : SetupOutputVariable(state,
524 550 : reportVarPrefix + "Mass Flow Rate",
525 : OutputProcessor::Unit::kg_s,
526 : this->MassFlowRate,
527 : OutputProcessor::SOVTimeStepType::System,
528 : OutputProcessor::SOVStoreType::Average,
529 : this->Name);
530 275 : }
531 :
532 0 : void OutsideEnergySourceSpecs::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
533 : {
534 0 : }
535 :
536 2313 : } // namespace EnergyPlus::OutsideEnergySources
|