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 <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 31899 : ZoneTimestepObject::ZoneTimestepObject()
68 : {
69 31899 : kindOfSim = DataGlobalConstants::KindOfSim::Invalid;
70 31899 : envrnNum = 0;
71 31899 : dayOfSim = 0;
72 31899 : hourOfDay = 0;
73 31899 : ztStepsIntoPeriod = 0;
74 31899 : stepStartMinute = 0.0;
75 31899 : stepEndMinute = 0.0;
76 31899 : timeStepDuration = 0.0;
77 31899 : }
78 :
79 12965 : ZoneTimestepObject::ZoneTimestepObject(
80 : DataGlobalConstants::KindOfSim kindSim, // kind of simulation
81 : int environmentNum, // index in Environment data structure, usually WeatherManager::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 12965 : )
88 : : kindOfSim(kindSim), envrnNum(environmentNum), dayOfSim(daySim), hourOfDay(hourDay),
89 :
90 12965 : timeStepDuration(timeStepDurat)
91 : {
92 12965 : Real64 constexpr minutesPerHour(60.0);
93 12965 : int constexpr hoursPerDay(24);
94 :
95 12965 : stepEndMinute = timeStepDuration * minutesPerHour + (timeStep - 1) * timeStepDuration * minutesPerHour;
96 :
97 12965 : stepStartMinute = stepEndMinute - timeStepDuration * minutesPerHour;
98 :
99 12965 : if (stepStartMinute < 0.0) {
100 0 : stepStartMinute = 0.0;
101 0 : stepEndMinute = timeStepDuration * minutesPerHour;
102 : }
103 :
104 38895 : ztStepsIntoPeriod = ((dayOfSim - 1) * (hoursPerDay * numOfTimeStepsPerHour)) + // multiple days
105 38895 : ((hourOfDay - 1) * numOfTimeStepsPerHour) + // so far this day's hours
106 12965 : round((stepStartMinute / minutesPerHour) / (timeStepDuration)); // into current hour
107 :
108 12965 : if (ztStepsIntoPeriod < 0) ztStepsIntoPeriod = 0;
109 :
110 : // We only expect this feature to be used with systems, so there will always be a system timestep update, at least one.
111 12965 : hasSystemSubSteps = true;
112 12965 : numSubSteps = 1;
113 12965 : subSteps.resize(numSubSteps);
114 12965 : }
115 :
116 78 : SizingLog::SizingLog(double &rVariable) : p_rVariable(rVariable)
117 : {
118 78 : }
119 :
120 82065 : int SizingLog::GetZtStepIndex(const ZoneTimestepObject tmpztStepStamp)
121 : {
122 :
123 : int vecIndex;
124 :
125 82065 : if (tmpztStepStamp.ztStepsIntoPeriod > 0) { // discard any negative value for safety
126 80997 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]] + tmpztStepStamp.ztStepsIntoPeriod;
127 : } else {
128 1068 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]];
129 : }
130 :
131 : // next for safety sake, constrain index to lie inside correct envronment
132 82065 : if (vecIndex < envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]]) {
133 0 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]]; // first step in environment
134 : }
135 164130 : if (vecIndex > (envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]] +
136 82065 : ztStepCountByEnvrnMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]])) {
137 0 : vecIndex = envrnStartZtStepIndexMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]] +
138 0 : ztStepCountByEnvrnMap[newEnvrnToSeedEnvrnMap[tmpztStepStamp.envrnNum]]; // last step in environment
139 : }
140 82065 : return vecIndex;
141 : }
142 :
143 27360 : void SizingLog::FillZoneStep(ZoneTimestepObject tmpztStepStamp)
144 : {
145 27360 : int index = GetZtStepIndex(tmpztStepStamp);
146 :
147 27360 : ztStepObj[index].kindOfSim = tmpztStepStamp.kindOfSim;
148 27360 : ztStepObj[index].envrnNum = tmpztStepStamp.envrnNum;
149 27360 : ztStepObj[index].dayOfSim = tmpztStepStamp.dayOfSim;
150 27360 : ztStepObj[index].hourOfDay = tmpztStepStamp.hourOfDay;
151 27360 : ztStepObj[index].ztStepsIntoPeriod = tmpztStepStamp.ztStepsIntoPeriod;
152 27360 : ztStepObj[index].stepStartMinute = tmpztStepStamp.stepStartMinute;
153 27360 : ztStepObj[index].stepEndMinute = tmpztStepStamp.stepEndMinute;
154 27360 : ztStepObj[index].timeStepDuration = tmpztStepStamp.timeStepDuration;
155 :
156 27360 : ztStepObj[index].logDataValue = p_rVariable;
157 27360 : }
158 :
159 54561 : int SizingLog::GetSysStepZtStepIndex(ZoneTimestepObject tmpztStepStamp)
160 : {
161 : // this method finds a zone timestep for the system timestep update to use
162 : // system timesteps are substeps inside a zone timestep, but are updated
163 : // before the zone step has been called.
164 : // the zone timestamp passed in is now accurate, not lagged, so this is simpler
165 :
166 54561 : int znStepIndex = GetZtStepIndex(tmpztStepStamp);
167 :
168 : // safety checks for range
169 54561 : if (znStepIndex >= NumOfStepsInLogSet) znStepIndex = NumOfStepsInLogSet - 1;
170 54561 : if (znStepIndex < 0) znStepIndex = 0;
171 :
172 54561 : return znStepIndex;
173 : }
174 :
175 54561 : void SizingLog::FillSysStep(ZoneTimestepObject tmpztStepStamp, SystemTimestepObject tmpSysStepStamp)
176 : {
177 :
178 54561 : int ztIndex(0);
179 54561 : int oldNumSubSteps(0);
180 54561 : int newNumSubSteps(0);
181 54561 : Real64 constexpr MinutesPerHour(60.0);
182 54561 : Real64 ZoneStepStartMinutes(0.0);
183 :
184 54561 : ztIndex = GetSysStepZtStepIndex(tmpztStepStamp);
185 :
186 54561 : if (ztStepObj[ztIndex].hasSystemSubSteps) {
187 :
188 27201 : oldNumSubSteps = ztStepObj[ztIndex].numSubSteps;
189 27201 : newNumSubSteps = round(tmpztStepStamp.timeStepDuration / tmpSysStepStamp.TimeStepDuration);
190 27201 : if (newNumSubSteps != oldNumSubSteps) {
191 0 : ztStepObj[ztIndex].subSteps.resize(newNumSubSteps);
192 0 : ztStepObj[ztIndex].numSubSteps = newNumSubSteps;
193 : }
194 : } else {
195 27360 : newNumSubSteps = round(tmpztStepStamp.timeStepDuration / tmpSysStepStamp.TimeStepDuration);
196 27360 : ztStepObj[ztIndex].subSteps.resize(newNumSubSteps);
197 27360 : ztStepObj[ztIndex].numSubSteps = newNumSubSteps;
198 27360 : ztStepObj[ztIndex].hasSystemSubSteps = true;
199 : }
200 :
201 : // figure out which index this substep needs to go into
202 54561 : ZoneStepStartMinutes = tmpztStepStamp.stepStartMinute;
203 :
204 54561 : tmpSysStepStamp.stStepsIntoZoneStep =
205 54561 : round((((tmpSysStepStamp.CurMinuteStart - ZoneStepStartMinutes) / MinutesPerHour) / tmpSysStepStamp.TimeStepDuration));
206 :
207 54561 : if ((tmpSysStepStamp.stStepsIntoZoneStep >= 0) && (tmpSysStepStamp.stStepsIntoZoneStep < ztStepObj[ztIndex].numSubSteps)) {
208 54561 : ztStepObj[ztIndex].subSteps[tmpSysStepStamp.stStepsIntoZoneStep] = tmpSysStepStamp;
209 54561 : ztStepObj[ztIndex].subSteps[tmpSysStepStamp.stStepsIntoZoneStep].LogDataValue = p_rVariable;
210 : } else {
211 0 : ztStepObj[ztIndex].subSteps[0] = tmpSysStepStamp;
212 0 : ztStepObj[ztIndex].subSteps[0].LogDataValue = p_rVariable;
213 : }
214 54561 : }
215 :
216 108 : void SizingLog::AverageSysTimeSteps()
217 : {
218 : Real64 RunningSum;
219 :
220 27468 : for (auto &zt : ztStepObj) {
221 27360 : if (zt.numSubSteps > 0) {
222 27360 : RunningSum = 0.0;
223 81921 : for (auto &SysT : zt.subSteps) {
224 54561 : RunningSum += SysT.LogDataValue;
225 : }
226 27360 : zt.logDataValue = RunningSum / double(zt.numSubSteps);
227 : }
228 : }
229 108 : }
230 :
231 108 : void SizingLog::ProcessRunningAverage()
232 : {
233 108 : Real64 RunningSum = 0.0;
234 108 : Real64 divisor = double(timeStepsInAverage);
235 :
236 216 : std::map<int, int>::iterator end = ztStepCountByEnvrnMap.end();
237 324 : for (std::map<int, int>::iterator itr = ztStepCountByEnvrnMap.begin(); itr != end; ++itr) {
238 27576 : for (int i = 0; i < itr->second; ++i) { // next inner loop over zone timestep steps
239 :
240 27360 : if (timeStepsInAverage > 0) {
241 27360 : RunningSum = 0.0;
242 71136 : for (int j = 0; j < timeStepsInAverage; ++j) { //
243 43776 : if ((i - j) < 0) {
244 168 : RunningSum += ztStepObj[envrnStartZtStepIndexMap[itr->first]].logDataValue; // just use first value to fill early steps
245 : } else {
246 43608 : RunningSum += ztStepObj[((i - j) + envrnStartZtStepIndexMap[itr->first])].logDataValue;
247 : }
248 : }
249 27360 : ztStepObj[(i + envrnStartZtStepIndexMap[itr->first])].runningAvgDataValue = RunningSum / divisor;
250 : }
251 : }
252 : }
253 108 : }
254 :
255 72 : ZoneTimestepObject SizingLog::GetLogVariableDataMax(EnergyPlusData &state)
256 : {
257 : Real64 MaxVal;
258 72 : ZoneTimestepObject tmpztStepStamp;
259 72 : MaxVal = 0.0;
260 :
261 72 : if (!ztStepObj.empty()) {
262 72 : tmpztStepStamp = ztStepObj[0];
263 : }
264 :
265 18312 : for (auto &zt : ztStepObj) {
266 18240 : if (zt.envrnNum > 0 && zt.kindOfSim != DataGlobalConstants::KindOfSim::Invalid && zt.runningAvgDataValue > MaxVal) {
267 615 : MaxVal = zt.runningAvgDataValue;
268 615 : tmpztStepStamp = zt;
269 17625 : } else if (zt.envrnNum == 0 && zt.kindOfSim == DataGlobalConstants::KindOfSim::Invalid) { // null timestamp, problem to fix
270 0 : ShowWarningMessage(state, "GetLogVariableDataMax: null timestamp in log");
271 : }
272 : }
273 72 : return tmpztStepStamp;
274 : }
275 :
276 144 : Real64 SizingLog::GetLogVariableDataAtTimestamp(ZoneTimestepObject tmpztStepStamp)
277 : {
278 144 : int const index = GetZtStepIndex(tmpztStepStamp);
279 :
280 144 : Real64 const val = ztStepObj[index].runningAvgDataValue;
281 :
282 144 : return val;
283 : }
284 :
285 90 : void SizingLog::ReInitLogForIteration()
286 : {
287 180 : ZoneTimestepObject tmpNullztStepObj;
288 :
289 22266 : for (auto &zt : ztStepObj) {
290 22176 : zt = tmpNullztStepObj;
291 : }
292 90 : }
293 :
294 639 : void SizingLog::SetupNewEnvironment(int const seedEnvrnNum, int const newEnvrnNum)
295 : {
296 639 : newEnvrnToSeedEnvrnMap[newEnvrnNum] = seedEnvrnNum;
297 639 : }
298 :
299 78 : int SizingLoggerFramework::SetupVariableSizingLog(EnergyPlusData &state, Real64 &rVariable, int stepsInAverage)
300 : {
301 78 : int VectorLength(0);
302 78 : int constexpr HoursPerDay(24);
303 :
304 156 : SizingLog tmpLog(rVariable);
305 78 : tmpLog.NumOfEnvironmentsInLogSet = 0;
306 78 : tmpLog.NumOfDesignDaysInLogSet = 0;
307 78 : tmpLog.NumberOfSizingPeriodsInLogSet = 0;
308 :
309 : // search environment structure for sizing periods
310 : // this is coded to occur before the additions to Environment structure that will occur to run them as HVAC Sizing sims
311 345 : for (int i = 1; i <= state.dataWeatherManager->NumOfEnvrn; ++i) {
312 267 : if (state.dataWeatherManager->Environment(i).KindOfEnvrn == DataGlobalConstants::KindOfSim::DesignDay) {
313 156 : ++tmpLog.NumOfEnvironmentsInLogSet;
314 156 : ++tmpLog.NumOfDesignDaysInLogSet;
315 : }
316 267 : if (state.dataWeatherManager->Environment(i).KindOfEnvrn == DataGlobalConstants::KindOfSim::RunPeriodDesign) {
317 0 : ++tmpLog.NumOfEnvironmentsInLogSet;
318 0 : ++tmpLog.NumberOfSizingPeriodsInLogSet;
319 : }
320 : }
321 :
322 : // next fill in the count of steps into map
323 345 : for (int i = 1; i <= state.dataWeatherManager->NumOfEnvrn; ++i) {
324 :
325 267 : if (state.dataWeatherManager->Environment(i).KindOfEnvrn == DataGlobalConstants::KindOfSim::DesignDay) {
326 156 : tmpLog.ztStepCountByEnvrnMap[i] = HoursPerDay * state.dataGlobal->NumOfTimeStepInHour;
327 : }
328 267 : if (state.dataWeatherManager->Environment(i).KindOfEnvrn == DataGlobalConstants::KindOfSim::RunPeriodDesign) {
329 0 : tmpLog.ztStepCountByEnvrnMap[i] =
330 0 : HoursPerDay * state.dataGlobal->NumOfTimeStepInHour * state.dataWeatherManager->Environment(i).TotalDays;
331 : }
332 : }
333 :
334 78 : int stepSum = 0;
335 156 : std::map<int, int>::iterator end = tmpLog.ztStepCountByEnvrnMap.end();
336 234 : for (std::map<int, int>::iterator itr = tmpLog.ztStepCountByEnvrnMap.begin(); itr != end; ++itr) {
337 :
338 156 : tmpLog.envrnStartZtStepIndexMap[itr->first] = stepSum;
339 156 : stepSum += itr->second;
340 : }
341 :
342 78 : tmpLog.timeStepsInAverage = stepsInAverage;
343 :
344 78 : VectorLength = stepSum;
345 :
346 78 : tmpLog.NumOfStepsInLogSet = VectorLength;
347 78 : tmpLog.ztStepObj.resize(VectorLength);
348 :
349 78 : logObjs.push_back(tmpLog);
350 78 : ++NumOfLogs;
351 156 : return NumOfLogs - 1;
352 : }
353 :
354 95 : void SizingLoggerFramework::SetupSizingLogsNewEnvironment(EnergyPlusData &state)
355 : {
356 : using namespace WeatherManager;
357 :
358 734 : for (auto &l : logObjs) {
359 639 : l.SetupNewEnvironment(state.dataWeatherManager->Environment(state.dataWeatherManager->Envrn).SeedEnvrnNum, state.dataWeatherManager->Envrn);
360 : }
361 95 : }
362 :
363 12965 : ZoneTimestepObject SizingLoggerFramework::PrepareZoneTimestepStamp(EnergyPlusData &state)
364 : {
365 : // prepare current timing data once and then pass into fill routines
366 : // function used by both zone and system frequency log updates
367 :
368 12965 : int locDayOfSim(1);
369 :
370 12965 : if (state.dataGlobal->WarmupFlag) { // DayOfSim not okay during warmup, keeps incrementing up during warmup days
371 0 : locDayOfSim = 1;
372 : } else {
373 12965 : locDayOfSim = state.dataGlobal->DayOfSim;
374 : }
375 :
376 : ZoneTimestepObject tmpztStepStamp( // call constructor
377 12965 : state.dataGlobal->KindOfSim,
378 12965 : state.dataWeatherManager->Envrn,
379 : locDayOfSim,
380 12965 : state.dataGlobal->HourOfDay,
381 12965 : state.dataGlobal->TimeStep,
382 25930 : *state.dataOutputProcessor->TimeValue.at(OutputProcessor::TimeStepType::Zone).TimeStep,
383 77790 : state.dataGlobal->NumOfTimeStepInHour);
384 :
385 12965 : return tmpztStepStamp;
386 : }
387 :
388 4224 : void SizingLoggerFramework::UpdateSizingLogValuesZoneStep(EnergyPlusData &state)
389 : {
390 8448 : ZoneTimestepObject tmpztStepStamp;
391 :
392 4224 : tmpztStepStamp = PrepareZoneTimestepStamp(state);
393 :
394 31584 : for (auto &l : logObjs) {
395 27360 : l.FillZoneStep(tmpztStepStamp);
396 : }
397 4224 : }
398 :
399 8741 : void SizingLoggerFramework::UpdateSizingLogValuesSystemStep(EnergyPlusData &state)
400 : {
401 8741 : Real64 constexpr MinutesPerHour(60.0);
402 17482 : ZoneTimestepObject tmpztStepStamp;
403 8741 : SystemTimestepObject tmpSysStepStamp;
404 :
405 8741 : tmpztStepStamp = PrepareZoneTimestepStamp(state);
406 :
407 : // prepare system timestep stamp
408 8741 : tmpSysStepStamp.CurMinuteEnd = state.dataOutputProcessor->TimeValue.at(OutputProcessor::TimeStepType::System).CurMinute;
409 8741 : if (tmpSysStepStamp.CurMinuteEnd == 0.0) {
410 0 : tmpSysStepStamp.CurMinuteEnd = MinutesPerHour;
411 : }
412 8741 : tmpSysStepStamp.CurMinuteStart =
413 17482 : tmpSysStepStamp.CurMinuteEnd - (*state.dataOutputProcessor->TimeValue.at(OutputProcessor::TimeStepType::System).TimeStep) * MinutesPerHour;
414 8741 : tmpSysStepStamp.TimeStepDuration = *state.dataOutputProcessor->TimeValue.at(OutputProcessor::TimeStepType::System).TimeStep;
415 :
416 63302 : for (auto &l : logObjs) {
417 54561 : l.FillSysStep(tmpztStepStamp, tmpSysStepStamp);
418 : }
419 8741 : }
420 :
421 13 : void SizingLoggerFramework::IncrementSizingPeriodSet()
422 : {
423 103 : for (auto &l : this->logObjs) {
424 90 : l.ReInitLogForIteration();
425 : }
426 13 : }
427 :
428 26 : PlantCoinicidentAnalysis::PlantCoinicidentAnalysis(
429 26 : std::string loopName, int loopIndex, int nodeNum, Real64 density, Real64 cp, int numStepsInAvg, int sizingIndex)
430 : {
431 26 : name = loopName;
432 26 : plantLoopIndex = loopIndex;
433 26 : supplySideInletNodeNum = nodeNum;
434 26 : densityForSizing = density;
435 26 : specificHeatForSizing = cp;
436 26 : numTimeStepsInAvg = numStepsInAvg;
437 26 : plantSizingIndex = sizingIndex;
438 26 : }
439 :
440 36 : void PlantCoinicidentAnalysis::ResolveDesignFlowRate(EnergyPlusData &state, int const HVACSizingIterCount)
441 : {
442 : using DataSizing::GlobalCoolingSizingFactorMode;
443 : using DataSizing::GlobalHeatingSizingFactorMode;
444 : using DataSizing::LoopComponentSizingFactorMode;
445 : using DataSizing::NoSizingFactorMode;
446 :
447 : using namespace DataPlant;
448 : using namespace OutputReportPredefined;
449 : using DataHVACGlobals::SmallWaterVolFlow;
450 : bool setNewSizes;
451 : Real64 sizingFac;
452 : Real64 normalizedChange;
453 : Real64 newFoundVolFlowRate;
454 : Real64 peakLoadCalculatedMassFlow;
455 72 : std::string chIteration;
456 72 : std::string chSetSizes;
457 72 : std::string chDemandTrapUsed;
458 36 : bool changedByDemand(false);
459 : bool nullStampProblem;
460 :
461 : // first make sure we have valid time stamps to work with
462 36 : if (CheckTimeStampForNull(newFoundMassFlowRateTimeStamp) && CheckTimeStampForNull(NewFoundMaxDemandTimeStamp)) {
463 : // problem, don't have valid stamp, don't have any info to report either
464 0 : nullStampProblem = true;
465 : } else {
466 36 : nullStampProblem = false;
467 : }
468 :
469 36 : previousVolDesignFlowRate = state.dataSize->PlantSizData(plantSizingIndex).DesVolFlowRate;
470 :
471 36 : if (!CheckTimeStampForNull(newFoundMassFlowRateTimeStamp) && (newFoundMassFlowRateTimeStamp.runningAvgDataValue > 0.0)) { // issue 5665, was ||
472 36 : newFoundMassFlowRate = newFoundMassFlowRateTimeStamp.runningAvgDataValue;
473 : } else {
474 0 : newFoundMassFlowRate = 0.0;
475 : }
476 :
477 : // step 3 calculate mdot from max load and delta T
478 71 : if ((!CheckTimeStampForNull(NewFoundMaxDemandTimeStamp) && (NewFoundMaxDemandTimeStamp.runningAvgDataValue > 0.0)) &&
479 35 : ((specificHeatForSizing * state.dataSize->PlantSizData(plantSizingIndex).DeltaT) > 0.0)) {
480 35 : peakLoadCalculatedMassFlow =
481 35 : NewFoundMaxDemandTimeStamp.runningAvgDataValue / (specificHeatForSizing * state.dataSize->PlantSizData(plantSizingIndex).DeltaT);
482 : } else {
483 1 : peakLoadCalculatedMassFlow = 0.0;
484 : }
485 :
486 36 : if (peakLoadCalculatedMassFlow > newFoundMassFlowRate) {
487 22 : changedByDemand = true;
488 : } else {
489 14 : changedByDemand = false;
490 : }
491 36 : newFoundMassFlowRate = max(newFoundMassFlowRate, peakLoadCalculatedMassFlow); // step 4, take larger of the two
492 :
493 36 : newFoundVolFlowRate = newFoundMassFlowRate / densityForSizing;
494 :
495 : // now apply the correct sizing factor depending on input option
496 36 : sizingFac = 1.0;
497 36 : if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == NoSizingFactorMode) {
498 36 : sizingFac = 1.0;
499 0 : } else if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == GlobalHeatingSizingFactorMode) {
500 0 : sizingFac = state.dataSize->GlobalHeatSizingFactor;
501 0 : } else if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == GlobalCoolingSizingFactorMode) {
502 0 : sizingFac = state.dataSize->GlobalCoolSizingFactor;
503 0 : } else if (state.dataSize->PlantSizData(plantSizingIndex).SizingFactorOption == LoopComponentSizingFactorMode) {
504 : // multiplier used for pumps, often 1.0, from component level sizing fractions
505 0 : sizingFac = state.dataPlnt->PlantLoop(plantLoopIndex).LoopSide(LoopSideLocation::Supply).Branch(1).PumpSizFac;
506 : }
507 :
508 36 : newAdjustedMassFlowRate = newFoundMassFlowRate * sizingFac; // apply overall heating or cooling sizing factor
509 :
510 36 : newVolDesignFlowRate = newAdjustedMassFlowRate / densityForSizing;
511 :
512 : // compare threshold,
513 36 : setNewSizes = false;
514 36 : normalizedChange = 0.0;
515 36 : if (newVolDesignFlowRate > SmallWaterVolFlow && !nullStampProblem) { // do not use zero size or bad stamp data
516 :
517 36 : normalizedChange = std::abs((newVolDesignFlowRate - previousVolDesignFlowRate) / previousVolDesignFlowRate);
518 36 : if (normalizedChange > significantNormalizedChange) {
519 25 : anotherIterationDesired = true;
520 25 : setNewSizes = true;
521 : } else {
522 11 : anotherIterationDesired = false;
523 : }
524 : }
525 :
526 36 : if (setNewSizes) {
527 : // set new size values for rest of simulation
528 25 : state.dataSize->PlantSizData(plantSizingIndex).DesVolFlowRate = newVolDesignFlowRate;
529 :
530 25 : if (state.dataPlnt->PlantLoop(plantLoopIndex).MaxVolFlowRateWasAutoSized) {
531 25 : state.dataPlnt->PlantLoop(plantLoopIndex).MaxVolFlowRate = newVolDesignFlowRate;
532 25 : state.dataPlnt->PlantLoop(plantLoopIndex).MaxMassFlowRate = newAdjustedMassFlowRate;
533 : }
534 25 : if (state.dataPlnt->PlantLoop(plantLoopIndex).VolumeWasAutoSized) {
535 : // Note this calculation also appears in PlantManager::SizePlantLoop and PlantManager::ResizePlantLoopLevelSizes
536 25 : state.dataPlnt->PlantLoop(plantLoopIndex).Volume =
537 25 : state.dataPlnt->PlantLoop(plantLoopIndex).MaxVolFlowRate * state.dataPlnt->PlantLoop(plantLoopIndex).CirculationTime * 60.0;
538 25 : state.dataPlnt->PlantLoop(plantLoopIndex).Mass = state.dataPlnt->PlantLoop(plantLoopIndex).Volume * densityForSizing;
539 : }
540 : }
541 :
542 : // add a seperate eio summary report about what happened, did demand trap get used, what were the key values.
543 36 : if (!state.dataGlobal->sizingAnalysisEioHeaderDoneOnce) {
544 11 : print(state.files.eio,
545 : "{}",
546 : "! <Plant Coincident Sizing Algorithm>,Plant Loop Name,Sizing Pass {#},Measured Mass "
547 : "Flow{kg/s},Measured Demand {W},Demand Calculated Mass Flow{kg/s},Sizes Changed {Yes/No},Previous "
548 : "Volume Flow Rate {m3/s},New Volume Flow Rate {m3/s},Demand Check Applied {Yes/No},Sizing Factor "
549 11 : "{},Normalized Change {},Specific Heat{J/kg-K},Density {kg/m3}\n");
550 11 : state.dataGlobal->sizingAnalysisEioHeaderDoneOnce = true;
551 : }
552 36 : chIteration = fmt::to_string(HVACSizingIterCount);
553 36 : if (setNewSizes) {
554 25 : chSetSizes = "Yes";
555 : } else {
556 11 : chSetSizes = "No";
557 : }
558 36 : if (changedByDemand) {
559 22 : chDemandTrapUsed = "Yes";
560 : } else {
561 14 : chDemandTrapUsed = "No";
562 : }
563 :
564 72 : print(state.files.eio,
565 : "Plant Coincident Sizing Algorithm,{},{},{:.7R},{:.2R},{:.7R},{},{:.6R},{:.6R},{},{:.4R},{:.6R},{:.4R},{:.4R}\n",
566 : name,
567 : chIteration,
568 : newFoundMassFlowRateTimeStamp.runningAvgDataValue,
569 : NewFoundMaxDemandTimeStamp.runningAvgDataValue,
570 : peakLoadCalculatedMassFlow,
571 : chSetSizes,
572 : previousVolDesignFlowRate,
573 : newVolDesignFlowRate,
574 : chDemandTrapUsed,
575 : sizingFac,
576 : normalizedChange,
577 : specificHeatForSizing,
578 36 : densityForSizing);
579 :
580 : // report to sizing summary table called Plant Loop Coincident Design Fluid Flow Rates
581 :
582 144 : PreDefTableEntry(state,
583 36 : state.dataOutRptPredefined->pdchPlantSizPrevVdot,
584 72 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
585 : previousVolDesignFlowRate,
586 : 6);
587 144 : PreDefTableEntry(state,
588 36 : state.dataOutRptPredefined->pdchPlantSizMeasVdot,
589 72 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
590 : newFoundVolFlowRate,
591 : 6);
592 144 : PreDefTableEntry(state,
593 36 : state.dataOutRptPredefined->pdchPlantSizCalcVdot,
594 72 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
595 : newVolDesignFlowRate,
596 : 6);
597 :
598 36 : if (setNewSizes) {
599 75 : PreDefTableEntry(state,
600 25 : state.dataOutRptPredefined->pdchPlantSizCoincYesNo,
601 50 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
602 25 : "Yes");
603 : } else {
604 33 : PreDefTableEntry(state,
605 11 : state.dataOutRptPredefined->pdchPlantSizCoincYesNo,
606 22 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
607 11 : "No");
608 : }
609 :
610 36 : if (!nullStampProblem) {
611 36 : if (!changedByDemand && !CheckTimeStampForNull(newFoundMassFlowRateTimeStamp)) { // bug fix #5665
612 14 : if (newFoundMassFlowRateTimeStamp.envrnNum > 0) { // protect against invalid index
613 42 : PreDefTableEntry(state,
614 14 : state.dataOutRptPredefined->pdchPlantSizDesDay,
615 28 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
616 14 : state.dataWeatherManager->Environment(newFoundMassFlowRateTimeStamp.envrnNum).Title);
617 : }
618 42 : PreDefTableEntry(state,
619 14 : state.dataOutRptPredefined->pdchPlantSizPkTimeDayOfSim,
620 28 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
621 : newFoundMassFlowRateTimeStamp.dayOfSim);
622 42 : PreDefTableEntry(state,
623 14 : state.dataOutRptPredefined->pdchPlantSizPkTimeHour,
624 28 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
625 14 : newFoundMassFlowRateTimeStamp.hourOfDay - 1);
626 56 : PreDefTableEntry(state,
627 14 : state.dataOutRptPredefined->pdchPlantSizPkTimeMin,
628 28 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
629 : newFoundMassFlowRateTimeStamp.stepStartMinute,
630 : 0);
631 22 : } else if (changedByDemand && !CheckTimeStampForNull(NewFoundMaxDemandTimeStamp)) { // bug fix #5665
632 22 : if (NewFoundMaxDemandTimeStamp.envrnNum > 0) { // protect against invalid index
633 66 : PreDefTableEntry(state,
634 22 : state.dataOutRptPredefined->pdchPlantSizDesDay,
635 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
636 22 : state.dataWeatherManager->Environment(NewFoundMaxDemandTimeStamp.envrnNum).Title);
637 : }
638 66 : PreDefTableEntry(state,
639 22 : state.dataOutRptPredefined->pdchPlantSizPkTimeDayOfSim,
640 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
641 : NewFoundMaxDemandTimeStamp.dayOfSim);
642 66 : PreDefTableEntry(state,
643 22 : state.dataOutRptPredefined->pdchPlantSizPkTimeHour,
644 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
645 22 : NewFoundMaxDemandTimeStamp.hourOfDay - 1);
646 88 : PreDefTableEntry(state,
647 22 : state.dataOutRptPredefined->pdchPlantSizPkTimeMin,
648 44 : state.dataPlnt->PlantLoop(plantLoopIndex).Name + " Sizing Pass " + chIteration,
649 : NewFoundMaxDemandTimeStamp.stepStartMinute,
650 : 0);
651 : }
652 : }
653 36 : }
654 :
655 144 : bool PlantCoinicidentAnalysis::CheckTimeStampForNull(ZoneTimestepObject testStamp)
656 : {
657 :
658 144 : bool isNull = true;
659 :
660 144 : if (testStamp.envrnNum != 0) {
661 144 : isNull = false;
662 : }
663 144 : if (testStamp.kindOfSim != DataGlobalConstants::KindOfSim::Invalid) {
664 144 : isNull = false;
665 : }
666 :
667 144 : return isNull;
668 : }
669 2313 : } // namespace EnergyPlus
|