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