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