Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <map>
50 : #include <string>
51 : #include <vector>
52 :
53 : // EnergyPlus Headers
54 : #include <EnergyPlus/Data/EnergyPlusData.hh>
55 : #include <EnergyPlus/DataHVACGlobals.hh>
56 : #include <EnergyPlus/DataSizing.hh>
57 : #include <EnergyPlus/General.hh>
58 : #include <EnergyPlus/OutputProcessor.hh>
59 : #include <EnergyPlus/OutputReportPredefined.hh>
60 : #include <EnergyPlus/Plant/DataPlant.hh>
61 : #include <EnergyPlus/SizingAnalysisObjects.hh>
62 : #include <EnergyPlus/UtilityRoutines.hh>
63 : #include <EnergyPlus/WeatherManager.hh>
64 :
65 : namespace EnergyPlus {
66 :
67 16321 : ZoneTimestepObject::ZoneTimestepObject()
68 : {
69 16321 : kindOfSim = Constant::KindOfSim::Invalid;
70 16321 : envrnNum = 0;
71 16321 : dayOfSim = 0;
72 16321 : hourOfDay = 0;
73 16321 : ztStepsIntoPeriod = 0;
74 16321 : stepStartMinute = 0.0;
75 16321 : stepEndMinute = 0.0;
76 16321 : timeStepDuration = 0.0;
77 16321 : }
78 :
79 11816 : ZoneTimestepObject::ZoneTimestepObject(
80 : Constant::KindOfSim kindSim, // kind of simulation
81 : int environmentNum, // index in Environment data structure, usually Weather::Envrn
82 : int daySim, // days into simulation period, usually DataGlobals::DayOfSim
83 : int hourDay, // hour into day, 1-24, filled by DataGlobals::HourOfDay
84 : int timeStep, // time steps into hour, filled by DataGlobals::TimeStep
85 : Real64 timeStepDurat, // duration of timestep in fractional hours, usually OutputProcessor::TimeValue( ZoneIndex ).TimeStep
86 : int numOfTimeStepsPerHour // timesteps in each hour, usually DataGlobals::NumOfTimeStepInHour
87 11816 : )
88 11816 : : kindOfSim(kindSim), envrnNum(environmentNum), dayOfSim(daySim), hourOfDay(hourDay),
89 :
90 11816 : timeStepDuration(timeStepDurat)
91 : {
92 11816 : Real64 constexpr minutesPerHour(60.0);
93 11816 : int constexpr hoursPerDay(24);
94 :
95 11816 : stepEndMinute = timeStepDuration * minutesPerHour + (timeStep - 1) * timeStepDuration * minutesPerHour;
96 :
97 11816 : stepStartMinute = stepEndMinute - timeStepDuration * minutesPerHour;
98 :
99 11816 : if (stepStartMinute < 0.0) {
100 0 : stepStartMinute = 0.0;
101 0 : stepEndMinute = timeStepDuration * minutesPerHour;
102 : }
103 :
104 11816 : ztStepsIntoPeriod = ((dayOfSim - 1) * (hoursPerDay * numOfTimeStepsPerHour)) + // multiple days
105 11816 : ((hourOfDay - 1) * numOfTimeStepsPerHour) + // so far this day's hours
106 11816 : round((stepStartMinute / minutesPerHour) / (timeStepDuration)); // into current hour
107 :
108 11816 : if (ztStepsIntoPeriod < 0) {
109 0 : ztStepsIntoPeriod = 0;
110 : }
111 :
112 : // We only expect this feature to be used with systems, so there will always be a system timestep update, at least one.
113 11816 : hasSystemSubSteps = true;
114 11816 : numSubSteps = 1;
115 11816 : subSteps.resize(numSubSteps);
116 11816 : }
117 :
118 69 : SizingLog::SizingLog(double &rVariable) : p_rVariable(rVariable)
119 : {
120 69 : }
121 :
122 72717 : int SizingLog::GetZtStepIndex(const ZoneTimestepObject tmpztStepStamp)
123 : {
124 :
125 : int vecIndex;
126 :
127 72717 : if (tmpztStepStamp.ztStepsIntoPeriod > 0) { // discard any negative value for safety
128 71759 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]] + tmpztStepStamp.ztStepsIntoPeriod;
129 : } else {
130 958 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]];
131 : }
132 :
133 : // next for safety sake, constrain index to lie inside correct environment
134 72717 : if (vecIndex < envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]]) {
135 0 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]]; // first step in environment
136 : }
137 72717 : if (vecIndex > (envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]] +
138 72717 : ztStepCountByEnvrnMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]])) {
139 0 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]] +
140 0 : ztStepCountByEnvrnMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]]; // last step in environment
141 : }
142 72717 : return vecIndex;
143 : }
144 :
145 24768 : void SizingLog::FillZoneStep(ZoneTimestepObject tmpztStepStamp)
146 : {
147 24768 : int index = GetZtStepIndex(tmpztStepStamp);
148 :
149 24768 : ztStepObj[index].kindOfSim = tmpztStepStamp.kindOfSim;
150 24768 : ztStepObj[index].envrnNum = tmpztStepStamp.envrnNum;
151 24768 : ztStepObj[index].dayOfSim = tmpztStepStamp.dayOfSim;
152 24768 : ztStepObj[index].hourOfDay = tmpztStepStamp.hourOfDay;
153 24768 : ztStepObj[index].ztStepsIntoPeriod = tmpztStepStamp.ztStepsIntoPeriod;
154 24768 : ztStepObj[index].stepStartMinute = tmpztStepStamp.stepStartMinute;
155 24768 : ztStepObj[index].stepEndMinute = tmpztStepStamp.stepEndMinute;
156 24768 : ztStepObj[index].timeStepDuration = tmpztStepStamp.timeStepDuration;
157 :
158 24768 : ztStepObj[index].logDataValue = p_rVariable;
159 24768 : }
160 :
161 47817 : int SizingLog::GetSysStepZtStepIndex(ZoneTimestepObject tmpztStepStamp)
162 : {
163 : // this method finds a zone timestep for the system timestep update to use
164 : // system timesteps are substeps inside a zone timestep, but are updated
165 : // before the zone step has been called.
166 : // the zone timestamp passed in is now accurate, not lagged, so this is simpler
167 :
168 47817 : int znStepIndex = GetZtStepIndex(tmpztStepStamp);
169 :
170 : // safety checks for range
171 47817 : if (znStepIndex >= NumOfStepsInLogSet) {
172 0 : znStepIndex = NumOfStepsInLogSet - 1;
173 : }
174 47817 : if (znStepIndex < 0) {
175 0 : znStepIndex = 0;
176 : }
177 :
178 47817 : return znStepIndex;
179 : }
180 :
181 47817 : void SizingLog::FillSysStep(ZoneTimestepObject tmpztStepStamp, SystemTimestepObject tmpSysStepStamp)
182 : {
183 :
184 : int newNumSubSteps;
185 47817 : Real64 constexpr MinutesPerHour(60.0);
186 :
187 47817 : int ztIndex = GetSysStepZtStepIndex(tmpztStepStamp);
188 :
189 47817 : if (ztStepObj[ztIndex].hasSystemSubSteps) {
190 :
191 23049 : int oldNumSubSteps = ztStepObj[ztIndex].numSubSteps;
192 23049 : newNumSubSteps = round(tmpztStepStamp.timeStepDuration / tmpSysStepStamp.TimeStepDuration);
193 23049 : if (newNumSubSteps != oldNumSubSteps) {
194 0 : ztStepObj[ztIndex].subSteps.resize(newNumSubSteps);
195 0 : ztStepObj[ztIndex].numSubSteps = newNumSubSteps;
196 : }
197 : } else {
198 24768 : newNumSubSteps = round(tmpztStepStamp.timeStepDuration / tmpSysStepStamp.TimeStepDuration);
199 24768 : ztStepObj[ztIndex].subSteps.resize(newNumSubSteps);
200 24768 : ztStepObj[ztIndex].numSubSteps = newNumSubSteps;
201 24768 : ztStepObj[ztIndex].hasSystemSubSteps = true;
202 : }
203 :
204 : // figure out which index this substep needs to go into
205 47817 : Real64 ZoneStepStartMinutes = tmpztStepStamp.stepStartMinute;
206 :
207 47817 : tmpSysStepStamp.stStepsIntoZoneStep =
208 47817 : round((((tmpSysStepStamp.CurMinuteStart - ZoneStepStartMinutes) / MinutesPerHour) / tmpSysStepStamp.TimeStepDuration));
209 :
210 47817 : if ((tmpSysStepStamp.stStepsIntoZoneStep >= 0) && (tmpSysStepStamp.stStepsIntoZoneStep < ztStepObj[ztIndex].numSubSteps)) {
211 47817 : ztStepObj[ztIndex].subSteps[tmpSysStepStamp.stStepsIntoZoneStep] = tmpSysStepStamp;
212 47817 : ztStepObj[ztIndex].subSteps[tmpSysStepStamp.stStepsIntoZoneStep].LogDataValue = p_rVariable;
213 : } else {
214 0 : ztStepObj[ztIndex].subSteps[0] = tmpSysStepStamp;
215 0 : ztStepObj[ztIndex].subSteps[0].LogDataValue = p_rVariable;
216 : }
217 47817 : }
218 :
219 99 : void SizingLog::AverageSysTimeSteps()
220 : {
221 : Real64 RunningSum;
222 :
223 24867 : for (auto &zt : ztStepObj) {
224 24768 : if (zt.numSubSteps > 0) {
225 24768 : RunningSum = 0.0;
226 72585 : for (auto const &SysT : zt.subSteps) {
227 47817 : RunningSum += SysT.LogDataValue;
228 24768 : }
229 24768 : zt.logDataValue = RunningSum / double(zt.numSubSteps);
230 : }
231 99 : }
232 99 : }
233 :
234 99 : void SizingLog::ProcessRunningAverage()
235 : {
236 99 : Real64 RunningSum = 0.0;
237 99 : Real64 divisor = double(timeStepsInAverage);
238 :
239 99 : std::map<int, int>::iterator end = ztStepCountByEnvrnMap.end();
240 297 : for (std::map<int, int>::iterator itr = ztStepCountByEnvrnMap.begin(); itr != end; ++itr) {
241 24966 : for (int i = 0; i < itr->second; ++i) { // next inner loop over zone timestep steps
242 :
243 24768 : if (timeStepsInAverage > 0) {
244 24768 : RunningSum = 0.0;
245 58176 : for (int j = 0; j < timeStepsInAverage; ++j) { //
246 33408 : if ((i - j) < 0) {
247 60 : RunningSum += ztStepObj[envrnStartZtStepIndexMap[itr->first]].logDataValue; // just use first value to fill early steps
248 : } else {
249 33348 : RunningSum += ztStepObj[((i - j) + envrnStartZtStepIndexMap[itr->first])].logDataValue;
250 : }
251 : }
252 24768 : ztStepObj[(i + envrnStartZtStepIndexMap[itr->first])].runningAvgDataValue = RunningSum / divisor;
253 : }
254 : }
255 99 : }
256 99 : }
257 :
258 66 : ZoneTimestepObject SizingLog::GetLogVariableDataMax(EnergyPlusData &state)
259 : {
260 66 : ZoneTimestepObject tmpztStepStamp;
261 66 : Real64 MaxVal = 0.0;
262 :
263 66 : if (!ztStepObj.empty()) {
264 66 : tmpztStepStamp = ztStepObj[0];
265 : }
266 :
267 16578 : for (auto const &zt : ztStepObj) {
268 16512 : if (zt.envrnNum > 0 && zt.kindOfSim != Constant::KindOfSim::Invalid && zt.runningAvgDataValue > MaxVal) {
269 480 : MaxVal = zt.runningAvgDataValue;
270 480 : tmpztStepStamp = zt;
271 16032 : } else if (zt.envrnNum == 0 && zt.kindOfSim == Constant::KindOfSim::Invalid) { // null timestamp, problem to fix
272 0 : ShowWarningMessage(state, "GetLogVariableDataMax: null timestamp in log");
273 : }
274 66 : }
275 66 : return tmpztStepStamp;
276 0 : }
277 :
278 132 : Real64 SizingLog::GetLogVariableDataAtTimestamp(ZoneTimestepObject tmpztStepStamp)
279 : {
280 132 : int const index = GetZtStepIndex(tmpztStepStamp);
281 132 : return ztStepObj[index].runningAvgDataValue;
282 : }
283 :
284 81 : void SizingLog::ReInitLogForIteration()
285 : {
286 81 : ZoneTimestepObject tmpNullztStepObj;
287 81 : std::fill(ztStepObj.begin(), ztStepObj.end(), tmpNullztStepObj);
288 81 : }
289 :
290 594 : void SizingLog::SetupNewEnvironment(int const seedEnvrnNum, int const newEnvrnNum)
291 : {
292 594 : newEnvrnToSeedEnvrnMap[newEnvrnNum] = seedEnvrnNum;
293 594 : }
294 :
295 69 : int SizingLoggerFramework::SetupVariableSizingLog(EnergyPlusData &state, Real64 &rVariable, int stepsInAverage)
296 : {
297 69 : int constexpr HoursPerDay = 24;
298 :
299 69 : SizingLog tmpLog(rVariable);
300 69 : tmpLog.NumOfEnvironmentsInLogSet = 0;
301 69 : tmpLog.NumOfDesignDaysInLogSet = 0;
302 69 : tmpLog.NumberOfSizingPeriodsInLogSet = 0;
303 :
304 : // search environment structure for sizing periods
305 : // this is coded to occur before the additions to Environment structure that will occur to run them as HVAC Sizing sims
306 309 : for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
307 240 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
308 138 : ++tmpLog.NumOfEnvironmentsInLogSet;
309 138 : ++tmpLog.NumOfDesignDaysInLogSet;
310 : }
311 240 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
312 0 : ++tmpLog.NumOfEnvironmentsInLogSet;
313 0 : ++tmpLog.NumberOfSizingPeriodsInLogSet;
314 : }
315 : }
316 :
317 : // next fill in the count of steps into map
318 309 : for (int i = 1; i <= state.dataWeather->NumOfEnvrn; ++i) {
319 :
320 240 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::DesignDay) {
321 138 : tmpLog.ztStepCountByEnvrnMap[i] = HoursPerDay * state.dataGlobal->TimeStepsInHour;
322 : }
323 240 : if (state.dataWeather->Environment(i).KindOfEnvrn == Constant::KindOfSim::RunPeriodDesign) {
324 0 : tmpLog.ztStepCountByEnvrnMap[i] = HoursPerDay * state.dataGlobal->TimeStepsInHour * state.dataWeather->Environment(i).TotalDays;
325 : }
326 : }
327 :
328 69 : int stepSum = 0;
329 69 : std::map<int, int>::iterator end = tmpLog.ztStepCountByEnvrnMap.end();
330 207 : for (std::map<int, int>::iterator itr = tmpLog.ztStepCountByEnvrnMap.begin(); itr != end; ++itr) {
331 :
332 138 : tmpLog.envrnStartZtStepIndexMap[itr->first] = stepSum;
333 138 : stepSum += itr->second;
334 69 : }
335 :
336 69 : tmpLog.timeStepsInAverage = stepsInAverage;
337 :
338 69 : int VectorLength = stepSum;
339 :
340 69 : tmpLog.NumOfStepsInLogSet = VectorLength;
341 69 : tmpLog.ztStepObj.resize(VectorLength);
342 :
343 69 : logObjs.push_back(tmpLog);
344 69 : ++NumOfLogs;
345 69 : return NumOfLogs - 1;
346 69 : }
347 :
348 92 : void SizingLoggerFramework::SetupSizingLogsNewEnvironment(EnergyPlusData &state)
349 : {
350 : using namespace Weather;
351 :
352 686 : for (auto &l : logObjs) {
353 594 : l.SetupNewEnvironment(state.dataWeather->Environment(state.dataWeather->Envrn).SeedEnvrnNum, state.dataWeather->Envrn);
354 92 : }
355 92 : }
356 :
357 11816 : ZoneTimestepObject SizingLoggerFramework::PrepareZoneTimestepStamp(EnergyPlusData &state)
358 : {
359 : // prepare current timing data once and then pass into fill routines
360 : // function used by both zone and system frequency log updates
361 :
362 : int locDayOfSim;
363 :
364 11816 : if (state.dataGlobal->WarmupFlag) { // DayOfSim not okay during warmup, keeps incrementing up during warmup days
365 0 : locDayOfSim = 1;
366 : } else {
367 11816 : locDayOfSim = state.dataGlobal->DayOfSim;
368 : }
369 :
370 : ZoneTimestepObject tmpztStepStamp( // call constructor
371 11816 : state.dataGlobal->KindOfSim,
372 11816 : state.dataWeather->Envrn,
373 : locDayOfSim,
374 11816 : state.dataGlobal->HourOfDay,
375 11816 : state.dataGlobal->TimeStep,
376 11816 : *state.dataOutputProcessor->TimeValue[(int)OutputProcessor::TimeStepType::Zone].TimeStep,
377 23632 : state.dataGlobal->TimeStepsInHour);
378 :
379 11816 : return tmpztStepStamp;
380 : }
381 :
382 4080 : void SizingLoggerFramework::UpdateSizingLogValuesZoneStep(EnergyPlusData &state)
383 : {
384 4080 : ZoneTimestepObject tmpztStepStamp = PrepareZoneTimestepStamp(state);
385 :
386 28848 : for (auto &l : logObjs) {
387 24768 : l.FillZoneStep(tmpztStepStamp);
388 4080 : }
389 4080 : }
390 :
391 7736 : void SizingLoggerFramework::UpdateSizingLogValuesSystemStep(EnergyPlusData &state)
392 : {
393 7736 : Real64 constexpr MinutesPerHour = 60.0;
394 7736 : ZoneTimestepObject tmpztStepStamp = PrepareZoneTimestepStamp(state);
395 7736 : SystemTimestepObject tmpSysStepStamp;
396 :
397 : // prepare system timestep stamp
398 7736 : tmpSysStepStamp.CurMinuteEnd = state.dataOutputProcessor->TimeValue[(int)OutputProcessor::TimeStepType::System].CurMinute;
399 7736 : if (tmpSysStepStamp.CurMinuteEnd == 0.0) {
400 0 : tmpSysStepStamp.CurMinuteEnd = MinutesPerHour;
401 : }
402 7736 : tmpSysStepStamp.CurMinuteStart =
403 7736 : tmpSysStepStamp.CurMinuteEnd - (*state.dataOutputProcessor->TimeValue[(int)OutputProcessor::TimeStepType::System].TimeStep) * MinutesPerHour;
404 7736 : tmpSysStepStamp.TimeStepDuration = *state.dataOutputProcessor->TimeValue[(int)OutputProcessor::TimeStepType::System].TimeStep;
405 :
406 55553 : for (auto &l : logObjs) {
407 47817 : l.FillSysStep(tmpztStepStamp, tmpSysStepStamp);
408 7736 : }
409 7736 : }
410 :
411 11 : void SizingLoggerFramework::IncrementSizingPeriodSet()
412 : {
413 92 : for (auto &l : this->logObjs) {
414 81 : l.ReInitLogForIteration();
415 11 : }
416 11 : }
417 :
418 23 : PlantCoinicidentAnalysis::PlantCoinicidentAnalysis(
419 46 : std::string loopName, int loopIndex, int nodeNum, Real64 density, Real64 cp, int numStepsInAvg, int sizingIndex)
420 : {
421 23 : name = loopName;
422 23 : plantLoopIndex = loopIndex;
423 23 : supplySideInletNodeNum = nodeNum;
424 23 : densityForSizing = density;
425 23 : specificHeatForSizing = cp;
426 23 : numTimeStepsInAvg = numStepsInAvg;
427 23 : plantSizingIndex = sizingIndex;
428 23 : }
429 :
430 33 : void PlantCoinicidentAnalysis::ResolveDesignFlowRate(EnergyPlusData &state, int const HVACSizingIterCount)
431 : {
432 : using DataSizing::GlobalCoolingSizingFactorMode;
433 : using DataSizing::GlobalHeatingSizingFactorMode;
434 : using DataSizing::LoopComponentSizingFactorMode;
435 : using DataSizing::NoSizingFactorMode;
436 :
437 : using namespace DataPlant;
438 : using namespace OutputReportPredefined;
439 : using HVAC::SmallWaterVolFlow;
440 : bool setNewSizes;
441 : Real64 sizingFac;
442 : Real64 normalizedChange;
443 : Real64 newFoundVolFlowRate;
444 : Real64 peakLoadCalculatedMassFlow;
445 33 : std::string chIteration;
446 33 : std::string chSetSizes;
447 33 : std::string chDemandTrapUsed;
448 33 : bool changedByDemand(false);
449 : bool nullStampProblem;
450 :
451 : // first make sure we have valid time stamps to work with
452 33 : if (CheckTimeStampForNull(newFoundMassFlowRateTimeStamp) && CheckTimeStampForNull(NewFoundMaxDemandTimeStamp)) {
453 : // problem, don't have valid stamp, don't have any info to report either
454 0 : nullStampProblem = true;
455 : } else {
456 33 : nullStampProblem = false;
457 : }
458 :
459 33 : previousVolDesignFlowRate = state.dataSize->PlantSizData(plantSizingIndex).DesVolFlowRate;
460 :
461 33 : if (!CheckTimeStampForNull(newFoundMassFlowRateTimeStamp) && (newFoundMassFlowRateTimeStamp.runningAvgDataValue > 0.0)) { // issue 5665, was ||
462 33 : newFoundMassFlowRate = newFoundMassFlowRateTimeStamp.runningAvgDataValue;
463 : } else {
464 0 : newFoundMassFlowRate = 0.0;
465 : }
466 :
467 : // step 3 calculate mdot from max load and delta T
468 65 : if ((!CheckTimeStampForNull(NewFoundMaxDemandTimeStamp) && (NewFoundMaxDemandTimeStamp.runningAvgDataValue > 0.0)) &&
469 32 : ((specificHeatForSizing * state.dataSize->PlantSizData(plantSizingIndex).DeltaT) > 0.0)) {
470 32 : peakLoadCalculatedMassFlow =
471 32 : NewFoundMaxDemandTimeStamp.runningAvgDataValue / (specificHeatForSizing * state.dataSize->PlantSizData(plantSizingIndex).DeltaT);
472 : } else {
473 1 : peakLoadCalculatedMassFlow = 0.0;
474 : }
475 :
476 33 : if (peakLoadCalculatedMassFlow > newFoundMassFlowRate) {
477 22 : changedByDemand = true;
478 : } else {
479 11 : changedByDemand = false;
480 : }
481 33 : newFoundMassFlowRate = max(newFoundMassFlowRate, peakLoadCalculatedMassFlow); // step 4, take larger of the two
482 :
483 33 : newFoundVolFlowRate = newFoundMassFlowRate / densityForSizing;
484 :
485 : // now apply the correct sizing factor depending on input option
486 33 : sizingFac = 1.0;
487 33 : if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == NoSizingFactorMode) {
488 33 : sizingFac = 1.0;
489 0 : } else if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == GlobalHeatingSizingFactorMode) {
490 0 : sizingFac = state.dataSize->GlobalHeatSizingFactor;
491 0 : } else if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == GlobalCoolingSizingFactorMode) {
492 0 : sizingFac = state.dataSize->GlobalCoolSizingFactor;
493 0 : } else if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == LoopComponentSizingFactorMode) {
494 : // multiplier used for pumps, often 1.0, from component level sizing fractions
495 0 : sizingFac = state.dataPlnt->PlantLoop(plantLoopIndex).LoopSide(LoopSideLocation::Supply).Branch(1).PumpSizFac;
496 : }
497 :
498 33 : newAdjustedMassFlowRate = newFoundMassFlowRate * sizingFac; // apply overall heating or cooling sizing factor
499 :
500 33 : newVolDesignFlowRate = newAdjustedMassFlowRate / densityForSizing;
501 :
502 : // compare threshold,
503 33 : setNewSizes = false;
504 33 : normalizedChange = 0.0;
505 33 : if (newVolDesignFlowRate > SmallWaterVolFlow && !nullStampProblem) { // do not use zero size or bad stamp data
506 :
507 33 : normalizedChange = std::abs((newVolDesignFlowRate - previousVolDesignFlowRate) / previousVolDesignFlowRate);
508 33 : if (normalizedChange > significantNormalizedChange) {
509 22 : anotherIterationDesired = true;
510 22 : setNewSizes = true;
511 : } else {
512 11 : anotherIterationDesired = false;
513 : }
514 : }
515 :
516 33 : if (setNewSizes) {
517 : // set new size values for rest of simulation
518 22 : state.dataSize->PlantSizData(plantSizingIndex).DesVolFlowRate = newVolDesignFlowRate;
519 :
520 22 : if (state.dataPlnt->PlantLoop(plantLoopIndex).MaxVolFlowRateWasAutoSized) {
521 22 : state.dataPlnt->PlantLoop(plantLoopIndex).MaxVolFlowRate = newVolDesignFlowRate;
522 22 : state.dataPlnt->PlantLoop(plantLoopIndex).MaxMassFlowRate = newAdjustedMassFlowRate;
523 : }
524 22 : if (state.dataPlnt->PlantLoop(plantLoopIndex).VolumeWasAutoSized) {
525 : // Note this calculation also appears in PlantManager::SizePlantLoop and PlantManager::ResizePlantLoopLevelSizes
526 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Volume =
527 22 : state.dataPlnt->PlantLoop(plantLoopIndex).MaxVolFlowRate * state.dataPlnt->PlantLoop(plantLoopIndex).CirculationTime * 60.0;
528 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Mass = state.dataPlnt->PlantLoop(plantLoopIndex).Volume * densityForSizing;
529 : }
530 : }
531 :
532 : // add a separate eio summary report about what happened, did demand trap get used, what were the key values.
533 33 : if (!state.dataGlobal->sizingAnalysisEioHeaderDoneOnce) {
534 9 : print(state.files.eio,
535 : "{}",
536 : "! <Plant Coincident Sizing Algorithm>,Plant Loop Name,Sizing Pass {#},Measured Mass "
537 : "Flow{kg/s},Measured Demand {W},Demand Calculated Mass Flow{kg/s},Sizes Changed {Yes/No},Previous "
538 : "Volume Flow Rate {m3/s},New Volume Flow Rate {m3/s},Demand Check Applied {Yes/No},Sizing Factor "
539 : "{},Normalized Change {},Specific Heat{J/kg-K},Density {kg/m3}\n");
540 9 : state.dataGlobal->sizingAnalysisEioHeaderDoneOnce = true;
541 : }
542 33 : chIteration = fmt::to_string(HVACSizingIterCount);
543 33 : if (setNewSizes) {
544 22 : chSetSizes = "Yes";
545 : } else {
546 11 : chSetSizes = "No";
547 : }
548 33 : if (changedByDemand) {
549 22 : chDemandTrapUsed = "Yes";
550 : } else {
551 11 : chDemandTrapUsed = "No";
552 : }
553 :
554 33 : print(state.files.eio,
555 : "Plant Coincident Sizing Algorithm,{},{},{:.7R},{:.2R},{:.7R},{},{:.6R},{:.6R},{},{:.4R},{:.6R},{:.4R},{:.4R}\n",
556 33 : name,
557 : chIteration,
558 33 : newFoundMassFlowRateTimeStamp.runningAvgDataValue,
559 33 : NewFoundMaxDemandTimeStamp.runningAvgDataValue,
560 : peakLoadCalculatedMassFlow,
561 : chSetSizes,
562 33 : previousVolDesignFlowRate,
563 33 : newVolDesignFlowRate,
564 : chDemandTrapUsed,
565 : sizingFac,
566 : normalizedChange,
567 33 : specificHeatForSizing,
568 33 : densityForSizing);
569 :
570 : // report to sizing summary table called Plant Loop Coincident Design Fluid Flow Rates
571 :
572 66 : PreDefTableEntry(state,
573 33 : state.dataOutRptPredefined->pdchPlantSizPrevVdot,
574 66 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
575 : previousVolDesignFlowRate,
576 33 : 6);
577 66 : PreDefTableEntry(state,
578 33 : state.dataOutRptPredefined->pdchPlantSizMeasVdot,
579 66 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
580 : newFoundVolFlowRate,
581 33 : 6);
582 66 : PreDefTableEntry(state,
583 33 : state.dataOutRptPredefined->pdchPlantSizCalcVdot,
584 66 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
585 : newVolDesignFlowRate,
586 33 : 6);
587 :
588 33 : if (setNewSizes) {
589 66 : PreDefTableEntry(state,
590 22 : state.dataOutRptPredefined->pdchPlantSizCoincYesNo,
591 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
592 : "Yes");
593 : } else {
594 33 : PreDefTableEntry(state,
595 11 : state.dataOutRptPredefined->pdchPlantSizCoincYesNo,
596 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
597 : "No");
598 : }
599 :
600 33 : if (!nullStampProblem) {
601 33 : if (!changedByDemand && !CheckTimeStampForNull(newFoundMassFlowRateTimeStamp)) { // bug fix #5665
602 11 : if (newFoundMassFlowRateTimeStamp.envrnNum > 0) { // protect against invalid index
603 22 : PreDefTableEntry(state,
604 11 : state.dataOutRptPredefined->pdchPlantSizDesDay,
605 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
606 11 : state.dataWeather->Environment(newFoundMassFlowRateTimeStamp.envrnNum).Title);
607 : }
608 22 : PreDefTableEntry(state,
609 11 : state.dataOutRptPredefined->pdchPlantSizPkTimeDayOfSim,
610 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
611 : newFoundMassFlowRateTimeStamp.dayOfSim);
612 22 : PreDefTableEntry(state,
613 11 : state.dataOutRptPredefined->pdchPlantSizPkTimeHour,
614 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
615 11 : newFoundMassFlowRateTimeStamp.hourOfDay - 1);
616 22 : PreDefTableEntry(state,
617 11 : state.dataOutRptPredefined->pdchPlantSizPkTimeMin,
618 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
619 : newFoundMassFlowRateTimeStamp.stepStartMinute,
620 22 : 0);
621 22 : } else if (changedByDemand && !CheckTimeStampForNull(NewFoundMaxDemandTimeStamp)) { // bug fix #5665
622 22 : if (NewFoundMaxDemandTimeStamp.envrnNum > 0) { // protect against invalid index
623 44 : PreDefTableEntry(state,
624 22 : state.dataOutRptPredefined->pdchPlantSizDesDay,
625 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
626 22 : state.dataWeather->Environment(NewFoundMaxDemandTimeStamp.envrnNum).Title);
627 : }
628 44 : PreDefTableEntry(state,
629 22 : state.dataOutRptPredefined->pdchPlantSizPkTimeDayOfSim,
630 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
631 : NewFoundMaxDemandTimeStamp.dayOfSim);
632 44 : PreDefTableEntry(state,
633 22 : state.dataOutRptPredefined->pdchPlantSizPkTimeHour,
634 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
635 22 : NewFoundMaxDemandTimeStamp.hourOfDay - 1);
636 44 : PreDefTableEntry(state,
637 22 : state.dataOutRptPredefined->pdchPlantSizPkTimeMin,
638 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
639 : NewFoundMaxDemandTimeStamp.stepStartMinute,
640 44 : 0);
641 : }
642 : }
643 33 : }
644 :
645 132 : bool PlantCoinicidentAnalysis::CheckTimeStampForNull(ZoneTimestepObject testStamp)
646 : {
647 :
648 132 : bool isNull = true;
649 :
650 132 : if (testStamp.envrnNum != 0) {
651 132 : isNull = false;
652 : }
653 132 : if (testStamp.kindOfSim != Constant::KindOfSim::Invalid) {
654 132 : isNull = false;
655 : }
656 :
657 132 : return isNull;
658 : }
659 : } // namespace EnergyPlus
|