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 <cmath>
50 : #include <string>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/CurveManager.hh>
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataEnvironment.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/General.hh>
62 : #include <EnergyPlus/HybridEvapCoolingModel.hh>
63 : #include <EnergyPlus/Psychrometrics.hh>
64 : #include <EnergyPlus/ScheduleManager.hh>
65 : #include <EnergyPlus/UtilityRoutines.hh>
66 :
67 : namespace EnergyPlus { //***************
68 :
69 : namespace HybridEvapCoolingModel {
70 : // Module containing the EvaporativeCoolers simulation routines
71 :
72 : // MODULE INFORMATION:
73 : // AUTHOR Spencer Dutton
74 : // DATE WRITTEN May 2017
75 : // MODIFIED na
76 : // RE-ENGINEERED na
77 :
78 : // PURPOSE OF THIS MODULE:
79 : // To encapsulate the data and algorithms required for the HybridUnitaryHVAC system.
80 :
81 : // METHODOLOGY EMPLOYED:
82 : // uses 6D lookup tables to provide performance data that describe 8 key performance metrics .
83 :
84 : // Supply Air Temperature, Supply Air Humidity Ratio, Electric Power, Supply Fan Electric Power, External Static Pressure
85 : // System Second Fuel Consumption, System Third Fuel Consumption, System Water Use
86 :
87 : // USE STATEMENTS:
88 : using namespace EnergyPlus::DataEnvironment;
89 : using namespace Psychrometrics;
90 : using Curve::CurveValue;
91 : using Curve::GetCurveIndex;
92 : using Curve::GetCurveMinMaxValues;
93 :
94 : // Ummm these will have to go
95 : #define DEF_Tdb 0
96 : #define DEF_RH 1
97 :
98 : #define TEMP_CURVE 0
99 : #define W_CURVE 1
100 : #define POWER_CURVE 2
101 : #define SUPPLY_FAN_POWER 3
102 : #define EXTERNAL_STATIC_PRESSURE 4
103 : #define SECOND_FUEL_USE 5
104 : #define THIRD_FUEL_USE 6
105 : #define WATER_USE 7
106 :
107 341 : CMode::CMode()
108 682 : : ModeID(0.0), Max_Msa(0.0), Min_Msa(0.0), Min_OAF(0.0), Max_OAF(0.0), Minimum_Outdoor_Air_Temperature(0.0),
109 341 : Maximum_Outdoor_Air_Temperature(0.0), Minimum_Outdoor_Air_Humidity_Ratio(0.0), Maximum_Outdoor_Air_Humidity_Ratio(0.0),
110 341 : ModelScalingFactor(0.0)
111 : {
112 341 : MODE_BLOCK_OFFSET_Alpha = 9;
113 341 : BLOCK_HEADER_OFFSET_Alpha = 20;
114 341 : MODE1_BLOCK_OFFSET_Number = 2;
115 341 : MODE_BLOCK_OFFSET_Number = 16;
116 341 : BLOCK_HEADER_OFFSET_Number = 6;
117 341 : }
118 :
119 19 : bool CMode::InitializeOutdoorAirTemperatureConstraints(Real64 min, Real64 max)
120 : {
121 : // note If this field is blank, there should be no lower constraint on outside air temperature
122 19 : Minimum_Outdoor_Air_Temperature = min;
123 19 : Maximum_Outdoor_Air_Temperature = max;
124 19 : return true;
125 : }
126 19 : bool CMode::InitializeOutdoorAirHumidityRatioConstraints(Real64 min, Real64 max)
127 : {
128 : // minimum 0.00 maximum 0.10, units kgWater / kgDryAir
129 : // note Mode0 will not be considered when outside air absolute humidity is below the value in this field.
130 : // note If this field is blank, the lower constraint on outside air humidity ratio will be 0.00 kgWater / kgDryAir., default 0.00
131 : // the upper constraint on outside air humidity ratio will be 0.10 kgWater / kgDryAir, default 0.10
132 19 : Minimum_Outdoor_Air_Humidity_Ratio = min;
133 19 : Maximum_Outdoor_Air_Humidity_Ratio = max;
134 19 : return true;
135 : }
136 19 : bool CMode::InitializeOutdoorAirRelativeHumidityConstraints(Real64 min, Real64 max)
137 : {
138 : // minimum 0.00,maximum 100.00, units percent, Mode0 will not be considered when the outside air relative humidity is below the value in this
139 : // field.
140 : // note If this field is blank, the lower constraint on outside air relative humidity will be 0.00% (default 0.00), the upper constraint on
141 : // outside air relative humidity will be 100.00%, (default 100.00)
142 19 : Minimum_Outdoor_Air_Relative_Humidity = min;
143 19 : Maximum_Outdoor_Air_Relative_Humidity = max;
144 19 : return true;
145 : }
146 19 : bool CMode::InitializeReturnAirTemperatureConstraints(Real64 min, Real64 max)
147 : {
148 : // will not be considered when the return air temperature is below the value in this field.
149 : // If this field is blank, there will be no lower constraint on return air temperature
150 19 : Minimum_Return_Air_Temperature = min;
151 19 : Maximum_Return_Air_Temperature = max;
152 19 : return true;
153 : }
154 19 : bool CMode::InitializeReturnAirHumidityRatioConstraints(Real64 min, Real64 max)
155 : {
156 : // minimum 0.00 maximum 0.10, units kgWater / kgDryAir
157 : // note Mode0 will not be considered when outside air absolute humidity is below the value in this field.
158 : // note If this field is blank, the lower constraint on outside air humidity ratio will be 0.00 kgWater / kgDryAir., default 0.00
159 : // the upper constraint on outside air humidity ratio will be 0.10 kgWater / kgDryAir, default 0.10
160 19 : Minimum_Return_Air_Humidity_Ratio = min;
161 19 : Maximum_Return_Air_Humidity_Ratio = max;
162 19 : return true;
163 : }
164 19 : bool CMode::InitializeReturnAirRelativeHumidityConstraints(Real64 min, Real64 max)
165 : {
166 : // minimum 0.00,maximum 100.00, units percent, Mode0 will not be considered when the outside air relative humidity is below the value in this
167 : // field.
168 : // note If this field is blank, the lower constraint on outside air relative humidity will be 0.00% (default 0.00), the upper constraint on
169 : // outside air relative humidity will be 100.00%, (default 100.00)
170 19 : Minimum_Return_Air_Relative_Humidity = min;
171 19 : Maximum_Return_Air_Relative_Humidity = max;
172 19 : return true;
173 : }
174 19 : bool CMode::InitializeOSAFConstraints(Real64 minOSAF, Real64 maxOSAF)
175 : {
176 : // minimum 0.00, maximum 1.00, Outdoor air fractions below this value will not be considered.
177 : // If this field is blank, the lower constraint on outside air fraction will be 0.00,default 0.10
178 19 : Min_OAF = minOSAF;
179 19 : Max_OAF = maxOSAF;
180 19 : return true;
181 : }
182 19 : bool CMode::InitializeMsaRatioConstraints(Real64 minMsa, Real64 maxMsa)
183 : {
184 : // minimum 0.00, maximum 1.00, Supply air mass flow rate ratios below this value will not be considered.
185 : // Supply air mass flow rate ratio describes supply air mass flow rate as a fraction of mass flow rate associated with the value in field :
186 : // "System Maximum Supply Air Flow Rate". If this field is blank, the lower constraint on outside air fraction will be 0.00,default 0.10
187 19 : Min_Msa = minMsa;
188 19 : Max_Msa = maxMsa;
189 19 : return true;
190 : }
191 1570 : bool CMode::ValidPointer(int curve_pointer)
192 : {
193 1570 : if (curve_pointer >= 0)
194 1206 : return true;
195 : else
196 364 : return false;
197 : }
198 1570 : Real64 CMode::CalculateCurveVal(EnergyPlusData &state, Real64 Tosa, Real64 Wosa, Real64 Tra, Real64 Wra, Real64 Msa, Real64 OSAF, int curveType)
199 : {
200 : // SUBROUTINE INFORMATION:
201 : // AUTHOR Spencer Maxwell Dutton
202 : // DATE WRITTEN October 2017
203 : // MODIFIED
204 : // RE-ENGINEERED na
205 :
206 : // PURPOSE OF THIS SUBROUTINE:
207 : // Returns the normalized or scaled output from the performance curve as specified by curveType. 6 independent variables are required.
208 :
209 : // METHODOLOGY EMPLOYED:
210 : // Makes a call to the curve manager, then multiplies the result by either the by the ModelScalingFactor for extensive variables.
211 : // The ModelScalingFactor is the parent model's Scaling Factor, which is input by the user in the idf for this unit, and passed to
212 : // this CMode from the parent Model.
213 : // The following tables are for intensive variables : Supply Air Temperature, Supply Air Humidity, External Static Pressure
214 : // The following tables are for extensive variables : Electric Power, Fan Electric Power, Second Fuel Consumption,
215 : // Third Fuel Consumption, Water Use Lookup Table
216 : //
217 : // Tosa is the outside air temperature
218 : // Wosa is the outside humidity ratio
219 : // Tra return air temp
220 : // Wra return humidity ratio,
221 : // Msa supply air mass flow rate
222 : // OSAF outside air fraction
223 :
224 : // REFERENCES:
225 : // na
226 :
227 : // Using/Aliasing
228 1570 : Real64 Y_val = 0;
229 :
230 1570 : switch (curveType) {
231 197 : case TEMP_CURVE:
232 197 : if (ValidPointer(Tsa_curve_pointer)) {
233 197 : Y_val = CurveValue(state, Tsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
234 : } else {
235 0 : Y_val = Tra; // return air temp
236 : }
237 197 : break;
238 :
239 197 : case W_CURVE:
240 197 : if (ValidPointer(HRsa_curve_pointer)) {
241 197 : Y_val = CurveValue(state, HRsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
242 197 : Y_val = max(min(Y_val, 1.0), 0.0);
243 : } else {
244 0 : Y_val = Wra; // return HR
245 : }
246 197 : break;
247 :
248 : // The ModelScalingFactor factor for the Power, Fan Power, Water, and Fuel Use curves is equal to the Parent Model's Scaling Factor
249 174 : case POWER_CURVE:
250 174 : if (ValidPointer(Psa_curve_pointer)) {
251 174 : Y_val = ModelScalingFactor * CurveValue(state, Psa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
252 : } else {
253 0 : Y_val = 0;
254 : }
255 174 : break;
256 :
257 358 : case SUPPLY_FAN_POWER:
258 358 : if (ValidPointer(SFPsa_curve_pointer)) {
259 358 : Y_val = ModelScalingFactor * CurveValue(state, SFPsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
260 : } else {
261 0 : Y_val = 0;
262 : }
263 358 : break;
264 :
265 161 : case EXTERNAL_STATIC_PRESSURE:
266 161 : if (ValidPointer(ESPsa_curve_pointer)) {
267 161 : Y_val = CurveValue(state, ESPsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
268 : } else {
269 0 : Y_val = 0; // or set a more reasonable default
270 : }
271 161 : break;
272 :
273 161 : case SECOND_FUEL_USE:
274 161 : if (ValidPointer(SFUsa_curve_pointer)) {
275 35 : Y_val = ModelScalingFactor * CurveValue(state, SFUsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
276 : } else {
277 126 : Y_val = 0; // or set a more reasonable default
278 : }
279 161 : break;
280 :
281 161 : case THIRD_FUEL_USE:
282 161 : if (ValidPointer(TFUsa_curve_pointer)) {
283 0 : Y_val = ModelScalingFactor * CurveValue(state, TFUsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
284 : } else {
285 161 : Y_val = 0; // or set a more reasonable default
286 : }
287 161 : break;
288 :
289 161 : case WATER_USE:
290 161 : if (ValidPointer(WUsa_curve_pointer)) {
291 84 : Y_val = ModelScalingFactor * CurveValue(state, WUsa_curve_pointer, Tosa, Wosa, Tra, Wra, Msa, OSAF);
292 : } else {
293 77 : Y_val = 0; // or set a more reasonable default
294 : }
295 161 : break;
296 :
297 0 : default:
298 0 : break;
299 : }
300 :
301 1570 : return Y_val;
302 : }
303 224 : void CMode::InitializeCurve(int curveType, int curve_ID)
304 : {
305 : // SUBROUTINE INFORMATION:
306 : // AUTHOR Spencer Maxwell Dutton
307 : // DATE WRITTEN October 2017
308 : // MODIFIED
309 : // RE-ENGINEERED na
310 :
311 : // PURPOSE OF THIS SUBROUTINE:
312 : // Sets the curve ID assigned by the curve manager for the specific lookup Table, to a member variable.
313 :
314 : // METHODOLOGY EMPLOYED:
315 : //
316 :
317 : // REFERENCES:
318 : // na
319 :
320 : // Using/Aliasing
321 224 : switch (curveType) {
322 28 : case TEMP_CURVE:
323 28 : Tsa_curve_pointer = curve_ID;
324 28 : break;
325 28 : case W_CURVE:
326 28 : HRsa_curve_pointer = curve_ID;
327 28 : break;
328 28 : case POWER_CURVE:
329 28 : Psa_curve_pointer = curve_ID;
330 28 : break;
331 28 : case SUPPLY_FAN_POWER:
332 28 : SFPsa_curve_pointer = curve_ID;
333 28 : break;
334 28 : case EXTERNAL_STATIC_PRESSURE:
335 28 : ESPsa_curve_pointer = curve_ID;
336 28 : break;
337 28 : case SECOND_FUEL_USE:
338 28 : SFUsa_curve_pointer = curve_ID;
339 28 : break;
340 28 : case THIRD_FUEL_USE:
341 28 : TFUsa_curve_pointer = curve_ID;
342 28 : break;
343 28 : case WATER_USE:
344 28 : WUsa_curve_pointer = curve_ID;
345 28 : break;
346 0 : default:
347 0 : break;
348 : }
349 224 : }
350 :
351 16 : void CMode::GenerateSolutionSpace()
352 : {
353 : // SUBROUTINE INFORMATION:
354 : // AUTHOR Spencer Maxwell Dutton
355 : // DATE WRITTEN October 2017
356 : // MODIFIED
357 : // RE-ENGINEERED na
358 :
359 : // PURPOSE OF THIS SUBROUTINE:
360 : // Generates a matrix of all possible combinations of OSAF and supply air mass flow rates.
361 :
362 : // METHODOLOGY EMPLOYED:
363 : // Calculate the range of supply air mass flow rate (SAMF) and OSAF the mode can operate under.
364 : // Calculate a step size to increment through the ranges of SAMF and OSAF, based on the ResolutionMsa and
365 : // ResolutionOSAs respectively (these are hard coded in the constructor, this is an important consideration
366 : // because the number of increments greatly effects the simulation run time).
367 : //
368 : // if the range is smaller than the minimum resolution, then use the minimum resolution size as the minimum range over which to iterate
369 : // this should have the effect of keeping the number of solutions constant even if the range size varies.
370 :
371 : // REFERENCES:
372 : // na
373 :
374 : // Using/Aliasing
375 :
376 16 : if (Min_Msa == Max_Msa) {
377 5 : sol.MassFlowRatio.push_back(Max_Msa);
378 : } else {
379 11 : Real64 ResolutionMsa = (Max_Msa - Min_Msa) * 0.2;
380 73 : for (Real64 Msa_val = Max_Msa; Msa_val >= Min_Msa; Msa_val -= ResolutionMsa) {
381 62 : sol.MassFlowRatio.push_back(Msa_val);
382 : }
383 : }
384 :
385 16 : if (Min_OAF == Max_OAF) {
386 15 : sol.OutdoorAirFraction.push_back(Max_OAF);
387 : } else {
388 1 : Real64 ResolutionOSA = (Max_OAF - Min_OAF) * 0.2;
389 7 : for (Real64 OAF_val = Max_OAF; OAF_val >= Min_OAF; OAF_val -= ResolutionOSA) {
390 6 : sol.OutdoorAirFraction.push_back(OAF_val);
391 : }
392 : }
393 16 : }
394 :
395 28 : bool Model::ParseMode(EnergyPlusData &state,
396 : Array1D_string Alphas,
397 : Array1D_string cAlphaFields,
398 : Array1D<Real64> Numbers,
399 : Array1D_string cNumericFields,
400 : Array1D<bool> lAlphaBlanks,
401 : std::string cCurrentModuleObject)
402 : {
403 28 : CMode newMode;
404 28 : bool error = newMode.ParseMode(
405 : state, ModeCounter, &OperatingModes, ScalingFactor, Alphas, cAlphaFields, Numbers, cNumericFields, lAlphaBlanks, cCurrentModuleObject);
406 28 : ModeCounter++;
407 28 : return error;
408 28 : }
409 :
410 28 : bool CMode::ParseMode(EnergyPlusData &state,
411 : int ModeCounter,
412 : std::vector<CMode> *OperatingModes,
413 : Real64 ScalingFactor,
414 : Array1D_string Alphas,
415 : Array1D_string cAlphaFields,
416 : Array1D<Real64> Numbers,
417 : Array1D_string cNumericFields,
418 : Array1D<bool> lAlphaBlanks,
419 : std::string cCurrentModuleObject)
420 : {
421 : // SUBROUTINE INFORMATION:
422 : // AUTHOR Spencer Maxwell Dutton
423 : // DATE WRITTEN October 2017
424 : // MODIFIED
425 : // RE-ENGINEERED na
426 :
427 : // PURPOSE OF THIS SUBROUTINE:
428 : // Does the processing of each of the separate modes
429 :
430 : // METHODOLOGY EMPLOYED:
431 : // As the number of modes defined in the idf is not known until its read in, this method uses two counters to keep track of the inputs in the
432 : // Alphas and Numbers arrays. Three constants are used, BLOCK_HEADER_OFFSET_Alpha, BLOCK_HEADER_OFFSET_Number and MODE1_BLOCK_OFFSET_Number
433 : // if ever additional input parameters are added to the idf these offset counters would have to be adjusted accordingly.
434 :
435 : // REFERENCES:
436 : // na
437 :
438 : // Using/Aliasing
439 28 : ModeID = ModeCounter;
440 28 : ModelScalingFactor = ScalingFactor;
441 :
442 : int inter_Number;
443 28 : bool ErrorsFound = false;
444 28 : int inter_Alpha = BLOCK_HEADER_OFFSET_Alpha + MODE_BLOCK_OFFSET_Alpha * ModeID;
445 28 : if (ModeID > 0) {
446 19 : inter_Number = BLOCK_HEADER_OFFSET_Number + MODE1_BLOCK_OFFSET_Number + MODE_BLOCK_OFFSET_Number * (ModeID - 1);
447 : } else {
448 9 : inter_Number = BLOCK_HEADER_OFFSET_Number + MODE1_BLOCK_OFFSET_Number;
449 : }
450 28 : std::ostringstream strs;
451 28 : strs << ModeID;
452 :
453 28 : int curveID = -1;
454 28 : if (lAlphaBlanks(inter_Alpha)) {
455 0 : ModeName = "Mode" + strs.str();
456 : } else {
457 28 : ModeName = Alphas(inter_Alpha);
458 : }
459 :
460 28 : curveID = -1;
461 28 : inter_Alpha = inter_Alpha + 1;
462 28 : if (lAlphaBlanks(inter_Alpha)) {
463 10 : InitializeCurve(TEMP_CURVE, curveID); // as this is invalid curve id CalculateCurveVal will return a default when called
464 : } else {
465 18 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
466 18 : if (curveID == 0) {
467 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
468 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
469 0 : ErrorsFound = true;
470 0 : InitializeCurve(TEMP_CURVE, -1);
471 : } else {
472 18 : InitializeCurve(TEMP_CURVE, curveID);
473 : }
474 : }
475 :
476 28 : inter_Alpha = inter_Alpha + 1;
477 :
478 : // A22, \field Mode0 Supply Air Humidity Ratio Lookup Table Name
479 28 : curveID = -1;
480 28 : if (lAlphaBlanks(inter_Alpha)) {
481 10 : InitializeCurve(W_CURVE, curveID); // as this is invalid curve id CalculateCurveVal will return a default when called
482 : } else {
483 18 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
484 18 : if (curveID == 0) {
485 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
486 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
487 0 : ErrorsFound = true;
488 0 : InitializeCurve(W_CURVE, -1);
489 : } else {
490 18 : InitializeCurve(W_CURVE, curveID);
491 : }
492 : }
493 28 : inter_Alpha = inter_Alpha + 1;
494 : // A23, \field Mode0 System Electric Power Lookup Table Name
495 28 : curveID = -1;
496 28 : if (lAlphaBlanks(inter_Alpha)) {
497 7 : InitializeCurve(POWER_CURVE, curveID); // as this is invalid curve id CalculateCurveVal will return a default
498 : } else {
499 21 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
500 21 : if (curveID == 0) {
501 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
502 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
503 0 : ErrorsFound = true;
504 0 : InitializeCurve(POWER_CURVE, -1);
505 : } else {
506 21 : InitializeCurve(POWER_CURVE, curveID);
507 : }
508 : }
509 : // A24, \field Mode0 Supply Fan Electric Power Lookup Table Name
510 28 : inter_Alpha = inter_Alpha + 1;
511 28 : curveID = -1;
512 28 : if (lAlphaBlanks(inter_Alpha)) {
513 10 : InitializeCurve(SUPPLY_FAN_POWER, curveID); // as this is invalid curve id CalculateCurveVal will return a default
514 : } else {
515 18 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
516 18 : if (curveID == 0) {
517 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
518 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
519 0 : ErrorsFound = true;
520 0 : InitializeCurve(SUPPLY_FAN_POWER, -1);
521 : } else {
522 18 : InitializeCurve(SUPPLY_FAN_POWER, curveID);
523 : }
524 : }
525 : // A25, \field Mode0 External Static Pressure Lookup Table Name
526 28 : inter_Alpha = inter_Alpha + 1;
527 28 : curveID = -1;
528 28 : if (lAlphaBlanks(inter_Alpha)) {
529 13 : InitializeCurve(EXTERNAL_STATIC_PRESSURE, curveID); // as this is invalid curve id CalculateCurveVal will return a default
530 : } else {
531 15 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
532 15 : if (curveID == 0) {
533 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
534 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
535 0 : ErrorsFound = true;
536 0 : InitializeCurve(EXTERNAL_STATIC_PRESSURE, -1);
537 : } else {
538 15 : InitializeCurve(EXTERNAL_STATIC_PRESSURE, curveID);
539 : }
540 : }
541 : //
542 : // A26, \field Mode0 System Second Fuel Consumption Lookup Table Nam
543 28 : inter_Alpha = inter_Alpha + 1;
544 28 : curveID = -1;
545 28 : if (lAlphaBlanks(inter_Alpha)) {
546 25 : InitializeCurve(SECOND_FUEL_USE, curveID); // as this is invalid curve id CalculateCurveVal will return a default
547 : } else {
548 3 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
549 3 : if (curveID == 0) {
550 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
551 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
552 0 : ErrorsFound = true;
553 0 : InitializeCurve(SECOND_FUEL_USE, -1);
554 : } else {
555 3 : InitializeCurve(SECOND_FUEL_USE, curveID);
556 : }
557 : }
558 : // A27, \field Mode0 System Third Fuel Consumption Lookup Table Name
559 28 : inter_Alpha = inter_Alpha + 1;
560 28 : curveID = -1;
561 28 : if (lAlphaBlanks(inter_Alpha)) {
562 28 : InitializeCurve(THIRD_FUEL_USE, curveID); // as this is invalid curve id CalculateCurveVal will return a default
563 : } else {
564 0 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
565 0 : if (curveID == 0) {
566 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
567 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
568 0 : ErrorsFound = true;
569 0 : InitializeCurve(THIRD_FUEL_USE, -1);
570 : } else {
571 0 : InitializeCurve(THIRD_FUEL_USE, curveID);
572 : }
573 : }
574 : // A28, \field Mode0 System Water Use Lookup Table Name
575 28 : inter_Alpha = inter_Alpha + 1;
576 28 : curveID = -1;
577 28 : if (lAlphaBlanks(inter_Alpha)) {
578 19 : InitializeCurve(WATER_USE, curveID); // as this is invalid curve id CalculateCurveVal will return a default
579 : } else {
580 9 : curveID = GetCurveIndex(state, Alphas(inter_Alpha));
581 9 : if (curveID == 0) {
582 0 : ShowSevereError(state, format("Invalid {}={}", cAlphaFields(inter_Alpha), Alphas(inter_Alpha)));
583 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
584 0 : ErrorsFound = true;
585 0 : InitializeCurve(WATER_USE, -1);
586 : } else {
587 9 : InitializeCurve(WATER_USE, curveID);
588 : }
589 : }
590 28 : if (ModeID == 0) {
591 9 : (*OperatingModes).push_back(*this);
592 9 : return ErrorsFound;
593 : }
594 : // N8, \field Mode1 Minimum Outdoor Air Temperature
595 : // N9, \field Mode1 Maximum Outdoor Air Temperature
596 19 : bool ok = InitializeOutdoorAirTemperatureConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
597 19 : if (!ok) {
598 0 : ShowSevereError(state, format("Invalid {}Or Invalid{}", cNumericFields(inter_Number), cNumericFields(inter_Number + 1)));
599 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
600 0 : ErrorsFound = true;
601 : }
602 19 : inter_Number = inter_Number + 2;
603 : // N10, \field Mode1 Minimum Outdoor Air Humidity Ratio
604 : // N11, \field Mode1 Maximum Outdoor Air Humidity Ratio
605 19 : ok = InitializeOutdoorAirHumidityRatioConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
606 19 : if (!ok) {
607 0 : ShowSevereError(state, format("Invalid {}Or Invalid{}", cNumericFields(inter_Number), cNumericFields(inter_Number + 1)));
608 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
609 0 : ErrorsFound = true;
610 : }
611 19 : inter_Number = inter_Number + 2;
612 : // N12, \field Mode1 Minimum Outdoor Air Relative Humidity
613 : // N13, \field Mode1 Maximum Outdoor Air Relative Humidity
614 19 : ok = InitializeOutdoorAirRelativeHumidityConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
615 19 : if (!ok) {
616 0 : ShowSevereError(state, format("Invalid {}Or Invalid{}", cNumericFields(inter_Number), cNumericFields(inter_Number + 1)));
617 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
618 0 : ErrorsFound = true;
619 : }
620 19 : inter_Number = inter_Number + 2;
621 : // N14, \field Mode1 Minimum Return Air Temperature
622 : // N15, \field Mode1 Maximum Return Air Temperature
623 19 : ok = InitializeReturnAirTemperatureConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
624 19 : if (!ok) {
625 0 : ShowSevereError(state, format("Invalid {}Or Invalid{}", cNumericFields(inter_Number), cNumericFields(inter_Number + 1)));
626 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
627 0 : ErrorsFound = true;
628 : }
629 19 : inter_Number = inter_Number + 2;
630 : // N16, \field Mode1 Minimum Return Air Humidity Ratio
631 : // N17, \field Mode1 Maximum Return Air Humidity Ratio
632 19 : ok = InitializeReturnAirHumidityRatioConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
633 19 : if (!ok) {
634 0 : ShowSevereError(state, format("Invalid {}Or Invalid{}", cNumericFields(inter_Number), cNumericFields(inter_Number + 1)));
635 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
636 0 : ErrorsFound = true;
637 : }
638 19 : inter_Number = inter_Number + 2;
639 : // N18, \field Mode1 Minimum Return Air Relative HumidityInitialize
640 : // N19, \field Mode1 Maximum Return Air Relative Humidity
641 19 : ok = InitializeReturnAirRelativeHumidityConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
642 19 : if (!ok) {
643 0 : ShowSevereError(state,
644 0 : format("Invalid {}={}Or Invalid{}={}",
645 : cAlphaFields(inter_Number),
646 : Alphas(inter_Number),
647 : cAlphaFields(inter_Number + 1),
648 : Alphas(inter_Number + 1)));
649 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
650 0 : ErrorsFound = true;
651 : }
652 19 : inter_Number = inter_Number + 2;
653 : // N20, \field Mode1 Minimum Outdoor Air Fraction
654 : // N21, \field Mode1 Maximum Outdoor Air Fraction
655 :
656 19 : ok = InitializeOSAFConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
657 19 : if (!ok) {
658 0 : ShowSevereError(state, format("Error in OSAFConstraints{}through{}", cAlphaFields(inter_Number), cAlphaFields(inter_Number + 1)));
659 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
660 0 : ErrorsFound = true;
661 : }
662 : // N22, \field Mode1 Minimum Supply Air Mass Flow Rate Ratio
663 : // N23, \field Mode1 Maximum Supply Air Mass Flow Rate Ratio
664 19 : inter_Number = inter_Number + 2;
665 19 : ok = InitializeMsaRatioConstraints(Numbers(inter_Number), Numbers(inter_Number + 1));
666 19 : if (!ok) {
667 0 : ShowSevereError(state, format("Error in OSAFConstraints{}through{}", cAlphaFields(inter_Number), cAlphaFields(inter_Number + 1)));
668 0 : ShowContinueError(state, format("Entered in {}", cCurrentModuleObject));
669 0 : ErrorsFound = true;
670 : }
671 19 : (*OperatingModes).push_back(*this);
672 19 : return ErrorsFound;
673 28 : }
674 :
675 40 : bool CMode::MeetsOAEnvConstraints(Real64 Tosa, Real64 Wosa, Real64 RHosa)
676 : {
677 : // SUBROUTINE INFORMATION:
678 : // AUTHOR Spencer Maxwell Dutton
679 : // DATE WRITTEN October 2017
680 : // MODIFIED
681 : // RE-ENGINEERED na
682 :
683 : // PURPOSE OF THIS SUBROUTINE:
684 : // To check to see if this mode of operation is able to operate given the specified outdoor environmental conditions.
685 :
686 : // METHODOLOGY EMPLOYED:
687 : // Constraining certain modes to only operate over certain environmental conditions gives the user greater control in which
688 : // modes the algorithm selects.
689 :
690 : // REFERENCES:
691 : // na
692 :
693 : // Using/Aliasing
694 40 : bool OATempConstraintmet = false;
695 40 : bool OAHRConstraintmet = false;
696 40 : bool OARHConstraintmet = false;
697 :
698 40 : if (Tosa >= Minimum_Outdoor_Air_Temperature && Tosa <= Maximum_Outdoor_Air_Temperature) {
699 35 : OATempConstraintmet = true;
700 : }
701 :
702 40 : if (Wosa >= Minimum_Outdoor_Air_Humidity_Ratio && Wosa <= Maximum_Outdoor_Air_Humidity_Ratio) {
703 35 : OAHRConstraintmet = true;
704 : }
705 :
706 40 : if (RHosa >= Minimum_Outdoor_Air_Relative_Humidity && RHosa <= Maximum_Outdoor_Air_Relative_Humidity) {
707 40 : OARHConstraintmet = true;
708 : }
709 40 : if (OATempConstraintmet && OAHRConstraintmet && OARHConstraintmet) {
710 35 : return true;
711 : } else {
712 5 : return false;
713 : }
714 : }
715 :
716 196 : bool Model::MeetsSupplyAirTOC([[maybe_unused]] EnergyPlusData &state, Real64 Tsupplyair)
717 : {
718 : // SUBROUTINE INFORMATION:
719 : // AUTHOR Spencer Maxwell Dutton
720 : // DATE WRITTEN October 2017
721 : // MODIFIED
722 : // RE-ENGINEERED na
723 :
724 : // PURPOSE OF THIS SUBROUTINE:
725 : // To check to see if this this particular setting (combination of mode, OSAF and Msa) meets the required minimum
726 : // supply air temperature specified in the schedules
727 :
728 : // METHODOLOGY EMPLOYED:
729 : // Checks the minimum and maximum supply air temperatures and tests to see if the proposed supply air temperature is in the acceptable range.
730 :
731 : // REFERENCES:
732 : // na
733 :
734 : // Using/Aliasing
735 196 : Real64 MinSAT = 10;
736 196 : Real64 MaxSAT = 20;
737 196 : if (TsaMinSched != nullptr) {
738 196 : MinSAT = TsaMinSched->getCurrentVal();
739 : }
740 196 : if (TsaMaxSched != nullptr) {
741 196 : MaxSAT = TsaMaxSched->getCurrentVal();
742 : }
743 196 : if (Tsupplyair < MinSAT || Tsupplyair > MaxSAT) return false;
744 161 : return true;
745 : }
746 :
747 196 : bool Model::MeetsSupplyAirRHOC([[maybe_unused]] EnergyPlusData &state, Real64 SupplyW)
748 : {
749 : // SUBROUTINE INFORMATION:
750 : // AUTHOR Spencer Maxwell Dutton
751 : // DATE WRITTEN October 2017
752 : // MODIFIED
753 : // RE-ENGINEERED na
754 :
755 : // PURPOSE OF THIS SUBROUTINE:
756 : // To check to see if this this particular setting (combination of mode, OSAF and Msa) meets the required minimum
757 : // supply air relative humidity specified in the schedules
758 :
759 : // METHODOLOGY EMPLOYED:
760 : // Checks the scheduled minimum and maximum supply air RH and tests to see if the proposed supply air RH is in the acceptable range.
761 :
762 : // REFERENCES:
763 : // na
764 :
765 : // Using/Aliasing
766 196 : Real64 MinRH = 0;
767 196 : Real64 MaxRH = 1;
768 196 : if (RHsaMinSched != nullptr) {
769 196 : MinRH = RHsaMinSched->getCurrentVal();
770 : }
771 196 : if (RHsaMaxSched != nullptr) {
772 196 : MaxRH = RHsaMaxSched->getCurrentVal();
773 : }
774 196 : if (SupplyW < MinRH || SupplyW > MaxRH) return false;
775 196 : return true;
776 : }
777 :
778 20 : Model::Model()
779 60 : : Initialized(false), ZoneNum(0), SystemMaximumSupplyAirFlowRate(0.0), ScalingFactor(0.0), ScaledSystemMaximumSupplyAirMassFlowRate(0.0),
780 20 : UnitOn(0), UnitTotalCoolingRate(0.0), UnitTotalCoolingEnergy(0.0), UnitSensibleCoolingRate(0.0), UnitSensibleCoolingEnergy(0.0),
781 20 : UnitLatentCoolingRate(0.0), UnitLatentCoolingEnergy(0.0), SystemTotalCoolingRate(0.0), SystemTotalCoolingEnergy(0.0),
782 20 : SystemSensibleCoolingRate(0.0), SystemSensibleCoolingEnergy(0.0), SystemLatentCoolingRate(0.0), SystemLatentCoolingEnergy(0.0),
783 20 : UnitTotalHeatingRate(0.0), UnitTotalHeatingEnergy(0.0), UnitSensibleHeatingRate(0.0), UnitSensibleHeatingEnergy(0.0),
784 20 : UnitLatentHeatingRate(0.0), UnitLatentHeatingEnergy(0.0), SystemTotalHeatingRate(0.0), SystemTotalHeatingEnergy(0.0),
785 20 : SystemSensibleHeatingRate(0.0), SystemSensibleHeatingEnergy(0.0), SystemLatentHeatingRate(0.0), SystemLatentHeatingEnergy(0.0),
786 20 : SupplyFanElectricPower(0.0), SupplyFanElectricEnergy(0.0), SecondaryFuelConsumptionRate(0.0), SecondaryFuelConsumption(0.0),
787 20 : ThirdFuelConsumptionRate(0.0), ThirdFuelConsumption(0.0), WaterConsumptionRate(0.0), WaterConsumption(0.0), QSensZoneOut(0),
788 20 : QLatentZoneOut(0), QLatentZoneOutMass(0), ExternalStaticPressure(0.0), RequestedHumidificationMass(0.0), RequestedHumidificationLoad(0.0),
789 20 : RequestedHumidificationEnergy(0.0), RequestedDeHumidificationMass(0.0), RequestedDeHumidificationLoad(0.0),
790 20 : RequestedDeHumidificationEnergy(0.0), RequestedLoadToHeatingSetpoint(0.0), RequestedLoadToCoolingSetpoint(0.0), PrimaryMode(0),
791 20 : PrimaryModeRuntimeFraction(0.0), averageOSAF(0), ErrorCode(0), InletNode(0), OutletNode(0), SecondaryInletNode(0), SecondaryOutletNode(0),
792 20 : FinalElectricalPower(0.0), FinalElectricalEnergy(0.0), InletMassFlowRate(0.0), InletTemp(0.0), InletWetBulbTemp(0.0), InletHumRat(0.0),
793 20 : InletEnthalpy(0.0), InletPressure(0.0), InletRH(0.0), OutletVolumetricFlowRate(0.0), OutletMassFlowRate(0.0), PowerLossToAir(0.0),
794 20 : FanHeatTemp(0.0), OutletTemp(0.0), OutletWetBulbTemp(0.0), OutletHumRat(0.0), OutletEnthalpy(0.0), OutletPressure(0.0), OutletRH(0.0),
795 20 : SecInletMassFlowRate(0.0), SecInletTemp(0.0), SecInletWetBulbTemp(0.0), SecInletHumRat(0.0), SecInletEnthalpy(0.0), SecInletPressure(0.0),
796 20 : SecInletRH(0.0), SecOutletMassFlowRate(0.0), SecOutletTemp(0.0), SecOutletWetBulbTemp(0.0), SecOutletHumRat(0.0), SecOutletEnthalpy(0.0),
797 20 : SecOutletPressure(0.0), SecOutletRH(0.0), Wsa(0.0), SupplyVentilationAir(0.0), SupplyVentilationVolume(0.0), OutdoorAir(false),
798 20 : MinOA_Msa(0.0), OARequirementsPtr(0), Tsa(0.0), ModeCounter(0), CoolingRequested(false), HeatingRequested(false),
799 20 : VentilationRequested(false), DehumidificationRequested(false), HumidificationRequested(false)
800 : {
801 20 : WarnOnceFlag = false;
802 20 : count_EnvironmentConditionsMetOnce = 0;
803 20 : count_EnvironmentConditionsNotMet = 0;
804 20 : count_SAHR_OC_MetOnce = 0;
805 20 : count_SAT_OC_MetOnce = 0;
806 20 : count_DidWeMeetLoad = 0;
807 20 : count_DidWeNotMeetLoad = 0;
808 : // vector below used store the modes in each timestep that don't meet humidity or temperature limits, used in warnings
809 20 : std::vector<int> temp(25);
810 20 : SAT_OC_MetinMode_v = temp;
811 20 : SAHR_OC_MetinMode_v = temp;
812 :
813 20 : ModeCounter = 0;
814 :
815 20 : CurrentOperatingSettings.resize(5);
816 :
817 20 : InitializeModelParams();
818 20 : }
819 :
820 51 : void Model::ResetOutputs()
821 : {
822 51 : UnitTotalCoolingRate = 0;
823 51 : UnitTotalCoolingEnergy = 0;
824 51 : UnitSensibleCoolingRate = 0;
825 51 : UnitSensibleCoolingEnergy = 0;
826 51 : UnitLatentCoolingRate = 0;
827 51 : UnitLatentCoolingEnergy = 0;
828 51 : SystemTotalCoolingRate = 0;
829 51 : SystemTotalCoolingEnergy = 0;
830 51 : SystemSensibleCoolingRate = 0;
831 51 : SystemSensibleCoolingEnergy = 0;
832 51 : SystemLatentCoolingRate = 0;
833 51 : SystemLatentCoolingEnergy = 0;
834 51 : UnitTotalHeatingRate = 0;
835 51 : UnitTotalHeatingEnergy = 0;
836 51 : UnitSensibleHeatingRate = 0;
837 51 : UnitSensibleHeatingEnergy = 0;
838 51 : UnitLatentHeatingRate = 0;
839 51 : UnitLatentHeatingEnergy = 0;
840 51 : SystemTotalHeatingRate = 0;
841 51 : SystemTotalHeatingEnergy = 0;
842 51 : SystemSensibleHeatingRate = 0;
843 51 : SystemSensibleHeatingEnergy = 0;
844 51 : SystemLatentHeatingRate = 0;
845 51 : SystemLatentHeatingEnergy = 0;
846 51 : SupplyFanElectricPower = 0;
847 51 : SupplyFanElectricEnergy = 0;
848 51 : SecondaryFuelConsumptionRate = 0;
849 51 : SecondaryFuelConsumption = 0;
850 51 : ThirdFuelConsumptionRate = 0;
851 51 : ThirdFuelConsumption = 0;
852 51 : WaterConsumptionRate = 0;
853 51 : WaterConsumption = 0;
854 51 : ExternalStaticPressure = 0;
855 51 : }
856 :
857 39 : void Model::InitializeModelParams()
858 : {
859 : // SUBROUTINE INFORMATION:
860 : // AUTHOR Spencer Maxwell Dutton
861 : // DATE WRITTEN October 2017
862 : // MODIFIED
863 : // RE-ENGINEERED na
864 :
865 : // PURPOSE OF THIS SUBROUTINE:
866 : // Reset calculation values
867 :
868 : // METHODOLOGY EMPLOYED:
869 : //
870 :
871 : // REFERENCES:
872 : // na
873 :
874 : // Using/Aliasing
875 39 : ResetOutputs();
876 39 : PrimaryMode = 0;
877 39 : PrimaryModeRuntimeFraction = 0;
878 39 : optimal_EnvCondMet = false;
879 39 : Tsa = 0;
880 : // reset the power use to a high value, this is replaced during the calculation keeping the "best" setting.
881 :
882 39 : RunningPeakCapacity_EnvCondMet = false;
883 39 : Settings.clear();
884 39 : }
885 :
886 19 : void Model::Initialize(int ZoneNumber)
887 : {
888 : // SUBROUTINE INFORMATION:
889 : // AUTHOR Spencer Maxwell Dutton
890 : // DATE WRITTEN October 2017
891 : // MODIFIED
892 : // RE-ENGINEERED na
893 :
894 : // PURPOSE OF THIS SUBROUTINE:
895 : // Specify solution space resolution, and populate the solution spaces in each mode
896 :
897 : // METHODOLOGY EMPLOYED:
898 : // Solution spaces are the matrices of possible settings settings (combination of OSA fraction and supply air mass flow rate)
899 : // This method calls the GenerateSolutionSpace for each of the modes defined in the idf.
900 : // REFERENCES:
901 : // na
902 :
903 : // Using/Aliasing
904 :
905 19 : ZoneNum = ZoneNumber;
906 19 : if (Initialized) {
907 14 : return;
908 : }
909 5 : Initialized = true;
910 :
911 : // Iterate through modes of operation generating a matrix of OSAF and Msa to test in the algorithm.
912 21 : for (auto &thisOperatingMode : OperatingModes) {
913 16 : thisOperatingMode.GenerateSolutionSpace();
914 : }
915 :
916 5 : Initialized = true;
917 : }
918 :
919 173 : Real64 Model::CheckVal_W(EnergyPlusData &state, Real64 W, Real64 T, Real64 P)
920 : {
921 : // P must be in pascals NOT kPa
922 173 : Real64 OutletRHtest = PsyRhFnTdbWPb(state, T, W, P); // could also use outlet pressure instead of fixed
923 : Real64 OutletW =
924 173 : PsyWFnTdbRhPb(state, T, OutletRHtest, P, "Humidity ratio exceeded realistic range error called in " + Name + ", check performance curve");
925 173 : return OutletW;
926 : }
927 12 : Real64 Model::CheckVal_T(EnergyPlusData &state, Real64 T)
928 : {
929 12 : if ((T > 100) || (T < 0)) {
930 0 : ShowWarningError(state, format("Supply air temperature exceeded realistic range error called in {}, check performance curve", Name));
931 : }
932 12 : return T;
933 : }
934 12 : bool Model::SetStandByMode(EnergyPlusData &state, CMode Mode0, Real64 Tosa, Real64 Wosa, Real64 Tra, Real64 Wra)
935 : {
936 : // SUBROUTINE INFORMATION:
937 : // AUTHOR Spencer Maxwell Dutton
938 : // DATE WRITTEN October 2017
939 : // MODIFIED
940 : // RE-ENGINEERED na
941 :
942 : // PURPOSE OF THIS SUBROUTINE:
943 : // Set the supply air mass flow rate, power use, and all the other parameters for a setting.
944 :
945 : // METHODOLOGY EMPLOYED:
946 : // Uses the relevant lookup take to specify the parameters, or uses default conditions.
947 : // In setting the supply air temperature for now just use return air future improvement will use look up table
948 :
949 : // REFERENCES:
950 : // na
951 :
952 : // if the map of the solution space looks valid then populate the class member oStandBy (CSetting) with the settings data (what OSAF it runs
953 : // at, and how much power it uses etc.
954 12 : if (Mode0.sol.MassFlowRatio.size() > 0) {
955 12 : Real64 MsaRatio = Mode0.sol.MassFlowRatio[0];
956 12 : Real64 OSAF = Mode0.sol.OutdoorAirFraction[0];
957 :
958 12 : oStandBy.ScaledSupply_Air_Mass_Flow_Rate = MsaRatio * ScaledSystemMaximumSupplyAirMassFlowRate;
959 12 : oStandBy.Unscaled_Supply_Air_Mass_Flow_Rate = oStandBy.ScaledSupply_Air_Mass_Flow_Rate / ScalingFactor;
960 12 : oStandBy.ScaledSupply_Air_Ventilation_Volume = MsaRatio * ScaledSystemMaximumSupplyAirMassFlowRate / state.dataEnvrn->StdRhoAir;
961 12 : oStandBy.Supply_Air_Mass_Flow_Rate_Ratio = MsaRatio;
962 12 : oStandBy.ElectricalPower =
963 12 : Mode0.CalculateCurveVal(state, Tosa, Wosa, Tra, Wra, oStandBy.Unscaled_Supply_Air_Mass_Flow_Rate, OSAF, POWER_CURVE);
964 12 : oStandBy.Outdoor_Air_Fraction = OSAF;
965 12 : oStandBy.SupplyAirTemperature = Tra;
966 12 : oStandBy.SupplyAirW = Wra;
967 12 : oStandBy.Mode = 0;
968 12 : oStandBy.Mixed_Air_Temperature = Tra;
969 12 : oStandBy.Mixed_Air_W = Wra;
970 : } else {
971 : // if the solution space is invalid return true that an error occurred.
972 0 : return true;
973 : }
974 :
975 12 : return false;
976 : }
977 :
978 144 : Real64 Model::CalculateTimeStepAverage(SYSTEMOUTPUTS val)
979 : {
980 : // SUBROUTINE INFORMATION:
981 : // AUTHOR Spencer Maxwell Dutton
982 : // DATE WRITTEN October 2017
983 : // MODIFIED
984 : // RE-ENGINEERED na
985 :
986 : // PURPOSE OF THIS SUBROUTINE:
987 : // Calculates the resultant supply air conditions when the system operates in
988 : // multiple settings within a timestep.
989 :
990 : // METHODOLOGY EMPLOYED:
991 : // For longer simulation timesteps this model can consider partial runtime fractions
992 : // operating in different settings for a fraction of the total simulation time step reducing the likelihood of over conditioning.
993 : // Intensive variables that do not depend on system size (like temperature, pressure,etc), and extensive variables (variable whose values
994 : // depend on the quantity of substance) are handled differently
995 : //
996 : // Extensive variables ( Mass Flow, Volume flow, Fuel use etc), are averaged weighted by the amount of time spent in each setting.
997 : // for example if then system operates for 25% of the time with a mass flow of 4kg/s, and 75% of the time at a mass flow of 0kg/s then the
998 : // resultant time step average mass flow rate would be 1 kg/s.
999 :
1000 : // Intensive values in each part runtime fraction are first multiplied by the Scaled Supply Air Mass Flow Rate for each setting
1001 : // and then once all the various runtime fractions are added up, the resultant is divided by the overall time step average Scaled Supply Air
1002 : // Mass Flow Rate
1003 : //
1004 : // REFERENCES:
1005 : // na
1006 :
1007 : // Using/Aliasing
1008 144 : Real64 averagedVal = 0;
1009 144 : Real64 MassFlowDependentDenominator = 0;
1010 144 : Real64 value = 0;
1011 :
1012 864 : for (auto const &thisOperatingSettings : CurrentOperatingSettings) {
1013 720 : switch (val) {
1014 60 : case SYSTEMOUTPUTS::VENTILATION_AIR_V:
1015 60 : value = thisOperatingSettings.ScaledSupply_Air_Ventilation_Volume;
1016 60 : break;
1017 60 : case SYSTEMOUTPUTS::SYSTEM_FUEL_USE:
1018 60 : value = thisOperatingSettings.ElectricalPower;
1019 60 : break;
1020 60 : case SYSTEMOUTPUTS::OSUPPLY_FAN_POWER:
1021 60 : value = thisOperatingSettings.SupplyFanElectricPower;
1022 60 : break;
1023 60 : case SYSTEMOUTPUTS::OSECOND_FUEL_USE:
1024 60 : value = thisOperatingSettings.SecondaryFuelConsumptionRate;
1025 60 : break;
1026 60 : case SYSTEMOUTPUTS::OTHIRD_FUEL_USE:
1027 60 : value = thisOperatingSettings.ThirdFuelConsumptionRate;
1028 60 : break;
1029 60 : case SYSTEMOUTPUTS::OEXTERNAL_STATIC_PRESSURE:
1030 60 : value = thisOperatingSettings.ExternalStaticPressure * thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate;
1031 60 : break;
1032 60 : case SYSTEMOUTPUTS::OWATER_USE:
1033 60 : value = thisOperatingSettings.WaterConsumptionRate;
1034 60 : break;
1035 60 : case SYSTEMOUTPUTS::SUPPLY_AIR_TEMP:
1036 60 : value = thisOperatingSettings.SupplyAirTemperature * thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate;
1037 60 : break;
1038 60 : case SYSTEMOUTPUTS::MIXED_AIR_TEMP:
1039 60 : value = thisOperatingSettings.Mixed_Air_Temperature * thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate;
1040 60 : break;
1041 60 : case SYSTEMOUTPUTS::SUPPLY_MASS_FLOW:
1042 60 : value = thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate;
1043 60 : break;
1044 60 : case SYSTEMOUTPUTS::SUPPLY_AIR_HR:
1045 60 : value = thisOperatingSettings.SupplyAirW * thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate;
1046 60 : break;
1047 60 : case SYSTEMOUTPUTS::MIXED_AIR_HR:
1048 60 : value = thisOperatingSettings.Mixed_Air_W * thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate;
1049 60 : break;
1050 0 : default:
1051 0 : assert(false);
1052 : }
1053 720 : Real64 part_run = thisOperatingSettings.Runtime_Fraction;
1054 720 : averagedVal = averagedVal + value * part_run;
1055 720 : MassFlowDependentDenominator = thisOperatingSettings.ScaledSupply_Air_Mass_Flow_Rate * part_run + MassFlowDependentDenominator;
1056 : }
1057 :
1058 144 : CSetting StandbyMode = (*(CurrentOperatingSettings.begin()));
1059 144 : switch (val) {
1060 12 : case SYSTEMOUTPUTS::SUPPLY_AIR_TEMP:
1061 12 : if (MassFlowDependentDenominator == 0) {
1062 5 : averagedVal = StandbyMode.SupplyAirTemperature;
1063 : } else {
1064 7 : averagedVal = averagedVal / MassFlowDependentDenominator;
1065 : }
1066 12 : break;
1067 12 : case SYSTEMOUTPUTS::OEXTERNAL_STATIC_PRESSURE:
1068 12 : if (MassFlowDependentDenominator == 0) {
1069 5 : averagedVal = StandbyMode.ExternalStaticPressure;
1070 : } else {
1071 7 : averagedVal = averagedVal / MassFlowDependentDenominator;
1072 : }
1073 12 : break;
1074 12 : case SYSTEMOUTPUTS::SUPPLY_AIR_HR:
1075 12 : if (MassFlowDependentDenominator == 0) {
1076 5 : averagedVal = StandbyMode.SupplyAirW;
1077 : } else {
1078 7 : averagedVal = averagedVal / MassFlowDependentDenominator;
1079 : }
1080 12 : break;
1081 12 : case SYSTEMOUTPUTS::MIXED_AIR_TEMP:
1082 12 : if (MassFlowDependentDenominator == 0) {
1083 5 : averagedVal = StandbyMode.Mixed_Air_Temperature;
1084 : } else {
1085 7 : averagedVal = averagedVal / MassFlowDependentDenominator;
1086 : }
1087 12 : break;
1088 12 : case SYSTEMOUTPUTS::MIXED_AIR_HR:
1089 12 : if (MassFlowDependentDenominator == 0) {
1090 5 : averagedVal = StandbyMode.Mixed_Air_W;
1091 : } else {
1092 7 : averagedVal = averagedVal / MassFlowDependentDenominator;
1093 : }
1094 12 : break;
1095 84 : default:
1096 84 : break;
1097 : }
1098 288 : return averagedVal;
1099 : }
1100 :
1101 161 : Real64 Model::CalculatePartRuntimeFraction(Real64 MinOA_Msa,
1102 : Real64 Mvent,
1103 : Real64 RequestedCoolingLoad,
1104 : Real64 RequestedHeatingLoad,
1105 : Real64 SensibleRoomORZone,
1106 : Real64 RequestedDehumidificationLoad,
1107 : Real64 RequestedMoistureLoad,
1108 : Real64 LatentRoomORZone)
1109 : {
1110 : // SUBROUTINE INFORMATION:
1111 : // AUTHOR Spencer Maxwell Dutton
1112 : // DATE WRITTEN October 2017
1113 : // MODIFIED
1114 : // RE-ENGINEERED na
1115 :
1116 : // PURPOSE OF THIS SUBROUTINE:
1117 : // Calculates the minimum runtime fraction in a given setting needed to meet the
1118 : // sensible cooling, sensible heating, dehumidification and humidification loads
1119 : // and ventilation loads.
1120 :
1121 : // METHODOLOGY EMPLOYED:
1122 : // Calculate the minimum runtime fractions for each load that needs to be met and find the lowest of those runtime fractions.
1123 : // Go through each of the requirements (ventilation, heating, cooling, dehumidification, humidification and work out what the minimum runtime
1124 : // fraction you would need in order to meet all these requirements. Importantly the SensibleRoomORZone is either (-) for heating or (+) for
1125 : // cooling, where as the RequestedCoolingLoad and RequestedHeatingLoad, are both positive (never below 0).
1126 :
1127 : // REFERENCES:
1128 : // na
1129 :
1130 : // Using/Aliasing
1131 : Real64 PLHumidRatio, PLDehumidRatio, PLVentRatio, PLSensibleCoolingRatio, PLSensibleHeatingRatio, PartRuntimeFraction;
1132 161 : PLHumidRatio = PLDehumidRatio = PLVentRatio = PLSensibleCoolingRatio = PLSensibleHeatingRatio = 0;
1133 :
1134 161 : if (Mvent > 0) {
1135 161 : PLVentRatio = MinOA_Msa / Mvent;
1136 : }
1137 161 : PartRuntimeFraction = PLVentRatio;
1138 :
1139 161 : if (SensibleRoomORZone > 0) {
1140 44 : PLSensibleCoolingRatio = std::abs(RequestedCoolingLoad) / std::abs(SensibleRoomORZone);
1141 : }
1142 161 : if (PLSensibleCoolingRatio > PartRuntimeFraction) {
1143 22 : PartRuntimeFraction = PLSensibleCoolingRatio;
1144 : }
1145 :
1146 161 : if (SensibleRoomORZone < 0) {
1147 117 : PLSensibleHeatingRatio = std::abs(RequestedHeatingLoad) / std::abs(SensibleRoomORZone);
1148 : }
1149 :
1150 161 : if (PLSensibleHeatingRatio > PartRuntimeFraction) {
1151 73 : PartRuntimeFraction = PLSensibleHeatingRatio;
1152 : }
1153 :
1154 161 : if (RequestedDehumidificationLoad > 0) {
1155 0 : PLDehumidRatio = std::abs(RequestedDehumidificationLoad) / std::abs(LatentRoomORZone);
1156 : }
1157 :
1158 161 : if (PLDehumidRatio > PartRuntimeFraction) {
1159 0 : PartRuntimeFraction = PLDehumidRatio;
1160 : }
1161 :
1162 161 : if (RequestedMoistureLoad > 0) {
1163 0 : PLHumidRatio = std::abs(RequestedMoistureLoad) / std::abs(LatentRoomORZone);
1164 : }
1165 161 : if (PLHumidRatio > PartRuntimeFraction) {
1166 0 : PartRuntimeFraction = PLHumidRatio;
1167 : }
1168 :
1169 161 : if (PartRuntimeFraction < 0) {
1170 0 : PartRuntimeFraction = 0;
1171 : }
1172 161 : if (PartRuntimeFraction > 1) {
1173 90 : PartRuntimeFraction = 1;
1174 : }
1175 :
1176 161 : return PartRuntimeFraction;
1177 : }
1178 :
1179 8 : int Model::SetOperatingSetting(EnergyPlusData &state, CStepInputs StepIns)
1180 : {
1181 : // SUBROUTINE INFORMATION:
1182 : // AUTHOR Spencer Dutton
1183 : // DATE WRITTEN May 2017
1184 : // MODIFIED na
1185 : // RE-ENGINEERED na
1186 :
1187 : // PURPOSE OF THIS SUBROUTINE:
1188 : // This subroutine determines the set of operating settings for the HybridUniaryHVAC
1189 : // It is called from Model::doStep, the main calculation step
1190 : // at the system time step.
1191 :
1192 : // METHODOLOGY EMPLOYED:
1193 : // 1) Clear out the set of operating settings from the previous time step.
1194 : // 2) Iterate through each operating mode and weed out modes that are not intended to operate in current environmental conditions.
1195 : // -> For each mode that is viable iterate thought the solution space and identify settings that meet the ventilation
1196 : // requirements
1197 : // -> settings that do are stored in the a container (Settings)
1198 : // 3) Iterate through all the settings in Settings
1199 : //
1200 : // 4) Calculate the setting zone sensible cooling and heating load and humidification and dehumidification.
1201 : // 5) Test to see if conditioning and humidification loads are met.
1202 : // 6) Calculate setting power consumption, use the setting delivered ventilation and loads to calculate the
1203 : // 7) minimum runtime fraction needed to meet those loads, then assuming that part runtime fraction calculate the setting part run time power
1204 : // use. 8) If the setting meets both the conditioning and humidification loads then test to see if its optimal in terms of energy use.
1205 : // ->if so, save that setting as the current optimal.
1206 : // ->if not ignore it.
1207 : // If the setting failed to meet either the conditioning or humidification loads, then
1208 : // -> firstly check to see if no previous other setting (in this calculation step) has met both the load and humidification requirements
1209 : // -> if so
1210 : // -> check if this setting meets the conditioning load (only)
1211 : // -> if so
1212 : // ->check to see if this setting is better at meeting the dehumidification or humidification lad
1213 : // than any previous setting this step.
1214 : // -> if its not, ignore it.
1215 : // -> if not
1216 : // ->check to see if any previous setting met the conditioning load
1217 : // ->if not:
1218 : // ->see if this setting is better at meeting the conditioning load than
1219 : // any previous setting this calculation step.
1220 : // -> if so save as current optimal
1221 : // -> if its not, ignore it.
1222 : // -> if so: then ignore it.
1223 : // ->if not, then a previous setting is better than this one by default, and so ignore it.
1224 : // 9) Identify error states if the no setting meets the environmental conditions, or the supply air humidity or temperature constraints.
1225 : // 10) if we met the load set operating settings to be a combination of the optimal setting at the minimum required runtime fraction
1226 : // 11) if we partly met the load then do the best we can and run full out in that optimal setting.
1227 : // 12) if we didn't even partially meet the load make sure the operational settings are just the standby mode.
1228 : // 13) generate summary statistics for warnings.
1229 :
1230 : // REFERENCES:
1231 : // na
1232 :
1233 : // Using/Aliasing
1234 :
1235 : // Locals
1236 : // SUBROUTINE ARGUMENT DEFINITIONS:
1237 : // The CStepInputs are defined in the CStepInputs class definition.
1238 : // SUBROUTINE PARAMETER DEFINITIONS:
1239 : // na
1240 : // INTERFACE BLOCK SPECIFICATIONS
1241 : // na
1242 : // DERIVED TYPE DEFINITIONS
1243 : // na
1244 :
1245 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1246 8 : bool DidWeMeetLoad = false;
1247 8 : bool DidWeMeetHumidificaiton = false;
1248 8 : bool DidWePartlyMeetLoad = false;
1249 8 : Real64 OptimalSetting_RunFractionTotalFuel = IMPLAUSIBLE_POWER;
1250 : Real64 Tma;
1251 : Real64 Wma;
1252 : Real64 Hsa;
1253 : Real64 Hma;
1254 8 : Real64 PreviousMaxiumConditioningOutput = 0;
1255 8 : Real64 PreviousMaxiumHumidOrDehumidOutput = 0;
1256 8 : std::string ObjectID = Name.c_str();
1257 8 : if (StepIns.RHosa > 1) {
1258 0 : ShowSevereError(state,
1259 0 : format("Unitary hybrid system error, required relative humidity value 0-1, called in object{}.Check inputs", ObjectID));
1260 : assert(true);
1261 0 : return -1;
1262 : } // because it should be fractional, this should only really be possible if its called from a unit test
1263 :
1264 8 : if (StepIns.RHra > 1) {
1265 0 : ShowSevereError(state,
1266 0 : format("Unitary hybrid system error, required relative humidity value 0-1, called in object{}.Check inputs", ObjectID));
1267 : assert(true);
1268 0 : return -1;
1269 : } // because it should be fractional, this should only really be possible if its called from a unit test
1270 :
1271 8 : Real64 Wosa = PsyWFnTdbRhPb(state, StepIns.Tosa, StepIns.RHosa, state.dataEnvrn->OutBaroPress);
1272 8 : Real64 Wra = PsyWFnTdbRhPb(state, StepIns.Tra, StepIns.RHra, InletPressure);
1273 : bool EnvironmentConditionsMet, EnvironmentConditionsMetOnce, MinVRMet, SAT_OC_Met, SAT_OC_MetOnce, SARH_OC_Met, SAHR_OC_MetOnce;
1274 8 : EnvironmentConditionsMetOnce = SAT_OC_Met = SAT_OC_MetOnce = SARH_OC_Met = SAHR_OC_MetOnce = false;
1275 :
1276 8 : MinOA_Msa = StepIns.MinimumOA; // Set object version of minimum VR Kg/s
1277 :
1278 48 : for (std::vector<CMode>::const_iterator iterator = OperatingModes.begin() + 1; iterator != OperatingModes.end();
1279 40 : ++iterator) // iterate though the modes.
1280 : {
1281 40 : CMode Mode = *iterator;
1282 40 : bool SAHR_OC_MetinMode = false;
1283 40 : bool SAT_OC_MetinMode = false;
1284 40 : int solution_map_sizeX = Mode.sol.MassFlowRatio.size();
1285 40 : int solution_map_sizeY = Mode.sol.OutdoorAirFraction.size();
1286 :
1287 : // Check that in this mode the //Outdoor Air Relative Humidity(0 - 100 % ) //Outdoor Air Humidity Ratio(g / g)//Outdoor Air
1288 : // Temperature(degC)
1289 40 : if (Mode.MeetsOAEnvConstraints(StepIns.Tosa, Wosa, 100 * StepIns.RHosa)) {
1290 35 : EnvironmentConditionsMet = EnvironmentConditionsMetOnce = true;
1291 : } else {
1292 5 : EnvironmentConditionsMet = false;
1293 : }
1294 :
1295 40 : if (EnvironmentConditionsMet) {
1296 231 : for (int indexMassFlowRatio = 0; indexMassFlowRatio < solution_map_sizeX;
1297 : indexMassFlowRatio++) // within each mode go though all the combinations of solution spaces.
1298 : {
1299 392 : for (int indexOutdoorAirFraction = 0; indexOutdoorAirFraction < solution_map_sizeY; indexOutdoorAirFraction++) {
1300 : // Supply Air Mass Flow Rate(kg / s)
1301 : // Outdoor Air Fraction(0 - 1)
1302 :
1303 196 : Real64 MsaRatio = Mode.sol.MassFlowRatio[indexMassFlowRatio]; // fractions of rated mass flow rate, so for some modes this
1304 : // might be low but others hi
1305 196 : Real64 OSAF = Mode.sol.OutdoorAirFraction[indexOutdoorAirFraction];
1306 196 : Real64 ScaledMsa = ScaledSystemMaximumSupplyAirMassFlowRate * MsaRatio;
1307 196 : Real64 UnscaledMsa = ScaledSystemMaximumSupplyAirMassFlowRate / ScalingFactor;
1308 196 : Real64 Supply_Air_Ventilation_Volume = 0;
1309 : // Calculate the ventilation mass flow rate
1310 196 : Real64 Mvent = ScaledMsa * OSAF;
1311 :
1312 196 : if (state.dataEnvrn->StdRhoAir > 1) {
1313 196 : Supply_Air_Ventilation_Volume = Mvent / state.dataEnvrn->StdRhoAir;
1314 : } else {
1315 0 : Supply_Air_Ventilation_Volume = Mvent / 1.225; // stored as volumetric flow for reporting
1316 : }
1317 :
1318 196 : if (Mvent - MinOA_Msa > -0.000001) {
1319 196 : MinVRMet = true;
1320 : } else {
1321 0 : MinVRMet = false;
1322 : }
1323 :
1324 196 : if (MinVRMet) {
1325 : // reset outside air temp and return air temp before calculating curve values for each mode
1326 196 : StepIns.Tosa = SecInletTemp;
1327 196 : StepIns.Tra = InletTemp;
1328 : Real64 FanPower =
1329 196 : Mode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SUPPLY_FAN_POWER) *
1330 196 : ScalingFactor;
1331 :
1332 : // calculate power loss to air if in mixed air stream and divide fan heat between outside air stream and return air stream
1333 196 : if (FanHeatGain && FanHeatGainLocation == "MIXEDAIRSTREAM") {
1334 28 : PowerLossToAir = FanPower * FanHeatInAirFrac;
1335 : } else {
1336 168 : PowerLossToAir = 0.0;
1337 : }
1338 196 : Real64 FanHeatTempOA = PowerLossToAir / (PsyCpAirFnW(Wosa) * (ScaledMsa * OSAF));
1339 196 : StepIns.Tosa = StepIns.Tosa + FanHeatTempOA;
1340 196 : if (OSAF < 1.0) {
1341 0 : Real64 FanHeatTempRA = PowerLossToAir / (PsyCpAirFnW(Wra) * (ScaledMsa * (1 - OSAF)));
1342 0 : StepIns.Tra = StepIns.Tra + FanHeatTempRA;
1343 : }
1344 :
1345 : // Calculate prospective supply air temperature
1346 196 : Tsa = Mode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, TEMP_CURVE);
1347 : // Calculate prospective supply air Humidity Ratio
1348 196 : Wsa = Mode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, W_CURVE);
1349 :
1350 : // calculate power loss to supply air stream from fan power determined by curve value and fraction of fan heat in air
1351 : // stream
1352 196 : if (FanHeatGain && FanHeatGainLocation == "SUPPLYAIRSTREAM") {
1353 28 : PowerLossToAir = FanPower * FanHeatInAirFrac;
1354 : } else {
1355 168 : PowerLossToAir = 0.0;
1356 : }
1357 196 : FanHeatTemp = PowerLossToAir / (PsyCpAirFnW(Wsa) * ScaledMsa);
1358 196 : Tsa = Tsa + FanHeatTemp;
1359 :
1360 : // Check it meets constraints
1361 196 : if (MeetsSupplyAirTOC(state, Tsa)) {
1362 161 : SAT_OC_Met = SAT_OC_MetOnce = SAT_OC_MetinMode = true;
1363 : } else {
1364 35 : SAT_OC_Met = false;
1365 : }
1366 : // Return Air Relative Humidity(0 - 100 % ) //Return Air Humidity Ratio(g / g)
1367 196 : if (MeetsSupplyAirRHOC(state, Wsa)) {
1368 196 : SARH_OC_Met = SAHR_OC_MetOnce = SAHR_OC_MetinMode = true;
1369 : } else {
1370 0 : SARH_OC_Met = false;
1371 : }
1372 :
1373 196 : if (SARH_OC_Met && SAT_OC_Met) {
1374 161 : CSetting CandidateSetting;
1375 161 : CandidateSetting.Supply_Air_Ventilation_Volume = Supply_Air_Ventilation_Volume;
1376 161 : CandidateSetting.Mode = Mode.ModeID;
1377 161 : CandidateSetting.Outdoor_Air_Fraction = OSAF;
1378 161 : CandidateSetting.Supply_Air_Mass_Flow_Rate_Ratio = MsaRatio;
1379 161 : CandidateSetting.Unscaled_Supply_Air_Mass_Flow_Rate = UnscaledMsa;
1380 161 : CandidateSetting.ScaledSupply_Air_Mass_Flow_Rate = ScaledMsa;
1381 :
1382 : // If no load is requested but ventilation is required, set the supply air mass flow rate to the minimum of the
1383 : // required ventilation flow rate and the maximum supply air flow rate
1384 161 : if (!CoolingRequested && !HeatingRequested && !DehumidificationRequested && !HumidificationRequested) {
1385 73 : CandidateSetting.ScaledSupply_Air_Mass_Flow_Rate =
1386 73 : min(MinOA_Msa, CandidateSetting.ScaledSupply_Air_Mass_Flow_Rate);
1387 : // add fan heat if not included in lookup tables for supply air stream
1388 73 : Tsa = StepIns.Tosa + FanHeatTemp;
1389 : }
1390 :
1391 161 : CandidateSetting.ScaledSupply_Air_Ventilation_Volume =
1392 161 : CandidateSetting.ScaledSupply_Air_Mass_Flow_Rate / state.dataEnvrn->StdRhoAir;
1393 161 : CandidateSetting.oMode = Mode;
1394 161 : CandidateSetting.SupplyAirTemperature = Tsa;
1395 161 : CandidateSetting.SupplyAirW = CheckVal_W(state, Wsa, Tsa, OutletPressure);
1396 161 : CandidateSetting.Mode = Mode.ModeID;
1397 161 : Settings.push_back(CandidateSetting);
1398 161 : }
1399 : }
1400 : }
1401 : }
1402 : }
1403 40 : if (!state.dataGlobal->WarmupFlag) {
1404 : // Keep an account of the number of times the supply air temperature and humidity constraints were not met for a given mode but only
1405 : // do this when its not warmup.
1406 40 : if (!SAT_OC_MetinMode) {
1407 10 : SAT_OC_MetinMode_v[Mode.ModeID] = SAT_OC_MetinMode_v[Mode.ModeID] + 1;
1408 : }
1409 40 : if (!SAHR_OC_MetinMode) {
1410 5 : SAHR_OC_MetinMode_v[Mode.ModeID] = SAHR_OC_MetinMode_v[Mode.ModeID] + 1;
1411 : }
1412 : }
1413 40 : }
1414 :
1415 169 : for (auto &thisSetting : Settings) {
1416 : // Calculate the delta H
1417 161 : Real64 OSAF = thisSetting.Outdoor_Air_Fraction;
1418 161 : Real64 UnscaledMsa = thisSetting.Unscaled_Supply_Air_Mass_Flow_Rate;
1419 161 : Real64 ScaledMsa = thisSetting.ScaledSupply_Air_Mass_Flow_Rate;
1420 :
1421 : // send the scaled Msa to calculate energy and the unscaled for sending to curves.
1422 161 : Tsa = thisSetting.SupplyAirTemperature;
1423 161 : Wsa = thisSetting.SupplyAirW;
1424 161 : Tma = StepIns.Tra + OSAF * (StepIns.Tosa - StepIns.Tra);
1425 161 : Wma = Wra + OSAF * (Wosa - Wra);
1426 161 : thisSetting.Mixed_Air_Temperature = Tma;
1427 161 : thisSetting.Mixed_Air_W = Wma;
1428 :
1429 161 : Hma = PsyHFnTdbW(Tma, Wma);
1430 : // Calculate Enthalpy of return air
1431 161 : Real64 Hra = PsyHFnTdbW(StepIns.Tra, Wra);
1432 :
1433 161 : Hsa = PsyHFnTdbW(Tsa, Wsa);
1434 :
1435 161 : Real64 SupplyAirCp = PsyCpAirFnW(Wsa); // J/degreesK.kg
1436 161 : Real64 ReturnAirCP = PsyCpAirFnW(Wra); // J/degreesK.kg
1437 161 : Real64 OutdoorAirCP = PsyCpAirFnW(Wosa); // J/degreesK.kg
1438 :
1439 : // Calculations below of system cooling and heating capacity are ultimately reassessed when the resultant part runtime fraction is
1440 : // assessed. However its valuable that they are calculated here to at least provide a check.
1441 :
1442 : // System Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA + OSAF*(cpOSA-cpRA) + cpSA) {kJ/kg-C} * (T_RA + OSAF*(T_OSA - T_RA) - T_SA)
1443 : // System Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA + OSAF *(HR_OSA - HR_RA) - HR_SA) {kgWater/kgDryAir}
1444 : // System Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA + OSAF*(h_OSA - h_RA) - h_SA) {kJ/kgDryAir}
1445 161 : Real64 SystemCp = ReturnAirCP + OSAF * (OutdoorAirCP - ReturnAirCP) + SupplyAirCp; // J/degreesK.kg
1446 161 : Real64 SensibleSystem = ScaledMsa * 0.5 * SystemCp * (Tma - Tsa); // W dynamic cp
1447 161 : Real64 MsaDry = ScaledMsa * (1 - Wsa);
1448 161 : Real64 LambdaSa = Psychrometrics::PsyHfgAirFnWTdb(0, Tsa);
1449 161 : Real64 LatentSystem = LambdaSa * MsaDry * (Wma - Wsa); // W
1450 : // Total system cooling
1451 161 : thisSetting.TotalSystem = (Hma - Hsa) * ScaledMsa;
1452 : // Perform latent check
1453 : // Real64 latentCheck = TotalSystem - SensibleSystem;
1454 :
1455 : // Zone Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA+cpSA) {kJ/kg-C} * (T_RA - T_SA) {C}
1456 : // Zone Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA - HR_SA) {kgWater/kgDryAir}
1457 : // Zone Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA - h_SA) {kJ/kgDryAir}
1458 161 : Real64 SensibleRoomORZone = ScaledMsa * 0.5 * (SupplyAirCp + ReturnAirCP) * (StepIns.Tra - Tsa); // W dynamic cp
1459 161 : Real64 latentRoomORZone = LambdaSa * MsaDry * (Wra - Wsa); // W
1460 : // Total room cooling
1461 161 : Real64 TotalRoomORZone = (Hra - Hsa) * ScaledMsa; // W
1462 : // Perform latent check
1463 : // Real64 latentRoomORZoneCheck = TotalRoomORZone - SensibleRoomORZone;
1464 :
1465 161 : thisSetting.SensibleSystem = SensibleSystem;
1466 161 : thisSetting.LatentSystem = LatentSystem;
1467 161 : thisSetting.TotalZone = TotalRoomORZone;
1468 161 : thisSetting.SensibleZone = SensibleRoomORZone;
1469 161 : thisSetting.LatentZone = latentRoomORZone;
1470 :
1471 161 : bool Conditioning_load_met = false;
1472 161 : if (CoolingRequested && (SensibleRoomORZone > StepIns.RequestedCoolingLoad)) {
1473 27 : Conditioning_load_met = true;
1474 : }
1475 161 : if (HeatingRequested && (SensibleRoomORZone < StepIns.RequestedHeatingLoad)) {
1476 0 : Conditioning_load_met = true;
1477 : }
1478 161 : if (!(HeatingRequested || CoolingRequested)) {
1479 73 : Conditioning_load_met = true;
1480 : }
1481 :
1482 161 : bool Humidification_load_met = false;
1483 :
1484 161 : Real64 RequestedDeHumidificationLoad = StepIns.ZoneDehumidificationLoad;
1485 161 : if (DehumidificationRequested && latentRoomORZone > RequestedDeHumidificationLoad) {
1486 0 : Humidification_load_met = true;
1487 : }
1488 161 : Real64 RequestedHumidificationLoad = StepIns.ZoneMoistureLoad;
1489 161 : if (HumidificationRequested && latentRoomORZone < RequestedHumidificationLoad) {
1490 0 : Humidification_load_met = true;
1491 : }
1492 :
1493 161 : if (!(HumidificationRequested || DehumidificationRequested)) {
1494 161 : Humidification_load_met = true;
1495 : }
1496 :
1497 161 : thisSetting.ElectricalPower = thisSetting.oMode.CalculateCurveVal(
1498 : state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, POWER_CURVE); // [Kw] calculations for fuel in Kw
1499 161 : thisSetting.SupplyFanElectricPower =
1500 161 : thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SUPPLY_FAN_POWER);
1501 161 : thisSetting.ExternalStaticPressure =
1502 161 : thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, EXTERNAL_STATIC_PRESSURE);
1503 161 : thisSetting.SecondaryFuelConsumptionRate =
1504 161 : thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, SECOND_FUEL_USE);
1505 161 : thisSetting.ThirdFuelConsumptionRate =
1506 161 : thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, THIRD_FUEL_USE);
1507 161 : thisSetting.WaterConsumptionRate =
1508 161 : thisSetting.oMode.CalculateCurveVal(state, StepIns.Tosa, Wosa, StepIns.Tra, Wra, UnscaledMsa, OSAF, WATER_USE);
1509 :
1510 : // Calculate partload fraction required to meet all requirements
1511 161 : Real64 PartRuntimeFraction = 0;
1512 322 : PartRuntimeFraction = CalculatePartRuntimeFraction(MinOA_Msa,
1513 161 : thisSetting.Supply_Air_Ventilation_Volume * state.dataEnvrn->StdRhoAir,
1514 : StepIns.RequestedCoolingLoad,
1515 : StepIns.RequestedHeatingLoad,
1516 : SensibleRoomORZone,
1517 : StepIns.ZoneDehumidificationLoad,
1518 : StepIns.ZoneMoistureLoad,
1519 : latentRoomORZone); //
1520 :
1521 161 : Real64 RunFractionTotalFuel =
1522 161 : thisSetting.ElectricalPower * PartRuntimeFraction; // fraction can be above 1 meaning its not able to do it completely in a time step.
1523 161 : thisSetting.Runtime_Fraction = PartRuntimeFraction;
1524 :
1525 161 : if (Conditioning_load_met && Humidification_load_met) {
1526 : // store best performing mode
1527 100 : if (RunFractionTotalFuel < OptimalSetting_RunFractionTotalFuel) {
1528 8 : OptimalSetting_RunFractionTotalFuel = RunFractionTotalFuel;
1529 8 : OptimalSetting = thisSetting;
1530 8 : DidWeMeetLoad = true;
1531 8 : DidWeMeetHumidificaiton = true;
1532 : }
1533 : } else {
1534 61 : if (!DidWeMeetLoad && !DidWeMeetHumidificaiton) {
1535 39 : bool store_best_attempt = false;
1536 :
1537 39 : if (Conditioning_load_met) {
1538 0 : DidWeMeetLoad = true;
1539 0 : if (HumidificationRequested && (latentRoomORZone < PreviousMaxiumHumidOrDehumidOutput)) {
1540 0 : store_best_attempt = true;
1541 : }
1542 0 : if (DehumidificationRequested && (latentRoomORZone > PreviousMaxiumHumidOrDehumidOutput)) {
1543 0 : store_best_attempt = true;
1544 : }
1545 0 : if (store_best_attempt) {
1546 0 : PreviousMaxiumHumidOrDehumidOutput = latentRoomORZone;
1547 : }
1548 : } else {
1549 39 : if (!DidWeMeetLoad) {
1550 39 : if (CoolingRequested && (SensibleRoomORZone > PreviousMaxiumConditioningOutput)) {
1551 3 : store_best_attempt = true;
1552 : }
1553 39 : if (HeatingRequested && (SensibleRoomORZone < PreviousMaxiumConditioningOutput)) {
1554 0 : store_best_attempt = true;
1555 : }
1556 39 : if (store_best_attempt) {
1557 3 : PreviousMaxiumConditioningOutput = SensibleRoomORZone;
1558 : }
1559 : }
1560 : }
1561 39 : if (store_best_attempt) {
1562 3 : OptimalSetting_RunFractionTotalFuel = RunFractionTotalFuel;
1563 3 : OptimalSetting = thisSetting;
1564 3 : DidWePartlyMeetLoad = true;
1565 : }
1566 : }
1567 : }
1568 : }
1569 :
1570 8 : if (!EnvironmentConditionsMetOnce) {
1571 1 : ErrorCode = 1;
1572 1 : count_EnvironmentConditionsNotMet++;
1573 : }
1574 8 : if (!SAHR_OC_MetOnce) {
1575 1 : count_SAHR_OC_MetOnce++;
1576 1 : ErrorCode = 2;
1577 : }
1578 8 : if (!SAT_OC_MetOnce) {
1579 1 : count_SAT_OC_MetOnce++;
1580 1 : ErrorCode = 3;
1581 : }
1582 : // if we met the load set operating settings to be a combination of the optimal setting at the minimum required runtime fraction
1583 8 : if (DidWeMeetLoad) {
1584 : // add first setting to operating modes
1585 5 : ErrorCode = 0;
1586 : // save the optimal setting in the
1587 5 : CurrentOperatingSettings[0] = OptimalSetting;
1588 5 : PrimaryModeRuntimeFraction = OptimalSetting.Runtime_Fraction;
1589 5 : oStandBy.Runtime_Fraction = (1 - PrimaryModeRuntimeFraction);
1590 5 : if (oStandBy.Runtime_Fraction < 0) {
1591 0 : oStandBy.Runtime_Fraction = 0;
1592 : }
1593 5 : CurrentOperatingSettings[1] = oStandBy;
1594 : } else {
1595 : // if we partly met the load then do the best we can and run full out in that optimal setting.
1596 3 : if (!DidWeMeetLoad && DidWePartlyMeetLoad) {
1597 2 : ErrorCode = 0;
1598 2 : count_DidWeNotMeetLoad++;
1599 2 : if (OptimalSetting.ElectricalPower == IMPLAUSIBLE_POWER) {
1600 0 : ShowWarningError(state, "Model was not able to provide cooling for a time step, called in HybridEvapCooling:dostep");
1601 0 : OptimalSetting.ElectricalPower = 0;
1602 : }
1603 2 : OptimalSetting.Runtime_Fraction = 1;
1604 2 : CurrentOperatingSettings[0] = OptimalSetting;
1605 2 : PrimaryMode = OptimalSetting.Mode;
1606 2 : PrimaryModeRuntimeFraction = 1;
1607 : }
1608 : // if we didn't even partially meet the load make sure the operational settings are just the standby mode.
1609 : else {
1610 1 : oStandBy.Runtime_Fraction = 1;
1611 1 : CurrentOperatingSettings[0] = oStandBy;
1612 1 : ErrorCode = -1;
1613 1 : StandBy = true;
1614 1 : count_DidWeNotMeetLoad++;
1615 : }
1616 : }
1617 :
1618 : Real64 TimeElapsed =
1619 8 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
1620 :
1621 : // Use the elapsed time to only give a summary of warnings related to the number of Timesteps environmental conditions, or supply air
1622 : // temperature constraints were not met for a given day. ideally there would be a clear flag that indicates "this is the last timestep of the
1623 : // day, so report", but that doesn't seem to exist.
1624 8 : if ((TimeElapsed > 24) && WarnOnceFlag && !state.dataGlobal->WarmupFlag) {
1625 0 : if (count_EnvironmentConditionsNotMet > 0)
1626 0 : ShowWarningError(state,
1627 0 : format("In day {:.1R} was unable to operate for of simulation, {}{:.1R} timesteps because environment conditions "
1628 : "were beyond the allowable operating range for any mode.",
1629 0 : (Real64)state.dataGlobal->DayOfSim,
1630 0 : Name,
1631 0 : (Real64)count_EnvironmentConditionsNotMet));
1632 0 : if (count_SAHR_OC_MetOnce > 0)
1633 0 : ShowWarningError(state,
1634 0 : format("In day {:.1R} of simulation, {} failed to meet supply air humidity ratio for {:.1R} time steps. For these "
1635 : "time steps For these time steps was set to mode 0{}",
1636 0 : (Real64)state.dataGlobal->DayOfSim,
1637 0 : Name,
1638 0 : Real64(count_SAHR_OC_MetOnce),
1639 0 : Name));
1640 0 : if (count_SAT_OC_MetOnce > 0)
1641 0 : ShowWarningError(state,
1642 0 : format("In day {:.1R} of simulation, {} failed to meet supply air temperature constraints for {:.1R} time steps. "
1643 : "For these time steps For these time steps{} was set to mode 0",
1644 0 : (Real64)state.dataGlobal->DayOfSim,
1645 0 : Name,
1646 0 : Real64(count_SAT_OC_MetOnce),
1647 0 : Name));
1648 :
1649 0 : ShowWarningError(state,
1650 0 : format("In day {:.1R} of simulation, {} failed to satisfy sensible load for {:.1R} time steps. For these time steps "
1651 : "settings were selected to provide as much sensible cooling or heating as possible, given other constraints.",
1652 0 : (Real64)state.dataGlobal->DayOfSim,
1653 0 : Name,
1654 0 : (Real64)count_DidWeNotMeetLoad));
1655 :
1656 0 : count_SAT_OC_MetOnce = 0;
1657 0 : count_DidWeNotMeetLoad = 0;
1658 0 : count_SAHR_OC_MetOnce = 0;
1659 0 : count_EnvironmentConditionsMetOnce = 0;
1660 0 : count_EnvironmentConditionsNotMet = 0;
1661 0 : WarnOnceFlag = false;
1662 : }
1663 8 : if (state.dataGlobal->HourOfDay == 1 && !WarnOnceFlag && !state.dataGlobal->WarmupFlag) {
1664 2 : WarnOnceFlag = true;
1665 : }
1666 8 : return ErrorCode;
1667 8 : }
1668 :
1669 7 : int Model::CurrentPrimaryMode()
1670 : {
1671 : // SUBROUTINE INFORMATION:
1672 : // AUTHOR Spencer Maxwell Dutton
1673 : // DATE WRITTEN October 2017
1674 : // MODIFIED
1675 : // RE-ENGINEERED na
1676 :
1677 : // PURPOSE OF THIS SUBROUTINE:
1678 : // returns the primary mode of operation
1679 :
1680 : // METHODOLOGY EMPLOYED:
1681 : //
1682 :
1683 : // REFERENCES:
1684 : // na
1685 :
1686 : // Using/Aliasing
1687 :
1688 7 : if (CurrentOperatingSettings.size() > 0) {
1689 7 : return CurrentOperatingSettings[0].Mode;
1690 : } else
1691 0 : return -1;
1692 : }
1693 7 : Real64 Model::CurrentPrimaryRuntimeFraction()
1694 : {
1695 : // SUBROUTINE INFORMATION:
1696 : // AUTHOR Spencer Maxwell Dutton
1697 : // DATE WRITTEN October 2017
1698 : // MODIFIED
1699 : // RE-ENGINEERED na
1700 :
1701 : // PURPOSE OF THIS SUBROUTINE:
1702 : // returns the runtime fraction of the primary setting.
1703 :
1704 : // METHODOLOGY EMPLOYED:
1705 : //
1706 :
1707 : // REFERENCES:
1708 : // na
1709 :
1710 : // Using/Aliasing
1711 7 : if (CurrentOperatingSettings.size() > 0) {
1712 7 : return CurrentOperatingSettings[0].Runtime_Fraction;
1713 : } else
1714 0 : return -1;
1715 : }
1716 12 : void Model::DetermineCoolingVentilationOrHumidificationNeeds(CStepInputs &StepIns)
1717 : {
1718 : // SUBROUTINE INFORMATION:
1719 : // AUTHOR Spencer Maxwell Dutton
1720 : // DATE WRITTEN October 2017
1721 : // MODIFIED
1722 : // RE-ENGINEERED na
1723 :
1724 : // PURPOSE OF THIS SUBROUTINE:
1725 : // Sets member boolean variables to establish if the Cooling, Heating, ventilation or dehumidification needs are met.
1726 :
1727 : // METHODOLOGY EMPLOYED:
1728 : //
1729 :
1730 : // REFERENCES:
1731 : // na
1732 :
1733 : // Using/Aliasing
1734 12 : CoolingRequested = false;
1735 12 : HeatingRequested = false;
1736 12 : VentilationRequested = false;
1737 12 : DehumidificationRequested = false;
1738 12 : HumidificationRequested = false;
1739 : // establish if conditioning needed
1740 12 : if (StepIns.RequestedCoolingLoad >= MINIMUM_LOAD_TO_ACTIVATE) {
1741 8 : CoolingRequested = true;
1742 8 : StepIns.RequestedHeatingLoad = 0;
1743 : }
1744 12 : if (StepIns.RequestedHeatingLoad <= -MINIMUM_LOAD_TO_ACTIVATE) {
1745 0 : HeatingRequested = true;
1746 0 : StepIns.RequestedCoolingLoad = 0;
1747 : }
1748 : // establish if ventilation needed
1749 12 : if (StepIns.MinimumOA > 0) VentilationRequested = true;
1750 : // Load required to meet dehumidifying setpoint (<0 = a dehumidify load) [kgWater/s]
1751 12 : if (StepIns.ZoneDehumidificationLoad < 0) {
1752 0 : DehumidificationRequested = true;
1753 0 : StepIns.ZoneMoistureLoad = 0;
1754 : }
1755 : // Load required to meet humidifying setpoint (>0 = a humidify load) [kgWater/s]
1756 12 : if (StepIns.ZoneMoistureLoad > 0) {
1757 0 : StepIns.ZoneDehumidificationLoad = 0;
1758 0 : HumidificationRequested = true;
1759 : }
1760 12 : }
1761 :
1762 : // doStep is passed some variables that could have just used the class members, but this adds clarity about whats needed, especially helpful in
1763 : // unit testing
1764 12 : void Model::doStep(EnergyPlusData &state,
1765 : Real64 RequestedCoolingLoad, // in joules, cooling load as negative
1766 : Real64 RequestedHeatingLoad, // in joules, heating load as positive
1767 : Real64 OutputRequiredToHumidify, // Load required to meet humidifying setpoint (>0 = a humidify load) [kgWater/s]
1768 : Real64 OutputRequiredToDehumidify, // Load required to meet dehumidifying setpoint (<0 = a dehumidify load) [kgWater/s]
1769 : Real64 DesignMinVR) // mass flow rate of design ventilation air kg/s
1770 : {
1771 : // SUBROUTINE INFORMATION:
1772 : // AUTHOR Spencer Dutton
1773 : // DATE WRITTEN May 2017
1774 : // MODIFIED na
1775 : // RE-ENGINEERED na
1776 :
1777 : // PURPOSE OF THIS SUBROUTINE:
1778 : // This subroutine Model::doStep, the main calculation steps
1779 : // 1)Collate required inputs into a CStepInputs, this helps with the unit tests
1780 : // so we always know what values need to be set.
1781 : // 2)Calculate W humidity ratios for outdoor air and return air.
1782 : // 3)Sets boolean values for each potential conditioning requirement;
1783 : // CoolingRequested, HeatingRequested, VentilationRequested, DehumidificationRequested, HumidificationRequested
1784 : // 4)Take the first operating mode which is always standby and calculate the use curves to determine performance metrics for
1785 : // the standby mode including energy use and other outputs
1786 : // 5)Test system availability status and go into standby if unit is off or not needed (booleans listed in 3 are all false)
1787 : // 6) Set the operating conditions and respective part load fractions.
1788 : // 7) Set timestep average outlet condition, considering all operating conditions and runtimes.
1789 : // METHODOLOGY EMPLOYED:
1790 : // na
1791 :
1792 : // REFERENCES: OutletVolumetricFlowRate, SupplyVentilationVolume, MinOA_Msa, SupplyVentilationAir
1793 : // na
1794 :
1795 : // set requested loads to output variables
1796 12 : RequestedLoadToHeatingSetpoint = RequestedHeatingLoad;
1797 12 : RequestedLoadToCoolingSetpoint = RequestedCoolingLoad;
1798 12 : Real64 LambdaRa = Psychrometrics::PsyHfgAirFnWTdb(0, InletTemp);
1799 12 : RequestedHumidificationMass = OutputRequiredToHumidify;
1800 12 : RequestedHumidificationLoad = OutputRequiredToHumidify * LambdaRa; // [W];
1801 12 : RequestedHumidificationEnergy = OutputRequiredToHumidify * LambdaRa * state.dataHVACGlobal->TimeStepSysSec; // [j]
1802 :
1803 12 : RequestedDeHumidificationMass = OutputRequiredToDehumidify;
1804 12 : RequestedDeHumidificationLoad = OutputRequiredToDehumidify * LambdaRa; // [W];
1805 12 : RequestedDeHumidificationEnergy = OutputRequiredToDehumidify * LambdaRa * state.dataHVACGlobal->TimeStepSysSec; // [j]
1806 :
1807 12 : MinOA_Msa = DesignMinVR; // as mass flow kg/s
1808 :
1809 : // Collate all the inputs required for calculation into one local data structure CStepInputs, this helps with the unit tests so we always know
1810 : // what values need to be set
1811 12 : CStepInputs StepIns;
1812 12 : StepIns.Tosa = SecInletTemp; // degrees C
1813 12 : StepIns.Tra = InletTemp; // degrees C
1814 12 : StepIns.RHosa = SecInletRH; // RH as 0-1
1815 12 : StepIns.RHra = InletRH;
1816 : // For historical reasons cooling is positive, heating negative throughout the calculation
1817 12 : StepIns.RequestedCoolingLoad = -RequestedCoolingLoad; // Cooling positive now, heating negative
1818 12 : StepIns.RequestedHeatingLoad = -RequestedHeatingLoad; // Cooling positive now, heating negative
1819 :
1820 12 : StepIns.ZoneMoistureLoad = RequestedHumidificationLoad;
1821 12 : StepIns.ZoneDehumidificationLoad = RequestedDeHumidificationLoad;
1822 12 : StepIns.MinimumOA = DesignMinVR;
1823 : // calculate W humidity ratios for outdoor air and return air
1824 12 : Real64 Wosa = PsyWFnTdbRhPb(state, StepIns.Tosa, StepIns.RHosa, state.dataEnvrn->OutBaroPress);
1825 12 : Real64 Wra = PsyWFnTdbRhPb(state, StepIns.Tra, StepIns.RHra, InletPressure);
1826 : // Sets boolean values for each potential conditioning requirement; CoolingRequested, HeatingRequested, VentilationRequested,
1827 : // DehumidificationRequested, HumidificationRequested
1828 12 : DetermineCoolingVentilationOrHumidificationNeeds(StepIns);
1829 : // Take the first operating mode which is always standby and calculate the curve values
1830 : // to determine performance metrics for the standby mode including energy use and other outputs
1831 :
1832 12 : CMode Mode = *(OperatingModes.begin());
1833 12 : if (SetStandByMode(state, Mode, StepIns.Tosa, Wosa, StepIns.Tra, Wra)) {
1834 0 : std::string ObjectID = Name.c_str();
1835 0 : ShowSevereError(state,
1836 0 : format("Standby mode not defined correctly, as the mode is defined there are zero combinations of acceptable outside air "
1837 : "fractions and supply air mass flow rate, called in object {}",
1838 : ObjectID));
1839 0 : }
1840 : // Test system availability status
1841 12 : UnitOn = 1;
1842 12 : bool ForceOff = false;
1843 12 : StandBy = false;
1844 12 : if (availSched->getCurrentVal() <= 0 || availStatus == Avail::Status::ForceOff) {
1845 3 : UnitOn = 0;
1846 3 : ForceOff = true;
1847 : }
1848 :
1849 : // Initialize all settings for all operating modes
1850 12 : int size = CurrentOperatingSettings.size();
1851 12 : CSetting empty_setting;
1852 60 : for (int i = 1; i < size; i++) {
1853 48 : CurrentOperatingSettings[i] = empty_setting;
1854 : }
1855 :
1856 : // Go into standby if unit is off or not needed
1857 12 : if ((!CoolingRequested && !HeatingRequested && !VentilationRequested && !HumidificationRequested && !DehumidificationRequested) || ForceOff) {
1858 4 : StandBy = true;
1859 4 : oStandBy.Runtime_Fraction = 1;
1860 4 : CurrentOperatingSettings[0] = oStandBy;
1861 4 : ErrorCode = 0;
1862 4 : PrimaryMode = 0;
1863 4 : PrimaryModeRuntimeFraction = 0;
1864 : } else {
1865 : // set the operating conditions and respective part load fractions.
1866 8 : ErrorCode = SetOperatingSetting(state, StepIns);
1867 : }
1868 :
1869 12 : Real64 QTotZoneOut = 0;
1870 : // now class members QSensZoneOut = 0;
1871 : // QLatentZoneOut = 0;
1872 :
1873 12 : Real64 QTotSystemOut = 0;
1874 12 : Real64 QSensSystemOut = 0;
1875 12 : Real64 QLatentSystemOut = 0;
1876 : // Even if its off or in standby we still need to continue to calculate standby loads
1877 : // All powers are calculated in Watts and energies in Joules
1878 :
1879 12 : SupplyVentilationVolume = CalculateTimeStepAverage(SYSTEMOUTPUTS::VENTILATION_AIR_V);
1880 12 : if (state.dataEnvrn->StdRhoAir > 1) {
1881 12 : SupplyVentilationAir = SupplyVentilationVolume * state.dataEnvrn->StdRhoAir;
1882 : } else {
1883 0 : SupplyVentilationAir = SupplyVentilationVolume * 1.225;
1884 : }
1885 : // set timestep average outlet condition, considering all operating conditions and runtimes.
1886 12 : OutletTemp = CheckVal_T(state, CalculateTimeStepAverage(SYSTEMOUTPUTS::SUPPLY_AIR_TEMP));
1887 12 : OutletHumRat = CheckVal_W(state, CalculateTimeStepAverage(SYSTEMOUTPUTS::SUPPLY_AIR_HR), OutletTemp, OutletPressure);
1888 :
1889 12 : OutletRH = PsyRhFnTdbWPb(state, OutletTemp, OutletHumRat, OutletPressure);
1890 12 : Real64 OperatingAverageMixedAirTemperature = CalculateTimeStepAverage(SYSTEMOUTPUTS::MIXED_AIR_TEMP);
1891 12 : Real64 OperatingMixedAirW = CalculateTimeStepAverage(SYSTEMOUTPUTS::MIXED_AIR_HR);
1892 12 : Real64 MixedAirEnthalpy = PsyHFnTdbW(OperatingAverageMixedAirTemperature, OperatingMixedAirW);
1893 12 : OutletEnthalpy = PsyHFnTdbRhPb(state, OutletTemp, OutletRH, InletPressure);
1894 12 : OutletMassFlowRate = CalculateTimeStepAverage(SYSTEMOUTPUTS::SUPPLY_MASS_FLOW);
1895 :
1896 12 : if (state.dataEnvrn->StdRhoAir > 1) {
1897 12 : OutletVolumetricFlowRate = OutletMassFlowRate / state.dataEnvrn->StdRhoAir;
1898 : } else {
1899 0 : OutletVolumetricFlowRate = OutletMassFlowRate / 1.225;
1900 : }
1901 :
1902 12 : if (!StandBy) {
1903 7 : if (OutletMassFlowRate > 0) {
1904 7 : averageOSAF = SupplyVentilationAir / OutletMassFlowRate;
1905 : } else {
1906 0 : std::string ObjectID = Name.c_str();
1907 0 : if (CoolingRequested || HeatingRequested) {
1908 0 : ShowSevereError(
1909 : state,
1910 0 : format("Outlet air mass flow rate of zero during period with conditioning need, check mode definition. Called in object {}",
1911 0 : Name));
1912 : }
1913 0 : averageOSAF = 1;
1914 0 : }
1915 : // Calculate timestep average unit and system
1916 7 : PrimaryMode = CurrentPrimaryMode();
1917 7 : PrimaryModeRuntimeFraction = CurrentPrimaryRuntimeFraction();
1918 7 : Real64 Outletcp = PsyCpAirFnW(OutletHumRat); // J/degreesK.kg
1919 7 : Real64 Returncp = PsyCpAirFnW(Wra); // J/degreesK.kg
1920 7 : Real64 Outdoorcp = PsyCpAirFnW(Wosa); // J/degreesK.kg
1921 : // Zone Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA+cpSA) {kJ/kg-C} * (T_RA - T_SA) {C}
1922 : // Zone Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA - HR_SA) {kgWater/kgDryAir}
1923 : // Zone Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA - h_SA) {kJ/kgDryAir}
1924 7 : QSensZoneOut = OutletMassFlowRate * 0.5 * (Returncp + Outletcp) * (StepIns.Tra - OutletTemp); // Watts
1925 7 : Real64 OutletMassFlowRateDry = OutletMassFlowRate * (1 - Wsa);
1926 7 : Real64 LambdaSa = Psychrometrics::PsyHfgAirFnWTdb(0, OutletTemp);
1927 7 : QLatentZoneOutMass = OutletMassFlowRateDry * (InletHumRat - OutletHumRat); // Watts
1928 7 : QLatentZoneOut = QLatentZoneOutMass * LambdaSa;
1929 7 : QTotZoneOut = OutletMassFlowRateDry * (InletEnthalpy - OutletEnthalpy); // Watts
1930 7 : Real64 QLatentCheck = QTotZoneOut - QSensZoneOut; // Watts
1931 :
1932 : // System Sensible Cooling{ W } = m'SA {kg/s} * 0.5*(cpRA + OSAF*(cpOSA-cpRA) + cpSA) {kJ/kg-C} * (T_RA + OSAF*(T_OSA - T_RA) - T_SA)
1933 : // System Latent Cooling{ W } = m'SAdryair {kg/s} * L {kJ/kgWater} * (HR_RA + OSAF *(HR_OSA - HR_RA) - HR_SA) {kgWater/kgDryAir}
1934 : // System Total Cooling{ W } = m'SAdryair {kg/s} * (h_RA + OSAF*(h_OSA - h_RA) - h_SA) {kJ/kgDryAir}
1935 :
1936 7 : Real64 SystemTimeStepCp = Returncp + averageOSAF * (Outdoorcp - Returncp) + Outletcp; // cpRA + OSAF*(cpOSA-cpRA) + cpSA //J/degreesK.kg
1937 7 : Real64 SystemTimeStepW = InletHumRat + averageOSAF * (Wosa - Wra) - OutletHumRat; // HR_RA + OSAF *(HR_OSA - HR_RA) - HR_SA
1938 7 : Real64 SystemTimeStepT = StepIns.Tra + averageOSAF * (StepIns.Tosa - StepIns.Tra) - OutletTemp; // T_RA + OSAF *(T_OSA - T_RA) - T_SA
1939 7 : QSensSystemOut = 0.5 * SystemTimeStepCp * OutletMassFlowRate * SystemTimeStepT; // Watts
1940 :
1941 7 : QLatentSystemOut = LambdaSa * OutletMassFlowRateDry * SystemTimeStepW; // Watts
1942 7 : QTotSystemOut = OutletMassFlowRateDry * (MixedAirEnthalpy - OutletEnthalpy); // Watts
1943 7 : QLatentCheck = QTotSystemOut - QSensSystemOut; // Watts
1944 :
1945 : // reset outputs
1946 7 : ResetOutputs();
1947 : // set UNIT outputs for cooling and heating
1948 7 : if (QTotZoneOut > 0) // zone cooling is positive, else remain zero
1949 : {
1950 4 : UnitTotalCoolingRate = std::abs(QTotZoneOut); // Watts
1951 4 : UnitTotalCoolingEnergy = UnitTotalCoolingRate * state.dataHVACGlobal->TimeStepSysSec; // J
1952 : } else {
1953 3 : UnitTotalHeatingRate = std::abs(QTotZoneOut); // Watts
1954 3 : UnitTotalHeatingEnergy = UnitTotalHeatingRate * state.dataHVACGlobal->TimeStepSysSec; // J
1955 : }
1956 :
1957 7 : if (QSensZoneOut > 0) // zone cooling is positive, else remain zero
1958 : {
1959 4 : UnitSensibleCoolingRate = std::abs(QSensZoneOut); // Watts
1960 4 : UnitSensibleCoolingEnergy = UnitSensibleCoolingRate * state.dataHVACGlobal->TimeStepSysSec; // J
1961 : } else {
1962 3 : UnitSensibleHeatingRate = std::abs(QSensZoneOut); // Watts
1963 3 : UnitSensibleHeatingEnergy = UnitSensibleHeatingRate * state.dataHVACGlobal->TimeStepSysSec; // J
1964 : }
1965 :
1966 7 : if ((UnitTotalCoolingRate - UnitSensibleCoolingRate) > 0) {
1967 3 : UnitLatentCoolingRate = UnitTotalCoolingRate - UnitSensibleCoolingRate; // Watts
1968 3 : UnitLatentCoolingEnergy = UnitTotalCoolingEnergy - UnitSensibleCoolingEnergy; // J
1969 : }
1970 7 : if ((UnitTotalCoolingRate - UnitSensibleCoolingRate) < 0) {
1971 1 : UnitLatentHeatingRate = UnitTotalHeatingRate - UnitSensibleHeatingRate; // Watts
1972 1 : UnitLatentHeatingEnergy = UnitTotalHeatingEnergy - UnitSensibleHeatingEnergy; // J
1973 : }
1974 :
1975 : // set SYSTEM outputs
1976 7 : if (QTotSystemOut > 0) // system cooling
1977 : {
1978 6 : SystemTotalCoolingRate = std::abs(QTotSystemOut);
1979 6 : SystemTotalCoolingEnergy = SystemTotalCoolingRate * state.dataHVACGlobal->TimeStepSysSec;
1980 : } else {
1981 1 : SystemTotalHeatingRate = std::abs(QTotSystemOut);
1982 1 : SystemTotalHeatingEnergy = SystemTotalHeatingRate * state.dataHVACGlobal->TimeStepSysSec;
1983 : }
1984 :
1985 7 : if (QSensSystemOut > 0) // system sensible cooling
1986 : {
1987 4 : SystemSensibleCoolingRate = std::abs(QSensSystemOut);
1988 4 : SystemSensibleCoolingEnergy = SystemSensibleCoolingRate * state.dataHVACGlobal->TimeStepSysSec;
1989 : } else {
1990 3 : SystemSensibleHeatingRate = std::abs(QSensSystemOut);
1991 3 : SystemSensibleHeatingEnergy = SystemSensibleHeatingRate * state.dataHVACGlobal->TimeStepSysSec;
1992 : }
1993 7 : if ((SystemTotalCoolingRate - SystemSensibleCoolingRate) > 0) {
1994 5 : SystemLatentCoolingRate = SystemTotalCoolingRate - SystemSensibleCoolingRate;
1995 5 : SystemLatentCoolingEnergy = SystemTotalCoolingEnergy - SystemSensibleCoolingEnergy;
1996 : }
1997 7 : if ((SystemTotalHeatingRate - SystemSensibleHeatingRate) < 0) {
1998 2 : SystemLatentHeatingRate = SystemTotalHeatingRate - SystemSensibleHeatingRate;
1999 2 : SystemLatentHeatingEnergy = SystemTotalHeatingEnergy - SystemSensibleHeatingEnergy;
2000 : }
2001 : } else // unit is in standby so reset conditioning outputs
2002 : {
2003 5 : QTotZoneOut = 0;
2004 5 : QSensZoneOut = 0;
2005 5 : QLatentZoneOut = 0;
2006 5 : QLatentZoneOutMass = 0;
2007 5 : QTotSystemOut = 0;
2008 5 : QSensSystemOut = 0;
2009 5 : QLatentSystemOut = 0;
2010 : // reset outputs
2011 5 : ResetOutputs();
2012 : }
2013 :
2014 : // set timestep outputs calculated considering different runtime fractions.
2015 12 : SupplyFanElectricPower = CalculateTimeStepAverage(SYSTEMOUTPUTS::OSUPPLY_FAN_POWER); // Watts
2016 12 : SupplyFanElectricEnergy = SupplyFanElectricPower * state.dataHVACGlobal->TimeStepSysSec;
2017 12 : SecondaryFuelConsumptionRate = CalculateTimeStepAverage(SYSTEMOUTPUTS::OSECOND_FUEL_USE);
2018 12 : SecondaryFuelConsumption = SecondaryFuelConsumptionRate * state.dataHVACGlobal->TimeStepSysSec;
2019 12 : ThirdFuelConsumptionRate = CalculateTimeStepAverage(SYSTEMOUTPUTS::OTHIRD_FUEL_USE);
2020 12 : ThirdFuelConsumption = ThirdFuelConsumptionRate * state.dataHVACGlobal->TimeStepSysSec;
2021 12 : WaterConsumptionRate = CalculateTimeStepAverage(SYSTEMOUTPUTS::OWATER_USE);
2022 12 : WaterConsumption = WaterConsumptionRate * state.dataHVACGlobal->TimeStepSysSec;
2023 12 : ExternalStaticPressure = CalculateTimeStepAverage(SYSTEMOUTPUTS::OEXTERNAL_STATIC_PRESSURE);
2024 :
2025 12 : FinalElectricalPower = CalculateTimeStepAverage(SYSTEMOUTPUTS::SYSTEM_FUEL_USE);
2026 12 : FinalElectricalEnergy = FinalElectricalPower * state.dataHVACGlobal->TimeStepSysSec;
2027 12 : }
2028 :
2029 : } // namespace HybridEvapCoolingModel
2030 : } // namespace EnergyPlus
|