Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, 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 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/CurveManager.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataAirLoop.hh>
59 : #include <EnergyPlus/DataAirSystems.hh>
60 : #include <EnergyPlus/DataConvergParams.hh>
61 : #include <EnergyPlus/DataEnvironment.hh>
62 : #include <EnergyPlus/DataHVACGlobals.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataLoopNode.hh>
66 : #include <EnergyPlus/DataPrecisionGlobals.hh>
67 : #include <EnergyPlus/DataZoneControls.hh>
68 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
69 : #include <EnergyPlus/DataZoneEquipment.hh>
70 : #include <EnergyPlus/EMSManager.hh>
71 : #include <EnergyPlus/FluidProperties.hh>
72 : #include <EnergyPlus/General.hh>
73 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
74 : #include <EnergyPlus/NodeInputManager.hh>
75 : #include <EnergyPlus/OutAirNodeManager.hh>
76 : #include <EnergyPlus/OutputProcessor.hh>
77 : #include <EnergyPlus/Plant/DataPlant.hh>
78 : #include <EnergyPlus/PlantUtilities.hh>
79 : #include <EnergyPlus/Psychrometrics.hh>
80 : #include <EnergyPlus/ScheduleManager.hh>
81 : #include <EnergyPlus/SetPointManager.hh>
82 : #include <EnergyPlus/UtilityRoutines.hh>
83 :
84 : namespace EnergyPlus::SetPointManager {
85 :
86 : // Module containing the SetPoint Manager routines
87 :
88 : // MODULE INFORMATION:
89 : // AUTHOR Fred Buhl
90 : // DATE WRITTEN July 1998
91 : // MODIFIED Shirey/Raustad (FSEC), Jan 2004
92 : // Nov 2004 - Jan 2005 M. J. Witte, GARD Analytics, Inc.
93 : // Add new setpoint managers:
94 : // SET POINT MANAGER:SINGLE ZONE HEATING and
95 : // SET POINT MANAGER:SINGLE ZONE COOLING
96 : // SET POINT MANAGER:OUTSIDE AIR PRETREAT
97 : // Work supported by ASHRAE research project 1254-RP
98 : // Phil Haves Oct 2004
99 : // B. Griffith Aug. 2006.
100 : // R. Raustad - FSEC: added AllSetPtMgr used for node conflict checks
101 : // July 2010 B.A. Nigusse, FSEC/UCF
102 : // Added new setpoint managers:
103 : // SetpointManager:MultiZone:Heating:Average
104 : // SetpointManager:MultiZone:Cooling:Average
105 : // SetpointManager:MultiZone:MinimumHumidity:Average
106 : // SetpointManager:MultiZone:MaximumHumidity:Average
107 : // 22Aug2010 Craig Wray - added Fan:ComponentModel
108 : // Aug 2010 B.A. Nigusse, FSEC/UCF
109 : // Added new setpoint managers:
110 : // SetpointManager:MultiZone:Humidity:Minimum
111 : // SetpointManager:MultiZone:Humidity:Maximum
112 : // July 2011 Chandan Sharma, FSEC/UCF
113 : // Added new setpoint managers:
114 : // SetpointManager:FollowOutdoorAirTemperature
115 : // SetpointManager:FollowSystemNodeTemperature
116 : // SetpointManager:FollowGroundTemperature
117 : // March 2012, Atefe Makhmalbaf and Heejin Cho, PNNL
118 : // Added new setpoint manager:
119 : // SetpointManager:CondenserEnteringReset
120 : // Jan 2022 Wooyoung Jung, Jeremy Lerond and Jian Zhang, PNNL
121 : // Added new setpoint managers:
122 : // SetpointManager:SystemNodeReset:Temperature
123 : // SetpointManager:SystemNodeReset:Humidity
124 :
125 : // PURPOSE OF THIS MODULE:
126 : // To encapsulate the data and algorithms required to
127 : // determine all the controller setpoints in the problem.
128 :
129 : // METHODOLOGY EMPLOYED:
130 : // Previous time step node data will be used, in a set of fixed, precoded algorithms,
131 : // to determine the current time step's controller setpoints.
132 :
133 : using namespace DataLoopNode;
134 : using namespace DataAirLoop;
135 : using namespace ScheduleManager;
136 : using namespace Curve;
137 : using Psychrometrics::PsyCpAirFnW;
138 : using Psychrometrics::PsyHFnTdbW;
139 :
140 : constexpr std::array<std::string_view, (int)HVAC::CtrlVarType::Num> ctrlVarTypeNames = {"Temperature",
141 : "MaximumTemperature",
142 : "MinimumTemperature",
143 : "HumidityRatio",
144 : "MaximumHumidityRatio",
145 : "MinimumHumidityRatio",
146 : "MassFlowRate",
147 : "MaximumMassFlowRate",
148 : "MinimumMassFlowRate"};
149 :
150 : constexpr std::array<std::string_view, (int)HVAC::CtrlVarType::Num> ctrlVarTypeNamesUC = {"TEMPERATURE",
151 : "MAXIMUMTEMPERATURE",
152 : "MINIMUMTEMPERATURE",
153 : "HUMIDITYRATIO",
154 : "MAXIMUMHUMIDITYRATIO",
155 : "MINIMUMHUMIDITYRATIO",
156 : "MASSFLOWRATE",
157 : "MAXIMUMMASSFLOWRATE",
158 : "MINIMUMMASSFLOWRATE"};
159 :
160 : constexpr std::array<std::string_view, (int)ControlStrategy::Num> strategyNamesUC = {
161 : "TEMPERATUREFIRST",
162 : "FLOWFIRST",
163 : };
164 :
165 : constexpr std::array<std::string_view, (int)SPMType::Num> spmTypeNames = {"SetpointManager:Scheduled",
166 : "SetpointManager:Scheduled:DualSetpoint",
167 : "SetpointManager:OutdoorAirReset",
168 : "SetpointManager:SingleZone:Reheat",
169 : "SetpointManager:SingleZone:Heating",
170 : "SetpointManager:SingleZone:Cooling",
171 : "SetpointManager:SingleZone:Humidity:Minimum",
172 : "SetpointManager:SingleZone:Humidity:Maximum",
173 : "SetpointManager:MixedAir",
174 : "SetpointManager:OutdoorAirPretreat",
175 : "SetpointManager:Warmest",
176 : "SetpointManager:Coldest",
177 : "SetpointManager:WarmestTemperatureFlow",
178 : "SetpointManager:ReturnAirBypassFlow",
179 : "SetpointManager:MultiZone:Cooling:Average",
180 : "SetpointManager:MultiZone:Heating:Average",
181 : "SetpointManager:MultiZone:MinimumHumidity:Average",
182 : "SetpointManager:MultiZone:MaximumHumidity:Average",
183 : "SetpointManager:MultiZone:Humidity:Minimum",
184 : "SetpointManager:MultiZone:Humidity:Maximum",
185 : "SetpointManager:FollowOutdoorAirTemperature",
186 : "SetpointManager:FollowSystemNodeTemperature",
187 : "SetpointManager:FollowGroundTemperature",
188 : "SetpointManager:CondenserEnteringReset",
189 : "SetpointManager:CondenserEnteringReset:Ideal",
190 : "SetpointManager:SingleZone:OneStageCooling",
191 : "SetpointManager:SingleZone:OneStageHeating",
192 : "SetpointManager:ReturnTemperature:ChilledWater",
193 : "SetpointManager:ReturnTemperature:HotWater",
194 : "SetpointManager:ScheduledTES",
195 : "SetpointManager:SystemNodeReset:Temperature",
196 : "SetpointManager:SystemNodeReset:Humidity"};
197 :
198 : constexpr std::array<DataLoopNode::ConnectionObjectType, (int)SPMType::Num> spmNodeObjectTypes = {
199 : DataLoopNode::ConnectionObjectType::SetpointManagerScheduled,
200 : DataLoopNode::ConnectionObjectType::SetpointManagerScheduledDualSetpoint,
201 : DataLoopNode::ConnectionObjectType::SetpointManagerOutdoorAirReset,
202 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneReheat,
203 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneHeating,
204 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneCooling,
205 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneHumidityMinimum,
206 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneHumidityMaximum,
207 : DataLoopNode::ConnectionObjectType::SetpointManagerMixedAir,
208 : DataLoopNode::ConnectionObjectType::SetpointManagerOutdoorAirPretreat,
209 : DataLoopNode::ConnectionObjectType::SetpointManagerWarmest,
210 : DataLoopNode::ConnectionObjectType::SetpointManagerColdest,
211 : DataLoopNode::ConnectionObjectType::SetpointManagerWarmestTemperatureFlow,
212 : DataLoopNode::ConnectionObjectType::Invalid, // SPMType::ReturnAirBypass
213 : DataLoopNode::ConnectionObjectType::SetpointManagerMultiZoneCoolingAverage,
214 : DataLoopNode::ConnectionObjectType::SetpointManagerMultiZoneHeatingAverage,
215 : DataLoopNode::ConnectionObjectType::SetpointManagerMultiZoneMinimumHumidityAverage,
216 : DataLoopNode::ConnectionObjectType::SetpointManagerMultiZoneMaximumHumidityAverage,
217 : DataLoopNode::ConnectionObjectType::SetpointManagerMultiZoneHumidityMinimum,
218 : DataLoopNode::ConnectionObjectType::SetpointManagerMultiZoneHumidityMaximum,
219 : DataLoopNode::ConnectionObjectType::SetpointManagerFollowOutdoorAirTemperature,
220 : DataLoopNode::ConnectionObjectType::SetpointManagerFollowSystemNodeTemperature,
221 : DataLoopNode::ConnectionObjectType::SetpointManagerFollowGroundTemperature,
222 : DataLoopNode::ConnectionObjectType::SetpointManagerCondenserEnteringReset,
223 : DataLoopNode::ConnectionObjectType::SetpointManagerCondenserEnteringResetIdeal,
224 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneOneStageCooling,
225 : DataLoopNode::ConnectionObjectType::SetpointManagerSingleZoneOneStageHeating,
226 : DataLoopNode::ConnectionObjectType::SetpointManagerReturnTemperatureChilledWater,
227 : DataLoopNode::ConnectionObjectType::SetpointManagerReturnTemperatureHotWater,
228 : DataLoopNode::ConnectionObjectType::Invalid, // SPMType::TESScheduled
229 : DataLoopNode::ConnectionObjectType::SetpointManagerSystemNodeResetTemperature,
230 : DataLoopNode::ConnectionObjectType::SetpointManagerSystemNodeResetHumidity};
231 :
232 : constexpr std::array<std::string_view, (int)SupplyFlowTempStrategy::Num> supplyFlowTempStrategyNamesUC = {"MAXIMUMTEMPERATURE", "MINIMUMTEMPERATURE"};
233 :
234 : // Why?
235 : constexpr std::array<std::string_view, (int)AirTempType::Num> oaTempTypeNamesUC = {"OUTDOORAIRWETBULB", "OUTDOORAIRDRYBULB"};
236 :
237 : // No really, why?
238 : constexpr std::array<std::string_view, (int)AirTempType::Num> nodeTempTypeNamesUC = {"NODEWETBULB", "NODEDRYBULB"};
239 :
240 : constexpr std::array<std::string_view, (int)DataEnvironment::GroundTempType::Num> groundTempObjectTypeNamesUC = {
241 : "SITE:GROUNDTEMPERATURE:BUILDINGSURFACE",
242 : "SITE:GROUNDTEMPERATURE:SHALLOW",
243 : "SITE:GROUNDTEMPERATURE:DEEP",
244 : "SITE:GROUNDTEMPERATURE:FCFACTORMETHOD"};
245 :
246 : constexpr std::array<std::string_view, (int)ReturnTempType::Num> returnTempTypeNamesUC = {"SCHEDULED", "CONSTANT", "RETURNTEMPERATURESETPOINT"};
247 :
248 2839125 : void ManageSetPoints(EnergyPlusData &state)
249 : {
250 : // SUBROUTINE INFORMATION:
251 : // AUTHOR Russ Taylor, Rick Strand
252 : // DATE WRITTEN May 1998
253 : // MODIFIED Fred Buhl May 2000
254 :
255 : // PURPOSE OF THIS SUBROUTINE:
256 :
257 : // METHODOLOGY EMPLOYED:
258 : // Each flag is checked and the appropriate manager is then called.
259 :
260 : // First time ManageSetPoints is called, get the input for all the setpoint managers
261 2839125 : if (state.dataSetPointManager->GetInputFlag) {
262 792 : GetSetPointManagerInputs(state);
263 792 : state.dataSetPointManager->GetInputFlag = false;
264 : }
265 :
266 2839125 : InitSetPointManagers(state);
267 :
268 2839125 : if (state.dataSetPointManager->ManagerOn) {
269 2832891 : SimSetPointManagers(state);
270 2832891 : UpdateSetPointManagers(state);
271 : // The Mixed Air Setpoint Managers (since they depend on other setpoints, they must be calculated
272 : // and updated next to last).
273 20747344 : for (auto *spm : state.dataSetPointManager->spms) {
274 17914453 : if (spm->type == SPMType::MixedAir) spm->calculate(state);
275 : }
276 2832891 : UpdateMixedAirSetPoints(state);
277 : // The Outside Air Pretreat Setpoint Managers (since they depend on other setpoints, they must be calculated
278 : // and updated last).
279 20747344 : for (auto *spm : state.dataSetPointManager->spms) {
280 17914453 : if (spm->type == SPMType::OutsideAirPretreat) spm->calculate(state);
281 : }
282 :
283 2832891 : UpdateOAPretreatSetPoints(state);
284 : }
285 2839125 : } // ManageSetPoints()
286 :
287 0 : int GetSetPointManagerIndex(EnergyPlusData &state, std::string const &Name)
288 : {
289 0 : auto found = state.dataSetPointManager->spmMap.find(Name);
290 0 : return (found != state.dataSetPointManager->spmMap.end()) ? found->second : 0;
291 0 : } // GetSetPointManagerIndex()
292 :
293 796 : void GetSetPointManagerInputs(EnergyPlusData &state)
294 : {
295 : // wrapper for GetInput to allow unit testing when fatal inputs are detected
296 796 : constexpr std::string_view routineName = "GetSetPointManagerInputs"; // include trailing blank space
297 :
298 796 : if (state.dataSetPointManager->GetInputFlag) {
299 796 : bool ErrorsFound(false);
300 796 : GetSetPointManagerInputData(state, ErrorsFound);
301 :
302 796 : if (ErrorsFound) {
303 0 : ShowFatalError(state, format("{}: Errors found in input. Program terminates.", routineName));
304 : }
305 796 : state.dataSetPointManager->GetInputFlag = false;
306 : }
307 796 : } // GetSetPointManagerInputs()
308 :
309 796 : void GetSetPointManagerInputData(EnergyPlusData &state, bool &ErrorsFound)
310 : {
311 :
312 : // SUBROUTINE INFORMATION:
313 : // AUTHOR Fred Buhl
314 : // DATE WRITTEN July 1998
315 : // MODIFIED Shirey/Raustad (FSEC), Jan 2004
316 : // Nov 2004 - Jan 2005 M. J. Witte, GARD Analytics, Inc.
317 : // Add new setpoint managers:
318 : // SET POINT MANAGER:SINGLE ZONE HEATING and
319 : // SET POINT MANAGER:SINGLE ZONE COOLING
320 : // SET POINT MANAGER:OUTSIDE AIR PRETREAT
321 : // Work supported by ASHRAE research project 1254-RP
322 : // Haves October 2004
323 : // Witte (GARD), Sep 2006
324 : // July 2010 B.A. Nigusse, FSEC/UCF
325 : // Added new setpoint managers:
326 : // SetpointManager:MultiZone:Heating:Average
327 : // SetpointManager:MultiZone:Cooling:Average
328 : // SetpointManager:MultiZone:MinimumHumidity:Average
329 : // SetpointManager:MultiZone:MaximumHumidity:Average
330 : // Aug 2010 B.A. Nigusse, FSEC/UCF
331 : // Added new setpoint managers:
332 : // SetpointManager:MultiZone:Humidity:Minimum
333 : // SetpointManager:MultiZone:Humidity:Maximum
334 : // Jan 2022 Wooyoung Jung, Jeremy Lerond, and Jian Zhang, PNNL
335 : // Added new setpoint managers:
336 : // SetpointManager:SystemNodeReset:Temperature
337 : // SetpointManager:SystemNodeReset:Humidity
338 :
339 : // PURPOSE OF THIS SUBROUTINE
340 : // Input the SetPointManager data and store it in the SetPtMgrIn array.
341 : // Examine the Controllers in the input data and determine which ones
342 : // will have their setpoints set by a particular Setpoint Manager.
343 :
344 : // METHODOLOGY EMPLOYED:
345 : // Use the Get routines from the InputProcessor module.
346 :
347 : // Using/Aliasing
348 : using DataZoneEquipment::GetSystemNodeNumberForZone;
349 : using General::FindNumberInList;
350 :
351 : using NodeInputManager::GetNodeNums;
352 : using NodeInputManager::GetOnlySingleNode;
353 : using ScheduleManager::CheckScheduleValueMinMax;
354 : using ScheduleManager::GetScheduleIndex;
355 :
356 : // Locals
357 : // SUBROUTINE PARAMETER DEFINITIONS:
358 : static constexpr std::string_view routineName = "GetSetPointManagerInputs";
359 :
360 796 : std::string cCurrentModuleObject;
361 :
362 : int NumNodes;
363 796 : Array1D_int NodeNums;
364 796 : bool NodeListError(false);
365 : bool ErrInList;
366 :
367 796 : auto &ip = state.dataInputProcessing->inputProcessor;
368 :
369 796 : int NumNums = 0;
370 796 : int NumAlphas = 0;
371 796 : int NumParams = 0;
372 :
373 796 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "NodeList", NumParams, NumAlphas, NumNums);
374 796 : NodeNums.dimension(NumParams, 0);
375 :
376 : // Input the data for each Setpoint Manager
377 :
378 26268 : for (int iSPM = 0; iSPM < (int)SPMType::Num; ++iSPM) {
379 25472 : SPMType type = static_cast<SPMType>(iSPM);
380 25472 : cCurrentModuleObject = spmTypeNames[iSPM];
381 :
382 25472 : auto const instances = ip->epJSON.find(cCurrentModuleObject);
383 25472 : if (instances == ip->epJSON.end()) continue; // No SetPointManagers of this type
384 :
385 1305 : auto const &props = ip->getObjectSchemaProps(state, cCurrentModuleObject);
386 :
387 : #define PRESERVE_IDF_ORDER
388 : #ifdef PRESERVE_IDF_ORDER
389 :
390 : // Try to read these in IDF order (for now, can revert to simpler begin(), ++, end() iterator later)
391 :
392 : // Begin by grabbing the keys and IDF numbers of the objects.
393 : // Have to grab the keys because once you are no longer
394 : // dealing with the map iterator, you lose access to the key,
395 : // which you also need because it is the object name.
396 :
397 1305 : std::vector<int> idfNums;
398 1305 : std::vector<std::string> idfKeys;
399 1305 : int idfFakeNum = 0;
400 5993 : for (auto instance = instances.value().begin(); instance != instances.value().end(); ++instance) {
401 : // If the input comes from epJson then there are no idf_order fields.
402 4688 : if (state.dataGlobal->isEpJSON || !state.dataGlobal->preserveIDFOrder) {
403 13 : idfNums.push_back(++idfFakeNum);
404 : } else {
405 4675 : idfNums.push_back(instance.value().at("idf_order").get<int>());
406 : }
407 4688 : idfKeys.push_back(instance.key());
408 1305 : }
409 :
410 : // Now sort the IDF numbers
411 1305 : std::vector<int> idfSortedNums = idfNums;
412 1305 : std::sort(idfSortedNums.begin(), idfSortedNums.end());
413 :
414 : // Iterate through the sorted IDF numbers
415 5993 : for (int idfSortedNum : idfSortedNums) {
416 : // Find that number's position in the epJSON order
417 4688 : int epJsonNum = std::find(idfNums.begin(), idfNums.end(), idfSortedNum) - idfNums.begin();
418 : // Grab the corresponding name
419 4688 : std::string const &key = idfKeys[epJsonNum];
420 4688 : auto const &fields = instances.value().at(key);
421 :
422 : #else // !PRESERVE_IDF_ORDER
423 :
424 : // epJson order
425 : for (auto instance = instances.value().begin(); instance != instances.value().end(); ++instance) {
426 : std::string const &key = instance.key();
427 : auto const &fields = instance.value();
428 :
429 : #endif // PRESERVE_IDF_ORDER
430 :
431 4688 : ip->markObjectAsUsed(cCurrentModuleObject, key);
432 4688 : std::string name = Util::makeUPPER(key);
433 :
434 4688 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, name};
435 :
436 4688 : if (state.dataSetPointManager->spmMap.find(name) != state.dataSetPointManager->spmMap.end()) {
437 0 : ShowSevereDuplicateName(state, eoh);
438 0 : ErrorsFound = true;
439 : }
440 :
441 4688 : SPMBase *spm = nullptr;
442 :
443 : // Create a SetPointManagerObject of the right child type
444 4688 : switch (type) {
445 1942 : case SPMType::Scheduled: {
446 1942 : spm = new SPMScheduled;
447 1942 : } break;
448 22 : case SPMType::ScheduledDual: {
449 22 : spm = new SPMScheduledDual;
450 22 : } break;
451 101 : case SPMType::OutsideAir: {
452 101 : spm = new SPMOutsideAir;
453 101 : } break;
454 286 : case SPMType::SZReheat: {
455 286 : spm = new SPMSingleZoneReheat;
456 286 : } break;
457 294 : case SPMType::SZHeating:
458 : case SPMType::SZCooling: {
459 294 : spm = new SPMSingleZoneTemp;
460 294 : } break;
461 74 : case SPMType::SZMinHum:
462 : case SPMType::SZMaxHum: {
463 74 : spm = new SPMSingleZoneHum;
464 74 : } break;
465 1661 : case SPMType::MixedAir: {
466 1661 : spm = new SPMMixedAir;
467 1661 : } break;
468 9 : case SPMType::OutsideAirPretreat: {
469 9 : spm = new SPMOutsideAirPretreat;
470 9 : } break;
471 23 : case SPMType::Warmest:
472 : case SPMType::Coldest: {
473 23 : spm = new SPMTempest;
474 23 : } break;
475 4 : case SPMType::WarmestTempFlow: {
476 4 : spm = new SPMWarmestTempFlow;
477 4 : } break;
478 2 : case SPMType::MZCoolingAverage:
479 : case SPMType::MZHeatingAverage: {
480 2 : spm = new SPMMultiZoneTemp;
481 2 : } break;
482 24 : case SPMType::MZMinHumAverage:
483 : case SPMType::MZMaxHumAverage:
484 : case SPMType::MZMinHum:
485 : case SPMType::MZMaxHum: {
486 24 : spm = new SPMMultiZoneHum;
487 24 : } break;
488 1 : case SPMType::ReturnAirBypass: {
489 1 : spm = new SPMReturnAirBypassFlow;
490 1 : } break;
491 203 : case SPMType::FollowOutsideAirTemp: {
492 203 : spm = new SPMFollowOutsideAirTemp;
493 203 : } break;
494 1 : case SPMType::FollowSystemNodeTemp: {
495 1 : spm = new SPMFollowSysNodeTemp;
496 1 : } break;
497 20 : case SPMType::FollowGroundTemp: {
498 20 : spm = new SPMFollowGroundTemp;
499 20 : } break;
500 3 : case SPMType::CondenserEnteringTemp: {
501 3 : spm = new SPMCondenserEnteringTemp;
502 3 : } break;
503 2 : case SPMType::IdealCondenserEnteringTemp: {
504 2 : spm = new SPMIdealCondenserEnteringTemp;
505 2 : } break;
506 4 : case SPMType::SZOneStageCooling: {
507 4 : spm = new SPMSingleZoneOneStageCooling;
508 4 : } break;
509 4 : case SPMType::SZOneStageHeating: {
510 4 : spm = new SPMSingleZoneOneStageHeating;
511 4 : } break;
512 4 : case SPMType::ChilledWaterReturnTemp:
513 : case SPMType::HotWaterReturnTemp: {
514 4 : spm = new SPMReturnWaterTemp;
515 4 : } break;
516 0 : case SPMType::TESScheduled: {
517 0 : spm = new SPMTESScheduled;
518 0 : } break;
519 4 : case SPMType::SystemNodeTemp:
520 : case SPMType::SystemNodeHum: {
521 4 : spm = new SPMSystemNode;
522 4 : } break;
523 0 : default: {
524 0 : assert(false);
525 : } break;
526 : } // switch (type)
527 :
528 : // Set name and append to array
529 4688 : spm->Name = name;
530 4688 : spm->type = type;
531 4688 : state.dataSetPointManager->spms.push_back(spm);
532 4688 : state.dataSetPointManager->spmMap.insert_or_assign(spm->Name, state.dataSetPointManager->spms.size());
533 :
534 : // control variable type
535 9376 : std::string ctrlVarName;
536 4688 : switch (spm->type) {
537 28 : case SPMType::SZMinHum: {
538 28 : spm->ctrlVar = HVAC::CtrlVarType::MinHumRat;
539 28 : } break;
540 46 : case SPMType::SZMaxHum: {
541 46 : spm->ctrlVar = HVAC::CtrlVarType::MaxHumRat;
542 46 : } break;
543 2 : case SPMType::MZHeatingAverage:
544 : case SPMType::MZCoolingAverage: {
545 2 : spm->ctrlVar = HVAC::CtrlVarType::Temp;
546 2 : } break;
547 9 : case SPMType::MZMinHumAverage:
548 : case SPMType::MZMinHum: {
549 9 : spm->ctrlVar = HVAC::CtrlVarType::MinHumRat;
550 9 : } break;
551 15 : case SPMType::MZMaxHumAverage:
552 : case SPMType::MZMaxHum: {
553 15 : spm->ctrlVar = HVAC::CtrlVarType::MaxHumRat;
554 15 : } break;
555 8 : case SPMType::SZOneStageCooling:
556 : case SPMType::SZOneStageHeating: {
557 8 : spm->ctrlVar = HVAC::CtrlVarType::Temp;
558 8 : } break;
559 4 : case SPMType::ChilledWaterReturnTemp:
560 : case SPMType::HotWaterReturnTemp: {
561 4 : spm->ctrlVar = HVAC::CtrlVarType::Temp;
562 4 : } break;
563 1 : case SPMType::ReturnAirBypass: {
564 1 : spm->ctrlVar = HVAC::CtrlVarType::MassFlowRate;
565 1 : } break;
566 :
567 4575 : default: {
568 4575 : ctrlVarName = ip->getAlphaFieldValue(fields, props, "control_variable");
569 4575 : spm->ctrlVar = static_cast<HVAC::CtrlVarType>(getEnumValue(ctrlVarTypeNamesUC, ctrlVarName));
570 4575 : } break;
571 : } // switch (spm->type)
572 :
573 : // Load Min and Max Temp setpoints for some SPMs
574 4688 : switch (spm->type) {
575 261 : case SPMType::OutsideAirPretreat:
576 : case SPMType::Warmest:
577 : case SPMType::Coldest:
578 : case SPMType::WarmestTempFlow:
579 : case SPMType::MZCoolingAverage:
580 : case SPMType::MZHeatingAverage:
581 : case SPMType::FollowOutsideAirTemp:
582 : case SPMType::FollowGroundTemp: {
583 261 : spm->minSetTemp = ip->getRealFieldValue(fields, props, "minimum_setpoint_temperature");
584 261 : spm->maxSetTemp = ip->getRealFieldValue(fields, props, "maximum_setpoint_temperature");
585 261 : if (spm->maxSetTemp < spm->minSetTemp) {
586 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spm->Name));
587 0 : ShowContinueError(state,
588 0 : format("...maximum_supply_air_temperature=[{:.1R}] is less than minimum_supply_air_temperature=[{:.1R}].",
589 0 : spm->maxSetTemp,
590 0 : spm->minSetTemp));
591 : }
592 261 : } break;
593 :
594 580 : case SPMType::SZReheat:
595 : case SPMType::SZHeating:
596 : case SPMType::SZCooling: {
597 580 : spm->minSetTemp = ip->getRealFieldValue(fields, props, "minimum_supply_air_temperature");
598 580 : spm->maxSetTemp = ip->getRealFieldValue(fields, props, "maximum_supply_air_temperature");
599 580 : if (spm->maxSetTemp < spm->minSetTemp) {
600 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spm->Name));
601 0 : ShowContinueError(state,
602 0 : format("...maximum_supply_air_temperature=[{:.1R}] is less than minimum_supply_air_temperature=[{:.1R}].",
603 0 : spm->maxSetTemp,
604 0 : spm->minSetTemp));
605 : }
606 580 : } break;
607 :
608 1 : case SPMType::FollowSystemNodeTemp: {
609 1 : spm->minSetTemp = ip->getRealFieldValue(fields, props, "minimum_limit_setpoint_temperature");
610 1 : spm->maxSetTemp = ip->getRealFieldValue(fields, props, "maximum_limit_setpoint_temperature");
611 1 : if (spm->maxSetTemp < spm->minSetTemp) {
612 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spm->Name));
613 0 : ShowContinueError(state,
614 0 : format("...maximum_supply_air_temperature=[{:.1R}] is less than minimum_supply_air_temperature=[{:.1R}].",
615 0 : spm->maxSetTemp,
616 0 : spm->minSetTemp));
617 : }
618 1 : } break;
619 :
620 3846 : default:
621 3846 : break;
622 : } // switch (spm->type)
623 :
624 : // Read Min and Max HumRat for some SPMs
625 4688 : switch (spm->type) {
626 :
627 33 : case SPMType::OutsideAirPretreat:
628 : case SPMType::MZMinHumAverage:
629 : case SPMType::MZMaxHumAverage:
630 : case SPMType::MZMinHum:
631 : case SPMType::MZMaxHum: {
632 33 : spm->minSetHum = ip->getRealFieldValue(fields, props, "minimum_setpoint_humidity_ratio");
633 33 : spm->maxSetHum = ip->getRealFieldValue(fields, props, "maximum_setpoint_humidity_ratio");
634 33 : if (spm->maxSetHum < spm->minSetHum) {
635 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spm->Name));
636 0 : ShowContinueError(state,
637 0 : format("...maximum_setpoint_humidity_ratio=[{:.1R}] is less than minimum_setpoint_humidity_ratio=[{:.1R}].",
638 0 : spm->maxSetHum,
639 0 : spm->minSetHum));
640 : }
641 :
642 : // Because a zero humidity ratio setpoint is a special value indicating "off" or "no load"
643 : // must not allow MinSetHumRat or MaxSetHumRat to be <=0.0
644 33 : if (spm->minSetHum <= 0.0) {
645 0 : ShowWarningError(state, format("{}: {}=\"{}\", invalid value.", routineName, cCurrentModuleObject, spm->Name));
646 0 : ShowContinueError(state, "Minimum setpoint humidity ratio <=0.0, resetting to 0.00001");
647 0 : spm->minSetHum = 0.00001;
648 : }
649 33 : if (spm->maxSetHum <= 0.0) {
650 0 : ShowWarningError(state, format("{}: {}=\"{}\", invalid value.", routineName, cCurrentModuleObject, spm->Name));
651 0 : ShowContinueError(state, "Maximum setpoint humidity ratio <=0.0, resetting to 0.00001");
652 0 : spm->maxSetHum = 0.00001;
653 : }
654 :
655 33 : } break;
656 4655 : default:
657 4655 : break;
658 : } // switch (spm->type)
659 :
660 : // Read HVAC Air Loop name
661 4688 : switch (spm->type) {
662 54 : case SPMType::Warmest:
663 : case SPMType::Coldest:
664 : case SPMType::WarmestTempFlow:
665 : case SPMType::ReturnAirBypass:
666 : case SPMType::MZCoolingAverage:
667 : case SPMType::MZHeatingAverage:
668 : case SPMType::MZMinHumAverage:
669 : case SPMType::MZMaxHumAverage:
670 : case SPMType::MZMinHum:
671 : case SPMType::MZMaxHum: {
672 54 : spm->airLoopName = ip->getAlphaFieldValue(fields, props, "hvac_air_loop_name");
673 54 : spm->airLoopNum = 0;
674 54 : } break;
675 :
676 4634 : default:
677 4634 : break;
678 : } // switch (spm->type)
679 :
680 : // Read SPM-specific fields
681 4688 : switch (spm->type) {
682 :
683 : // SetpointManager:Scheduled
684 1942 : case SPMType::Scheduled: {
685 1942 : auto *spmS = dynamic_cast<SPMScheduled *>(spm);
686 1942 : assert(spmS != nullptr);
687 :
688 3884 : std::string schedName = ip->getAlphaFieldValue(fields, props, "schedule_name");
689 1942 : spmS->schedNum = GetScheduleIndex(state, schedName);
690 1942 : if (spmS->schedNum == 0) {
691 0 : ShowSevereItemNotFound(state, eoh, "schedule_name", schedName);
692 0 : ErrorsFound = true;
693 : }
694 1942 : spmS->setPt = 0.0;
695 1942 : } break;
696 :
697 : // SetpointManager:Scheduled:DualSetpoint
698 22 : case SPMType::ScheduledDual: {
699 22 : auto *spmSD = dynamic_cast<SPMScheduledDual *>(spm);
700 22 : assert(spmSD != nullptr);
701 :
702 22 : if (spmSD->ctrlVar != HVAC::CtrlVarType::Temp) {
703 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
704 0 : ErrorsFound = true;
705 : }
706 :
707 44 : std::string schedHiName = ip->getAlphaFieldValue(fields, props, "high_setpoint_schedule_name");
708 22 : spmSD->schedNumHi = GetScheduleIndex(state, schedHiName);
709 22 : if (spmSD->schedNumHi == 0) {
710 0 : ShowSevereItemNotFound(state, eoh, "high_setpoint_schedule_name", schedHiName);
711 0 : ErrorsFound = true;
712 : }
713 :
714 44 : std::string schedLoName = ip->getAlphaFieldValue(fields, props, "low_setpoint_schedule_name");
715 22 : spmSD->schedNumLo = GetScheduleIndex(state, schedLoName);
716 22 : if (spmSD->schedNumLo == 0) {
717 0 : ShowSevereItemNotFound(state, eoh, "low_setpoint_schedule_name", schedLoName);
718 0 : ErrorsFound = true;
719 : }
720 22 : spmSD->setPtHi = 0.0;
721 22 : spmSD->setPtLo = 0.0;
722 :
723 22 : } break;
724 :
725 : // SetpointManager:OutdoorAirReset
726 101 : case SPMType::OutsideAir: {
727 101 : auto *spmOA = dynamic_cast<SPMOutsideAir *>(spm);
728 101 : assert(spmOA != nullptr);
729 101 : if (spmOA->ctrlVar != HVAC::CtrlVarType::Temp && spmOA->ctrlVar != HVAC::CtrlVarType::MaxTemp &&
730 0 : spmOA->ctrlVar != HVAC::CtrlVarType::MinTemp) {
731 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
732 0 : ErrorsFound = true;
733 : }
734 :
735 101 : spmOA->lowSetPt1 = ip->getRealFieldValue(fields, props, "setpoint_at_outdoor_low_temperature");
736 101 : spmOA->low1 = ip->getRealFieldValue(fields, props, "outdoor_low_temperature");
737 101 : spmOA->highSetPt1 = ip->getRealFieldValue(fields, props, "setpoint_at_outdoor_high_temperature");
738 101 : spmOA->high1 = ip->getRealFieldValue(fields, props, "outdoor_high_temperature");
739 :
740 : // Get optional input: schedule and 2nd reset rule
741 101 : if (auto foundSched = fields.find("schedule_name"); foundSched != fields.end()) {
742 0 : std::string schedName = Util::makeUPPER(foundSched.value().get<std::string>());
743 0 : spmOA->schedNum = GetScheduleIndex(state, schedName);
744 0 : if (spmOA->schedNum == 0) {
745 0 : ShowSevereItemNotFound(state, eoh, "schedule_name", schedName);
746 0 : ErrorsFound = true;
747 : }
748 :
749 0 : Real64 minValSched = GetScheduleMinValue(state, spmOA->schedNum);
750 0 : Real64 maxValSched = GetScheduleMaxValue(state, spmOA->schedNum);
751 0 : if ((minValSched < 1.0) || (maxValSched > 2.0)) {
752 0 : ShowSevereError(state, format("{}: {}=\"{}\", invalid field.", routineName, cCurrentModuleObject, name));
753 0 : ShowContinueError(state, "..Schedule Values for the Outdoor Reset Schedule must be either 1 or 2");
754 0 : ShowContinueError(state, format("..Minimum Schedule Value = {} ", minValSched));
755 0 : ShowContinueError(state, format("..Maximum Schedule Value = {} ", minValSched));
756 0 : ShowContinueError(
757 0 : state, format("..Adjust the schedule values so that all of them are either 1 or 2 in schedule = \"{}\"", schedName));
758 0 : ErrorsFound = true;
759 : }
760 :
761 0 : if (auto found = fields.find("setpoint_at_outdoor_low_temperature_2"); found != fields.end())
762 0 : spmOA->lowSetPt2 = found.value().get<Real64>();
763 0 : if (auto found = fields.find("outdoor_low_temperature_2"); found != fields.end()) spmOA->low2 = found.value().get<Real64>();
764 0 : if (auto found = fields.find("setpoint_at_outdoor_high_temperature_2"); found != fields.end())
765 0 : spmOA->highSetPt2 = found.value().get<Real64>();
766 0 : if (auto found = fields.find("outdoor_high_temperature_2"); found != fields.end()) spmOA->high2 = found.value().get<Real64>();
767 0 : if (spmOA->high2 < spmOA->low2) {
768 0 : ShowWarningError(state, format("{}: {}=\"{}\", invalid field.", routineName, cCurrentModuleObject, spmOA->Name));
769 0 : ShowContinueError(state,
770 0 : format("...{}=[{:.1R}] is less than {}=[{:.1R}].",
771 : "outdoor_high_temperature_2",
772 0 : spmOA->high2,
773 : "outdoor_low_temperature_2",
774 0 : spmOA->low2));
775 : }
776 0 : } else { // !foundSched
777 101 : spmOA->schedNum = 0;
778 101 : spmOA->lowSetPt2 = 0.0;
779 101 : spmOA->low2 = 0.0;
780 101 : spmOA->highSetPt2 = 0.0;
781 101 : spmOA->high2 = 0.0;
782 101 : }
783 101 : } break;
784 :
785 : // SetpointManager:SingleZone:Reheat
786 286 : case SPMType::SZReheat: {
787 286 : auto *spmSZR = dynamic_cast<SPMSingleZoneReheat *>(spm);
788 286 : assert(spmSZR != nullptr);
789 :
790 286 : if (spmSZR->ctrlVar != HVAC::CtrlVarType::Temp) {
791 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
792 0 : ErrorsFound = true;
793 : }
794 :
795 572 : std::string ctrlZoneName = ip->getAlphaFieldValue(fields, props, "control_zone_name");
796 : // get the actual zone number of the control zone
797 286 : spmSZR->ctrlZoneNum = Util::FindItemInList(ctrlZoneName, state.dataHeatBal->Zone);
798 286 : if (spmSZR->ctrlZoneNum == 0) {
799 0 : ShowSevereItemNotFound(state, eoh, "control_zone_name", ctrlZoneName);
800 0 : ErrorsFound = true;
801 : }
802 286 : spmSZR->setPt = 0.0;
803 :
804 572 : spmSZR->zoneNodeNum = GetOnlySingleNode(state,
805 572 : ip->getAlphaFieldValue(fields, props, "zone_node_name"),
806 : ErrorsFound,
807 286 : spmNodeObjectTypes[(int)spm->type],
808 286 : spmSZR->Name,
809 : DataLoopNode::NodeFluidType::Air,
810 : DataLoopNode::ConnectionType::Sensor,
811 : NodeInputManager::CompFluidStream::Primary,
812 : ObjectIsNotParent);
813 572 : spmSZR->zoneInletNodeNum = GetOnlySingleNode(state,
814 572 : ip->getAlphaFieldValue(fields, props, "zone_inlet_node_name"),
815 : ErrorsFound,
816 286 : spmNodeObjectTypes[(int)spm->type],
817 286 : spmSZR->Name,
818 : DataLoopNode::NodeFluidType::Air,
819 : DataLoopNode::ConnectionType::Sensor,
820 : NodeInputManager::CompFluidStream::Primary,
821 : ObjectIsNotParent);
822 286 : } break;
823 :
824 : // SetpointManager:SingleZone:Heating
825 : // SetpointManager:SingleZone:Cooling
826 294 : case SPMType::SZHeating:
827 : case SPMType::SZCooling: {
828 294 : auto *spmSZTemp = dynamic_cast<SPMSingleZoneTemp *>(spm);
829 294 : assert(spmSZTemp != nullptr);
830 :
831 294 : if (spmSZTemp->ctrlVar != HVAC::CtrlVarType::Temp) {
832 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
833 0 : ErrorsFound = true;
834 : }
835 :
836 588 : std::string ctrlZoneName = ip->getAlphaFieldValue(fields, props, "control_zone_name");
837 294 : spmSZTemp->ctrlZoneNum = Util::FindItemInList(ctrlZoneName, state.dataHeatBal->Zone);
838 294 : if (spmSZTemp->ctrlZoneNum == 0) {
839 0 : ShowSevereItemNotFound(state, eoh, "control_zone_name", ctrlZoneName);
840 0 : ErrorsFound = true;
841 : }
842 294 : spmSZTemp->setPt = 0.0;
843 :
844 588 : spmSZTemp->zoneNodeNum = GetOnlySingleNode(state,
845 588 : ip->getAlphaFieldValue(fields, props, "zone_node_name"),
846 : ErrorsFound,
847 294 : spmNodeObjectTypes[(int)spm->type],
848 294 : spmSZTemp->Name,
849 : DataLoopNode::NodeFluidType::Air,
850 : DataLoopNode::ConnectionType::Sensor,
851 : NodeInputManager::CompFluidStream::Primary,
852 : ObjectIsNotParent);
853 588 : spmSZTemp->zoneInletNodeNum = GetOnlySingleNode(state,
854 588 : ip->getAlphaFieldValue(fields, props, "zone_inlet_node_name"),
855 : ErrorsFound,
856 294 : spmNodeObjectTypes[(int)spm->type],
857 294 : spmSZTemp->Name,
858 : DataLoopNode::NodeFluidType::Air,
859 : DataLoopNode::ConnectionType::Sensor,
860 : NodeInputManager::CompFluidStream::Primary,
861 : ObjectIsNotParent);
862 :
863 294 : } break;
864 :
865 : // SetpointManager:SingleZone:Humidity:Minimum
866 : // SetpointManager:SingleZone:Humidity:Maximum
867 74 : case SPMType::SZMinHum:
868 : case SPMType::SZMaxHum: {
869 74 : auto *spmSZHum = dynamic_cast<SPMSingleZoneHum *>(spm);
870 74 : assert(spmSZHum != nullptr);
871 :
872 74 : ErrInList = false;
873 148 : std::string ctrlZoneNodeName = ip->getAlphaFieldValue(fields, props, "control_zone_air_node_name");
874 148 : GetNodeNums(state,
875 : ctrlZoneNodeName,
876 : NumNodes,
877 : NodeNums,
878 : ErrInList,
879 : DataLoopNode::NodeFluidType::Air,
880 74 : spmNodeObjectTypes[(int)spm->type],
881 74 : spmSZHum->Name,
882 : DataLoopNode::ConnectionType::Sensor,
883 : NodeInputManager::CompFluidStream::Primary,
884 : ObjectIsNotParent,
885 : false,
886 : "control_zone_air_node_name"); // nodes of zones whose humidity is being controlled
887 :
888 74 : if (ErrInList) {
889 0 : ErrorsFound = true;
890 : }
891 :
892 : // only allow one control zone for now
893 74 : if (NumNodes > 1) {
894 0 : ShowSevereError(state, format("{}: {}=\"{}\", entered nodelist.", routineName, cCurrentModuleObject, spmSZHum->Name));
895 0 : ShowContinueError(state, format("..invalid ctrl_zone_node_name=\"{}\".", ctrlZoneNodeName));
896 0 : ShowContinueError(state, "..only one control zone is allowed.");
897 0 : ErrorsFound = true;
898 : }
899 :
900 74 : spmSZHum->zoneNodeNum = NodeNums(1);
901 74 : spmSZHum->ctrlZoneNum = 0;
902 74 : } break;
903 :
904 : // SetpointManager:MixedAir
905 1661 : case SPMType::MixedAir: {
906 1661 : auto *spmMA = dynamic_cast<SPMMixedAir *>(spm);
907 1661 : assert(spmMA != nullptr);
908 :
909 1661 : if (spmMA->ctrlVar != HVAC::CtrlVarType::Temp) {
910 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
911 0 : ErrorsFound = true;
912 : }
913 :
914 3322 : spmMA->refNodeNum = GetOnlySingleNode(state,
915 3322 : ip->getAlphaFieldValue(fields, props, "reference_setpoint_node_name"),
916 : ErrorsFound,
917 1661 : spmNodeObjectTypes[(int)spm->type],
918 1661 : spmMA->Name,
919 : DataLoopNode::NodeFluidType::Air,
920 : DataLoopNode::ConnectionType::Sensor,
921 : NodeInputManager::CompFluidStream::Primary,
922 : ObjectIsNotParent);
923 3322 : spmMA->fanInNodeNum = GetOnlySingleNode(state,
924 3322 : ip->getAlphaFieldValue(fields, props, "fan_inlet_node_name"),
925 : ErrorsFound,
926 1661 : spmNodeObjectTypes[(int)spm->type],
927 1661 : spmMA->Name,
928 : DataLoopNode::NodeFluidType::Air,
929 : DataLoopNode::ConnectionType::Sensor,
930 : NodeInputManager::CompFluidStream::Primary,
931 : ObjectIsNotParent);
932 3322 : spmMA->fanOutNodeNum = GetOnlySingleNode(state,
933 3322 : ip->getAlphaFieldValue(fields, props, "fan_outlet_node_name"),
934 : ErrorsFound,
935 1661 : spmNodeObjectTypes[(int)spm->type],
936 1661 : spmMA->Name,
937 : DataLoopNode::NodeFluidType::Air,
938 : DataLoopNode::ConnectionType::Sensor,
939 : NodeInputManager::CompFluidStream::Primary,
940 : ObjectIsNotParent);
941 1661 : } break;
942 :
943 : // SetpointManager:OutdoorAirPretreat
944 9 : case SPMType::OutsideAirPretreat: {
945 9 : auto *spmOAP = dynamic_cast<SPMOutsideAirPretreat *>(spm);
946 9 : assert(spmOAP != nullptr);
947 :
948 18 : spmOAP->refNodeNum = GetOnlySingleNode(state,
949 18 : ip->getAlphaFieldValue(fields, props, "reference_setpoint_node_name"),
950 : ErrorsFound,
951 9 : spmNodeObjectTypes[(int)spm->type],
952 9 : spmOAP->Name,
953 : DataLoopNode::NodeFluidType::Air,
954 : DataLoopNode::ConnectionType::Sensor,
955 : NodeInputManager::CompFluidStream::Primary,
956 : ObjectIsNotParent);
957 18 : spmOAP->mixedOutNodeNum = GetOnlySingleNode(state,
958 18 : ip->getAlphaFieldValue(fields, props, "mixed_air_stream_node_name"),
959 : ErrorsFound,
960 9 : spmNodeObjectTypes[(int)spm->type],
961 9 : spmOAP->Name,
962 : DataLoopNode::NodeFluidType::Air,
963 : DataLoopNode::ConnectionType::Sensor,
964 : NodeInputManager::CompFluidStream::Primary,
965 : ObjectIsNotParent);
966 18 : spmOAP->oaInNodeNum = GetOnlySingleNode(state,
967 18 : ip->getAlphaFieldValue(fields, props, "outdoor_air_stream_node_name"),
968 : ErrorsFound,
969 9 : spmNodeObjectTypes[(int)spm->type],
970 9 : spmOAP->Name,
971 : DataLoopNode::NodeFluidType::Air,
972 : DataLoopNode::ConnectionType::Sensor,
973 : NodeInputManager::CompFluidStream::Primary,
974 : ObjectIsNotParent);
975 18 : spmOAP->returnInNodeNum = GetOnlySingleNode(state,
976 18 : ip->getAlphaFieldValue(fields, props, "return_air_stream_node_name"),
977 : ErrorsFound,
978 9 : spmNodeObjectTypes[(int)spm->type],
979 9 : spmOAP->Name,
980 : DataLoopNode::NodeFluidType::Air,
981 : DataLoopNode::ConnectionType::Sensor,
982 : NodeInputManager::CompFluidStream::Primary,
983 : ObjectIsNotParent);
984 :
985 9 : if (std::find(spmOAP->ctrlNodeNums.begin(), spmOAP->ctrlNodeNums.end(), spmOAP->refNodeNum) != spmOAP->ctrlNodeNums.end()) {
986 0 : ShowSevereError(state, format("{}: {}=\"{}\", reference node.", routineName, cCurrentModuleObject, spmOAP->Name));
987 0 : if (spmOAP->ctrlNodeNums.size() > 1) {
988 0 : ShowContinueError(state, "..Reference Node is the same as one of the nodes in SetPoint NodeList");
989 : } else {
990 0 : ShowContinueError(state, "..Reference Node is the same as the SetPoint Node");
991 : }
992 0 : ShowContinueError(state, format("Reference Node Name=\"{}\".", state.dataLoopNodes->NodeID(spmOAP->refNodeNum)));
993 0 : ErrorsFound = true;
994 : }
995 9 : } break;
996 :
997 : // SetpointManager:Warmest
998 : // SetpointManager:Coldest
999 23 : case SPMType::Warmest:
1000 : case SPMType::Coldest: {
1001 23 : auto *spmEst = dynamic_cast<SPMTempest *>(spm);
1002 23 : assert(spmEst != nullptr);
1003 :
1004 23 : if (spmEst->ctrlVar != HVAC::CtrlVarType::Temp) {
1005 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1006 0 : ErrorsFound = true;
1007 : }
1008 :
1009 46 : std::string strategyName = ip->getAlphaFieldValue(fields, props, "strategy");
1010 23 : spmEst->strategy = static_cast<SupplyFlowTempStrategy>(getEnumValue(supplyFlowTempStrategyNamesUC, strategyName));
1011 :
1012 23 : if ((spmEst->type == SPMType::Warmest && spmEst->strategy != SupplyFlowTempStrategy::MaxTemp) ||
1013 23 : (spmEst->type == SPMType::Coldest && spmEst->strategy != SupplyFlowTempStrategy::MinTemp)) {
1014 0 : ShowSevereInvalidKey(state, eoh, "strategy", strategyName);
1015 0 : ErrorsFound = true;
1016 : }
1017 23 : } break;
1018 :
1019 : // SetpointManager:WarmestTemperatureFlow
1020 4 : case SPMType::WarmestTempFlow: {
1021 4 : auto *spmWTF = dynamic_cast<SPMWarmestTempFlow *>(spm);
1022 4 : assert(spmWTF != nullptr);
1023 :
1024 4 : if (spmWTF->ctrlVar != HVAC::CtrlVarType::Temp) {
1025 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1026 0 : ErrorsFound = true;
1027 : }
1028 :
1029 4 : spmWTF->minTurndown = ip->getRealFieldValue(fields, props, "minimum_turndown_ratio");
1030 4 : if (spmWTF->minTurndown >= 0.8) {
1031 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spmWTF->Name));
1032 0 : ShowContinueError(state, format("...minimum_turndown_ratio=[{:.2R}] is greater than 0.8;", spmWTF->minTurndown));
1033 0 : ShowContinueError(state, "...typical values for minimum_turndown_ratio are less than 0.8.");
1034 : }
1035 :
1036 4 : spmWTF->strategy = static_cast<ControlStrategy>(getEnumValue(strategyNamesUC, ip->getAlphaFieldValue(fields, props, "strategy")));
1037 :
1038 4 : SetupOutputVariable(state,
1039 : "Setpoint Manager Warmest Temperature Critical Zone Number",
1040 : Constant::Units::None,
1041 4 : spmWTF->critZoneNum,
1042 : OutputProcessor::TimeStepType::System,
1043 : OutputProcessor::StoreType::Average,
1044 4 : spmWTF->Name);
1045 8 : SetupOutputVariable(state,
1046 : "Setpoint Manager Warmest Temperature Turndown Flow Fraction",
1047 : Constant::Units::None,
1048 4 : spmWTF->turndown,
1049 : OutputProcessor::TimeStepType::System,
1050 : OutputProcessor::StoreType::Average,
1051 4 : spmWTF->Name);
1052 4 : } break;
1053 :
1054 : // SetpointManager:ReturnAirBypassFlow
1055 1 : case SPMType::ReturnAirBypass: {
1056 1 : auto *spmRAB = dynamic_cast<SPMReturnAirBypassFlow *>(spm);
1057 1 : assert(spmRAB != nullptr);
1058 :
1059 2 : std::string schedName = ip->getAlphaFieldValue(fields, props, "temperature_setpoint_schedule_name");
1060 1 : spmRAB->schedNum = GetScheduleIndex(state, schedName);
1061 1 : if (spmRAB->schedNum == 0) {
1062 0 : ShowSevereItemNotFound(state, eoh, "temperature_setpoint_schedule_name", schedName);
1063 0 : ErrorsFound = true;
1064 : }
1065 1 : } break;
1066 :
1067 : // SetpointManager:FollowOutdoorAirTemperature
1068 203 : case SPMType::FollowOutsideAirTemp: {
1069 203 : auto *spmFOAT = dynamic_cast<SPMFollowOutsideAirTemp *>(spm);
1070 203 : assert(spmFOAT != nullptr);
1071 :
1072 203 : if (spmFOAT->ctrlVar != HVAC::CtrlVarType::Temp && spmFOAT->ctrlVar != HVAC::CtrlVarType::MaxTemp &&
1073 0 : spmFOAT->ctrlVar != HVAC::CtrlVarType::MinTemp) {
1074 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1075 0 : ErrorsFound = true;
1076 : }
1077 :
1078 203 : spmFOAT->refTempType =
1079 203 : static_cast<AirTempType>(getEnumValue(oaTempTypeNamesUC, ip->getAlphaFieldValue(fields, props, "reference_temperature_type")));
1080 :
1081 203 : spmFOAT->offset = ip->getRealFieldValue(fields, props, "offset_temperature_difference");
1082 203 : } break;
1083 :
1084 : // SetpointManager:FollowSystemNodeTemperature
1085 1 : case SPMType::FollowSystemNodeTemp: {
1086 1 : auto *spmFNT = dynamic_cast<SPMFollowSysNodeTemp *>(spm);
1087 1 : assert(spmFNT != nullptr);
1088 :
1089 1 : if (spmFNT->ctrlVar != HVAC::CtrlVarType::Temp && spmFNT->ctrlVar != HVAC::CtrlVarType::MaxTemp &&
1090 0 : spmFNT->ctrlVar != HVAC::CtrlVarType::MinTemp) {
1091 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1092 0 : ErrorsFound = true;
1093 : }
1094 :
1095 2 : spmFNT->refNodeNum = GetOnlySingleNode(state,
1096 2 : ip->getAlphaFieldValue(fields, props, "reference_node_name"),
1097 : ErrorsFound,
1098 1 : spmNodeObjectTypes[(int)spm->type],
1099 1 : spmFNT->Name,
1100 : DataLoopNode::NodeFluidType::Blank,
1101 : DataLoopNode::ConnectionType::Sensor,
1102 : NodeInputManager::CompFluidStream::Primary,
1103 : ObjectIsNotParent);
1104 :
1105 1 : spmFNT->refTempType =
1106 1 : static_cast<AirTempType>(getEnumValue(nodeTempTypeNamesUC, ip->getAlphaFieldValue(fields, props, "reference_temperature_type")));
1107 :
1108 1 : spmFNT->offset = ip->getRealFieldValue(fields, props, "offset_temperature_difference");
1109 1 : } break;
1110 :
1111 : // SetpointManager:FollowGroundTemperature
1112 20 : case SPMType::FollowGroundTemp: {
1113 20 : auto *spmFGT = dynamic_cast<SPMFollowGroundTemp *>(spm);
1114 20 : assert(spmFGT != nullptr);
1115 :
1116 20 : if (spmFGT->ctrlVar != HVAC::CtrlVarType::Temp && spmFGT->ctrlVar != HVAC::CtrlVarType::MaxTemp &&
1117 0 : spmFGT->ctrlVar != HVAC::CtrlVarType::MinTemp) {
1118 : // should not come here if idd type choice and key list is working
1119 0 : ShowSevereItemNotFound(state, eoh, "control_variable", ctrlVarName);
1120 0 : ErrorsFound = true;
1121 : }
1122 :
1123 20 : spmFGT->refTempType = static_cast<DataEnvironment::GroundTempType>(
1124 20 : getEnumValue(groundTempObjectTypeNamesUC, ip->getAlphaFieldValue(fields, props, "reference_ground_temperature_object_type")));
1125 :
1126 20 : if (state.dataSetPointManager->NoGroundTempObjWarning[(int)spmFGT->refTempType]) {
1127 20 : if (!state.dataEnvrn->GroundTempInputs[(int)spmFGT->refTempType]) {
1128 0 : ShowWarningError(state,
1129 0 : format("{}: {}=\"{}\" requires \"Site:GroundTemperature:BuildingSurface\" in the input..",
1130 : routineName,
1131 : cCurrentModuleObject,
1132 0 : spmFGT->Name));
1133 0 : ShowContinueError(state,
1134 0 : format("Defaults, constant throughout the year of ({:.1R}) will be used.",
1135 0 : state.dataEnvrn->GroundTemp[(int)spmFGT->refTempType]));
1136 : }
1137 20 : state.dataSetPointManager->NoGroundTempObjWarning[(int)spmFGT->refTempType] = false;
1138 : }
1139 :
1140 20 : spmFGT->offset = ip->getRealFieldValue(fields, props, "offset_temperature_difference");
1141 20 : } break;
1142 :
1143 : // SetpointManager:CondenserEnteringReset
1144 3 : case SPMType::CondenserEnteringTemp: {
1145 3 : auto *spmCET = dynamic_cast<SPMCondenserEnteringTemp *>(spm);
1146 3 : assert(spmCET != nullptr);
1147 :
1148 3 : if (spmCET->ctrlVar != HVAC::CtrlVarType::Temp) {
1149 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1150 0 : ErrorsFound = true;
1151 : }
1152 :
1153 : std::string condenserEnteringTempSchedName =
1154 6 : ip->getAlphaFieldValue(fields, props, "default_condenser_entering_water_temperature_schedule_name");
1155 3 : spmCET->condenserEnteringTempSchedNum = GetScheduleIndex(state, condenserEnteringTempSchedName);
1156 3 : if (spmCET->condenserEnteringTempSchedNum == 0) {
1157 0 : ShowSevereItemNotFound(state, eoh, "default_condenser_entering_water_temperature_schedule_name", condenserEnteringTempSchedName);
1158 0 : ErrorsFound = true;
1159 : }
1160 :
1161 6 : std::string minDesignWetBulbCurveName = ip->getAlphaFieldValue(fields, props, "minimum_design_wetbulb_temperature_curve_name");
1162 3 : spmCET->minTowerDesignWetBulbCurveNum = GetCurveIndex(state, minDesignWetBulbCurveName);
1163 :
1164 6 : std::string minOAWetBulbCurveName = ip->getAlphaFieldValue(fields, props, "minimum_outside_air_wetbulb_temperature_curve_name");
1165 3 : spmCET->minOAWetBulbCurveNum = GetCurveIndex(state, minOAWetBulbCurveName);
1166 :
1167 : std::string optCondenserEnteringTempCurveName =
1168 6 : ip->getAlphaFieldValue(fields, props, "optimized_cond_entering_water_temperature_curve_name");
1169 :
1170 3 : spmCET->optCondenserEnteringTempCurveNum = GetCurveIndex(state, optCondenserEnteringTempCurveName);
1171 3 : spmCET->minLift = ip->getRealFieldValue(fields, props, "minimum_lift");
1172 :
1173 3 : spmCET->maxCondenserEnteringTemp = ip->getRealFieldValue(fields, props, "maximum_condenser_entering_water_temperature");
1174 3 : spmCET->towerDesignInletAirWetBulbTemp = ip->getRealFieldValue(fields, props, "cooling_tower_design_inlet_air_wet_bulb_temperature");
1175 :
1176 3 : if (spmCET->maxCondenserEnteringTemp < spmCET->towerDesignInletAirWetBulbTemp) {
1177 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spmCET->Name));
1178 0 : ShowContinueError(state,
1179 0 : format("...maximum_condenser_entering_water_temperature=[{:.1R}] is less than "
1180 : "cooling_tower_design_inlet_air_wet-bulb_temperature=[{:.1R}].",
1181 0 : spmCET->maxCondenserEnteringTemp,
1182 0 : spmCET->towerDesignInletAirWetBulbTemp));
1183 : }
1184 :
1185 3 : } break;
1186 :
1187 : // SetpointManager:CondenserEnteringReset:Ideal
1188 2 : case SPMType::IdealCondenserEnteringTemp: {
1189 2 : auto *spmIdealCET = dynamic_cast<SPMIdealCondenserEnteringTemp *>(spm);
1190 2 : assert(spmIdealCET != nullptr);
1191 :
1192 2 : if (spmIdealCET->ctrlVar != HVAC::CtrlVarType::Temp) {
1193 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1194 0 : ErrorsFound = true;
1195 : }
1196 :
1197 2 : spmIdealCET->minLift = ip->getRealFieldValue(fields, props, "minimum_lift");
1198 2 : spmIdealCET->maxCondenserEnteringTemp = ip->getRealFieldValue(fields, props, "maximum_condenser_entering_water_temperature");
1199 :
1200 2 : } break;
1201 :
1202 : // SetpointManager:SingleZone:OneStageCooling
1203 4 : case SPMType::SZOneStageCooling: {
1204 4 : auto *spmSZOSC = dynamic_cast<SPMSingleZoneOneStageCooling *>(spm);
1205 4 : assert(spmSZOSC != nullptr);
1206 :
1207 4 : spmSZOSC->coolingOnSetPt = ip->getRealFieldValue(fields, props, "cooling_stage_on_supply_air_setpoint_temperature");
1208 4 : spmSZOSC->coolingOffSetPt = ip->getRealFieldValue(fields, props, "cooling_stage_off_supply_air_setpoint_temperature");
1209 :
1210 4 : if (spmSZOSC->coolingOffSetPt < spmSZOSC->coolingOnSetPt) {
1211 : // throw warning, off must be warmer than on
1212 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spmSZOSC->Name));
1213 0 : ShowContinueError(state,
1214 0 : format("...cooling_stage_off_supply_air_setpoint_temperature=[{:.1R}] is less than "
1215 : "cooling_stage_on_supply_air_setpoint_temperature=[{:.1R}].",
1216 0 : spmSZOSC->coolingOffSetPt,
1217 0 : spmSZOSC->coolingOnSetPt));
1218 : }
1219 :
1220 8 : std::string ctrlZoneName = ip->getAlphaFieldValue(fields, props, "control_zone_name");
1221 : // get the actual zone number of the control zone
1222 4 : spmSZOSC->ctrlZoneNum = Util::FindItemInList(ctrlZoneName, state.dataHeatBal->Zone);
1223 4 : if (spmSZOSC->ctrlZoneNum == 0) {
1224 0 : ShowSevereItemNotFound(state, eoh, "control_zone_name", ctrlZoneName);
1225 0 : ErrorsFound = true;
1226 : } else {
1227 4 : spmSZOSC->zoneNodeNum = GetSystemNodeNumberForZone(state, spmSZOSC->ctrlZoneNum);
1228 4 : if (allocated(state.dataZoneCtrls->StageZoneLogic)) {
1229 4 : if (!state.dataZoneCtrls->StageZoneLogic(spmSZOSC->ctrlZoneNum)) {
1230 0 : ShowSevereError(state, format("{}: {}=\"{}\", invalid field.", routineName, cCurrentModuleObject, spmSZOSC->Name));
1231 0 : ShowContinueError(state, format("..invalid control_zone_name=\"{}\".", ctrlZoneName));
1232 0 : ShowContinueError(state, "Zone thermostat must use ZoneControl:Thermostat:StagedDualSetpoint.");
1233 0 : ErrorsFound = true;
1234 : }
1235 : }
1236 : }
1237 4 : } break;
1238 :
1239 : // SetpointManager:SingleZone:OneStageHeating
1240 4 : case SPMType::SZOneStageHeating: {
1241 4 : auto *spmSZOSH = dynamic_cast<SPMSingleZoneOneStageHeating *>(spm);
1242 4 : assert(spmSZOSH != nullptr);
1243 :
1244 4 : spmSZOSH->heatingOnSetPt = ip->getRealFieldValue(fields, props, "heating_stage_on_supply_air_setpoint_temperature");
1245 4 : spmSZOSH->heatingOffSetPt = ip->getRealFieldValue(fields, props, "heating_stage_off_supply_air_setpoint_temperature");
1246 :
1247 4 : if (spmSZOSH->heatingOffSetPt > spmSZOSH->heatingOnSetPt) {
1248 : // throw warning, off must be warmer than on
1249 0 : ShowWarningError(state, format("{}: {}=\"{}\",", routineName, cCurrentModuleObject, spmSZOSH->Name));
1250 0 : ShowContinueError(state,
1251 0 : format("...heating_stage_off_supply_air_setpoint_temperature=[{:.1R}] is less than "
1252 : "heating_stage_on_supply_air_setpoint_temperature=[{:.1R}].",
1253 0 : spmSZOSH->heatingOffSetPt,
1254 0 : spmSZOSH->heatingOnSetPt));
1255 : }
1256 :
1257 8 : std::string ctrlZoneName = ip->getAlphaFieldValue(fields, props, "control_zone_name");
1258 : // get the actual zone number of the control zone
1259 4 : spmSZOSH->ctrlZoneNum = Util::FindItemInList(ctrlZoneName, state.dataHeatBal->Zone);
1260 4 : if (spmSZOSH->ctrlZoneNum == 0) {
1261 0 : ShowSevereItemNotFound(state, eoh, "control_zone_name", ctrlZoneName);
1262 0 : ErrorsFound = true;
1263 : } else {
1264 4 : spmSZOSH->zoneNodeNum = GetSystemNodeNumberForZone(state, spmSZOSH->ctrlZoneNum);
1265 4 : if (allocated(state.dataZoneCtrls->StageZoneLogic)) {
1266 4 : if (!state.dataZoneCtrls->StageZoneLogic(spmSZOSH->ctrlZoneNum)) {
1267 0 : ShowSevereError(state, format("{}: {}=\"{}\", invalid field.", routineName, cCurrentModuleObject, spmSZOSH->Name));
1268 0 : ShowContinueError(state, format("..invalid control_zone_name=\"{}\".", ctrlZoneName));
1269 0 : ShowContinueError(state, "Zone thermostat must use ZoneControl:Thermostat:StagedDualSetpoint.");
1270 0 : ErrorsFound = true;
1271 : }
1272 : }
1273 : }
1274 4 : } break;
1275 :
1276 : // SetpointManager:ReturnTemperature:ChilledWater
1277 : // SetpointManager:ReturnTemperature:HotWater
1278 4 : case SPMType::ChilledWaterReturnTemp:
1279 : case SPMType::HotWaterReturnTemp: {
1280 4 : auto *spmRWT = dynamic_cast<SPMReturnWaterTemp *>(spm);
1281 4 : assert(spmRWT != nullptr);
1282 :
1283 4 : bool errFlag = false;
1284 8 : spmRWT->supplyNodeNum = GetOnlySingleNode(state,
1285 8 : ip->getAlphaFieldValue(fields, props, "plant_loop_supply_outlet_node"),
1286 : errFlag,
1287 4 : spmNodeObjectTypes[(int)spm->type],
1288 4 : spmRWT->Name,
1289 : DataLoopNode::NodeFluidType::Blank,
1290 : DataLoopNode::ConnectionType::SetPoint,
1291 : NodeInputManager::CompFluidStream::Primary,
1292 : ObjectIsNotParent,
1293 : "plant_loop_supply_outlet_node"); // setpoint nodes
1294 8 : spmRWT->returnNodeNum = GetOnlySingleNode(state,
1295 8 : ip->getAlphaFieldValue(fields, props, "plant_loop_supply_inlet_node"),
1296 : errFlag,
1297 4 : spmNodeObjectTypes[(int)spm->type],
1298 4 : spmRWT->Name,
1299 : DataLoopNode::NodeFluidType::Blank,
1300 : DataLoopNode::ConnectionType::Sensor,
1301 : NodeInputManager::CompFluidStream::Primary,
1302 : ObjectIsNotParent,
1303 : "plant_loop_supply_inlet_node"); // setpoint nodes
1304 :
1305 : // process the setpoint inputs
1306 4 : spmRWT->minSetTemp = ip->getRealFieldValue(fields, props, "minimum_supply_temperature_setpoint");
1307 4 : spmRWT->maxSetTemp = ip->getRealFieldValue(fields, props, "maximum_supply_temperature_setpoint");
1308 :
1309 4 : spmRWT->returnTempType = static_cast<ReturnTempType>(
1310 4 : getEnumValue(returnTempTypeNamesUC, ip->getAlphaFieldValue(fields, props, "return_temperature_setpoint_input_type")));
1311 :
1312 4 : if (spmRWT->returnTempType == ReturnTempType::Scheduled) {
1313 0 : std::string schedName = ip->getAlphaFieldValue(fields, props, "return_temperature_setpoint_scheduled_name");
1314 0 : spmRWT->returnTempSchedNum = GetScheduleIndex(state, schedName);
1315 0 : if (spmRWT->returnTempSchedNum == 0) {
1316 0 : ShowSevereItemNotFound(state, eoh, "return_temperature_setpoint_scheduled_name", schedName);
1317 0 : ErrorsFound = true;
1318 : }
1319 4 : } else if (spmRWT->returnTempType == ReturnTempType::Constant) {
1320 3 : spmRWT->returnTempConstantTarget = ip->getRealFieldValue(fields, props, "return_temperature_setpoint_constant_value");
1321 : }
1322 4 : } break;
1323 :
1324 : // SetpointManager:SystemNodeReset:Temperature
1325 3 : case SPMType::SystemNodeTemp: {
1326 3 : auto *spmSNRTemp = dynamic_cast<SPMSystemNode *>(spm);
1327 3 : assert(spmSNRTemp != nullptr);
1328 :
1329 3 : if (spmSNRTemp->ctrlVar != HVAC::CtrlVarType::Temp && spmSNRTemp->ctrlVar != HVAC::CtrlVarType::MaxTemp &&
1330 0 : spmSNRTemp->ctrlVar != HVAC::CtrlVarType::MinTemp) {
1331 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1332 0 : ErrorsFound = true;
1333 : }
1334 :
1335 3 : spmSNRTemp->lowRefSetPt = ip->getRealFieldValue(fields, props, "setpoint_at_low_reference_temperature");
1336 3 : spmSNRTemp->highRefSetPt = ip->getRealFieldValue(fields, props, "setpoint_at_high_reference_temperature");
1337 3 : spmSNRTemp->lowRef = ip->getRealFieldValue(fields, props, "low_reference_temperature");
1338 3 : spmSNRTemp->highRef = ip->getRealFieldValue(fields, props, "high_reference_temperature");
1339 :
1340 6 : spmSNRTemp->refNodeNum = GetOnlySingleNode(state,
1341 6 : ip->getAlphaFieldValue(fields, props, "reference_node_name"),
1342 : ErrorsFound,
1343 3 : spmNodeObjectTypes[(int)spm->type],
1344 3 : spmSNRTemp->Name,
1345 : DataLoopNode::NodeFluidType::Blank,
1346 : DataLoopNode::ConnectionType::Sensor,
1347 : NodeInputManager::CompFluidStream::Primary,
1348 : ObjectIsNotParent);
1349 3 : } break;
1350 :
1351 : // SetpointManager:SystemNodeReset:Humidity
1352 1 : case SPMType::SystemNodeHum: {
1353 1 : auto *spmSNRHum = dynamic_cast<SPMSystemNode *>(spm);
1354 1 : assert(spmSNRHum != nullptr);
1355 :
1356 1 : if (spmSNRHum->ctrlVar != HVAC::CtrlVarType::HumRat && spmSNRHum->ctrlVar != HVAC::CtrlVarType::MaxHumRat &&
1357 0 : spmSNRHum->ctrlVar != HVAC::CtrlVarType::MinHumRat) {
1358 0 : ShowSevereInvalidKey(state, eoh, "control_variable", ctrlVarName);
1359 0 : ErrorsFound = true;
1360 : }
1361 :
1362 1 : spmSNRHum->lowRefSetPt = ip->getRealFieldValue(fields, props, "setpoint_at_low_reference_humidity_ratio");
1363 1 : spmSNRHum->highRefSetPt = ip->getRealFieldValue(fields, props, "setpoint_at_high_reference_humidity_ratio");
1364 1 : spmSNRHum->lowRef = ip->getRealFieldValue(fields, props, "low_reference_humidity_ratio");
1365 1 : spmSNRHum->highRef = ip->getRealFieldValue(fields, props, "high_reference_humidity_ratio");
1366 :
1367 2 : spmSNRHum->refNodeNum = GetOnlySingleNode(state,
1368 2 : ip->getAlphaFieldValue(fields, props, "reference_node_name"),
1369 : ErrorsFound,
1370 1 : spmNodeObjectTypes[(int)spm->type],
1371 1 : spmSNRHum->Name,
1372 : DataLoopNode::NodeFluidType::Blank,
1373 : DataLoopNode::ConnectionType::Sensor,
1374 : NodeInputManager::CompFluidStream::Primary,
1375 : ObjectIsNotParent);
1376 1 : } break;
1377 :
1378 : // SetpointManager:MultiZone:Cooling:Average
1379 : // SetpointManager:MultiZone:Heating:Average
1380 : // SetpointManager:MultiZone:MinimumHumidity:Average
1381 : // SetpointManager:MultiZone:MaximumHumidity:Average
1382 : // SetpointManager:MultiZone:Humidity:Minimum
1383 : // SetpointManager:MultiZone:Humidity:Maximum
1384 :
1385 26 : default:
1386 26 : break;
1387 : } // switch (spm->type)
1388 :
1389 : // Load control node list
1390 : // Do this at the end to preserve node order
1391 4688 : if (spm->type != SPMType::ReturnAirBypass && spm->type != SPMType::ChilledWaterReturnTemp && spm->type != SPMType::HotWaterReturnTemp) {
1392 9366 : std::string ctrlNodeListName = ip->getAlphaFieldValue(fields, props, "setpoint_node_or_nodelist_name");
1393 4683 : NodeListError = false;
1394 9366 : GetNodeNums(state,
1395 : ctrlNodeListName,
1396 : NumNodes,
1397 : NodeNums,
1398 : NodeListError,
1399 : DataLoopNode::NodeFluidType::Blank,
1400 4683 : spmNodeObjectTypes[iSPM],
1401 : name,
1402 : DataLoopNode::ConnectionType::SetPoint,
1403 : NodeInputManager::CompFluidStream::Primary,
1404 : ObjectIsNotParent,
1405 : false,
1406 : "setpoint_node_or_nodelist_name");
1407 :
1408 4683 : if (!NodeListError) {
1409 9764 : for (int iNode = 1; iNode <= NumNodes; ++iNode) {
1410 5081 : spm->ctrlNodeNums.push_back(NodeNums(iNode));
1411 : }
1412 : } else {
1413 0 : ErrorsFound = true;
1414 : }
1415 4683 : }
1416 :
1417 : // Now load all of the optional fields
1418 4688 : switch (spm->type) {
1419 1661 : case SPMType::MixedAir: {
1420 1661 : auto *spmMA = dynamic_cast<SPMMixedAir *>(spm);
1421 1661 : assert(spmMA != nullptr);
1422 1661 : if (auto found = fields.find("cooling_coil_inlet_node_name"); found != fields.end()) {
1423 2 : spmMA->coolCoilInNodeNum = GetOnlySingleNode(state,
1424 2 : Util::makeUPPER(found.value().get<std::string>()),
1425 : ErrorsFound,
1426 1 : spmNodeObjectTypes[(int)spm->type],
1427 1 : spmMA->Name,
1428 : DataLoopNode::NodeFluidType::Air,
1429 : DataLoopNode::ConnectionType::Sensor,
1430 : NodeInputManager::CompFluidStream::Primary,
1431 : ObjectIsNotParent);
1432 1661 : }
1433 :
1434 1661 : if (auto found = fields.find("cooling_coil_outlet_node_name"); found != fields.end()) {
1435 2 : spmMA->coolCoilOutNodeNum = GetOnlySingleNode(state,
1436 2 : Util::makeUPPER(found.value().get<std::string>()),
1437 : ErrorsFound,
1438 1 : spmNodeObjectTypes[(int)spm->type],
1439 1 : spmMA->Name,
1440 : DataLoopNode::NodeFluidType::Air,
1441 : DataLoopNode::ConnectionType::Sensor,
1442 : NodeInputManager::CompFluidStream::Primary,
1443 : ObjectIsNotParent);
1444 1661 : }
1445 :
1446 1661 : if (auto found = fields.find("minimum_temperature_at_cooling_coil_outlet_node"); found != fields.end()) {
1447 1 : spmMA->minCoolCoilOutTemp = found.value().get<Real64>();
1448 1661 : }
1449 :
1450 : // Also, do this check now that we have both RefNodeNum and ctrlNodeNums
1451 1661 : if (std::find(spmMA->ctrlNodeNums.begin(), spmMA->ctrlNodeNums.end(), spmMA->refNodeNum) != spmMA->ctrlNodeNums.end()) {
1452 0 : ShowSevereError(state, format("{}: {}=\"{}\", reference node.", routineName, cCurrentModuleObject, spmMA->Name));
1453 0 : if (spmMA->ctrlNodeNums.size() > 1) {
1454 0 : ShowContinueError(state, "..Reference Node is the same as one of the nodes in SetPoint NodeList");
1455 : } else {
1456 0 : ShowContinueError(state, "..Reference Node is the same as the SetPoint Node");
1457 : }
1458 0 : ShowContinueError(state, format("Reference Node Name=\"{}\".", state.dataLoopNodes->NodeID(spmMA->refNodeNum)));
1459 0 : ErrorsFound = true;
1460 : }
1461 1661 : } break;
1462 :
1463 3027 : default:
1464 3027 : break;
1465 : } // switch (spm->type)
1466 :
1467 5993 : } // for (instance)
1468 25472 : } // for (iSPM)
1469 :
1470 796 : } // GetSetPointManagerInputData()
1471 :
1472 797 : void VerifySetPointManagers(EnergyPlusData &state, [[maybe_unused]] bool &ErrorsFound) // flag to denote node conflicts in input. !unused1208
1473 : {
1474 : // SUBROUTINE INFORMATION:
1475 : // AUTHOR Richard Raustad, FSEC
1476 : // DATE WRITTEN July 2008
1477 : // MODIFIED Rick Strand, Aug 2014 (removed deallocation of AllSetPtMgrs so ScheduledTES could also verify control nodes)
1478 :
1479 : // PURPOSE OF THIS SUBROUTINE
1480 : // Check the SetPointManager data to eliminate conflicts.
1481 :
1482 : // METHODOLOGY EMPLOYED:
1483 : // 1) Check for duplicate names in individual setpoint managers.
1484 : // Control nodes = A B C D
1485 : // Check A with B, C, and D
1486 : // Check B with C and D
1487 : // Check C with D
1488 : // 2) Check for duplicate names in all other setpoint managers
1489 : // Verify setpoint managers use same control type (e.g. TEMP) and then check for duplicate nodes
1490 : // SPM 1 - Control nodes A - D, SPM 2 - Control nodes E - H, SPM 3 - Control nodes I - L
1491 : // If SPM 1 has same control type as SPM 2 and SPM 3 (e.g. all use SPM%CtrlTypeMode = HVAC::CtrlVarType::Temp) then:
1492 : // Check A with E-H and I-L
1493 : // Check B with E-H and I-L
1494 : // Check C with E-H and I-L
1495 : // Check D with E-H and I-L
1496 : // Then check SPM 2 nodes with SPM 3. Check E with I-L, F with I-L, etc.
1497 : // 3) For SET POINT MANAGER:RETURN AIR BYPASS FLOW
1498 : // check for duplicate air loop names.
1499 :
1500 5481 : for (int iSPM = 1; iSPM <= (int)state.dataSetPointManager->spms.size(); ++iSPM) {
1501 4684 : auto const *spm = state.dataSetPointManager->spms(iSPM);
1502 :
1503 : // check for duplicate nodes in each setpoint managers control node list (node lists of size 1 do not need verification)
1504 : // issue warning only since duplicate node names within a setpoint manager does not cause a conflict (i.e., same
1505 : // value written to node) but may indicate an error in the node name.
1506 5086 : for (int iNode = 0; iNode < (int)spm->ctrlNodeNums.size() - 1; ++iNode) {
1507 971 : for (int jNode = iNode + 1; (int)jNode < spm->ctrlNodeNums.size(); ++jNode) {
1508 569 : if (spm->ctrlNodeNums[iNode] != spm->ctrlNodeNums[jNode]) continue;
1509 0 : ShowWarningError(state, format("{} =\"{}\"", spmTypeNames[(int)spm->type], spm->Name));
1510 0 : ShowContinueError(state, format("...duplicate node specified = {}", state.dataLoopNodes->NodeID(spm->ctrlNodeNums[iNode])));
1511 0 : ShowContinueError(state, format("...control type variable = {}", ctrlVarTypeNamesUC[(int)spm->ctrlVar]));
1512 : }
1513 : }
1514 :
1515 : // check for node conflicts in all other setpoint managers
1516 45376 : for (int jSPM = iSPM + 1; jSPM <= (int)state.dataSetPointManager->spms.size(); ++jSPM) {
1517 40692 : auto const *spm2 = state.dataSetPointManager->spms(jSPM);
1518 :
1519 40692 : if (spm == spm2) continue;
1520 :
1521 40692 : if (spm->type == SPMType::ReturnAirBypass && spm2->type == SPMType::ReturnAirBypass) {
1522 :
1523 : // check the air loop name for duplicates in this SP manager type
1524 0 : if (spm->airLoopNum == spm2->airLoopNum) {
1525 0 : ShowWarningError(state, format("{}=\"{}\"", spmTypeNames[(int)spm->type], spm->Name));
1526 0 : ShowContinueError(state, "...air loop name conflicts with another setpoint manager.");
1527 0 : ShowContinueError(state, format("...conflicting setpoint manager = {} \"{}\"", spmTypeNames[(int)spm2->type], spm2->Name));
1528 0 : ShowContinueError(state, format("...conflicting air loop name = {}", spm->airLoopName));
1529 : // ErrorsFound=.TRUE.
1530 : }
1531 :
1532 : // check for duplicate control nodes
1533 0 : if (spm->ctrlVar != spm2->ctrlVar) continue;
1534 :
1535 0 : for (int iNode = 0; iNode < (int)spm->ctrlNodeNums.size(); ++iNode) {
1536 0 : for (int jNode = 0; jNode < (int)spm2->ctrlNodeNums.size(); ++jNode) {
1537 0 : if ((spm->ctrlNodeNums[iNode] == spm2->ctrlNodeNums[jNode]) && spm->ctrlNodeNums[iNode] != 0) {
1538 0 : ShowWarningError(state, format("{}=\"{}\"", spmTypeNames[(int)spm->type], spm->Name));
1539 0 : ShowContinueError(state, "...setpoint node conflicts with another setpoint manager.");
1540 0 : ShowContinueError(state,
1541 0 : format("...conflicting setpoint manager = {} \"{}\"", spmTypeNames[(int)spm2->type], spm2->Name));
1542 0 : ShowContinueError(state, format("...conflicting node name = {}", state.dataLoopNodes->NodeID(spm->ctrlNodeNums[iNode])));
1543 0 : ShowContinueError(state, format("...control type variable = {}", ctrlVarTypeNames[(int)spm->ctrlVar]));
1544 : // ErrorsFound=.TRUE.
1545 : }
1546 : }
1547 : }
1548 :
1549 0 : } else { // not a RAB setpoint manager
1550 :
1551 : // check just the control nodes for other types of SP managers
1552 40692 : if (spm->ctrlVar != spm2->ctrlVar) continue;
1553 :
1554 75871 : for (int iNode = 0; iNode < (int)spm->ctrlNodeNums.size(); ++iNode) {
1555 78132 : for (int jNode = 0; jNode < (int)spm2->ctrlNodeNums.size(); ++jNode) {
1556 :
1557 39928 : if (spm->ctrlNodeNums[iNode] != spm2->ctrlNodeNums[jNode]) continue;
1558 :
1559 : // only warn if scheduled setpoint manager is setting mass flow rate on the same node used by RAB
1560 1 : if (spm->type == SPMType::ReturnAirBypass || spm2->type == SPMType::ReturnAirBypass) {
1561 0 : ShowWarningError(state, format("{}=\"{}\"", spmTypeNames[(int)spm->type], spm->Name));
1562 0 : ShowContinueError(state, "...setpoint node conflicts with another setpoint manager.");
1563 0 : ShowContinueError(state, format("...conflicting setpoint manager ={}:\"{}\"", spmTypeNames[(int)spm2->type], spm2->Name));
1564 0 : ShowContinueError(state, format("...conflicting node name = {}", state.dataLoopNodes->NodeID(spm->ctrlNodeNums[iNode])));
1565 0 : ShowContinueError(state, format("...control type variable = {}", ctrlVarTypeNames[(int)spm->ctrlVar]));
1566 0 : ShowContinueError(state,
1567 : "...return air bypass flow setpoint manager will have priority setting mass flow rate on this node.");
1568 : } else { // severe error for other SP manager types
1569 1 : ShowWarningError(state, format("{}=\"{}\"", spmTypeNames[(int)spm->type], spm->Name));
1570 1 : ShowContinueError(state, "...setpoint node conflicts with another setpoint manager.");
1571 2 : ShowContinueError(state,
1572 2 : format("...conflicting setpoint manager = {}:\"{}\"", spmTypeNames[(int)spm2->type], spm2->Name));
1573 1 : ShowContinueError(state, format("...conflicting node name = {}", state.dataLoopNodes->NodeID(spm->ctrlNodeNums[iNode])));
1574 1 : ShowContinueError(state, format("...control type variable = {}", ctrlVarTypeNames[(int)spm->ctrlVar]));
1575 : // ErrorsFound=.TRUE.
1576 : }
1577 : }
1578 : }
1579 : }
1580 :
1581 : } // for (jSPM)
1582 :
1583 : } // for (iSPM)
1584 :
1585 : // Removed the following line for ScheduledTES control implementation
1586 : // if ( allocated( AllSetPtMgr ) ) AllSetPtMgr.deallocate();
1587 797 : } // VerifySetPointManager()
1588 :
1589 2839125 : void InitSetPointManagers(EnergyPlusData &state)
1590 : {
1591 : // SUBROUTINE INFORMATION:
1592 : // AUTHOR Fred Buhl
1593 : // DATE WRITTEN October 2000
1594 : // MODIFIED Shirey/Raustad (FSEC), Jan 2004
1595 : // Nov 2004 - Jan 2005 M. J. Witte, GARD Analytics, Inc.
1596 : // Add new setpoint managers:
1597 : // SET POINT MANAGER:SINGLE ZONE HEATING and
1598 : // SET POINT MANAGER:SINGLE ZONE COOLING
1599 : // SET POINT MANAGER:OUTSIDE AIR PRETREAT
1600 : // Work supported by ASHRAE research project 1254-RP
1601 : // Haves Oct 2004
1602 : // July 2010 B.A. Nigusse, FSEC/UCF
1603 : // Added new setpoint managers:
1604 : // SetpointManager:MultiZone:Heating:Average
1605 : // SetpointManager:MultiZone:Cooling:Average
1606 : // SetpointManager:MultiZone:MinimumHumidity:Average
1607 : // SetpointManager:MultiZone:MaximumHumidity:Average
1608 : // Aug 2010 B.A. Nigusse, FSEC/UCF
1609 : // Added new setpoint managers:
1610 : // SetpointManager:MultiZone:Humidity:Minimum
1611 : // SetpointManager:MultiZone:Humidity:Maximum
1612 : // Sep 2010 B.A. Nigusse, FSEC/UCF
1613 : // Added control varibles for SetpointManage:Scheduled
1614 : // Jan 2022 Wooyoung Jung, Jeremy Lerond and Jian Zhang, PNNL
1615 : // Added new setpoint managers:
1616 : // SetpointManager:SystemNodeReset:Temperature
1617 : // SetpointManager:SystemNodeReset:Humidity
1618 :
1619 : // PURPOSE OF THIS SUBROUTINE:
1620 : // This subroutine is for initializations of the Setpoint Manager objects.
1621 :
1622 : // METHODOLOGY EMPLOYED:
1623 : // Uses the status flags to trigger initializations.
1624 :
1625 : // Using/Aliasing
1626 : using namespace DataPlant;
1627 : using OutAirNodeManager::CheckOutAirNodeNumber;
1628 :
1629 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1630 2839125 : constexpr std::string_view routineName = "InitSetPointManagers";
1631 :
1632 2839125 : bool ErrorsFound(false);
1633 :
1634 2839125 : state.dataSetPointManager->ManagerOn = true;
1635 :
1636 : // One time initializations
1637 :
1638 5678250 : if (state.dataZoneEquip->ZoneEquipInputsFilled &&
1639 2839125 : state.dataAirLoop->AirLoopInputsFilled) { // check that the zone equipment and air loop data has been read in
1640 :
1641 2837534 : if (state.dataSetPointManager->InitSetPointManagersOneTimeFlag) {
1642 :
1643 5464 : for (auto *spm : state.dataSetPointManager->spms) {
1644 4669 : std::string_view spmName = spm->Name;
1645 4669 : std::string_view spmTypeName = spmTypeNames[(int)spm->type];
1646 :
1647 4669 : ErrorObjectHeader eoh{routineName, spmTypeName, spmName};
1648 :
1649 4669 : switch (spm->type) {
1650 :
1651 294 : case SPMType::SZHeating:
1652 : case SPMType::SZCooling: {
1653 294 : auto *spmSZT = dynamic_cast<SPMSingleZoneTemp *>(spm);
1654 294 : assert(spmSZT != nullptr);
1655 : // find the index in the ZoneEquipConfig array of the control zone (the one with the main or only thermostat)
1656 294 : int ConZoneNum = 0;
1657 4918 : for (int ZoneNum = 1; ZoneNum <= state.dataGlobal->NumOfZones; ++ZoneNum) {
1658 4624 : if (state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ZoneNode == spmSZT->zoneNodeNum) {
1659 294 : ConZoneNum = ZoneNum;
1660 : }
1661 : }
1662 294 : if (ConZoneNum == 0) {
1663 0 : ShowSevereError(state, format("{}=\"{}\", Zone Node not found:", spmTypeName, spmName));
1664 0 : ShowContinueError(state,
1665 0 : format("Node=\"{}\", not found in any controlled Zone", state.dataLoopNodes->NodeID(spmSZT->zoneNodeNum)));
1666 0 : ErrorsFound = true;
1667 : } else {
1668 294 : auto &zoneEquip = state.dataZoneEquip->ZoneEquipConfig(ConZoneNum);
1669 294 : bool found = false;
1670 634 : for (int zoneInNode = 1; zoneInNode <= zoneEquip.NumInletNodes; ++zoneInNode) {
1671 340 : if (spmSZT->zoneInletNodeNum == zoneEquip.InletNode(zoneInNode)) {
1672 294 : found = true;
1673 : }
1674 : }
1675 294 : if (!found) {
1676 0 : ShowSevereError(state,
1677 0 : format("{}=\"{}\", The zone inlet node of {}",
1678 : spmTypeName,
1679 : spmName,
1680 0 : state.dataLoopNodes->NodeID(spmSZT->zoneInletNodeNum)));
1681 0 : ShowContinueError(state, format("is not found in Zone = {}. Please check inputs.", zoneEquip.ZoneName));
1682 0 : ErrorsFound = true;
1683 : }
1684 : }
1685 294 : } break;
1686 :
1687 74 : case SPMType::SZMinHum:
1688 : case SPMType::SZMaxHum: {
1689 74 : auto *spmSZH = dynamic_cast<SPMSingleZoneHum *>(spm);
1690 74 : assert(spmSZH != nullptr);
1691 :
1692 : // set the actual and controlled zone numbers
1693 2450 : for (int ZoneNum = 1; ZoneNum <= state.dataGlobal->NumOfZones; ++ZoneNum) {
1694 2450 : if (state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ZoneNode == spmSZH->zoneNodeNum) {
1695 74 : spmSZH->ctrlZoneNum = ZoneNum;
1696 74 : break;
1697 : }
1698 : }
1699 : // still need to validate...
1700 74 : if (spmSZH->ctrlZoneNum == 0) { // didn't find
1701 0 : ShowSevereCustomMessage(
1702 0 : state, eoh, format("could not find Controlled Zone={}", state.dataHeatBal->Zone(spmSZH->ctrlZoneNum).Name));
1703 0 : ErrorsFound = true;
1704 : } else {
1705 : // make sure humidity controlled zone
1706 74 : bool HstatZoneFound = false;
1707 140 : for (int iZone = 1; iZone <= state.dataZoneCtrls->NumHumidityControlZones; ++iZone) {
1708 140 : if (state.dataZoneCtrls->HumidityControlZone(iZone).ActualZoneNum == spmSZH->ctrlZoneNum) {
1709 74 : HstatZoneFound = true;
1710 74 : break;
1711 : }
1712 : }
1713 74 : if (!HstatZoneFound) {
1714 0 : ShowSevereError(state, format("{}=\"{}\", invalid humidistat specification", spmTypeName, spmName));
1715 0 : ShowContinueError(state,
1716 0 : format("could not locate Humidistat in Zone={}", state.dataHeatBal->Zone(spmSZH->ctrlZoneNum).Name));
1717 0 : ErrorsFound = true;
1718 : }
1719 : }
1720 74 : } break;
1721 :
1722 281 : case SPMType::SZReheat: {
1723 281 : auto *spmSZR = dynamic_cast<SPMSingleZoneReheat *>(spm);
1724 281 : assert(spmSZR != nullptr);
1725 :
1726 281 : int FanNodeIn = 0;
1727 281 : int FanNodeOut = 0;
1728 281 : int MixedAirNode = 0;
1729 281 : int InletBranchNum = 0;
1730 281 : int LoopInNode = 0;
1731 281 : bool LookForFan = false;
1732 :
1733 : // find the index in the ZoneEquipConfig array of the control zone (the one with the main or only thermostat)
1734 281 : int ConZoneNum = 0;
1735 4722 : for (int ZoneNum = 1; ZoneNum <= state.dataGlobal->NumOfZones; ++ZoneNum) {
1736 4441 : if (state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ZoneNode == spmSZR->zoneNodeNum) {
1737 281 : ConZoneNum = ZoneNum;
1738 : }
1739 : }
1740 :
1741 281 : if (ConZoneNum == 0) {
1742 0 : ShowSevereError(state, format("{}=\"{}\", Zone Node not found:", spmTypeName, spmName));
1743 0 : ShowContinueError(state,
1744 0 : format("Node=\"{}\", not found in any controlled Zone", state.dataLoopNodes->NodeID(spmSZR->zoneNodeNum)));
1745 0 : ErrorsFound = true;
1746 : } else {
1747 281 : bool found = false;
1748 281 : auto const &zoneEquip = state.dataZoneEquip->ZoneEquipConfig(ConZoneNum);
1749 566 : for (int zoneInNode = 1; zoneInNode <= zoneEquip.NumInletNodes; ++zoneInNode) {
1750 285 : if (spmSZR->zoneInletNodeNum == zoneEquip.InletNode(zoneInNode)) {
1751 281 : spmSZR->airLoopNum = zoneEquip.InletNodeAirLoopNum(zoneInNode);
1752 281 : found = true;
1753 : }
1754 : }
1755 281 : if (!found) {
1756 0 : ShowSevereError(state,
1757 0 : format("{}=\"{}\", The zone inlet node of {}",
1758 : spmTypeName,
1759 : spmName,
1760 0 : state.dataLoopNodes->NodeID(spmSZR->zoneInletNodeNum)));
1761 0 : ShowContinueError(state, format("is not found in Zone = {}. Please check inputs.", zoneEquip.ZoneName));
1762 0 : ErrorsFound = true;
1763 : }
1764 281 : if (spmSZR->airLoopNum == 0) {
1765 0 : ShowSevereError(state, format("{}=\"{}\", The zone inlet node is not connected to an air loop.", spmTypeName, spmName));
1766 0 : ErrorsFound = true;
1767 0 : continue;
1768 : }
1769 : }
1770 :
1771 281 : auto const &primaryAirSystem = state.dataAirSystemsData->PrimaryAirSystems(spmSZR->airLoopNum);
1772 281 : MixedAirNode = primaryAirSystem.OASysOutletNodeNum;
1773 281 : InletBranchNum = primaryAirSystem.InletBranchNum[0];
1774 281 : LoopInNode = primaryAirSystem.Branch(InletBranchNum).NodeNumIn;
1775 : // get the supply fan inlet and outlet nodes
1776 281 : if (MixedAirNode > 0) {
1777 558 : for (auto const &branch : primaryAirSystem.Branch) {
1778 1032 : for (auto const &comp : branch.Comp) {
1779 964 : if (MixedAirNode == comp.NodeNumIn) {
1780 279 : LookForFan = true;
1781 : }
1782 964 : if (LookForFan) {
1783 1633 : if (Util::SameString(comp.TypeOf, "Fan:ConstantVolume") || Util::SameString(comp.TypeOf, "Fan:VariableVolume") ||
1784 1633 : Util::SameString(comp.TypeOf, "Fan:OnOff") || Util::SameString(comp.TypeOf, "Fan:ComponentModel")) {
1785 211 : FanNodeIn = comp.NodeNumIn;
1786 211 : FanNodeOut = comp.NodeNumOut;
1787 211 : break;
1788 : }
1789 : }
1790 : }
1791 : } // for (BranchNum)
1792 : } else {
1793 4 : for (auto const &branch : primaryAirSystem.Branch) {
1794 8 : for (auto const &comp : branch.Comp) {
1795 14 : if (Util::SameString(comp.TypeOf, "Fan:ConstantVolume") || Util::SameString(comp.TypeOf, "Fan:VariableVolume") ||
1796 14 : Util::SameString(comp.TypeOf, "Fan:OnOff") || Util::SameString(comp.TypeOf, "Fan:ComponentModel")) {
1797 2 : FanNodeIn = comp.NodeNumIn;
1798 2 : FanNodeOut = comp.NodeNumOut;
1799 : }
1800 : }
1801 : }
1802 : }
1803 :
1804 281 : spmSZR->fanInNodeNum = FanNodeIn;
1805 281 : spmSZR->fanOutNodeNum = FanNodeOut;
1806 281 : spmSZR->mixedAirNodeNum = MixedAirNode;
1807 281 : spmSZR->oaInNodeNum = primaryAirSystem.OAMixOAInNodeNum;
1808 : // this next line assumes that OA system is the first thing on the branch, what if there is a relief fan or heat recovery coil
1809 : // or other component in there first? does it matter?
1810 281 : spmSZR->retNodeNum = primaryAirSystem.OASysInletNodeNum;
1811 281 : spmSZR->loopInNodeNum = LoopInNode;
1812 :
1813 281 : } break;
1814 :
1815 23 : case SPMType::Warmest:
1816 : case SPMType::Coldest: {
1817 23 : auto *spmT = dynamic_cast<SPMTempest *>(spm);
1818 23 : assert(spmT != nullptr);
1819 23 : if (state.dataHVACGlobal->NumPrimaryAirSys > 0) {
1820 23 : spmT->airLoopNum =
1821 23 : Util::FindItemInList(spmT->airLoopName, state.dataAirLoop->AirToZoneNodeInfo, &AirLoopZoneEquipConnectData::AirLoopName);
1822 23 : if (spmT->airLoopNum == 0) {
1823 0 : ShowSevereItemNotFound(state, eoh, "hvac_air_loop_name", spmT->airLoopName);
1824 0 : ErrorsFound = true;
1825 23 : } else if (state.dataAirLoop->AirToZoneNodeInfo(spmT->airLoopNum).NumZonesCooled == 0) {
1826 0 : ShowSevereError(state, format("{}=\"{}\", no zones with cooling found:", spmTypeName, spmName));
1827 0 : ShowContinueError(state, format("Air Loop provides no cooling, Air Loop=\"{}\".", spmT->airLoopName));
1828 0 : ErrorsFound = true;
1829 : }
1830 : } else {
1831 0 : ShowSevereError(state, format("{}=\"{}\", no AirLoopHVAC objects found:", spmTypeName, spmName));
1832 0 : ShowContinueError(state, "Setpoint Manager needs an AirLoopHVAC to operate.");
1833 0 : ErrorsFound = true;
1834 : }
1835 23 : } break;
1836 :
1837 4 : case SPMType::WarmestTempFlow: {
1838 4 : auto *spmWTF = dynamic_cast<SPMWarmestTempFlow *>(spm);
1839 4 : assert(spmWTF != nullptr);
1840 :
1841 4 : if (state.dataHVACGlobal->NumPrimaryAirSys > 0) {
1842 4 : spmWTF->airLoopNum = Util::FindItemInList(
1843 4 : spmWTF->airLoopName, state.dataAirLoop->AirToZoneNodeInfo, &AirLoopZoneEquipConnectData::AirLoopName);
1844 4 : if (spmWTF->airLoopNum == 0) {
1845 0 : ShowSevereItemNotFound(state, eoh, "hvac_air_loop_name", spmWTF->airLoopName);
1846 0 : ErrorsFound = true;
1847 : } else {
1848 4 : spmWTF->simReady = true;
1849 : }
1850 4 : if (state.dataAirLoop->AirToZoneNodeInfo(spmWTF->airLoopNum).NumZonesCooled == 0) {
1851 0 : ShowSevereError(state, format("{}=\"{}\", no zones with cooling found:", spmTypeName, spmName));
1852 0 : ShowContinueError(state, format("Air Loop provides no cooling, Air Loop=\"{}\".", spmWTF->airLoopName));
1853 0 : ErrorsFound = true;
1854 : }
1855 : } else {
1856 0 : ShowSevereError(state, format("{}=\"{}\", no AirLoopHVAC objects found:", spmTypeName, spmName));
1857 0 : ShowContinueError(state, "Setpoint Manager needs an AirLoopHVAC to operate.");
1858 0 : ErrorsFound = true;
1859 : }
1860 4 : } break;
1861 :
1862 1 : case SPMType::ReturnAirBypass: {
1863 1 : auto *spmRAB = dynamic_cast<SPMReturnAirBypassFlow *>(spm);
1864 1 : assert(spmRAB != nullptr);
1865 :
1866 1 : if (state.dataHVACGlobal->NumPrimaryAirSys > 0) {
1867 1 : spmRAB->airLoopNum = Util::FindItemInList(
1868 1 : spmRAB->airLoopName, state.dataAirLoop->AirToZoneNodeInfo, &AirLoopZoneEquipConnectData::AirLoopName);
1869 1 : if (spmRAB->airLoopNum == 0) {
1870 0 : ShowSevereItemNotFound(state, eoh, "hvac_air_loop_name", spmRAB->airLoopName);
1871 0 : ErrorsFound = true;
1872 : }
1873 :
1874 1 : auto &primaryAirSystem = state.dataAirSystemsData->PrimaryAirSystems(spmRAB->airLoopNum);
1875 1 : if (primaryAirSystem.RABExists) {
1876 1 : spmRAB->rabMixInNodeNum = primaryAirSystem.RABMixInNode;
1877 1 : spmRAB->supMixInNodeNum = primaryAirSystem.SupMixInNode;
1878 1 : spmRAB->mixOutNodeNum = primaryAirSystem.MixOutNode;
1879 1 : spmRAB->rabSplitOutNodeNum = primaryAirSystem.RABSplitOutNode;
1880 1 : spmRAB->sysOutNodeNum = state.dataAirLoop->AirToZoneNodeInfo(spmRAB->airLoopNum).AirLoopSupplyNodeNum(1);
1881 1 : spmRAB->ctrlNodeNums.push_back(spmRAB->rabSplitOutNodeNum);
1882 : } else {
1883 0 : ShowSevereError(state, format("{}=\"{}\", no RAB in air loop found:", spmTypeName, spmName));
1884 0 : ShowContinueError(state, format("Air Loop=\"{}\".", spmRAB->airLoopName));
1885 0 : ErrorsFound = true;
1886 : }
1887 : } else {
1888 0 : ShowSevereError(state, format("{}=\"{}\", no AirLoopHVAC objects found:", spmTypeName, spmName));
1889 0 : ShowContinueError(state, "Setpoint Manager needs an AirLoopHVAC to operate.");
1890 0 : ErrorsFound = true;
1891 : }
1892 1 : } break;
1893 :
1894 2 : case SPMType::MZCoolingAverage:
1895 : case SPMType::MZHeatingAverage: {
1896 2 : auto *spmMZTemp = dynamic_cast<SPMMultiZoneTemp *>(spm);
1897 2 : assert(spmMZTemp != nullptr);
1898 :
1899 2 : if (state.dataHVACGlobal->NumPrimaryAirSys > 0) {
1900 2 : spmMZTemp->airLoopNum = Util::FindItemInList(
1901 2 : spmMZTemp->airLoopName, state.dataAirLoop->AirToZoneNodeInfo, &AirLoopZoneEquipConnectData::AirLoopName);
1902 2 : if (spmMZTemp->airLoopNum == 0) {
1903 0 : ShowSevereItemNotFound(state, eoh, "hvac_air_loop_name", spmMZTemp->airLoopName);
1904 0 : ErrorsFound = true;
1905 : }
1906 :
1907 2 : if (state.dataAirLoop->AirToZoneNodeInfo(spmMZTemp->airLoopNum).NumZonesCooled == 0) {
1908 0 : ShowSevereError(state, format("{}=\"{}\", no zones with cooling found:", spmTypeName, spmName));
1909 0 : ShowContinueError(state, format("Air Loop provides no cooling, Air Loop=\"{}\".", spmMZTemp->airLoopName));
1910 0 : ErrorsFound = true;
1911 : }
1912 : } else {
1913 0 : ShowSevereError(state, format("{}=\"{}\", no AirLoopHVAC objects found:", spmTypeName, spmName));
1914 0 : ShowContinueError(state, "Setpoint Manager needs an AirLoopHVAC to operate.");
1915 0 : ErrorsFound = true;
1916 : }
1917 2 : } break;
1918 :
1919 24 : case SPMType::MZMinHumAverage:
1920 : case SPMType::MZMaxHumAverage:
1921 : case SPMType::MZMinHum:
1922 : case SPMType::MZMaxHum: {
1923 24 : auto *spmMZHum = dynamic_cast<SPMMultiZoneHum *>(spm);
1924 24 : assert(spmMZHum != nullptr);
1925 :
1926 24 : if (state.dataHVACGlobal->NumPrimaryAirSys > 0) {
1927 24 : spmMZHum->airLoopNum = Util::FindItemInList(
1928 24 : spmMZHum->airLoopName, state.dataAirLoop->AirToZoneNodeInfo, &AirLoopZoneEquipConnectData::AirLoopName);
1929 24 : if (spmMZHum->airLoopNum == 0) {
1930 0 : ShowSevereItemNotFound(state, eoh, "hvac_air_loop_name", spmMZHum->airLoopName);
1931 0 : ErrorsFound = true;
1932 : } else {
1933 : // make sure humidity controlled zone
1934 24 : auto const &primaryAirSystem = state.dataAirSystemsData->PrimaryAirSystems(spmMZHum->airLoopNum);
1935 24 : auto const &airToZoneNode = state.dataAirLoop->AirToZoneNodeInfo(spmMZHum->airLoopNum);
1936 24 : bool HstatZoneFound = false;
1937 136 : for (int iZone = 1; iZone <= state.dataZoneCtrls->NumHumidityControlZones; ++iZone) {
1938 574 : for (int jZone = 1; jZone <= airToZoneNode.NumZonesCooled; ++jZone) {
1939 534 : if (state.dataZoneCtrls->HumidityControlZone(iZone).ActualZoneNum == airToZoneNode.CoolCtrlZoneNums(jZone)) {
1940 72 : HstatZoneFound = true;
1941 72 : break;
1942 : }
1943 : }
1944 : }
1945 :
1946 24 : if (!HstatZoneFound) {
1947 0 : ShowSevereError(state, format("{}=\"{}\", invalid humidistat specification", spmTypeName, spmName));
1948 0 : ShowContinueError(
1949 : state,
1950 0 : format("could not locate Humidistat in any of the zones served by the Air loop={}", primaryAirSystem.Name));
1951 0 : ErrorsFound = true;
1952 : }
1953 : }
1954 : } else {
1955 0 : ShowSevereError(state, format("{}=\"{}\", no AirLoopHVAC objects found:", spmTypeName, spmName));
1956 0 : ShowContinueError(state, "Setpoint Manager needs an AirLoopHVAC to operate.");
1957 0 : ErrorsFound = true;
1958 : }
1959 24 : } break;
1960 :
1961 3 : case SPMType::CondenserEnteringTemp: {
1962 3 : auto *spmCET = dynamic_cast<SPMCondenserEnteringTemp *>(spm);
1963 3 : assert(spmCET != nullptr);
1964 :
1965 : // Scan loops and find the loop index that includes the condenser cooling tower node used as setpoint
1966 : // Begin demand side loops ... When condenser is added becomes NumLoops
1967 12 : for (int LoopNum = 1; LoopNum <= state.dataHVACGlobal->NumCondLoops + state.dataHVACGlobal->NumPlantLoops; ++LoopNum) {
1968 9 : auto &plantLoop = state.dataPlnt->PlantLoop(LoopNum);
1969 18 : for (int ctrlNodeNum : spmCET->ctrlNodeNums) {
1970 9 : if (plantLoop.TempSetPointNodeNum != ctrlNodeNum) continue;
1971 :
1972 16 : for (auto const &branch : plantLoop.LoopSide(LoopSideLocation::Supply).Branch) {
1973 26 : for (auto const &comp : branch.Comp) {
1974 13 : if (comp.Type == PlantEquipmentType::CoolingTower_SingleSpd) {
1975 0 : ShowSevereError(state, format("{}=\"{}\", invalid tower found", spmTypeName, spmName));
1976 0 : ShowContinueError(state, format("Found SingleSpeed Cooling Tower, Cooling Tower={}", comp.Name));
1977 0 : ShowContinueError(state, "SingleSpeed cooling towers cannot be used with this setpoint manager.");
1978 0 : ErrorsFound = true;
1979 : }
1980 : }
1981 : }
1982 :
1983 : // Scan all attached chillers in the condenser loop index found to find the chiller index
1984 16 : for (int BranchNum = 1; BranchNum <= plantLoop.LoopSide(LoopSideLocation::Demand).TotalBranches; ++BranchNum) {
1985 13 : auto &branch = plantLoop.LoopSide(LoopSideLocation::Demand).Branch(BranchNum);
1986 :
1987 26 : for (int CompNum = 1; CompNum <= branch.TotalComponents; ++CompNum) {
1988 13 : auto &comp = branch.Comp(CompNum);
1989 13 : switch (comp.Type) {
1990 :
1991 4 : case PlantEquipmentType::Chiller_Absorption:
1992 : case PlantEquipmentType::Chiller_Indirect_Absorption:
1993 : case PlantEquipmentType::Chiller_CombTurbine:
1994 : case PlantEquipmentType::Chiller_ConstCOP:
1995 : case PlantEquipmentType::Chiller_Electric:
1996 : case PlantEquipmentType::Chiller_ElectricEIR:
1997 : case PlantEquipmentType::Chiller_DFAbsorption:
1998 : case PlantEquipmentType::Chiller_ElectricReformEIR:
1999 : case PlantEquipmentType::Chiller_EngineDriven: {
2000 : // Scan the supply side to find the chiller index and branch index on plantloop
2001 4 : DataPlant::PlantEquipmentType ChillerType = comp.Type;
2002 16 : for (int LoopNum2 = 1; LoopNum2 <= state.dataHVACGlobal->NumCondLoops + state.dataHVACGlobal->NumPlantLoops;
2003 : ++LoopNum2) {
2004 12 : auto &plantLoop2 = state.dataPlnt->PlantLoop(LoopNum2);
2005 12 : auto &loopSide2 = plantLoop2.LoopSide(LoopSideLocation::Supply);
2006 65 : for (int BranchNum2 = 1; BranchNum2 <= loopSide2.TotalBranches; ++BranchNum2) {
2007 53 : auto const &branch2 = loopSide2.Branch(BranchNum2);
2008 :
2009 102 : for (int CompNum2 = 1; CompNum2 <= branch2.TotalComponents; ++CompNum2) {
2010 53 : auto const &comp2 = branch2.Comp(CompNum2);
2011 53 : if (comp2.Type == ChillerType) {
2012 4 : spmCET->plantPloc = {LoopNum2, LoopSideLocation::Supply, BranchNum2, CompNum2};
2013 4 : break;
2014 : }
2015 : }
2016 : }
2017 : }
2018 4 : spmCET->chillerType = ChillerType;
2019 4 : spmCET->demandPloc = {LoopNum, LoopSideLocation::Demand, BranchNum, CompNum};
2020 4 : } break;
2021 :
2022 9 : default:
2023 9 : break;
2024 : }
2025 : } // for (comp)
2026 : } // for (branch)
2027 9 : } // if (
2028 : } // for (LoopNum)
2029 3 : } break;
2030 :
2031 2 : case SPMType::IdealCondenserEnteringTemp: {
2032 2 : auto *spmIdealCET = dynamic_cast<SPMIdealCondenserEnteringTemp *>(spm);
2033 2 : assert(spmIdealCET != nullptr);
2034 :
2035 2 : PlantEquipmentType InitType = PlantEquipmentType::Invalid;
2036 2 : int NumChiller = 0;
2037 :
2038 : // Scan loops and find the loop index that includes the condenser cooling tower node used as setpoint
2039 : // Begin demand side loops ... When condenser is added becomes NumLoops
2040 8 : for (int LoopNum = 1; LoopNum <= state.dataHVACGlobal->NumCondLoops + state.dataHVACGlobal->NumPlantLoops; ++LoopNum) {
2041 6 : auto &plantLoop = state.dataPlnt->PlantLoop(LoopNum);
2042 6 : auto &supplySide = plantLoop.LoopSide(LoopSideLocation::Supply);
2043 15 : for (int ctrlNodeNum : spmIdealCET->ctrlNodeNums) {
2044 9 : if (plantLoop.TempSetPointNodeNum != ctrlNodeNum) continue;
2045 :
2046 11 : for (int BranchNum = 1; BranchNum <= supplySide.TotalBranches; ++BranchNum) {
2047 9 : auto &branch = supplySide.Branch(BranchNum);
2048 18 : for (int CompNum = 1; CompNum <= branch.TotalComponents; ++CompNum) {
2049 9 : auto &comp = branch.Comp(CompNum);
2050 : // Check if cooling tower is single speed and generate and error
2051 9 : InitType = comp.Type;
2052 9 : if (InitType == PlantEquipmentType::CoolingTower_SingleSpd) {
2053 0 : ShowSevereError(state, format("{}=\"{}\", invalid cooling tower found", spmTypeName, spmName));
2054 0 : ShowContinueError(state, format("Found Single Speed Cooling Tower, Cooling Tower={}", comp.Name));
2055 0 : ShowContinueError(state, "SingleSpeed cooling towers cannot be used with this setpoint manager on each loop");
2056 0 : ErrorsFound = true;
2057 9 : } else if (InitType == PlantEquipmentType::CoolingTower_TwoSpd ||
2058 : InitType == PlantEquipmentType::CoolingTower_VarSpd) {
2059 3 : spmIdealCET->towerPlocs.push_back(PlantLocation(LoopNum, LoopSideLocation::Supply, BranchNum, CompNum));
2060 3 : spmIdealCET->numTowers++;
2061 : }
2062 : // Scan the pump on the condenser water loop
2063 9 : if (InitType == PlantEquipmentType::PumpVariableSpeed || InitType == PlantEquipmentType::PumpConstantSpeed) {
2064 2 : spmIdealCET->condenserPumpPloc = {LoopNum, LoopSideLocation::Supply, BranchNum, CompNum};
2065 : }
2066 : }
2067 : }
2068 :
2069 2 : auto &demandSide = plantLoop.LoopSide(LoopSideLocation::Demand);
2070 : // Scan all attached chillers in the condenser loop index found to find the chiller index
2071 10 : for (int BranchNum = 1; BranchNum <= demandSide.TotalBranches; ++BranchNum) {
2072 8 : auto &branch = demandSide.Branch(BranchNum);
2073 16 : for (int CompNum = 1; CompNum <= branch.TotalComponents; ++CompNum) {
2074 8 : auto &comp = branch.Comp(CompNum);
2075 8 : InitType = comp.Type;
2076 :
2077 : switch (InitType) {
2078 2 : case PlantEquipmentType::Chiller_Absorption:
2079 : case PlantEquipmentType::Chiller_Indirect_Absorption:
2080 : case PlantEquipmentType::Chiller_CombTurbine:
2081 : case PlantEquipmentType::Chiller_ConstCOP:
2082 : case PlantEquipmentType::Chiller_Electric:
2083 : case PlantEquipmentType::Chiller_ElectricEIR:
2084 : case PlantEquipmentType::Chiller_DFAbsorption:
2085 : case PlantEquipmentType::Chiller_ElectricReformEIR:
2086 : case PlantEquipmentType::Chiller_EngineDriven: {
2087 : // Scan the supply side to find the chiller index and branch index on plantloop
2088 2 : DataPlant::PlantEquipmentType ChillerType = comp.Type;
2089 8 : for (int LoopNum2 = 1; LoopNum2 <= state.dataHVACGlobal->NumCondLoops + state.dataHVACGlobal->NumPlantLoops;
2090 : ++LoopNum2) {
2091 6 : auto &plantLoop2 = state.dataPlnt->PlantLoop(LoopNum2);
2092 6 : auto &supplySide2 = plantLoop2.LoopSide(LoopSideLocation::Supply);
2093 31 : for (int BranchNum2 = 1; BranchNum2 <= supplySide2.TotalBranches; ++BranchNum2) {
2094 25 : auto &branch2 = supplySide2.Branch(BranchNum2);
2095 50 : for (int CompNum2 = 1; CompNum2 <= branch2.TotalComponents; ++CompNum2) {
2096 25 : auto &comp2 = branch2.Comp(CompNum2);
2097 25 : InitType = comp2.Type;
2098 25 : if (InitType == ChillerType) {
2099 2 : ++NumChiller;
2100 2 : spmIdealCET->chillerPloc = {LoopNum2, LoopSideLocation::Supply, BranchNum2, CompNum2};
2101 : // Scan the pump on the chilled water loop
2102 10 : for (int BranchNum3 = 1; BranchNum3 <= supplySide2.TotalBranches; ++BranchNum3) {
2103 8 : auto &branch3 = supplySide2.Branch(BranchNum3);
2104 16 : for (int CompNum3 = 1; CompNum3 <= branch3.TotalComponents; ++CompNum3) {
2105 8 : auto &comp3 = branch3.Comp(CompNum3);
2106 8 : InitType = comp3.Type;
2107 8 : if (InitType == PlantEquipmentType::PumpVariableSpeed ||
2108 : InitType == PlantEquipmentType::PumpConstantSpeed) {
2109 2 : spmIdealCET->chilledWaterPumpPloc = {
2110 : LoopNum2, LoopSideLocation::Supply, BranchNum3, CompNum3};
2111 : }
2112 : }
2113 : }
2114 : }
2115 : }
2116 : }
2117 : }
2118 2 : if (NumChiller > 1) {
2119 0 : ShowSevereError(state, format("{}=\"{}\", too many chillers found", spmTypeName, spmName));
2120 0 : ShowContinueError(state, "only one chiller can be used with this setpoint manager on each loop");
2121 0 : ShowContinueError(state, format("Found more than one chiller, chiller ={}", comp.Name));
2122 0 : ErrorsFound = true;
2123 : }
2124 2 : spmIdealCET->chillerType = ChillerType;
2125 2 : spmIdealCET->condenserPumpPloc.loopNum = LoopNum;
2126 2 : } break;
2127 :
2128 6 : default:
2129 6 : break;
2130 : } // switch (InitType)
2131 : } // for (CompNum)
2132 : } // for (BranchNum)
2133 2 : NumChiller = 0;
2134 6 : } // for (iNode)
2135 : } // for (LoopNum)
2136 2 : } break;
2137 :
2138 3961 : default:
2139 3961 : break;
2140 : } // switch (spm->type)
2141 : } // for (spm)
2142 :
2143 795 : VerifySetPointManagers(state, ErrorsFound);
2144 :
2145 795 : state.dataSetPointManager->InitSetPointManagersOneTimeFlag = false;
2146 : } // if (InitSetPointManagersOneTimeFlag)
2147 :
2148 2837534 : if (ErrorsFound) {
2149 0 : ShowFatalError(state, "InitSetPointManagers: Errors found in getting SetPointManager input.");
2150 : }
2151 : } // if (AirLoopInputsFilled)
2152 :
2153 5673606 : if ((state.dataGlobal->BeginEnvrnFlag && state.dataSetPointManager->InitSetPointManagersMyEnvrnFlag) ||
2154 2834481 : state.dataSetPointManager->InitSetPointManagersOneTimeFlag2) {
2155 :
2156 6234 : state.dataSetPointManager->ManagerOn = false;
2157 :
2158 45078 : for (auto *spm : state.dataSetPointManager->spms) {
2159 :
2160 38844 : switch (spm->type) {
2161 16039 : case SPMType::Scheduled: {
2162 16039 : auto *spmS = dynamic_cast<SPMScheduled *>(spm);
2163 16039 : assert(spmS != nullptr);
2164 :
2165 32408 : for (int ctrlNodeNum : spmS->ctrlNodeNums) {
2166 16369 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2167 16369 : Real64 SchedValue = GetCurrentScheduleValue(state, spmS->schedNum);
2168 : // Initialize scheduled setpoints
2169 16369 : switch (spmS->ctrlVar) {
2170 16290 : case HVAC::CtrlVarType::Temp: {
2171 16290 : node.TempSetPoint = SchedValue;
2172 16290 : } break;
2173 0 : case HVAC::CtrlVarType::MaxTemp: {
2174 0 : node.TempSetPointHi = SchedValue;
2175 0 : } break;
2176 0 : case HVAC::CtrlVarType::MinTemp: {
2177 0 : node.TempSetPointLo = SchedValue;
2178 0 : } break;
2179 0 : case HVAC::CtrlVarType::HumRat: {
2180 0 : node.HumRatSetPoint = SchedValue;
2181 0 : } break;
2182 71 : case HVAC::CtrlVarType::MaxHumRat: {
2183 71 : node.HumRatMax = SchedValue;
2184 71 : } break;
2185 8 : case HVAC::CtrlVarType::MinHumRat: {
2186 8 : node.HumRatMin = SchedValue;
2187 8 : } break;
2188 0 : case HVAC::CtrlVarType::MassFlowRate: {
2189 0 : node.MassFlowRateSetPoint = SchedValue;
2190 0 : } break;
2191 0 : case HVAC::CtrlVarType::MaxMassFlowRate: {
2192 0 : node.MassFlowRateMax = SchedValue;
2193 0 : } break;
2194 0 : case HVAC::CtrlVarType::MinMassFlowRate: {
2195 0 : node.MassFlowRateMin = SchedValue;
2196 0 : } break;
2197 0 : default:
2198 0 : break;
2199 : }
2200 16039 : }
2201 16039 : } break;
2202 :
2203 179 : case SPMType::ScheduledDual: {
2204 179 : auto *spmSD = dynamic_cast<SPMScheduledDual *>(spm);
2205 179 : assert(spmSD != nullptr);
2206 420 : for (int ctrlNodeNum : spmSD->ctrlNodeNums) {
2207 241 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2208 241 : if (spmSD->ctrlVar == HVAC::CtrlVarType::Temp) {
2209 241 : node.TempSetPointHi = GetCurrentScheduleValue(state, spmSD->schedNumHi);
2210 241 : node.TempSetPointLo = GetCurrentScheduleValue(state, spmSD->schedNumLo);
2211 241 : node.TempSetPoint = (node.TempSetPointHi + node.TempSetPointLo) / 2.0;
2212 : }
2213 179 : }
2214 179 : } break;
2215 :
2216 956 : case SPMType::OutsideAir: {
2217 956 : auto *spmOA = dynamic_cast<SPMOutsideAir *>(spm);
2218 956 : assert(spmOA != nullptr);
2219 :
2220 2142 : for (int NodeNum : spmOA->ctrlNodeNums) {
2221 1186 : spmOA->calculate(state); // Why is this calculated for every node?
2222 :
2223 1186 : auto &node = state.dataLoopNodes->Node(NodeNum);
2224 1186 : if (spmOA->ctrlVar == HVAC::CtrlVarType::Temp) {
2225 1186 : node.TempSetPoint = spmOA->setPt;
2226 0 : } else if (spmOA->ctrlVar == HVAC::CtrlVarType::MaxTemp) {
2227 0 : node.TempSetPointHi = spmOA->setPt;
2228 0 : } else if (spmOA->ctrlVar == HVAC::CtrlVarType::MinTemp) {
2229 0 : node.TempSetPointLo = spmOA->setPt;
2230 : }
2231 956 : }
2232 956 : } break;
2233 :
2234 230 : case SPMType::SZMinHum: {
2235 230 : auto *spmSZH = dynamic_cast<SPMSingleZoneHum *>(spm);
2236 230 : assert(spmSZH != nullptr);
2237 :
2238 230 : state.dataLoopNodes->Node(spmSZH->zoneNodeNum).MassFlowRate = 0.0;
2239 460 : for (int ctrlNodeNum : spmSZH->ctrlNodeNums) {
2240 230 : state.dataLoopNodes->Node(ctrlNodeNum).HumRatMin = 0.007;
2241 230 : }
2242 230 : } break;
2243 :
2244 380 : case SPMType::SZMaxHum: {
2245 380 : auto *spmSZH = dynamic_cast<SPMSingleZoneHum *>(spm);
2246 380 : assert(spmSZH != nullptr);
2247 :
2248 380 : state.dataLoopNodes->Node(spmSZH->zoneNodeNum).MassFlowRate = 0.0;
2249 760 : for (int ctrlNodeNum : spmSZH->ctrlNodeNums) {
2250 380 : state.dataLoopNodes->Node(ctrlNodeNum).HumRatMax = 0.011;
2251 380 : }
2252 380 : } break;
2253 :
2254 2081 : case SPMType::SZReheat: {
2255 2081 : auto *spmSZR = dynamic_cast<SPMSingleZoneReheat *>(spm);
2256 2081 : assert(spmSZR != nullptr);
2257 :
2258 2081 : state.dataLoopNodes->Node(spmSZR->zoneInletNodeNum).MassFlowRate = 0.0;
2259 2081 : state.dataLoopNodes->Node(spmSZR->zoneNodeNum).MassFlowRate = 0.0;
2260 2081 : if (spmSZR->ctrlVar == HVAC::CtrlVarType::Temp) {
2261 4343 : for (int ctrlNodeNum : spmSZR->ctrlNodeNums) {
2262 2262 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = 20.0; // Set the setpoint
2263 2081 : }
2264 : }
2265 :
2266 2081 : } break;
2267 :
2268 2470 : case SPMType::SZHeating:
2269 : case SPMType::SZCooling: {
2270 2470 : auto *spmSZT = dynamic_cast<SPMSingleZoneTemp *>(spm);
2271 2470 : assert(spmSZT != nullptr);
2272 :
2273 2470 : state.dataLoopNodes->Node(spmSZT->zoneInletNodeNum).MassFlowRate = 0.0;
2274 2470 : state.dataLoopNodes->Node(spmSZT->zoneNodeNum).MassFlowRate = 0.0;
2275 :
2276 2470 : if (spmSZT->ctrlVar == HVAC::CtrlVarType::Temp) {
2277 5068 : for (int ctrlNodeNum : spmSZT->ctrlNodeNums) {
2278 2598 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = 20.0; // Set the setpoint
2279 2470 : }
2280 : }
2281 2470 : } break;
2282 :
2283 13936 : case SPMType::MixedAir: {
2284 13936 : auto *spmMA = dynamic_cast<SPMMixedAir *>(spm);
2285 13936 : assert(spmMA != nullptr);
2286 :
2287 13936 : auto &refNode = state.dataLoopNodes->Node(spmMA->refNodeNum);
2288 13936 : auto &fanInNode = state.dataLoopNodes->Node(spmMA->fanInNodeNum);
2289 13936 : auto &fanOutNode = state.dataLoopNodes->Node(spmMA->fanOutNodeNum);
2290 :
2291 13936 : refNode.MassFlowRate = fanInNode.MassFlowRate = fanOutNode.MassFlowRate = 0.0;
2292 13936 : refNode.Temp = fanInNode.Temp = fanOutNode.Temp = 20.0;
2293 13936 : refNode.HumRat = fanInNode.HumRat = fanOutNode.HumRat = state.dataEnvrn->OutHumRat;
2294 13936 : refNode.Quality = fanInNode.Quality = fanOutNode.Quality = 1.0;
2295 13936 : refNode.Press = fanInNode.Press = fanOutNode.Press = state.dataEnvrn->OutBaroPress;
2296 13936 : refNode.Enthalpy = fanInNode.Enthalpy = fanOutNode.Enthalpy = PsyHFnTdbW(20.0, state.dataEnvrn->OutHumRat);
2297 :
2298 13936 : if (spmMA->ctrlVar == HVAC::CtrlVarType::Temp) {
2299 30054 : for (int ctrlNodeNum : spmMA->ctrlNodeNums) {
2300 16118 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = 20.0; // Set the setpoint
2301 13936 : }
2302 : }
2303 :
2304 13936 : } break;
2305 :
2306 75 : case SPMType::OutsideAirPretreat: {
2307 75 : auto *spmOAP = dynamic_cast<SPMOutsideAirPretreat *>(spm);
2308 75 : assert(spmOAP != nullptr);
2309 :
2310 75 : auto &refNode = state.dataLoopNodes->Node(spmOAP->refNodeNum);
2311 75 : auto &mixedOutNode = state.dataLoopNodes->Node(spmOAP->mixedOutNodeNum);
2312 75 : auto &oaInNode = state.dataLoopNodes->Node(spmOAP->oaInNodeNum);
2313 75 : auto &returnInNode = state.dataLoopNodes->Node(spmOAP->returnInNodeNum);
2314 :
2315 75 : refNode.MassFlowRate = mixedOutNode.MassFlowRate = oaInNode.MassFlowRate = returnInNode.MassFlowRate = 0.0;
2316 75 : refNode.Temp = mixedOutNode.Temp = oaInNode.Temp = returnInNode.Temp = 20.0;
2317 75 : refNode.HumRat = mixedOutNode.HumRat = oaInNode.HumRat = returnInNode.HumRat = state.dataEnvrn->OutHumRat;
2318 75 : refNode.Quality = mixedOutNode.Quality = oaInNode.Quality = returnInNode.Quality = 1.0;
2319 75 : refNode.Press = mixedOutNode.Press = oaInNode.Press = returnInNode.Press = state.dataEnvrn->OutBaroPress;
2320 75 : refNode.Enthalpy = mixedOutNode.Enthalpy = oaInNode.Enthalpy = returnInNode.Enthalpy = PsyHFnTdbW(20.0, state.dataEnvrn->OutHumRat);
2321 :
2322 150 : for (int ctrlNodeNum : spmOAP->ctrlNodeNums) {
2323 75 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2324 75 : if (spmOAP->ctrlVar == HVAC::CtrlVarType::Temp) {
2325 45 : node.TempSetPoint = 20.0; // Set the setpoint
2326 30 : } else if (spmOAP->ctrlVar == HVAC::CtrlVarType::MaxHumRat) {
2327 30 : node.HumRatMax = state.dataEnvrn->OutHumRat; // Set the setpoint
2328 0 : } else if (spmOAP->ctrlVar == HVAC::CtrlVarType::MinHumRat) {
2329 0 : node.HumRatMin = state.dataEnvrn->OutHumRat; // Set the setpoint
2330 0 : } else if (spmOAP->ctrlVar == HVAC::CtrlVarType::HumRat) {
2331 0 : node.HumRatSetPoint = state.dataEnvrn->OutHumRat; // Set the setpoint
2332 : }
2333 75 : }
2334 75 : } break;
2335 :
2336 198 : case SPMType::Warmest:
2337 : case SPMType::Coldest: {
2338 :
2339 396 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
2340 198 : if (spm->ctrlVar == HVAC::CtrlVarType::Temp) {
2341 198 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = 20.0; // Set the setpoint
2342 : }
2343 198 : }
2344 :
2345 198 : } break;
2346 :
2347 29 : case SPMType::WarmestTempFlow: {
2348 29 : auto *spmWTF = dynamic_cast<SPMWarmestTempFlow *>(spm);
2349 29 : assert(spmWTF != nullptr);
2350 :
2351 29 : if (spmWTF->ctrlVar == HVAC::CtrlVarType::Temp) {
2352 58 : for (int ctrlNodeNum : spmWTF->ctrlNodeNums) {
2353 29 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = 20.0; // Set the temperature setpoint
2354 29 : if (spmWTF->airLoopNum != 0) {
2355 21 : state.dataAirLoop->AirLoopFlow(spmWTF->airLoopNum).ReqSupplyFrac = 1.0; // PH 10/09/04 Set the flow
2356 21 : state.dataAirLoop->AirLoopControlInfo(spmWTF->airLoopNum).LoopFlowRateSet = true; // PH 10/09/04 Set the flag
2357 : }
2358 29 : }
2359 : }
2360 29 : } break;
2361 :
2362 8 : case SPMType::ReturnAirBypass: {
2363 8 : if (state.dataZoneEquip->ZoneEquipInputsFilled && state.dataAirLoop->AirLoopInputsFilled) {
2364 :
2365 6 : auto *spmRAB = dynamic_cast<SPMReturnAirBypassFlow *>(spm);
2366 6 : assert(spmRAB != nullptr);
2367 :
2368 6 : if (spmRAB->ctrlVar == HVAC::CtrlVarType::MassFlowRate) {
2369 6 : state.dataLoopNodes->Node(spmRAB->rabSplitOutNodeNum).MassFlowRateSetPoint = 0.0;
2370 : }
2371 : }
2372 8 : } break;
2373 :
2374 16 : case SPMType::MZCoolingAverage:
2375 : case SPMType::MZHeatingAverage: {
2376 16 : if (spm->ctrlVar == HVAC::CtrlVarType::Temp) {
2377 32 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
2378 16 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = 20.0; // Set the setpoint
2379 16 : }
2380 : }
2381 16 : } break;
2382 :
2383 78 : case SPMType::MZMinHumAverage:
2384 : case SPMType::MZMinHum: {
2385 156 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
2386 78 : state.dataLoopNodes->Node(ctrlNodeNum).HumRatMin = 0.007; // Set the setpoint
2387 78 : }
2388 78 : } break;
2389 :
2390 129 : case SPMType::MZMaxHumAverage:
2391 : case SPMType::MZMaxHum: {
2392 298 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
2393 169 : state.dataLoopNodes->Node(ctrlNodeNum).HumRatMax = 0.011; // Set the setpoint
2394 129 : }
2395 129 : } break;
2396 :
2397 1691 : case SPMType::FollowOutsideAirTemp: {
2398 1691 : auto *spmFOAT = dynamic_cast<SPMFollowOutsideAirTemp *>(spm);
2399 1691 : assert(spmFOAT != nullptr);
2400 1691 : bool isWetBulb = spmFOAT->refTempType == AirTempType::WetBulb;
2401 :
2402 3382 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
2403 1691 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2404 1691 : if (spmFOAT->ctrlVar == HVAC::CtrlVarType::Temp) {
2405 1691 : node.TempSetPoint = isWetBulb ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp;
2406 0 : } else if (spmFOAT->ctrlVar == HVAC::CtrlVarType::MaxTemp) {
2407 0 : node.TempSetPointHi = isWetBulb ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp;
2408 0 : } else if (spmFOAT->ctrlVar == HVAC::CtrlVarType::MinTemp) {
2409 0 : node.TempSetPointLo = isWetBulb ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp;
2410 : }
2411 1691 : }
2412 1691 : } break;
2413 :
2414 8 : case SPMType::FollowSystemNodeTemp: {
2415 8 : auto *spmFSNT = dynamic_cast<SPMFollowSysNodeTemp *>(spm);
2416 8 : assert(spmFSNT != nullptr);
2417 :
2418 8 : bool isWetBulb = spmFSNT->refTempType == AirTempType::WetBulb;
2419 8 : auto &refNode = state.dataLoopNodes->Node(spmFSNT->refNodeNum);
2420 16 : for (int ctrlNodeNum : spmFSNT->ctrlNodeNums) {
2421 8 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2422 8 : if (CheckOutAirNodeNumber(state, spmFSNT->refNodeNum)) {
2423 8 : refNode.SPMNodeWetBulbRepReq = isWetBulb;
2424 8 : if (spmFSNT->ctrlVar == HVAC::CtrlVarType::Temp) {
2425 8 : node.TempSetPoint = isWetBulb ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp;
2426 0 : } else if (spmFSNT->ctrlVar == HVAC::CtrlVarType::MaxTemp) {
2427 0 : node.TempSetPointHi = isWetBulb ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp;
2428 0 : } else if (spmFSNT->ctrlVar == HVAC::CtrlVarType::MinTemp) {
2429 0 : node.TempSetPointLo = isWetBulb ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp;
2430 : }
2431 : } else { // If reference node is a water node, then set RefTypeMode to NodeDryBulb
2432 :
2433 0 : if (refNode.FluidType == DataLoopNode::NodeFluidType::Water) {
2434 0 : spmFSNT->refTempType = AirTempType::DryBulb;
2435 0 : } else if (refNode.FluidType == DataLoopNode::NodeFluidType::Air) {
2436 0 : if (spmFSNT->refTempType == AirTempType::WetBulb) {
2437 0 : refNode.SPMNodeWetBulbRepReq = true;
2438 : }
2439 : }
2440 0 : if (spmFSNT->ctrlVar == HVAC::CtrlVarType::Temp) {
2441 0 : node.TempSetPoint = 20.0; // Set the setpoint
2442 0 : } else if (spmFSNT->ctrlVar == HVAC::CtrlVarType::MaxTemp) {
2443 0 : node.TempSetPointHi = 20.0; // Set the setpoint
2444 0 : } else if (spmFSNT->ctrlVar == HVAC::CtrlVarType::MinTemp) {
2445 0 : node.TempSetPointLo = 20.0; // Set the setpoint
2446 : }
2447 : }
2448 8 : }
2449 8 : } break;
2450 :
2451 150 : case SPMType::FollowGroundTemp: {
2452 150 : auto *spmFGT = dynamic_cast<SPMFollowGroundTemp *>(spm);
2453 150 : assert(spmFGT != nullptr);
2454 :
2455 150 : Real64 GroundTemp = state.dataEnvrn->GroundTemp[(int)spmFGT->refTempType];
2456 :
2457 300 : for (int ctrlNodeNum : spmFGT->ctrlNodeNums) {
2458 150 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2459 150 : if (spmFGT->ctrlVar == HVAC::CtrlVarType::Temp) {
2460 150 : node.TempSetPoint = GroundTemp;
2461 0 : } else if (spmFGT->ctrlVar == HVAC::CtrlVarType::MaxTemp) {
2462 0 : node.TempSetPointHi = GroundTemp;
2463 0 : } else if (spmFGT->ctrlVar == HVAC::CtrlVarType::MinTemp) {
2464 0 : node.TempSetPointLo = GroundTemp;
2465 : }
2466 150 : }
2467 150 : } break;
2468 :
2469 24 : case SPMType::CondenserEnteringTemp: {
2470 24 : auto *spmCER = dynamic_cast<SPMCondenserEnteringTemp *>(spm);
2471 24 : assert(spmCER != nullptr);
2472 24 : Real64 SchedValue = GetCurrentScheduleValue(state, spmCER->condenserEnteringTempSchedNum);
2473 48 : for (int ctrlNodeNum : spmCER->ctrlNodeNums) {
2474 24 : if (spmCER->ctrlVar == HVAC::CtrlVarType::Temp) {
2475 24 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = SchedValue;
2476 : }
2477 24 : }
2478 24 : } break;
2479 :
2480 15 : case SPMType::IdealCondenserEnteringTemp: {
2481 15 : auto *spmICER = dynamic_cast<SPMIdealCondenserEnteringTemp *>(spm);
2482 15 : assert(spmICER != nullptr);
2483 :
2484 15 : if (spmICER->ctrlVar == HVAC::CtrlVarType::Temp) {
2485 37 : for (int ctrlNodeNum : spmICER->ctrlNodeNums) {
2486 22 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = spmICER->maxCondenserEnteringTemp;
2487 15 : }
2488 : }
2489 15 : } break;
2490 :
2491 28 : case SPMType::SZOneStageCooling: {
2492 28 : auto *spmSZOSC = dynamic_cast<SPMSingleZoneOneStageCooling *>(spm);
2493 28 : assert(spmSZOSC != nullptr);
2494 :
2495 28 : if (spmSZOSC->ctrlVar == HVAC::CtrlVarType::Temp) {
2496 56 : for (int ctrlNodeNum : spmSZOSC->ctrlNodeNums) {
2497 28 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = spmSZOSC->coolingOffSetPt;
2498 28 : }
2499 : }
2500 28 : } break;
2501 :
2502 28 : case SPMType::SZOneStageHeating: {
2503 28 : auto *spmSZOSH = dynamic_cast<SPMSingleZoneOneStageHeating *>(spm);
2504 28 : assert(spmSZOSH != nullptr);
2505 :
2506 28 : if (spmSZOSH->ctrlVar == HVAC::CtrlVarType::Temp) {
2507 56 : for (int ctrlNodeNum : spmSZOSH->ctrlNodeNums) {
2508 28 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = spmSZOSH->heatingOffSetPt;
2509 28 : }
2510 : }
2511 :
2512 28 : } break;
2513 :
2514 37 : case SPMType::ChilledWaterReturnTemp: {
2515 37 : auto *spmRWT = dynamic_cast<SPMReturnWaterTemp *>(spm);
2516 37 : assert(spmRWT != nullptr);
2517 :
2518 37 : state.dataLoopNodes->Node(spmRWT->supplyNodeNum).TempSetPoint = spmRWT->minSetTemp;
2519 :
2520 37 : } break;
2521 :
2522 23 : case SPMType::HotWaterReturnTemp: {
2523 23 : auto *spmRWT = dynamic_cast<SPMReturnWaterTemp *>(spm);
2524 23 : assert(spmRWT != nullptr);
2525 :
2526 23 : state.dataLoopNodes->Node(spmRWT->supplyNodeNum).TempSetPoint = spmRWT->maxSetTemp;
2527 23 : } break;
2528 :
2529 24 : case SPMType::SystemNodeTemp:
2530 : case SPMType::SystemNodeHum: {
2531 48 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
2532 24 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
2533 24 : spm->calculate(state);
2534 24 : switch (spm->ctrlVar) {
2535 18 : case HVAC::CtrlVarType::Temp: {
2536 18 : node.TempSetPoint = spm->setPt;
2537 18 : } break;
2538 0 : case HVAC::CtrlVarType::MaxTemp: {
2539 0 : node.TempSetPointHi = spm->setPt;
2540 0 : } break;
2541 0 : case HVAC::CtrlVarType::MinTemp: {
2542 0 : node.TempSetPointLo = spm->setPt;
2543 0 : } break;
2544 0 : case HVAC::CtrlVarType::HumRat: {
2545 0 : node.HumRatSetPoint = spm->setPt;
2546 0 : } break;
2547 6 : case HVAC::CtrlVarType::MaxHumRat: {
2548 6 : node.HumRatMax = spm->setPt;
2549 6 : } break;
2550 0 : case HVAC::CtrlVarType::MinHumRat: {
2551 0 : node.HumRatMin = spm->setPt;
2552 0 : } break;
2553 0 : default:
2554 0 : break;
2555 : }
2556 24 : }
2557 24 : } break;
2558 :
2559 12 : default:
2560 12 : break;
2561 : } // switch (spm->type)
2562 : } // for (spm)
2563 :
2564 6234 : state.dataSetPointManager->InitSetPointManagersMyEnvrnFlag = false;
2565 6234 : if (!state.dataSetPointManager->InitSetPointManagersOneTimeFlag) state.dataSetPointManager->InitSetPointManagersOneTimeFlag2 = false;
2566 :
2567 6234 : if (ErrorsFound) {
2568 0 : ShowFatalError(state, "InitSetPointManagers: Errors found. Program Terminates.");
2569 : }
2570 :
2571 : } // end begin environment inits
2572 :
2573 2839125 : if (!state.dataGlobal->BeginEnvrnFlag) {
2574 2827478 : state.dataSetPointManager->InitSetPointManagersMyEnvrnFlag = true;
2575 : }
2576 2839125 : } // InitSetPointManagers()
2577 :
2578 2832891 : void SimSetPointManagers(EnergyPlusData &state)
2579 : {
2580 : // SUBROUTINE INFORMATION:
2581 : // AUTHOR Fred Buhl
2582 : // DATE WRITTEN July 1998
2583 : // MODIFIED Shirey/Raustad (FSEC), Jan 2004
2584 : // Nov 2004 M. J. Witte, GARD Analytics, Inc.
2585 : // Add new setpoint managers:
2586 : // SET POINT MANAGER:SINGLE ZONE HEATING and
2587 : // SET POINT MANAGER:SINGLE ZONE COOLING
2588 : // Work supported by ASHRAE research project 1254-RP
2589 : // Haves Oct 2004
2590 : // July 2010 B.A. Nigusse, FSEC/UCF
2591 : // Added new setpoint managers
2592 : // SetpointManager:MultiZone:Heating:Average
2593 : // SetpointManager:MultiZone:Cooling:Average
2594 : // SetpointManager:MultiZone:MinimumHumidity:Average
2595 : // SetpointManager:MultiZone:MaximumHumidity:Average
2596 : // Aug 2010 B.A. Nigusse, FSEC/UCF
2597 : // Added new setpoint managers:
2598 : // SetpointManager:MultiZone:Humidity:Minimum
2599 : // SetpointManager:MultiZone:Humidity:Maximum
2600 : // Aug 2014 Rick Strand, UIUC
2601 : // SetpointManager:ScheduleTES (internally defined)
2602 : // Jan 2022 Wooyoung Jung, Jeremy Lerond and Jian Zhang, PNNL
2603 : // Added new setpoint managers:
2604 : // SetpointManager:SystemNodeReset:Temperature
2605 : // SetpointManager:SystemNodeReset:Humidity
2606 :
2607 : // PURPOSE OF THIS SUBROUTINE
2608 : // Loop over all the Setpoint Managers and invoke the correct
2609 : // Setpoint Manager algorithm.
2610 :
2611 20747344 : for (auto *spm : state.dataSetPointManager->spms) {
2612 17914453 : if (spm->type != SPMType::MixedAir && spm->type != SPMType::OutsideAirPretreat) {
2613 10812909 : spm->calculate(state);
2614 : }
2615 : }
2616 2832891 : } // SimSetPointManagers()
2617 :
2618 7013818 : void SPMScheduled::calculate(EnergyPlusData &state)
2619 : {
2620 : // SUBROUTINE INFORMATION:
2621 : // AUTHOR Fred Buhl
2622 : // DATE WRITTEN July 1998
2623 :
2624 : // PURPOSE OF THIS SUBROUTINE:
2625 : // Set the setpoint using a simple schedule.
2626 7013818 : this->setPt = GetCurrentScheduleValue(state, this->schedNum);
2627 7013818 : } // SPMScheduled::calculate()
2628 :
2629 3422 : void SPMTESScheduled::calculate(EnergyPlusData &state)
2630 : {
2631 : // SUBROUTINE INFORMATION:
2632 : // AUTHOR Rick Strand
2633 : // DATE WRITTEN Aug 2014
2634 :
2635 : // PURPOSE OF THIS SUBROUTINE:
2636 : // Set the setpoint using a simple schedule, then modify the value based on TES simple controls logic
2637 :
2638 : // METHODOLOGY EMPLOYED:
2639 : // Modified schedule setpoint manager logic
2640 :
2641 : // Locals
2642 3422 : Real64 constexpr OnVal(0.5);
2643 :
2644 3422 : Real64 CurSchValOnPeak = GetCurrentScheduleValue(state, this->schedNum);
2645 3422 : Real64 CurSchValCharge = GetCurrentScheduleValue(state, this->schedNumCharge);
2646 :
2647 : // CtrlType bug
2648 : // if (this->CompOpType == DataPlant::CtrlType::CoolingOp) { // this is some sort of chiller
2649 3422 : if (this->compOpType == DataPlant::CtrlType::HeatingOp) { // this is some sort of chiller
2650 1711 : if (CurSchValOnPeak >= OnVal) {
2651 476 : this->setPt = this->nonChargeCHWTemp;
2652 1235 : } else if (CurSchValCharge < OnVal) {
2653 693 : this->setPt = this->nonChargeCHWTemp;
2654 : } else {
2655 542 : this->setPt = this->chargeCHWTemp;
2656 : }
2657 : // CtrlType Bug
2658 : // } else if (this->CompOpType == DataPlant::CtrlType::DualOp) { // this is some sort of ice storage system
2659 1711 : } else if (this->compOpType == DataPlant::CtrlType::CoolingOp) { // this is some sort of ice storage system
2660 1711 : this->setPt = this->nonChargeCHWTemp;
2661 : }
2662 3422 : } // SPMTESSScheduled::calculate()
2663 :
2664 33649 : void SPMScheduledDual::calculate(EnergyPlusData &state)
2665 : {
2666 : // SUBROUTINE INFORMATION:
2667 : // AUTHOR Richard Liesen
2668 : // DATE WRITTEN May 2004
2669 :
2670 : // PURPOSE OF THIS SUBROUTINE:
2671 : // Set the both setpoint using a simple schedule.
2672 33649 : this->setPtHi = GetCurrentScheduleValue(state, this->schedNumHi);
2673 33649 : this->setPtLo = GetCurrentScheduleValue(state, this->schedNumLo);
2674 33649 : } // SPMScheduledDual::calculate()
2675 :
2676 504036 : void SPMOutsideAir::calculate(EnergyPlusData &state)
2677 : {
2678 504036 : Real64 SchedVal = (this->schedNum > 0) ? GetCurrentScheduleValue(state, this->schedNum) : 0.0;
2679 :
2680 504036 : if (SchedVal == 2.0) {
2681 0 : this->setPt = interpSetPoint(this->low2, this->high2, state.dataEnvrn->OutDryBulbTemp, this->lowSetPt2, this->highSetPt2);
2682 : } else {
2683 504036 : if ((this->schedNum > 0) && (SchedVal != 1.0)) { // Since schedule is optional, only check this if the user entered a schedule
2684 0 : ++this->setPtErrorCount;
2685 0 : if (this->setPtErrorCount <= 10) {
2686 0 : ShowSevereError(state,
2687 0 : format("Schedule Values for the Outside Air Setpoint Manager = {} are something other than 1 or 2.", this->Name));
2688 0 : ShowContinueError(state, format("...the value for the schedule currently is {}", SchedVal));
2689 0 : ShowContinueError(state, "...the value is being interpreted as 1 for this run but should be fixed.");
2690 : } else {
2691 0 : ShowRecurringSevereErrorAtEnd(
2692 : state,
2693 0 : format("Schedule Values for the Outside Air Setpoint Manager = {} are something other than 1 or 2.", this->Name),
2694 0 : this->invalidSchedValErrorIndex);
2695 : }
2696 : }
2697 504036 : this->setPt = interpSetPoint(this->low1, this->high1, state.dataEnvrn->OutDryBulbTemp, this->lowSetPt1, this->highSetPt1);
2698 : }
2699 :
2700 504036 : } // SPMOutsideAir::calculate()
2701 :
2702 1154210 : void SPMSingleZoneReheat::calculate(EnergyPlusData &state)
2703 : {
2704 : // SUBROUTINE INFORMATION:
2705 : // AUTHOR Fred Buhl
2706 : // DATE WRITTEN May 2000
2707 :
2708 : // PURPOSE OF THIS SUBROUTINE:
2709 : // From the heating or cooling load of the control zone, calculate the supply air setpoint
2710 : // needed to meet that zone load
2711 :
2712 : // Using/Aliasing
2713 : using namespace DataZoneEnergyDemands;
2714 : using Psychrometrics::PsyTdbFnHW;
2715 :
2716 : Real64 TSetPt;
2717 :
2718 1154210 : auto const &zoneInletNode = state.dataLoopNodes->Node(this->zoneInletNodeNum);
2719 :
2720 : // changed from MinOAFrac, now updates to current oa fraction for improve deadband control
2721 1154210 : Real64 OAFrac = state.dataAirLoop->AirLoopFlow(this->airLoopNum).OAFrac;
2722 1154210 : Real64 ZoneMassFlow = zoneInletNode.MassFlowRate;
2723 :
2724 1154210 : auto const &zoneSysEnergyDemand = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->ctrlZoneNum);
2725 1154210 : Real64 ZoneLoad = zoneSysEnergyDemand.TotalOutputRequired;
2726 1154210 : Real64 ZoneLoadToCoolSetPt = zoneSysEnergyDemand.OutputRequiredToCoolingSP;
2727 1154210 : Real64 ZoneLoadToHeatSetPt = zoneSysEnergyDemand.OutputRequiredToHeatingSP;
2728 1154210 : bool DeadBand = state.dataZoneEnergyDemand->DeadBandOrSetback(this->ctrlZoneNum);
2729 1154210 : Real64 ZoneTemp = state.dataLoopNodes->Node(this->zoneNodeNum).Temp;
2730 :
2731 : Real64 TMixAtMinOA;
2732 1154210 : if (this->oaInNodeNum > 0) {
2733 1146738 : auto const &oaInNode = state.dataLoopNodes->Node(this->oaInNodeNum);
2734 1146738 : auto const &retNode = state.dataLoopNodes->Node(this->retNodeNum);
2735 1146738 : Real64 HumRatMixAtMinOA = (1.0 - OAFrac) * retNode.HumRat + OAFrac * oaInNode.HumRat;
2736 1146738 : Real64 EnthMixAtMinOA = (1.0 - OAFrac) * retNode.Enthalpy + OAFrac * oaInNode.Enthalpy;
2737 1146738 : TMixAtMinOA = PsyTdbFnHW(EnthMixAtMinOA, HumRatMixAtMinOA);
2738 : } else {
2739 7472 : TMixAtMinOA = state.dataLoopNodes->Node(this->loopInNodeNum).Temp;
2740 : }
2741 :
2742 : Real64 FanDeltaT;
2743 1154210 : if (this->fanOutNodeNum > 0 && this->fanInNodeNum > 0) {
2744 933423 : FanDeltaT = state.dataLoopNodes->Node(this->fanOutNodeNum).Temp - state.dataLoopNodes->Node(this->fanInNodeNum).Temp;
2745 : } else {
2746 220787 : FanDeltaT = 0.0;
2747 : }
2748 :
2749 1154210 : Real64 TSupNoHC = TMixAtMinOA + FanDeltaT;
2750 1154210 : Real64 CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
2751 1154210 : Real64 ExtrRateNoHC = CpAir * ZoneMassFlow * (TSupNoHC - ZoneTemp);
2752 1154210 : if (ZoneMassFlow <= HVAC::SmallMassFlow) {
2753 314821 : TSetPt = TSupNoHC;
2754 :
2755 839389 : } else if (DeadBand || std::abs(ZoneLoad) < HVAC::SmallLoad) {
2756 : // if air with no active heating or cooling provides cooling
2757 64728 : if (ExtrRateNoHC < 0.0) {
2758 : // if still in deadband, do no active heating or cooling;
2759 : // if below heating setpoint, set a supply temp that will cool to the heating setpoint
2760 26881 : TSetPt = (ExtrRateNoHC >= ZoneLoadToHeatSetPt) ? TSupNoHC : (ZoneTemp + ZoneLoadToHeatSetPt / (CpAir * ZoneMassFlow));
2761 :
2762 : // if air with no active heating or cooling provides heating
2763 37847 : } else if (ExtrRateNoHC > 0.0) {
2764 : // if still in deadband, do no active heating or cooling;
2765 : // if above cooling setpoint, set a supply temp that will heat to the cooling setpoint
2766 37847 : TSetPt = (ExtrRateNoHC <= ZoneLoadToCoolSetPt) ? TSupNoHC : (ZoneTemp + ZoneLoadToCoolSetPt / (CpAir * ZoneMassFlow));
2767 :
2768 : } else {
2769 0 : TSetPt = TSupNoHC;
2770 : }
2771 :
2772 774661 : } else if (ZoneLoad < (-1.0 * HVAC::SmallLoad)) {
2773 349992 : Real64 TSetPt1 = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlow);
2774 349992 : Real64 TSetPt2 = ZoneTemp + ZoneLoadToHeatSetPt / (CpAir * ZoneMassFlow);
2775 349992 : TSetPt = (TSetPt1 <= TSupNoHC) ? TSetPt1 : ((TSetPt2 > TSupNoHC) ? TSetPt2 : TSupNoHC);
2776 :
2777 424669 : } else if (ZoneLoad > HVAC::SmallLoad) {
2778 424669 : Real64 TSetPt1 = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlow);
2779 424669 : Real64 TSetPt2 = ZoneTemp + ZoneLoadToCoolSetPt / (CpAir * ZoneMassFlow);
2780 424669 : TSetPt = (TSetPt1 >= TSupNoHC) ? TSetPt1 : ((TSetPt2 < TSupNoHC) ? TSetPt2 : TSupNoHC);
2781 :
2782 : } else {
2783 0 : TSetPt = TSupNoHC;
2784 : }
2785 :
2786 1154210 : this->setPt = std::clamp(TSetPt, this->minSetTemp, this->maxSetTemp);
2787 1154210 : } // SPMSZReheat::calculate()
2788 :
2789 830303 : void SPMSingleZoneTemp::calculate(EnergyPlusData &state)
2790 : {
2791 : // SUBROUTINE INFORMATION:
2792 : // AUTHOR M. J. Witte based on CalcSingZoneRhSetPoint by Fred Buhl,
2793 : // Work supported by ASHRAE research project 1254-RP
2794 : // DATE WRITTEN November 2004
2795 :
2796 : // PURPOSE OF THIS SUBROUTINE:
2797 : // From the heating load of the control zone, calculate the supply air setpoint
2798 : // needed to meet that zone load (based on CalcSingZoneRhSetPoint)
2799 830303 : auto const &zoneInletNode = state.dataLoopNodes->Node(this->zoneInletNodeNum);
2800 :
2801 : // This function handles both heating and cooling
2802 830303 : auto const &zoneEnergyDemand = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->ctrlZoneNum);
2803 830303 : Real64 ZoneLoadToSP =
2804 830303 : (this->type == SPMType::SZHeating) ? zoneEnergyDemand.OutputRequiredToHeatingSP : zoneEnergyDemand.OutputRequiredToCoolingSP;
2805 :
2806 830303 : Real64 ZoneTemp = state.dataLoopNodes->Node(this->zoneNodeNum).Temp;
2807 830303 : if (zoneInletNode.MassFlowRate <= HVAC::SmallMassFlow) {
2808 202172 : this->setPt = (this->type == SPMType::SZHeating) ? this->minSetTemp : this->maxSetTemp;
2809 : } else {
2810 628131 : Real64 CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
2811 628131 : this->setPt = ZoneTemp + ZoneLoadToSP / (CpAir * zoneInletNode.MassFlowRate);
2812 628131 : this->setPt = std::clamp(this->setPt, this->minSetTemp, this->maxSetTemp);
2813 : }
2814 830303 : } // SPMSZTemp::calculate()
2815 :
2816 86412 : void SPMSingleZoneOneStageCooling::calculate(EnergyPlusData &state)
2817 : {
2818 : // SUBROUTINE INFORMATION:
2819 : // AUTHOR B. Griffith
2820 : // DATE WRITTEN August 2013
2821 :
2822 : // PURPOSE OF THIS SUBROUTINE:
2823 : // calculate the setpoint for staged on/off cooling
2824 :
2825 : // METHODOLOGY EMPLOYED:
2826 : // Evaluate stage in zone energy demand structure and choose setpoint accordingly
2827 :
2828 86412 : this->setPt = (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->ctrlZoneNum).StageNum >= 0) ? this->coolingOffSetPt : this->coolingOnSetPt;
2829 : // negative so a cooling stage is set
2830 86412 : } // SPMSingleZoneOneStageCooling::calculate()
2831 :
2832 86412 : void SPMSingleZoneOneStageHeating::calculate(EnergyPlusData &state)
2833 : {
2834 : // SUBROUTINE INFORMATION:
2835 : // AUTHOR B. Griffith
2836 : // DATE WRITTEN August 2013
2837 :
2838 : // PURPOSE OF THIS SUBROUTINE:
2839 : // calculate the setpoint for staged on/off control
2840 :
2841 : // METHODOLOGY EMPLOYED:
2842 : // Evaluate stage in zone energy demand structure and choose setpoint accordingly
2843 :
2844 86412 : this->setPt = (state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->ctrlZoneNum).StageNum <= 0) ? this->heatingOffSetPt : this->heatingOnSetPt;
2845 86412 : } // SPMSingleZoneOneStageHeating::calculate()
2846 :
2847 216413 : void SPMSingleZoneHum::calculate(EnergyPlusData &state)
2848 : {
2849 : // SUBROUTINE INFORMATION:
2850 : // AUTHOR Fred Buhl
2851 : // DATE WRITTEN October 2000
2852 : // MODIFIED Shirey/Raustad Jan 2002
2853 : // Gu, Dec 2007
2854 :
2855 : // PURPOSE OF THIS SUBROUTINE:
2856 : // From humidity load of the control zone, calculate the supply air humidity
2857 : // needed to meet the minimum humidity setpoint
2858 :
2859 : // METHODOLOGY EMPLOYED:
2860 : // Zone moisture load from ZoneTempPredictorCorrector (via DataZoneEnergyDemands)
2861 : // is used to calculate the minimum supply air humidity ratio
2862 : // needed to meet minimum zone relative humidity requirement
2863 :
2864 : // Using/Aliasing
2865 : using Psychrometrics::PsyWFnTdbRhPb;
2866 :
2867 : // Only use one zone for now
2868 216413 : auto &zoneNode = state.dataLoopNodes->Node(this->zoneNodeNum);
2869 :
2870 216413 : Real64 ZoneMassFlow = zoneNode.MassFlowRate;
2871 216413 : if (ZoneMassFlow > HVAC::SmallMassFlow) {
2872 213898 : auto const &zoneMoistureDemand = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(this->ctrlZoneNum);
2873 213898 : Real64 MoistureLoad =
2874 213898 : (this->type == SPMType::SZMinHum) ? zoneMoistureDemand.OutputRequiredToHumidifyingSP : zoneMoistureDemand.OutputRequiredToDehumidifyingSP;
2875 :
2876 : // This function handles both SZMinHum and SZMaxHum
2877 : // MoistureLoad (negative for dehumidification) may be so large that a negative humrat results, cap at 0.00001
2878 213898 : Real64 MaxHum = (this->type == SPMType::SZMinHum) ? 0.0 : 0.00001;
2879 :
2880 : // Positive Humidity Ratio MoistureLoad means a humidification load and only humidifying can raise up to a minimum
2881 : // IF(MoistureLoad .GT. 0.0) SZMinHumSetPtMgr(SetPtMgrNum)%SetPt = SupplyAirHumRat
2882 213898 : this->setPt = max(MaxHum, zoneNode.HumRat + MoistureLoad / ZoneMassFlow);
2883 :
2884 : // This hum rat is currently used in Controller:Simple, control variable "TEMPandHUMRAT" (Jan 2004)
2885 : // Negative MoistureLoad means a dehumidification load
2886 : } else {
2887 2515 : this->setPt = 0.0;
2888 : }
2889 216413 : } // SPMSingleZoneHum::calculate()
2890 :
2891 7079405 : void SPMMixedAir::calculate(EnergyPlusData &state)
2892 : {
2893 : // SUBROUTINE INFORMATION:
2894 : // AUTHOR Fred Buhl
2895 : // DATE WRITTEN May 2001
2896 :
2897 : // PURPOSE OF THIS SUBROUTINE:
2898 : // Starting with the setpoint at the reference node, subtract the supply fan
2899 : // temperature rise and set the resulting temperature at the mixed air node.
2900 : // Using/Aliasing
2901 : using EMSManager::CheckIfNodeSetPointManagedByEMS;
2902 :
2903 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2904 7079405 : auto &fanInNode = state.dataLoopNodes->Node(this->fanInNodeNum);
2905 7079405 : auto &fanOutNode = state.dataLoopNodes->Node(this->fanOutNodeNum);
2906 7079405 : auto &refNode = state.dataLoopNodes->Node(this->refNodeNum);
2907 :
2908 7079405 : this->freezeCheckEnable = false;
2909 :
2910 7079405 : if (!state.dataGlobal->SysSizingCalc && this->mySetPointCheckFlag) {
2911 :
2912 1646 : if (refNode.TempSetPoint == SensedNodeFlagValue) {
2913 0 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
2914 0 : ShowSevereError(state,
2915 0 : format("CalcMixedAirSetPoint: Missing reference temperature setpoint for Mixed Air Setpoint Manager {}", this->Name));
2916 0 : ShowContinueError(state, format("Node Referenced ={}", state.dataLoopNodes->NodeID(this->refNodeNum)));
2917 0 : ShowContinueError(
2918 : state, " use an additional Setpoint Manager with Control Variable = \"Temperature\" to establish a setpoint at this node.");
2919 0 : state.dataHVACGlobal->SetPointErrorFlag = true;
2920 : } else {
2921 : // need call to check if this is the target of an EnergyManagementSystem:Actuator object
2922 0 : CheckIfNodeSetPointManagedByEMS(state, this->refNodeNum, HVAC::CtrlVarType::Temp, state.dataHVACGlobal->SetPointErrorFlag);
2923 0 : if (state.dataHVACGlobal->SetPointErrorFlag) {
2924 0 : ShowSevereError(
2925 0 : state, format("CalcMixedAirSetPoint: Missing reference temperature setpoint for Mixed Air Setpoint Manager {}", this->Name));
2926 0 : ShowContinueError(state, format("Node Referenced ={}", state.dataLoopNodes->NodeID(this->refNodeNum)));
2927 0 : ShowContinueError(
2928 : state, " use an additional Setpoint Manager with Control Variable = \"Temperature\" to establish a setpoint at this node.");
2929 0 : ShowContinueError(state, "Or add EMS Actuator to provide temperature setpoint at this node");
2930 : }
2931 : }
2932 : }
2933 :
2934 1646 : this->mySetPointCheckFlag = false;
2935 : }
2936 :
2937 7079405 : this->setPt = refNode.TempSetPoint - (fanOutNode.Temp - fanInNode.Temp);
2938 7079405 : if (this->coolCoilInNodeNum > 0 && this->coolCoilOutNodeNum > 0) {
2939 2057 : auto &coolCoilInNode = state.dataLoopNodes->Node(this->coolCoilInNodeNum);
2940 2057 : auto &coolCoilOutNode = state.dataLoopNodes->Node(this->coolCoilOutNodeNum);
2941 2057 : Real64 dtFan = fanOutNode.Temp - fanInNode.Temp;
2942 2057 : Real64 dtCoolCoil = coolCoilInNode.Temp - coolCoilOutNode.Temp;
2943 2057 : if (dtCoolCoil > 0.0 && this->minCoolCoilOutTemp > state.dataEnvrn->OutDryBulbTemp) {
2944 0 : this->freezeCheckEnable = true;
2945 0 : if (refNode.Temp == coolCoilOutNode.Temp) { // blow through
2946 0 : this->setPt = max(refNode.TempSetPoint, this->minCoolCoilOutTemp) - dtFan + dtCoolCoil;
2947 0 : } else if (this->refNodeNum != this->coolCoilOutNodeNum) { // // draw through Ref node is outlet node
2948 0 : this->setPt = max(refNode.TempSetPoint - dtFan, this->minCoolCoilOutTemp) + dtCoolCoil;
2949 : } else {
2950 0 : this->setPt = max(refNode.TempSetPoint, this->minCoolCoilOutTemp) + dtCoolCoil;
2951 : }
2952 : }
2953 : }
2954 7079405 : } // SPMMixedAir::calculate()
2955 :
2956 22139 : void SPMOutsideAirPretreat::calculate(EnergyPlusData &state)
2957 : {
2958 : // SUBROUTINE INFORMATION:
2959 : // AUTHOR M. J. Witte based on CalcMixedAirSetPoint by Fred Buhl,
2960 : // Work supported by ASHRAE research project 1254-RP
2961 : // DATE WRITTEN January 2005
2962 : // MODIFIED Witte (GARD), Sep 2006
2963 : // Griffith( NREL), May 2009, added EMS setpoint checks
2964 :
2965 : // PURPOSE OF THIS SUBROUTINE:
2966 : // Starting with the setpoint at the reference node, determine the required
2967 : // outside air inlet conditions which when mixed with return air result in
2968 : // the reference setpoint at the mixed air node.
2969 : // (based on CalcMixedAirSetPoint)
2970 :
2971 : // Using/Aliasing
2972 : using EMSManager::CheckIfNodeSetPointManagedByEMS;
2973 :
2974 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2975 22139 : Real64 ReturnInValue = 0; // return air inlet node mass flow rate
2976 22139 : Real64 RefNodeSetPoint = 0; // setpoint at reference node
2977 22139 : Real64 MinSetPoint = 0; // minimum allowed setpoint
2978 22139 : Real64 MaxSetPoint = 0; // maximum allowed setpoint
2979 :
2980 22139 : auto &refNode = state.dataLoopNodes->Node(this->refNodeNum);
2981 22139 : auto &mixedOutNode = state.dataLoopNodes->Node(this->mixedOutNodeNum);
2982 22139 : auto &oaInNode = state.dataLoopNodes->Node(this->oaInNodeNum);
2983 22139 : auto &returnInNode = state.dataLoopNodes->Node(this->returnInNodeNum);
2984 :
2985 22139 : bool isHumiditySetPoint = false;
2986 :
2987 22139 : switch (this->ctrlVar) {
2988 11459 : case HVAC::CtrlVarType::Temp: { // 'Temperature'
2989 11459 : RefNodeSetPoint = refNode.TempSetPoint;
2990 11459 : ReturnInValue = returnInNode.Temp;
2991 11459 : MinSetPoint = this->minSetTemp;
2992 11459 : MaxSetPoint = this->maxSetTemp;
2993 11459 : } break;
2994 10680 : case HVAC::CtrlVarType::MaxHumRat: { // 'HUMRATMAX'
2995 10680 : RefNodeSetPoint = refNode.HumRatMax;
2996 10680 : ReturnInValue = returnInNode.HumRat;
2997 10680 : MinSetPoint = this->minSetHum;
2998 10680 : MaxSetPoint = this->maxSetHum;
2999 10680 : isHumiditySetPoint = true;
3000 10680 : } break;
3001 0 : case HVAC::CtrlVarType::MinHumRat: { // 'HUMRATMIN'
3002 0 : RefNodeSetPoint = refNode.HumRatMin;
3003 0 : ReturnInValue = returnInNode.HumRat;
3004 0 : MinSetPoint = this->minSetHum;
3005 0 : MaxSetPoint = this->maxSetHum;
3006 0 : isHumiditySetPoint = true;
3007 0 : } break;
3008 0 : case HVAC::CtrlVarType::HumRat: { // 'HumidityRatio'
3009 0 : RefNodeSetPoint = refNode.HumRatSetPoint;
3010 0 : ReturnInValue = returnInNode.HumRat;
3011 0 : MinSetPoint = this->minSetHum;
3012 0 : MaxSetPoint = this->maxSetHum;
3013 0 : isHumiditySetPoint = true;
3014 0 : } break;
3015 0 : default:
3016 0 : break;
3017 : }
3018 :
3019 22139 : if (!state.dataGlobal->SysSizingCalc && this->mySetPointCheckFlag) {
3020 9 : this->mySetPointCheckFlag = false;
3021 9 : if (RefNodeSetPoint == SensedNodeFlagValue) {
3022 0 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
3023 0 : ShowSevereError(
3024 0 : state, format("CalcOAPretreatSetPoint: Missing reference setpoint for Outdoor Air Pretreat Setpoint Manager {}", this->Name));
3025 0 : ShowContinueError(state, format("Node Referenced ={}", state.dataLoopNodes->NodeID(this->refNodeNum)));
3026 0 : ShowContinueError(state, "use a Setpoint Manager to establish a setpoint at this node.");
3027 0 : ShowFatalError(state, "Missing reference setpoint.");
3028 : } else {
3029 0 : bool LocalSetPointCheckFailed = false;
3030 0 : switch (this->ctrlVar) {
3031 0 : case HVAC::CtrlVarType::Temp: // 'Temperature'
3032 : case HVAC::CtrlVarType::MaxHumRat: // 'HUMRATMAX'
3033 : case HVAC::CtrlVarType::MinHumRat: // 'HUMRATMIN'
3034 : case HVAC::CtrlVarType::HumRat: { // 'HumidityRatio'
3035 0 : CheckIfNodeSetPointManagedByEMS(state, this->refNodeNum, this->ctrlVar, LocalSetPointCheckFailed);
3036 0 : } break;
3037 0 : default:
3038 0 : break;
3039 : }
3040 0 : if (LocalSetPointCheckFailed) {
3041 0 : ShowSevereError(
3042 0 : state, format("CalcOAPretreatSetPoint: Missing reference setpoint for Outdoor Air Pretreat Setpoint Manager {}", this->Name));
3043 0 : ShowContinueError(state, format("Node Referenced ={}", state.dataLoopNodes->NodeID(this->refNodeNum)));
3044 0 : ShowContinueError(state, "use a Setpoint Manager to establish a setpoint at this node.");
3045 0 : ShowContinueError(state, "Or use an EMS actuator to control a setpoint at this node.");
3046 0 : ShowFatalError(state, "Missing reference setpoint.");
3047 : }
3048 : }
3049 : }
3050 : }
3051 22139 : if ((mixedOutNode.MassFlowRate <= 0.0) || (oaInNode.MassFlowRate <= 0.0)) {
3052 110 : this->setPt = RefNodeSetPoint;
3053 22029 : } else if (isHumiditySetPoint && (RefNodeSetPoint == 0.0)) {
3054 : // For humidity setpoints, zero is special meaning "off" or "no load"
3055 : // so pass through zero setpoints without enforcing the max/min setpoint limits
3056 0 : this->setPt = 0.0;
3057 : } else {
3058 22029 : Real64 OAFraction = oaInNode.MassFlowRate / mixedOutNode.MassFlowRate;
3059 22029 : this->setPt = ReturnInValue + (RefNodeSetPoint - ReturnInValue) / OAFraction;
3060 : // Apply maximum and minimum values
3061 22029 : this->setPt = std::clamp(this->setPt, MinSetPoint, MaxSetPoint);
3062 : }
3063 22139 : } // SPMOutsideAirPretreat::calculate()
3064 :
3065 52464 : void SPMTempest::calculate(EnergyPlusData &state)
3066 : {
3067 : // SUBROUTINE INFORMATION:
3068 : // AUTHOR Fred Buhl
3069 : // DATE WRITTEN May 2002
3070 :
3071 : // PURPOSE OF THIS SUBROUTINE:
3072 : // Calculate the "warmest" supply air setpoint temperature that will satisfy the cooling
3073 : // requirements of all the zones served by a central air system.
3074 :
3075 : // METHODOLOGY EMPLOYED:
3076 : // Zone sensible heat balance
3077 :
3078 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3079 52464 : Real64 SetPointTemp = 0.0;
3080 :
3081 52464 : auto &airToZoneNode = state.dataAirLoop->AirToZoneNodeInfo(this->airLoopNum);
3082 :
3083 52464 : if (this->type == SPMType::Warmest) {
3084 :
3085 49774 : Real64 TotCoolLoad = 0.0;
3086 49774 : SetPointTemp = this->maxSetTemp;
3087 :
3088 490519 : for (int iZoneNum = 1; iZoneNum <= airToZoneNode.NumZonesCooled; ++iZoneNum) {
3089 440745 : int CtrlZoneNum = airToZoneNode.CoolCtrlZoneNums(iZoneNum);
3090 440745 : auto &zoneInletNode = state.dataLoopNodes->Node(airToZoneNode.CoolZoneInletNodes(iZoneNum));
3091 440745 : auto &zoneNode = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(CtrlZoneNum).ZoneNode);
3092 :
3093 440745 : Real64 ZoneMassFlowMax = zoneInletNode.MassFlowRateMax;
3094 440745 : Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(CtrlZoneNum).TotalOutputRequired;
3095 440745 : Real64 ZoneTemp = zoneNode.Temp;
3096 440745 : Real64 ZoneSetPointTemp = this->maxSetTemp;
3097 440745 : if (ZoneLoad < 0.0) {
3098 101135 : TotCoolLoad += std::abs(ZoneLoad);
3099 101135 : Real64 CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
3100 101135 : if (ZoneMassFlowMax > HVAC::SmallMassFlow) {
3101 101135 : ZoneSetPointTemp = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlowMax);
3102 : }
3103 : }
3104 440745 : SetPointTemp = min(SetPointTemp, ZoneSetPointTemp);
3105 : }
3106 :
3107 49774 : SetPointTemp = std::clamp(SetPointTemp, this->minSetTemp, this->maxSetTemp);
3108 49774 : if (TotCoolLoad < HVAC::SmallLoad) {
3109 27514 : SetPointTemp = this->maxSetTemp;
3110 : }
3111 :
3112 : } else { // (spm->type == SPMType::Coldest)
3113 2690 : Real64 TotHeatLoad = 0.0;
3114 2690 : SetPointTemp = this->minSetTemp;
3115 :
3116 2690 : if (airToZoneNode.NumZonesHeated > 0) {
3117 : // dual-duct heated only zones
3118 10760 : for (int iZoneNum = 1; iZoneNum <= airToZoneNode.NumZonesHeated; ++iZoneNum) {
3119 8070 : int CtrlZoneNum = airToZoneNode.HeatCtrlZoneNums(iZoneNum);
3120 8070 : auto &zoneInletNode = state.dataLoopNodes->Node(airToZoneNode.HeatZoneInletNodes(iZoneNum));
3121 8070 : auto &zoneNode = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(CtrlZoneNum).ZoneNode);
3122 8070 : Real64 ZoneMassFlowMax = zoneInletNode.MassFlowRateMax;
3123 8070 : Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(CtrlZoneNum).TotalOutputRequired;
3124 8070 : Real64 ZoneTemp = zoneNode.Temp;
3125 8070 : Real64 ZoneSetPointTemp = this->minSetTemp;
3126 8070 : if (ZoneLoad > 0.0) {
3127 4059 : TotHeatLoad += ZoneLoad;
3128 4059 : Real64 CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
3129 4059 : if (ZoneMassFlowMax > HVAC::SmallMassFlow) {
3130 4059 : ZoneSetPointTemp = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlowMax);
3131 : }
3132 : }
3133 8070 : SetPointTemp = max(SetPointTemp, ZoneSetPointTemp);
3134 : }
3135 : } else {
3136 : // single-duct or central heated and cooled zones
3137 0 : for (int iZoneNum = 1; iZoneNum <= airToZoneNode.NumZonesCooled; ++iZoneNum) {
3138 0 : int CtrlZoneNum = airToZoneNode.CoolCtrlZoneNums(iZoneNum);
3139 0 : auto &zoneInletNode = state.dataLoopNodes->Node(airToZoneNode.CoolZoneInletNodes(iZoneNum));
3140 0 : auto &zoneNode = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(CtrlZoneNum).ZoneNode);
3141 0 : Real64 ZoneMassFlowMax = zoneInletNode.MassFlowRateMax;
3142 0 : Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(CtrlZoneNum).TotalOutputRequired;
3143 0 : Real64 ZoneTemp = zoneNode.Temp;
3144 0 : Real64 ZoneSetPointTemp = this->minSetTemp;
3145 0 : if (ZoneLoad > 0.0) {
3146 0 : TotHeatLoad += ZoneLoad;
3147 0 : Real64 CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
3148 0 : if (ZoneMassFlowMax > HVAC::SmallMassFlow) {
3149 0 : ZoneSetPointTemp = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlowMax);
3150 : }
3151 : }
3152 0 : SetPointTemp = max(SetPointTemp, ZoneSetPointTemp);
3153 : }
3154 : }
3155 :
3156 2690 : SetPointTemp = std::clamp(SetPointTemp, this->minSetTemp, this->maxSetTemp);
3157 2690 : if (TotHeatLoad < HVAC::SmallLoad) {
3158 1287 : SetPointTemp = this->minSetTemp;
3159 : }
3160 : }
3161 :
3162 52464 : this->setPt = SetPointTemp;
3163 52464 : } // SMPTempest::calculate()
3164 :
3165 6620 : void SPMWarmestTempFlow::calculate(EnergyPlusData &state)
3166 : {
3167 : // SUBROUTINE INFORMATION:
3168 : // AUTHOR Fred Buhl
3169 : // DATE WRITTEN May 2002
3170 : // MODIFIED Haves, Oct 2004
3171 :
3172 : // PURPOSE OF THIS SUBROUTINE:
3173 : // Calculate the "warmest" supply air setpoint temperature that will satisfy the cooling
3174 : // requirements of all the zones served by a central air system.
3175 :
3176 : // METHODOLOGY EMPLOYED:
3177 : // Zone sensible heat balance
3178 :
3179 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3180 :
3181 6620 : if (!this->simReady) return;
3182 6620 : Real64 TotCoolLoad = 0.0;
3183 6620 : Real64 MaxSetPointTemp = this->maxSetTemp;
3184 6620 : Real64 SetPointTemp = MaxSetPointTemp;
3185 6620 : Real64 MinSetPointTemp = this->minSetTemp;
3186 6620 : Real64 MinFracFlow = this->minTurndown;
3187 6620 : Real64 FracFlow = MinFracFlow;
3188 6620 : int CritZoneNumTemp = 0;
3189 6620 : int CritZoneNumFlow = 0;
3190 :
3191 6620 : auto &airToZoneNode = state.dataAirLoop->AirToZoneNodeInfo(this->airLoopNum);
3192 :
3193 33104 : for (int iZoneNum = 1; iZoneNum <= airToZoneNode.NumZonesCooled; ++iZoneNum) {
3194 26484 : int CtrlZoneNum = airToZoneNode.CoolCtrlZoneNums(iZoneNum);
3195 26484 : auto &zoneInletNode = state.dataLoopNodes->Node(airToZoneNode.CoolZoneInletNodes(iZoneNum));
3196 26484 : auto &zoneNode = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(CtrlZoneNum).ZoneNode);
3197 :
3198 26484 : Real64 ZoneMassFlowMax = zoneInletNode.MassFlowRateMax;
3199 26484 : Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(CtrlZoneNum).TotalOutputRequired;
3200 26484 : Real64 ZoneTemp = zoneNode.Temp;
3201 26484 : Real64 ZoneSetPointTemp = MaxSetPointTemp;
3202 26484 : Real64 ZoneFracFlow = MinFracFlow;
3203 :
3204 26484 : if (ZoneLoad < 0.0) {
3205 5253 : TotCoolLoad += std::abs(ZoneLoad);
3206 5253 : Real64 CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
3207 5253 : if (ZoneMassFlowMax > HVAC::SmallMassFlow) {
3208 5253 : if (this->strategy == ControlStrategy::TempFirst) {
3209 : // First find supply air temperature required to meet the load at minimum flow. If this is
3210 : // below the minimum supply air temperature, calculate the fractional flow rate required to meet the
3211 : // load at the minimum supply air temperature.
3212 5253 : ZoneSetPointTemp = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlowMax * MinFracFlow);
3213 5253 : if (ZoneSetPointTemp < MinSetPointTemp) {
3214 3746 : ZoneFracFlow = (ZoneLoad / (CpAir * (MinSetPointTemp - ZoneTemp))) / ZoneMassFlowMax;
3215 : } else {
3216 1507 : ZoneFracFlow = MinFracFlow;
3217 : }
3218 : } else { // ControlStrategy = FlowFirst
3219 : // First find supply air flow rate required to meet the load at maximum supply air temperature. If this
3220 : // is above the maximum supply air flow rate, calculate the supply air temperature required to meet the
3221 : // load at the maximum flow.
3222 0 : ZoneFracFlow = (ZoneLoad / (CpAir * (MaxSetPointTemp - ZoneTemp))) / ZoneMassFlowMax;
3223 0 : if (ZoneFracFlow > 1.0 || ZoneFracFlow < 0.0) {
3224 0 : ZoneSetPointTemp = ZoneTemp + ZoneLoad / (CpAir * ZoneMassFlowMax);
3225 : } else {
3226 0 : ZoneSetPointTemp = MaxSetPointTemp;
3227 : }
3228 : }
3229 : }
3230 : }
3231 26484 : if (ZoneSetPointTemp < SetPointTemp) {
3232 2444 : SetPointTemp = ZoneSetPointTemp;
3233 2444 : CritZoneNumTemp = CtrlZoneNum;
3234 : }
3235 26484 : if (ZoneFracFlow > FracFlow) {
3236 2159 : FracFlow = ZoneFracFlow;
3237 2159 : CritZoneNumFlow = CtrlZoneNum;
3238 : }
3239 : }
3240 :
3241 6620 : SetPointTemp = std::clamp(SetPointTemp, MinSetPointTemp, MaxSetPointTemp);
3242 6620 : FracFlow = std::clamp(FracFlow, MinFracFlow, 1.0);
3243 6620 : if (TotCoolLoad < HVAC::SmallLoad) {
3244 4595 : SetPointTemp = MaxSetPointTemp;
3245 4595 : FracFlow = MinFracFlow;
3246 : }
3247 :
3248 6620 : this->setPt = SetPointTemp;
3249 6620 : this->turndown = FracFlow;
3250 6620 : if (this->strategy == ControlStrategy::TempFirst) {
3251 6620 : this->critZoneNum = (CritZoneNumFlow != 0) ? CritZoneNumFlow : CritZoneNumTemp;
3252 : } else { // ControlStrategy = FlowFirst
3253 0 : this->critZoneNum = (CritZoneNumTemp != 0) ? CritZoneNumTemp : CritZoneNumFlow;
3254 : }
3255 : } // SPMWarmestTempFlow::calculate()
3256 :
3257 2005 : void SPMReturnAirBypassFlow::calculate(EnergyPlusData &state)
3258 : {
3259 : // SUBROUTINE INFORMATION:
3260 : // AUTHOR Fred Buhl
3261 : // DATE WRITTEN July 2005
3262 :
3263 : // PURPOSE OF THIS SUBROUTINE:
3264 : // Given the desired setpoint temperature, calulate the flow rate through the
3265 : // return asir branch that will deliver the desired temperature at the loop outlet
3266 : // node.
3267 :
3268 2005 : auto &mixerRABInNode = state.dataLoopNodes->Node(this->rabMixInNodeNum);
3269 2005 : auto &mixerSupInNode = state.dataLoopNodes->Node(this->supMixInNodeNum);
3270 2005 : auto &mixerOutNode = state.dataLoopNodes->Node(this->mixOutNodeNum);
3271 2005 : auto &loopOutNode = state.dataLoopNodes->Node(this->sysOutNodeNum);
3272 :
3273 2005 : Real64 TempSetPt = GetCurrentScheduleValue(state, this->schedNum);
3274 2005 : Real64 TempSetPtMod = TempSetPt - (loopOutNode.Temp - mixerOutNode.Temp);
3275 2005 : Real64 SupFlow = mixerSupInNode.MassFlowRate;
3276 2005 : Real64 TempSup = mixerSupInNode.Temp;
3277 2005 : Real64 TotSupFlow = mixerOutNode.MassFlowRate;
3278 2005 : Real64 TempRAB = mixerRABInNode.Temp;
3279 2005 : Real64 RABFlow = (TotSupFlow * TempSetPtMod - SupFlow * TempSup) / max(TempRAB, 1.0);
3280 2005 : RABFlow = std::clamp(RABFlow, 0.0, TotSupFlow);
3281 2005 : this->FlowSetPt = RABFlow;
3282 2005 : }
3283 :
3284 5354 : void SPMMultiZoneTemp::calculate(EnergyPlusData &state)
3285 : {
3286 : // SUBROUTINE INFORMATION:
3287 : // AUTHOR Bereket Nigusse, FSEC
3288 : // DATE WRITTEN July 2010
3289 :
3290 : // PURPOSE OF THIS SUBROUTINE:
3291 : // Calculates the "Average" supply air setpoint temperature that will satisfy the heating
3292 : // requirements of multizones served by a central air system.
3293 :
3294 : // This function handles both MZAverageHeating and MZAverageCooling
3295 :
3296 : // METHODOLOGY EMPLOYED:
3297 : // Zone sensible (heating load) heat balance around the zones served by a central air system
3298 :
3299 : // sum of the zone's predicted loads for this air loop [W]
3300 5354 : Real64 SumLoad = 0.0;
3301 : // sum of the product of zone inlet node actual mass flow rate, and
3302 : // Cp of air at zone inlet node for all heated zones in the airloop [W/C]
3303 5354 : Real64 SumProductMdotCp = 0.0;
3304 : // sum of the product of zone inlet node actual mass flow rate, and
3305 : // Cp of air at zone air node for all zones in the airloop [W/C]
3306 5354 : Real64 SumProductMdotCpTot = 0.0;
3307 : // sum of the product of zone inlet node actual mass flow rate,
3308 : // Cp of air at zone air node and zone air node temperature for
3309 : // all zones in the air loop [W]
3310 5354 : Real64 SumProductMdotCpTZoneTot = 0.0;
3311 :
3312 5354 : auto &airToZoneNode = state.dataAirLoop->AirToZoneNodeInfo(this->airLoopNum);
3313 21416 : for (int iZoneNum = 1; iZoneNum <= airToZoneNode.NumZonesCooled; ++iZoneNum) {
3314 : // DO ZonesHeatedIndex=1,AirToZoneNodeInfo(AirLoopNum)%NumZonesHeated
3315 : // Using AirToZoneNodeInfo(AirLoopNum)%Cool* structure variables since they include heating and cooling.
3316 :
3317 : // The data for number of zones heated is included in the data structure of the variable
3318 : // "AirToZoneNodeInfo(AirLoopNum)%NumZonesCooled" for all systems. The data structure
3319 : // "AirToZoneNodeInfo(AirLoopNum)%NumZonesHeated" applies to Dual Duct System only and
3320 : // if used will limit the application of this setpoint manager to other systems. Thus,
3321 : // the "AirToZoneNodeInfo(AirLoopNum)%NumZonesCooled" data is used instead.
3322 :
3323 16062 : int CtrlZoneNum = airToZoneNode.CoolCtrlZoneNums(iZoneNum);
3324 16062 : auto &zoneInletNode = state.dataLoopNodes->Node(airToZoneNode.CoolZoneInletNodes(iZoneNum));
3325 16062 : auto &zoneNode = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(CtrlZoneNum).ZoneNode);
3326 16062 : Real64 ZoneMassFlowRate = zoneInletNode.MassFlowRate;
3327 16062 : Real64 ZoneLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(CtrlZoneNum).TotalOutputRequired;
3328 16062 : Real64 ZoneTemp = zoneNode.Temp;
3329 16062 : Real64 CpAir = PsyCpAirFnW(zoneNode.HumRat);
3330 16062 : SumProductMdotCpTot += ZoneMassFlowRate * CpAir;
3331 16062 : SumProductMdotCpTZoneTot += ZoneMassFlowRate * CpAir * ZoneTemp;
3332 16062 : if ((this->type == SPMType::MZHeatingAverage && ZoneLoad > 0.0) || (this->type == SPMType::MZCoolingAverage && ZoneLoad < 0.0)) {
3333 8031 : CpAir = PsyCpAirFnW(zoneInletNode.HumRat);
3334 8031 : SumLoad += ZoneLoad;
3335 8031 : SumProductMdotCp += ZoneMassFlowRate * CpAir;
3336 : }
3337 : }
3338 5354 : Real64 ZoneAverageTemp = (SumProductMdotCpTot > 0.0) ? (SumProductMdotCpTZoneTot / SumProductMdotCpTot) : 0.0;
3339 8987 : Real64 SetPointTemp = (SumProductMdotCp > 0.0) ? (ZoneAverageTemp + SumLoad / SumProductMdotCp)
3340 3633 : : ((this->type == SPMType::MZHeatingAverage) ? this->minSetTemp : this->maxSetTemp);
3341 :
3342 5354 : SetPointTemp = std::clamp(SetPointTemp, this->minSetTemp, this->maxSetTemp);
3343 5354 : if (std::abs(SumLoad) < HVAC::SmallLoad) {
3344 2564 : SetPointTemp = (this->type == SPMType::MZHeatingAverage) ? this->minSetTemp : this->maxSetTemp;
3345 : }
3346 5354 : this->setPt = SetPointTemp;
3347 5354 : } // SPMMultiZoneTemp::calculate()
3348 :
3349 48090 : void SPMMultiZoneHum::calculate(EnergyPlusData &state)
3350 : {
3351 : // SUBROUTINE INFORMATION:
3352 : // AUTHOR Bereket Nigusse, FSEC
3353 : // DATE WRITTEN July 2010
3354 :
3355 : // PURPOSE OF THIS SUBROUTINE:
3356 : // Calculate the "Average" supply air minimum humidity setpoint that will satisfy the minimum
3357 : // humidity ratio requirements of multiple zones served by a central air system.
3358 :
3359 : // This function handles both MZMinHumAverage and MZMaxHumAverage
3360 :
3361 : // METHODOLOGY EMPLOYED:
3362 : // Zone latent load balance around the zones served by a central air system
3363 :
3364 48090 : Real64 constexpr SmallMoistureLoad(0.00001); // small moisture load [kgWater/s]
3365 :
3366 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3367 48090 : Real64 SumMdot = 0.0; // sum of the actual mass flow rate for controlled zones in the air loop [kg/s]
3368 48090 : Real64 SumMdotTot = 0.0; // sum of the actual mass flow rate for this air loop [kg/s]
3369 48090 : Real64 SumMoistureLoad = 0.0; // sum of the zone moisture loads for this air loop [W]
3370 : // sum of product of actual mass flow rate at the zone inlet node,
3371 : // and humidity ratio at zones air node for all zones in the airloop [kgWater/s]
3372 48090 : Real64 SumProductMdotHumTot = 0.0;
3373 :
3374 48090 : auto &airToZoneNode = state.dataAirLoop->AirToZoneNodeInfo(this->airLoopNum);
3375 :
3376 48090 : Real64 SetPointHum = (this->type == SPMType::MZMinHum || this->type == SPMType::MZMinHumAverage) ? this->minSetHum : this->maxSetHum;
3377 :
3378 479020 : for (int iZoneNum = 1; iZoneNum <= airToZoneNode.NumZonesCooled; ++iZoneNum) {
3379 430930 : int CtrlZoneNum = airToZoneNode.CoolCtrlZoneNums(iZoneNum);
3380 430930 : auto &zoneInletNode = state.dataLoopNodes->Node(airToZoneNode.CoolZoneInletNodes(iZoneNum));
3381 430930 : auto &zoneNode = state.dataLoopNodes->Node(state.dataZoneEquip->ZoneEquipConfig(CtrlZoneNum).ZoneNode);
3382 430930 : auto &zoneMoistureDemand = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(CtrlZoneNum);
3383 430930 : Real64 ZoneMassFlowRate = zoneInletNode.MassFlowRate;
3384 256720 : Real64 MoistureLoad = (this->type == SPMType::MZMinHum || this->type == SPMType::MZMinHumAverage)
3385 687650 : ? zoneMoistureDemand.OutputRequiredToHumidifyingSP
3386 : : zoneMoistureDemand.OutputRequiredToDehumidifyingSP;
3387 :
3388 430930 : Real64 ZoneHum = zoneNode.HumRat;
3389 : // For humidification the moisture load is positive
3390 :
3391 430930 : switch (this->type) {
3392 7550 : case SPMType::MZMinHumAverage: {
3393 7550 : SumMdotTot += ZoneMassFlowRate;
3394 7550 : SumProductMdotHumTot += ZoneMassFlowRate * ZoneHum;
3395 7550 : if (MoistureLoad > 0.0) {
3396 2113 : SumMdot += ZoneMassFlowRate;
3397 2113 : SumMoistureLoad += MoistureLoad;
3398 : }
3399 7550 : } break;
3400 :
3401 7550 : case SPMType::MZMaxHumAverage: {
3402 7550 : SumMdotTot += ZoneMassFlowRate;
3403 7550 : SumProductMdotHumTot += ZoneMassFlowRate * ZoneHum;
3404 7550 : if (MoistureLoad < 0.0) {
3405 521 : SumMdot += ZoneMassFlowRate;
3406 521 : SumMoistureLoad += MoistureLoad;
3407 : }
3408 7550 : } break;
3409 :
3410 174210 : case SPMType::MZMinHum: {
3411 174210 : Real64 ZoneSetPointHum = this->minSetHum;
3412 174210 : if (MoistureLoad > 0.0) {
3413 11958 : SumMoistureLoad += MoistureLoad;
3414 11958 : if (ZoneMassFlowRate > HVAC::SmallMassFlow) {
3415 11958 : ZoneSetPointHum = max(0.0, ZoneHum + MoistureLoad / ZoneMassFlowRate);
3416 : }
3417 : }
3418 174210 : SetPointHum = max(SetPointHum, ZoneSetPointHum);
3419 174210 : } break;
3420 :
3421 241620 : case SPMType::MZMaxHum: {
3422 241620 : Real64 ZoneSetPointHum = this->maxSetHum;
3423 241620 : if (MoistureLoad < 0.0) {
3424 34397 : SumMoistureLoad += MoistureLoad;
3425 34397 : if (ZoneMassFlowRate > HVAC::SmallMassFlow) {
3426 29430 : ZoneSetPointHum = max(0.0, ZoneHum + MoistureLoad / ZoneMassFlowRate);
3427 : }
3428 : }
3429 241620 : SetPointHum = min(SetPointHum, ZoneSetPointHum);
3430 241620 : } break;
3431 :
3432 0 : default:
3433 0 : break;
3434 : } // switch (this->type)
3435 : }
3436 :
3437 48090 : if (this->type == SPMType::MZMinHumAverage || this->type == SPMType::MZMaxHumAverage) {
3438 3020 : Real64 AverageZoneHum = (SumMdotTot > HVAC::SmallMassFlow) ? (SumProductMdotHumTot / SumMdotTot) : 0.0;
3439 3020 : if (SumMdot > HVAC::SmallMassFlow) {
3440 912 : SetPointHum = max(0.0, AverageZoneHum + SumMoistureLoad / SumMdot);
3441 : }
3442 3020 : } else {
3443 45070 : if (std::abs(SumMoistureLoad) < SmallMoistureLoad) {
3444 28625 : SetPointHum = (this->type == SPMType::MZMinHum) ? this->minSetHum : this->maxSetHum;
3445 : }
3446 : }
3447 :
3448 48090 : this->setPt = std::clamp(SetPointHum, this->minSetHum, this->maxSetHum);
3449 48090 : } // SPMMultiZoneHum::calculate()
3450 :
3451 680895 : void SPMFollowOutsideAirTemp::calculate(EnergyPlusData &state)
3452 : {
3453 : // SUBROUTINE INFORMATION:
3454 : // AUTHOR Chandan Sharma, FSEC
3455 : // DATE WRITTEN July 2011
3456 :
3457 : // PURPOSE OF THIS SUBROUTINE:
3458 : // Set the setpoint based on outdoor air dry-bulb/wet-bulb temperature
3459 :
3460 : // METHODOLOGY EMPLOYED:
3461 : // Based on reference temperature type specifed in the setpoint manager,
3462 : // the setpoint is calculated as OutWetBulbTemp(Or OutDryBulbTemp) + Offset.
3463 : // The sign convention is that a positive Offset will increase the resulting setpoint.
3464 : // Final value of the setpoint is limited by the Max and Min limit specified in the setpoint manager.
3465 680895 : this->setPt = ((this->refTempType == AirTempType::WetBulb) ? state.dataEnvrn->OutWetBulbTemp : state.dataEnvrn->OutDryBulbTemp) + this->offset;
3466 :
3467 : // Apply maximum and minimum values
3468 680895 : this->setPt = std::clamp(this->setPt, this->minSetTemp, this->maxSetTemp);
3469 680895 : } // SPMFollowOutsideAirTemp::calculate()
3470 :
3471 2860 : void SPMFollowSysNodeTemp::calculate(EnergyPlusData &state)
3472 : {
3473 : // SUBROUTINE INFORMATION:
3474 : // AUTHOR Chandan Sharma, FSEC
3475 : // DATE WRITTEN July 2011
3476 :
3477 : // PURPOSE OF THIS SUBROUTINE:
3478 : // Set the setpoint based on current temperatures at a separate system node.
3479 :
3480 : // METHODOLOGY EMPLOYED:
3481 : // The current value of the temperature at a reference node are obtained and used
3482 : // to generate setpoint on a second system node. If the reference node is also designated
3483 : // to be an outdoor air (intake) node, then this setpoint manager can be used to follow
3484 : // outdoor air conditions that are adjusted for altitude.
3485 : // Also, based on reference temperature type specifed in the setpoint manager, the out door air wet-bulb
3486 : // or dry-bulb temperature at the reference node could be used.
3487 : // A temperature offset will be applied to the value obtained from the reference system node.
3488 : // If this value is zero, and the limits are met, then the resulting setpoint will be exactly the same
3489 : // as the reference system node temperature. The sign convention is that a positive offset will increase
3490 : // the resulting setpoint.
3491 :
3492 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3493 2860 : Real64 RefNodeTemp = (this->refTempType == AirTempType::DryBulb)
3494 5720 : ? state.dataLoopNodes->Node(this->refNodeNum).Temp
3495 2860 : : (allocated(state.dataLoopNodes->MoreNodeInfo) ? state.dataLoopNodes->MoreNodeInfo(this->refNodeNum).WetBulbTemp : 0.0);
3496 :
3497 2860 : this->setPt = RefNodeTemp + this->offset;
3498 :
3499 : // Apply maximum and minimum values
3500 2860 : this->setPt = std::clamp(this->setPt, this->minSetTemp, this->maxSetTemp);
3501 2860 : } // SPMFollowSysNodeTemp::calculate()
3502 :
3503 37652 : void SPMFollowGroundTemp::calculate(EnergyPlusData &state)
3504 : {
3505 : // SUBROUTINE INFORMATION:
3506 : // AUTHOR Chandan Sharma, FSEC
3507 : // DATE WRITTEN July 2011
3508 :
3509 : // PURPOSE OF THIS SUBROUTINE:
3510 : // Set the setpoint based on current ground temperature
3511 :
3512 : // METHODOLOGY EMPLOYED:
3513 : // Based on reference ground temperature object type specifed in the setpoint manager,
3514 : // the setpoint is calculated as GroundTemperature + Offset.
3515 : // The sign convention is that a positive Offset will increase the resulting setpoint.
3516 : // Final value of the setpoint is limited by the Max and Min limit specified in the setpoint manager.
3517 37652 : this->setPt = state.dataEnvrn->GroundTemp[(int)this->refTempType] + this->offset;
3518 :
3519 : // Apply maximum and minimum values
3520 37652 : this->setPt = std::clamp(this->setPt, this->minSetTemp, this->maxSetTemp);
3521 37652 : } // SPMFollowGrounTemp::calculate()
3522 :
3523 5842 : void SPMCondenserEnteringTemp::calculate(EnergyPlusData &state)
3524 : {
3525 : // SUBROUTINE INFORMATION:
3526 : // AUTHOR Atefe Makhmalbaf and Heejin Cho, PNNL
3527 : // DATE WRITTEN March 2012
3528 :
3529 : // PURPOSE OF THIS SUBROUTINE:
3530 : // Calculate the optimal condenser water temperature set point for a chiller plant
3531 : // with one or more chillers. The condenser water leaving the tower should be at this temperature
3532 : // for optimal operation of the chiller plant.
3533 :
3534 : // METHODOLOGY EMPLOYED:
3535 : // using one curve to determine the optimum condenser entering water temperature for a given timestep
3536 : // and two other curves to place boundary conditions on the optimal setpoint value.
3537 :
3538 : // Using/Aliasing
3539 : using ScheduleManager::GetCurrentScheduleValue;
3540 : using namespace DataPlant;
3541 :
3542 5842 : auto &dspm = state.dataSetPointManager;
3543 :
3544 : // Current timestep's condenser water entering setpoint
3545 5842 : Real64 CondenserEnteringTempSetPoint = GetCurrentScheduleValue(state, this->condenserEnteringTempSchedNum);
3546 :
3547 5842 : auto &supplyLoop = state.dataPlnt->PlantLoop(this->plantPloc.loopNum).LoopSide(LoopSideLocation::Supply);
3548 5842 : auto &supplyComp = supplyLoop.Branch(this->plantPloc.branchNum).Comp(this->plantPloc.compNum);
3549 :
3550 5842 : auto &demandLoop = state.dataPlnt->PlantLoop(this->demandPloc.loopNum).LoopSide(LoopSideLocation::Demand);
3551 5842 : auto &demandComp = demandLoop.Branch(this->demandPloc.branchNum).Comp(this->demandPloc.compNum);
3552 :
3553 : // If chiller is on
3554 5842 : Real64 CurLoad = std::abs(supplyComp.MyLoad);
3555 5842 : if (CurLoad > 0) {
3556 :
3557 2425 : Real64 CondInletTemp = 0.0;
3558 2425 : Real64 EvapOutletTemp = 0.0;
3559 :
3560 2425 : Real64 DesignLoad = 0.0; // array of chiller design loads
3561 2425 : Real64 ActualLoad = 0.0; // array of chiller actual loads
3562 2425 : Real64 DesignCondenserInTemp = 0.0; // Design condenser inlet temp. C , or 25.d0
3563 2425 : Real64 DesignEvapOutTemp = 0.0; // design evaporator outlet temperature, water side
3564 :
3565 : // Get from tower design values
3566 2425 : constexpr Real64 NormDesignCondenserFlow = 5.38e-8; // m3/s per watt (typically 3 gpm/ton)=(Volume of condenser fluid)/(ton of heat rejection)
3567 :
3568 2425 : if (this->chillerType == PlantEquipmentType::Chiller_Absorption || this->chillerType == PlantEquipmentType::Chiller_CombTurbine ||
3569 2425 : this->chillerType == PlantEquipmentType::Chiller_Electric || this->chillerType == PlantEquipmentType::Chiller_ElectricReformEIR ||
3570 0 : this->chillerType == PlantEquipmentType::Chiller_EngineDriven) {
3571 2425 : DesignCondenserInTemp = supplyComp.TempDesCondIn;
3572 2425 : CondInletTemp = state.dataLoopNodes->Node(demandComp.NodeNumIn).Temp;
3573 2425 : EvapOutletTemp = state.dataLoopNodes->Node(supplyComp.NodeNumOut).Temp;
3574 2425 : DesignEvapOutTemp = supplyComp.TempDesEvapOut;
3575 2425 : DesignLoad = supplyComp.MaxLoad;
3576 2425 : ActualLoad = state.dataPlnt->PlantLoop(this->plantPloc.loopNum).CoolingDemand;
3577 0 : } else if (this->chillerType == PlantEquipmentType::Chiller_Indirect_Absorption ||
3578 0 : this->chillerType == PlantEquipmentType::Chiller_DFAbsorption) {
3579 0 : DesignCondenserInTemp = supplyComp.TempDesCondIn;
3580 0 : DesignEvapOutTemp = 6.666;
3581 : } else {
3582 0 : DesignCondenserInTemp = 25.0;
3583 0 : DesignEvapOutTemp = 6.666;
3584 : }
3585 :
3586 : // for attached chillers (that are running this timestep) find their Dsn_MinCondSetpt and Dsn_EntCondTemp
3587 2425 : dspm->CET_DesignMinCondenserSetPt = 999.0;
3588 2425 : dspm->CET_DesignEnteringCondenserTemp = 0.0;
3589 :
3590 : // Design Minimum Condenser Entering as a function of the minimum lift and TEvapLvg
3591 : // for chillers operating on current cond loop this timestep
3592 2425 : Real64 DesignMinCondenserEnteringTempThisChiller = DesignEvapOutTemp + (this->minLift);
3593 2425 : dspm->CET_DesignMinCondenserSetPt = min(dspm->CET_DesignMinCondenserSetPt, DesignMinCondenserEnteringTempThisChiller);
3594 :
3595 : // Design entering condenser water temperature for chillers operating
3596 : // on current cond loop this timestep
3597 2425 : dspm->CET_DesignEnteringCondenserTemp = max(dspm->CET_DesignEnteringCondenserTemp, DesignCondenserInTemp);
3598 :
3599 : // ***** Load Calculations *****
3600 : // In this section the sum of the actual load (watts) and design load (watts)
3601 : // of the chillers that are on is calculated.
3602 2425 : dspm->CET_ActualLoadSum += ActualLoad;
3603 2425 : dspm->CET_DesignLoadSum += DesignLoad;
3604 :
3605 : // Exit if the chillers are all off this hour
3606 2425 : if (dspm->CET_ActualLoadSum <= 0) {
3607 0 : CondenserEnteringTempSetPoint = dspm->CET_DesignEnteringCondenserTemp;
3608 0 : return;
3609 : }
3610 :
3611 : // ***** Weighted Ratio Calculation *****
3612 : // This section first calculates the actual (ALW) and design (DLW) individual
3613 : // weights. Then the weighted actual and design loads are computed. Finally
3614 : // the Weighted Ratio is found.
3615 2425 : Real64 WeightedActualLoad = 0.0; // Actual load weighting of each chiller, W
3616 2425 : Real64 WeightedDesignLoad = 0.0; // Design capacity of each chiller, W
3617 2425 : if (dspm->CET_ActualLoadSum != 0 && dspm->CET_DesignLoadSum != 0) {
3618 2425 : WeightedActualLoad = ((ActualLoad / dspm->CET_ActualLoadSum) * ActualLoad);
3619 2425 : WeightedDesignLoad = ((DesignLoad / dspm->CET_DesignLoadSum) * DesignLoad);
3620 : }
3621 :
3622 2425 : dspm->CET_WeightedActualLoadSum += WeightedActualLoad;
3623 2425 : dspm->CET_WeightedDesignLoadSum += WeightedDesignLoad;
3624 2425 : dspm->CET_WeightedLoadRatio = dspm->CET_WeightedActualLoadSum / dspm->CET_WeightedDesignLoadSum;
3625 :
3626 : // ***** Optimal Temperature Calculation *****
3627 : // In this section the optimal temperature is computed along with the minimum
3628 : // design wet bulb temp and the minimum actual wet bulb temp.
3629 : // Min_DesignWB = ACoef1 + ACoef2*OaWb + ACoef3*WPLR + ACoef4*TwrDsnWB + ACoef5*NF
3630 2425 : dspm->CET_DesignMinWetBulbTemp = EnergyPlus::Curve::CurveValue(state,
3631 : this->minTowerDesignWetBulbCurveNum,
3632 2425 : state.dataEnvrn->OutWetBulbTemp,
3633 2425 : dspm->CET_WeightedLoadRatio,
3634 : this->towerDesignInletAirWetBulbTemp,
3635 : NormDesignCondenserFlow);
3636 :
3637 : // Min_ActualWb = BCoef1 + BCoef2*MinDsnWB + BCoef3*WPLR + BCoef4*TwrDsnWB + BCoef5*NF
3638 2425 : dspm->CET_MinActualWetBulbTemp = EnergyPlus::Curve::CurveValue(state,
3639 : this->minOAWetBulbCurveNum,
3640 2425 : dspm->CET_DesignMinWetBulbTemp,
3641 2425 : dspm->CET_WeightedLoadRatio,
3642 : this->towerDesignInletAirWetBulbTemp,
3643 : NormDesignCondenserFlow);
3644 :
3645 : // Opt_CondEntTemp = CCoef1 + CCoef2*OaWb + CCoef3*WPLR + CCoef4*TwrDsnWB + CCoef5*NF
3646 2425 : dspm->CET_OptCondenserEnteringTemp = EnergyPlus::Curve::CurveValue(state,
3647 : this->optCondenserEnteringTempCurveNum,
3648 2425 : state.dataEnvrn->OutWetBulbTemp,
3649 2425 : dspm->CET_WeightedLoadRatio,
3650 : this->towerDesignInletAirWetBulbTemp,
3651 : NormDesignCondenserFlow);
3652 :
3653 : // ***** Calculate (Cond ent - Evap lvg) Section *****
3654 : // In this section we find the worst case of (Cond ent - Evap lvg) for the
3655 : // chillers that are running.
3656 2425 : dspm->CET_CurMinLift = 9999.0;
3657 : // temp_MinLiftTD = 20.0 / 1.8;
3658 2425 : Real64 TempMinLift = CondInletTemp - EvapOutletTemp;
3659 2425 : dspm->CET_CurMinLift = min(dspm->CET_CurMinLift, TempMinLift);
3660 : }
3661 :
3662 5842 : Real64 SetPoint = 0.0; // Condenser entering water temperature setpoint this timestep, C
3663 :
3664 : // ***** Limit conditions Section *****
3665 : // Check for limit conditions and control to the proper value.
3666 5842 : if ((dspm->CET_WeightedLoadRatio >= 0.90) && (dspm->CET_OptCondenserEnteringTemp >= (dspm->CET_DesignEnteringCondenserTemp + 1.0))) {
3667 : // Optimized value exceeds the design condenser entering condition or chillers
3668 : // near full load condition; reset condenser entering setpoint to its design value
3669 0 : SetPoint = dspm->CET_DesignEnteringCondenserTemp + 1.0;
3670 5842 : } else if ((state.dataEnvrn->OutWetBulbTemp >= dspm->CET_MinActualWetBulbTemp) &&
3671 5842 : (this->towerDesignInletAirWetBulbTemp >= dspm->CET_DesignMinWetBulbTemp) && (dspm->CET_CurMinLift > this->minLift)) {
3672 : // Boundaries are satified; use optimized condenser entering water temp
3673 3198 : SetPoint = dspm->CET_OptCondenserEnteringTemp;
3674 : } else {
3675 : // Boundaries violated; Reset to scheduled value of condenser water entering setpoint
3676 2644 : SetPoint = CondenserEnteringTempSetPoint;
3677 : }
3678 :
3679 : // Do not allow new setpoint to be less than the design condenser minimum entering condition,
3680 : // i.e., TCondWaterEnt not allowed to be less than DsnEvapWaterLvg + MinimumLiftTD
3681 5842 : this->setPt = max(SetPoint, dspm->CET_DesignMinCondenserSetPt);
3682 : } // SPMCondenserEneteringTemp::calculate()
3683 :
3684 5378 : void SPMIdealCondenserEnteringTemp::calculate(EnergyPlusData &state)
3685 : {
3686 : // SUBROUTINE INFORMATION:
3687 : // AUTHOR Heejin Cho, PNNL
3688 : // DATE WRITTEN March 2012
3689 :
3690 : // PURPOSE OF THIS SUBROUTINE:
3691 : // Calculate the optimal condenser water entering temperature set point for a chiller plant.
3692 :
3693 : // METHODOLOGY EMPLOYED:
3694 : // The "ideal" chiller-tower optimization scheme uses a search algorithm to find the ideal optimal setpoint
3695 : // at a given timestep. This requires resimulating HVAC systems at each timestep until finding
3696 : // an "optimal" condenser water entering setpoint (OptSetpoint) which gives the minimum total chiller,
3697 : // cooling tower, chilled water pump and condenser water pump power consumption.
3698 : // The OptSetpoint falls between realistic minimum and maximum boundaries, which are set by the user.
3699 : // The minimum boundary is determined based on the minimum lift (user input)
3700 : // and evaporator leaving water temperature. The maximum boundary is specified by the user.
3701 : // It is assumed that a single minimum point exists between these boundaries.
3702 :
3703 : // Using/Aliasing
3704 : using namespace DataPlant;
3705 :
3706 5378 : auto &dspm = state.dataSetPointManager;
3707 :
3708 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3709 5378 : auto &supplyLoop = state.dataPlnt->PlantLoop(this->chillerPloc.loopNum).LoopSide(LoopSideLocation::Supply);
3710 5378 : auto &supplyComp = supplyLoop.Branch(this->chillerPloc.branchNum).Comp(this->chillerPloc.compNum);
3711 :
3712 5378 : if (state.dataGlobal->MetersHaveBeenInitialized) {
3713 : // Setup meter vars
3714 5366 : if (this->setupIdealCondEntSetPtVars) {
3715 2 : this->SetupMeteredVarsForSetPt(state);
3716 2 : this->setupIdealCondEntSetPtVars = false;
3717 : }
3718 : }
3719 :
3720 5378 : if (state.dataGlobal->MetersHaveBeenInitialized && state.dataGlobal->RunOptCondEntTemp) {
3721 :
3722 : // If chiller is on
3723 2114 : Real64 CurLoad = std::abs(supplyComp.MyLoad);
3724 :
3725 2114 : if (CurLoad > 0) {
3726 :
3727 : Real64 EvapOutletTemp =
3728 1922 : (this->chillerType == PlantEquipmentType::Chiller_Absorption || this->chillerType == PlantEquipmentType::Chiller_CombTurbine ||
3729 1922 : this->chillerType == PlantEquipmentType::Chiller_Electric || this->chillerType == PlantEquipmentType::Chiller_ElectricReformEIR ||
3730 0 : this->chillerType == PlantEquipmentType::Chiller_EngineDriven)
3731 3844 : ? state.dataLoopNodes->Node(supplyComp.NodeNumOut).Temp
3732 1922 : : 6.666;
3733 :
3734 1922 : Real64 CondTempLimit = this->minLift + EvapOutletTemp;
3735 :
3736 1922 : Real64 TotEnergy = this->calculateCurrentEnergyUsage(state);
3737 :
3738 1922 : this->setupSetPointAndFlags(TotEnergy,
3739 1922 : dspm->ICET_TotEnergyPre,
3740 1922 : dspm->ICET_CondenserWaterSetPt,
3741 : CondTempLimit,
3742 1922 : state.dataGlobal->RunOptCondEntTemp,
3743 1922 : dspm->ICET_RunSubOptCondEntTemp,
3744 1922 : dspm->ICET_RunFinalOptCondEntTemp);
3745 :
3746 : } else {
3747 192 : dspm->ICET_CondenserWaterSetPt = this->maxCondenserEnteringTemp;
3748 192 : dspm->ICET_TotEnergyPre = 0.0;
3749 192 : state.dataGlobal->RunOptCondEntTemp = false;
3750 192 : dspm->ICET_RunSubOptCondEntTemp = false;
3751 : }
3752 : } else {
3753 3264 : dspm->ICET_CondenserWaterSetPt = this->maxCondenserEnteringTemp;
3754 3264 : state.dataGlobal->RunOptCondEntTemp = false;
3755 3264 : dspm->ICET_RunSubOptCondEntTemp = false;
3756 : }
3757 :
3758 5378 : this->setPt = dspm->ICET_CondenserWaterSetPt;
3759 5378 : } // SPMIdealCondenserEnteringTemp::calculate()
3760 :
3761 1922 : void SPMIdealCondenserEnteringTemp::setupSetPointAndFlags(Real64 &TotEnergy,
3762 : Real64 &TotEnergyPre,
3763 : Real64 &CondWaterSetPoint,
3764 : Real64 &CondTempLimit,
3765 : bool &RunOptCondEntTemp,
3766 : bool &RunSubOptCondEntTemp,
3767 : bool &RunFinalOptCondEntTemp) const
3768 : {
3769 : Real64 DeltaTotEnergy;
3770 1922 : if (TotEnergyPre != 0.0) {
3771 : // Calculate the total energy consumption difference
3772 1654 : DeltaTotEnergy = TotEnergyPre - TotEnergy;
3773 : // Search for the minimum total energy consumption
3774 1654 : if ((DeltaTotEnergy > 0) && (CondWaterSetPoint >= CondTempLimit) && (!RunFinalOptCondEntTemp)) {
3775 851 : if (!RunSubOptCondEntTemp) {
3776 596 : --CondWaterSetPoint;
3777 596 : RunOptCondEntTemp = true;
3778 : } else {
3779 255 : CondWaterSetPoint -= 0.2;
3780 255 : RunOptCondEntTemp = true;
3781 : }
3782 851 : TotEnergyPre = TotEnergy;
3783 : // Set smaller set point (0.2 degC) decrease
3784 803 : } else if ((DeltaTotEnergy < 0) && (!RunSubOptCondEntTemp) && (CondWaterSetPoint > CondTempLimit) && (!RunFinalOptCondEntTemp)) {
3785 267 : CondWaterSetPoint += 0.8;
3786 267 : RunOptCondEntTemp = true;
3787 267 : RunSubOptCondEntTemp = true;
3788 : } else {
3789 536 : if (!RunFinalOptCondEntTemp) {
3790 268 : CondWaterSetPoint += 0.2;
3791 268 : RunOptCondEntTemp = true;
3792 268 : RunSubOptCondEntTemp = false;
3793 268 : RunFinalOptCondEntTemp = true;
3794 : } else {
3795 : // CondWaterSetPoint = CondWaterSetPoint; // Self-assignment commented out
3796 268 : TotEnergyPre = 0.0;
3797 268 : RunOptCondEntTemp = false;
3798 268 : RunSubOptCondEntTemp = false;
3799 268 : RunFinalOptCondEntTemp = false;
3800 : }
3801 : }
3802 : } else {
3803 268 : CondWaterSetPoint = this->maxCondenserEnteringTemp - 1.0;
3804 268 : TotEnergyPre = TotEnergy;
3805 268 : RunOptCondEntTemp = true;
3806 268 : RunSubOptCondEntTemp = false;
3807 : }
3808 1922 : } // SPMIdealCondenserEneteringTemp::()
3809 :
3810 1922 : Real64 SPMIdealCondenserEnteringTemp::calculateCurrentEnergyUsage(EnergyPlusData &state)
3811 : {
3812 1922 : Real64 ChillerEnergy = GetInternalVariableValue(state, this->chillerVar.Type, this->chillerVar.Num);
3813 1922 : Real64 ChilledPumpEnergy = GetInternalVariableValue(state, this->chilledWaterPumpVar.Type, this->chilledWaterPumpVar.Num);
3814 1922 : Real64 TowerFanEnergy = 0;
3815 4768 : for (int i = 1; i <= this->numTowers; i++) {
3816 2846 : TowerFanEnergy += GetInternalVariableValue(state, this->towerVars(i).Type, this->towerVars(i).Num);
3817 : }
3818 1922 : Real64 CondPumpEnergy = GetInternalVariableValue(state, this->condenserPumpVar.Type, this->condenserPumpVar.Num);
3819 1922 : return (ChillerEnergy + ChilledPumpEnergy + TowerFanEnergy + CondPumpEnergy);
3820 : } // SPMIdealCondenserEnteringTemp::calculateCurrentEnergyUsage()
3821 :
3822 31366 : void SPMReturnWaterTemp::calculate(EnergyPlusData &state)
3823 : {
3824 : // SUBROUTINE INFORMATION:
3825 : // AUTHOR Edwin Lee, NREL
3826 : // DATE WRITTEN May 2015
3827 :
3828 : // PURPOSE OF THIS SUBROUTINE:
3829 : // Calculate the plant supply temperature reset required to achieve a target plant return temperature
3830 :
3831 : // METHODOLOGY EMPLOYED:
3832 : // The setpoint manager follows this procedure:
3833 : // 1. Calculate the current demand
3834 : // a. Sense the current return temperature
3835 : // b. Sense the current supply temperature
3836 : // c. Sense the current flow rate
3837 : // d. Approximate the fluid properties (rho, Cp) from the temperatures
3838 : // ---> Use these to calculate the demand with Q_demand = V_dot * rho * C_p * (T_return_sensed - T_supply_sensed)
3839 : // 2. Calculate a new value of supply setpoint that will reject this much Q_demand, while providing a target return temperature
3840 : // * this assumes that the demand will be the same value on the next time around
3841 : // * at any time step, the value of target return temperature may vary if it is scheduled (or actuated with EMS)
3842 : // a. T_supply_setpoint = T_return_target - Q_demand / ( V_dot * rho * C_p )
3843 : // 3. Constrain this value to limits
3844 : // a. T_supply_setpoint will be within: [ Design Chilled Water Supply Temperature, Maximum Supply Water Reset Temperature ]
3845 :
3846 : // NOTES:
3847 : // The assumptions related to lagging of setpoint are most suited for smaller timesteps and/or plants that don't vary wildly from one time
3848 : // step to another The assumptions also become affected by variable flow plants more-so than constant-flow plants
3849 :
3850 : // Using/Aliasing
3851 : using namespace DataPlant;
3852 :
3853 31366 : auto &supplyNode = state.dataLoopNodes->Node(this->supplyNodeNum);
3854 31366 : auto &returnNode = state.dataLoopNodes->Node(this->returnNodeNum);
3855 :
3856 : // we need to know the plant to get the fluid ID in case it is glycol
3857 : // but we have to wait in case plant isn't initialized yet
3858 : // if plant isn't initialized, assume index=1 (water)
3859 31366 : if (this->plantLoopNum == 0) {
3860 14 : for (int LoopNum = 1; LoopNum <= state.dataPlnt->TotNumLoops; ++LoopNum) {
3861 10 : auto &plantLoop = state.dataPlnt->PlantLoop(LoopNum);
3862 10 : if (this->supplyNodeNum == plantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut) {
3863 4 : this->plantLoopNum = LoopNum;
3864 4 : this->plantSetPtNodeNum = plantLoop.TempSetPointNodeNum;
3865 : // now that we've found the plant populated, let's verify that the nodes match
3866 4 : if (!PlantUtilities::verifyTwoNodeNumsOnSamePlantLoop(state, this->supplyNodeNum, this->returnNodeNum)) {
3867 0 : ShowSevereError(state, "Node problem for SetpointManager:ReturnTemperature:ChilledWater.");
3868 0 : ShowContinueError(state, "Return and Supply nodes were not found on the same plant loop. Verify node names.");
3869 0 : ShowFatalError(state, "Simulation aborts due to setpoint node problem");
3870 : }
3871 : }
3872 : }
3873 : }
3874 :
3875 : // get the operating flow rate
3876 31366 : Real64 const mdot = supplyNode.MassFlowRate;
3877 31366 : Real64 const deltaT = (this->type == SPMType::ChilledWaterReturnTemp) ? (returnNode.Temp - supplyNode.Temp) : (supplyNode.Temp - returnNode.Temp);
3878 :
3879 : // // calculate the current demand
3880 : // fluidIndex = state.dataPlnt->PlantLoop(this->plantLoopNum).FluidIndex;
3881 : // // we don't need fluid names since we have a real index, so just pass in the temperature and get properties
3882 : // Real64 const avgTemp = (returnNode.Temp + supplyNode.Temp) / 2;
3883 : // Real64 const cp = FluidProperties::GetSpecificHeatGlycol(state, "", avgTemp, fluidIndex, "ReturnWaterChWSetPointManager::calculate");
3884 : // Real64 const Qdemand = mdot * cp * deltaT;
3885 :
3886 : // check for strange conditions
3887 31366 : if (deltaT < 0) {
3888 0 : this->currentSupplySetPt = (this->type == SPMType::ChilledWaterReturnTemp) ? this->minSetTemp : this->maxSetTemp;
3889 0 : return;
3890 : }
3891 :
3892 : // Determine a return target, default is to use the constant value, but scheduled or externally
3893 : // set on the return node TempSetPoint will overwrite it. Note that the schedule index is only
3894 : // greater than zero if the input type is scheduled, and the useReturnTempSetpoint flag is only
3895 : // true if the input type is specified as such
3896 31366 : Real64 T_return_target = this->returnTempConstantTarget;
3897 31366 : if (this->returnTempSchedNum > 0) {
3898 0 : T_return_target = GetCurrentScheduleValue(state, this->returnTempSchedNum);
3899 31366 : } else if (this->returnTempType == ReturnTempType::Setpoint) {
3900 963 : if (returnNode.TempSetPoint != SensedNodeFlagValue) {
3901 963 : T_return_target = returnNode.TempSetPoint;
3902 : } else {
3903 0 : ShowSevereError(state, "Return temperature reset setpoint manager encountered an error.");
3904 0 : ShowContinueError(state,
3905 : "The manager is specified to look to the return node setpoint to find a target return temperature, but the node "
3906 : "setpoint was invalid");
3907 0 : ShowContinueError(state,
3908 0 : format("Verify that a separate sepoint manager is specified to set the setpoint on the return node named \"{}\"",
3909 0 : state.dataLoopNodes->NodeID(this->returnNodeNum)));
3910 0 : ShowContinueError(state, "Or change the target return temperature input type to constant or scheduled");
3911 0 : ShowFatalError(state, "Missing reference setpoint");
3912 : }
3913 : }
3914 :
3915 : // calculate the supply setpoint to use, default to the design value if flow is zero
3916 31366 : Real64 T_supply_setpoint = (this->type == SPMType::ChilledWaterReturnTemp) ? this->minSetTemp : this->maxSetTemp;
3917 31366 : if (mdot > DataConvergParams::PlantFlowRateToler) {
3918 13366 : T_supply_setpoint = T_return_target + ((this->type == SPMType::ChilledWaterReturnTemp) ? -deltaT : deltaT);
3919 : }
3920 :
3921 31366 : this->currentSupplySetPt = std::clamp(T_supply_setpoint, this->minSetTemp, this->maxSetTemp);
3922 : } // SPMReturnWaterTemp::calculate()
3923 :
3924 2 : void SPMIdealCondenserEnteringTemp::SetupMeteredVarsForSetPt(EnergyPlusData &state)
3925 : {
3926 : // SUBROUTINE INFORMATION:
3927 : // AUTHOR Linda Lawrie
3928 : // DATE WRITTEN Sep 2013
3929 :
3930 : // PURPOSE OF THIS SUBROUTINE:
3931 : // For the Ideal Cond reset setpoint manager, this sets up the
3932 : // report variables used during the calculation.
3933 :
3934 : // Using/Aliasing
3935 : using namespace DataPlant;
3936 :
3937 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3938 2 : std::string TypeOfComp;
3939 2 : std::string NameOfComp;
3940 :
3941 2 : Array1D<OutputProcessor::MeteredVar> meteredVars;
3942 : int NumVariables;
3943 :
3944 2 : auto &plantLoop = state.dataPlnt->PlantLoop(this->chillerPloc.loopNum);
3945 2 : auto &supplySide = plantLoop.LoopSide(this->chillerPloc.loopSideNum);
3946 2 : auto &chillerBranch = supplySide.Branch(this->chillerPloc.branchNum);
3947 2 : auto &chillerComp = chillerBranch.Comp(this->chillerPloc.compNum);
3948 :
3949 2 : NumVariables = GetNumMeteredVariables(state, chillerComp.TypeOf, chillerComp.Name);
3950 2 : meteredVars.allocate(NumVariables);
3951 :
3952 2 : GetMeteredVariables(state, chillerComp.Name, meteredVars);
3953 2 : this->chillerVar.Type = meteredVars(1).varType;
3954 2 : this->chillerVar.Num = meteredVars(1).num;
3955 :
3956 2 : auto &chilledWaterPumpBranch = supplySide.Branch(this->chilledWaterPumpPloc.branchNum);
3957 2 : auto &chilledWaterPumpComp = chilledWaterPumpBranch.Comp(this->chilledWaterPumpPloc.compNum);
3958 :
3959 2 : NumVariables = GetNumMeteredVariables(state, chilledWaterPumpComp.TypeOf, chilledWaterPumpComp.Name);
3960 2 : meteredVars.allocate(NumVariables);
3961 :
3962 2 : GetMeteredVariables(state, chilledWaterPumpComp.Name, meteredVars);
3963 2 : this->chilledWaterPumpVar.Type = meteredVars(1).varType;
3964 2 : this->chilledWaterPumpVar.Num = meteredVars(1).num;
3965 :
3966 2 : auto &towerLoopSide = state.dataPlnt->PlantLoop(this->towerPlocs(1).loopNum).LoopSide(this->towerPlocs(1).loopSideNum);
3967 :
3968 5 : for (int i = 1; i <= this->numTowers; i++) {
3969 3 : auto &towerComp = towerLoopSide.Branch(this->towerPlocs(i).branchNum).Comp(this->towerPlocs(i).compNum);
3970 3 : NumVariables = GetNumMeteredVariables(state, towerComp.TypeOf, towerComp.Name);
3971 3 : meteredVars.allocate(NumVariables);
3972 :
3973 3 : GetMeteredVariables(state, towerComp.Name, meteredVars);
3974 3 : this->towerVars.push_back({meteredVars(1).varType, meteredVars(1).num});
3975 : }
3976 :
3977 2 : auto &condenserPumpComp = towerLoopSide.Branch(this->condenserPumpPloc.branchNum).Comp(this->condenserPumpPloc.compNum);
3978 2 : NumVariables = GetNumMeteredVariables(state, condenserPumpComp.TypeOf, condenserPumpComp.Name);
3979 2 : meteredVars.allocate(NumVariables);
3980 :
3981 2 : GetMeteredVariables(state, condenserPumpComp.Name, meteredVars);
3982 2 : this->condenserPumpVar = {meteredVars(1).varType, meteredVars(1).num};
3983 2 : } // SPMIdealCondenserEnteringTemp::SetupMeteredVarsForSetPt()
3984 :
3985 6920 : void SPMSystemNode::calculate(EnergyPlusData &state)
3986 : {
3987 6920 : Real64 RefValue = 0; // Reference value from the Reference node
3988 :
3989 6920 : auto &refNode = state.dataLoopNodes->Node(this->refNodeNum);
3990 :
3991 6920 : switch (this->ctrlVar) {
3992 5190 : case HVAC::CtrlVarType::Temp:
3993 : case HVAC::CtrlVarType::MaxTemp:
3994 : case HVAC::CtrlVarType::MinTemp: {
3995 5190 : RefValue = refNode.Temp;
3996 5190 : } break;
3997 1730 : case HVAC::CtrlVarType::HumRat:
3998 : case HVAC::CtrlVarType::MaxHumRat:
3999 : case HVAC::CtrlVarType::MinHumRat: {
4000 1730 : RefValue = refNode.HumRat;
4001 1730 : } break;
4002 0 : default:
4003 0 : break;
4004 : }
4005 :
4006 6920 : this->setPt = interpSetPoint(this->lowRef, this->highRef, RefValue, this->lowRefSetPt, this->highRefSetPt);
4007 6920 : } // SPMSystemNode::calculate()
4008 :
4009 510956 : Real64 interpSetPoint(Real64 const LowVal, Real64 const HighVal, Real64 const RefVal, Real64 const SetptAtLowVal, Real64 const SetptAtHighVal)
4010 : {
4011 510956 : if (LowVal >= HighVal) {
4012 0 : return 0.5 * (SetptAtLowVal + SetptAtHighVal);
4013 510956 : } else if (RefVal <= LowVal) {
4014 131858 : return SetptAtLowVal;
4015 379098 : } else if (RefVal >= HighVal) {
4016 87451 : return SetptAtHighVal;
4017 : } else {
4018 291647 : return SetptAtLowVal - ((RefVal - LowVal) / (HighVal - LowVal)) * (SetptAtLowVal - SetptAtHighVal);
4019 : }
4020 : }
4021 :
4022 2832891 : void UpdateSetPointManagers(EnergyPlusData &state)
4023 : {
4024 : // SUBROUTINE INFORMATION:
4025 : // AUTHOR Fred Buhl
4026 : // DATE WRITTEN July 1998
4027 : // MODIFIED Shirey/Raustad (FSEC), Jan 2004
4028 : // P. Haves Oct 2004
4029 : // Add new setpoint managers:
4030 : // SET POINT MANAGER:WARMEST TEMP FLOW and
4031 : // SET POINT MANAGER:COLDEST TEMP FLOW
4032 : // Nov 2004 M. J. Witte, GARD Analytics, Inc.
4033 : // Add new setpoint managers:
4034 : // SET POINT MANAGER:SINGLE ZONE HEATING and
4035 : // SET POINT MANAGER:SINGLE ZONE COOLING
4036 : // Work supported by ASHRAE research project 1254-RP
4037 : // B. Griffith Aug. 2006. Allow HUMRAT for scheduled setpoint manager
4038 : // P. Haves Aug 2007
4039 : // SET POINT MANAGER:WARMEST TEMP FLOW:
4040 : // Set AirLoopControlInfo()%LoopFlowRateSet every call not just on
4041 : // initialization (flag now reset in SUBROUTINE ResetHVACControl)
4042 : // Removed SET POINT MANAGER:COLDEST TEMP FLOW
4043 : // July 2010 B.A. Nigusse, FSEC/UCF
4044 : // Added new setpoint managers
4045 : // SetpointManager:MultiZone:Heating:Average
4046 : // SetpointManager:MultiZone:Cooling:Average
4047 : // SetpointManager:MultiZone:MinimumHumidity:Average
4048 : // SetpointManager:MultiZone:MaximumHumidity:Average
4049 : // Aug 2010 B.A. Nigusse, FSEC/UCF
4050 : // Added new setpoint managers:
4051 : // SetpointManager:MultiZone:Humidity:Minimum
4052 : // SetpointManager:MultiZone:Humidity:Maximum
4053 : // Aug 2014 Rick Strand, UIUC
4054 : // SetpointManager:ScheduledTES (internally defined)
4055 : // Jan 2022 Wooyoung Jung, Jeremy Lerond and Jian Zhang, PNNL
4056 : // Added new setpoint managers:
4057 : // SetpointManager:SystemNodeReset:Temperature
4058 : // SetpointManager:SystemNodeReset:Humidity
4059 :
4060 : // PURPOSE OF THIS SUBROUTINE
4061 : // Loop over all the Setpoint Managers and use their output arrays
4062 : // to set the node setpoints.
4063 :
4064 : // Using/Aliasing
4065 : using EMSManager::CheckIfNodeSetPointManagedByEMS;
4066 :
4067 : // Loop over all the Scheduled Setpoint Managers
4068 20747344 : for (auto *spm : state.dataSetPointManager->spms) {
4069 :
4070 17914453 : switch (spm->type) {
4071 :
4072 7020714 : case SPMType::Scheduled:
4073 : case SPMType::SystemNodeTemp:
4074 : case SPMType::SystemNodeHum: {
4075 14136210 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4076 7115496 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
4077 7115496 : switch (spm->ctrlVar) {
4078 : // set the setpoint depending on the type of variable being controlled
4079 7094008 : case HVAC::CtrlVarType::Temp: {
4080 7094008 : node.TempSetPoint = spm->setPt;
4081 7094008 : } break;
4082 0 : case HVAC::CtrlVarType::MaxTemp: {
4083 0 : node.TempSetPointHi = spm->setPt;
4084 0 : } break;
4085 0 : case HVAC::CtrlVarType::MinTemp: {
4086 0 : node.TempSetPointLo = spm->setPt;
4087 0 : } break;
4088 0 : case HVAC::CtrlVarType::HumRat: {
4089 0 : node.HumRatSetPoint = spm->setPt;
4090 0 : } break;
4091 19462 : case HVAC::CtrlVarType::MaxHumRat: {
4092 19462 : node.HumRatMax = spm->setPt;
4093 19462 : } break;
4094 2026 : case HVAC::CtrlVarType::MinHumRat: {
4095 2026 : node.HumRatMin = spm->setPt;
4096 2026 : } break;
4097 0 : case HVAC::CtrlVarType::MassFlowRate: {
4098 0 : node.MassFlowRateSetPoint = spm->setPt;
4099 0 : } break;
4100 0 : case HVAC::CtrlVarType::MaxMassFlowRate: {
4101 0 : node.MassFlowRateMax = spm->setPt;
4102 0 : } break;
4103 0 : case HVAC::CtrlVarType::MinMassFlowRate: {
4104 0 : node.MassFlowRateMin = spm->setPt;
4105 0 : } break;
4106 0 : default:
4107 0 : break;
4108 : }
4109 7020714 : } // for (CtrlNodeNum)
4110 7020714 : } break;
4111 :
4112 3420 : case SPMType::TESScheduled: {
4113 3420 : auto *spmTESS = dynamic_cast<SPMTESScheduled *>(spm);
4114 3420 : assert(spmTESS != nullptr);
4115 :
4116 3420 : state.dataLoopNodes->Node(spmTESS->ctrlNodeNum).TempSetPoint = spm->setPt;
4117 3420 : } break;
4118 :
4119 33649 : case SPMType::ScheduledDual: {
4120 33649 : auto *spmSD = dynamic_cast<SPMScheduledDual *>(spm);
4121 33649 : assert(spmSD != nullptr);
4122 :
4123 33649 : if (spmSD->ctrlVar == HVAC::CtrlVarType::Temp) {
4124 81148 : for (int ctrlNodeNum : spmSD->ctrlNodeNums) {
4125 47499 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
4126 :
4127 47499 : node.TempSetPointHi = spmSD->setPtHi; // Set the setpoint High
4128 47499 : node.TempSetPointLo = spmSD->setPtLo; // Set the setpoint Low
4129 47499 : node.TempSetPoint = (node.TempSetPointHi + node.TempSetPointLo) / 2.0; // average of the high and low
4130 33649 : }
4131 : }
4132 33649 : } break;
4133 :
4134 1224257 : case SPMType::OutsideAir:
4135 : case SPMType::FollowOutsideAirTemp:
4136 : case SPMType::FollowSystemNodeTemp:
4137 : case SPMType::FollowGroundTemp: {
4138 2525244 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4139 1300987 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
4140 1300987 : if (spm->ctrlVar == HVAC::CtrlVarType::Temp) {
4141 1300987 : node.TempSetPoint = spm->setPt;
4142 0 : } else if (spm->ctrlVar == HVAC::CtrlVarType::MaxTemp) {
4143 0 : node.TempSetPointHi = spm->setPt;
4144 0 : } else if (spm->ctrlVar == HVAC::CtrlVarType::MinTemp) {
4145 0 : node.TempSetPointLo = spm->setPt;
4146 : }
4147 1224257 : }
4148 :
4149 1224257 : } break;
4150 :
4151 2226375 : case SPMType::SZReheat:
4152 : case SPMType::SZHeating:
4153 : case SPMType::SZCooling:
4154 : case SPMType::Warmest:
4155 : case SPMType::Coldest:
4156 : case SPMType::MZCoolingAverage:
4157 : case SPMType::MZHeatingAverage:
4158 : case SPMType::CondenserEnteringTemp:
4159 : case SPMType::IdealCondenserEnteringTemp:
4160 : case SPMType::SZOneStageCooling:
4161 : case SPMType::SZOneStageHeating: {
4162 2226375 : if (spm->ctrlVar == HVAC::CtrlVarType::Temp) {
4163 4570344 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4164 2343969 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = spm->setPt; // Set the setpoint
4165 2226375 : }
4166 : }
4167 2226375 : } break;
4168 :
4169 94776 : case SPMType::SZMinHum:
4170 : case SPMType::MZMinHumAverage:
4171 : case SPMType::MZMinHum: {
4172 189552 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4173 94776 : if (spm->type == SPMType::SZMinHum || spm->ctrlVar == HVAC::CtrlVarType::MinHumRat) // Why is SZMinHum not tested for this?
4174 94776 : state.dataLoopNodes->Node(ctrlNodeNum).HumRatMin = spm->setPt;
4175 94776 : }
4176 94776 : } break;
4177 :
4178 169727 : case SPMType::SZMaxHum:
4179 : case SPMType::MZMaxHumAverage:
4180 : case SPMType::MZMaxHum: {
4181 349842 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4182 180115 : if (spm->type == SPMType::SZMaxHum || spm->ctrlVar == HVAC::CtrlVarType::MaxHumRat) // Why is SZMaxHum not tested for this?
4183 180115 : state.dataLoopNodes->Node(ctrlNodeNum).HumRatMax = spm->setPt;
4184 169727 : }
4185 169727 : } break;
4186 :
4187 6620 : case SPMType::WarmestTempFlow: {
4188 6620 : auto *spmWTF = dynamic_cast<SPMWarmestTempFlow *>(spm);
4189 6620 : assert(spmWTF != nullptr);
4190 :
4191 6620 : if (spmWTF->ctrlVar == HVAC::CtrlVarType::Temp) {
4192 13240 : for (int ctrlNodeNum : spmWTF->ctrlNodeNums) {
4193 6620 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = spmWTF->setPt; // Set the supply air temperature setpoint
4194 6620 : }
4195 :
4196 6620 : state.dataAirLoop->AirLoopFlow(spmWTF->airLoopNum).ReqSupplyFrac = spmWTF->turndown; // Set the supply air flow rate
4197 6620 : state.dataAirLoop->AirLoopControlInfo(spmWTF->airLoopNum).LoopFlowRateSet = true; // PH 8/17/07
4198 : }
4199 6620 : } break;
4200 :
4201 2005 : case SPMType::ReturnAirBypass: {
4202 2005 : auto *spmRAB = dynamic_cast<SPMReturnAirBypassFlow *>(spm);
4203 2005 : assert(spmRAB != nullptr);
4204 :
4205 2005 : if (spmRAB->ctrlVar == HVAC::CtrlVarType::MassFlowRate) {
4206 2005 : state.dataLoopNodes->Node(spmRAB->rabSplitOutNodeNum).MassFlowRateSetPoint = spmRAB->FlowSetPt; // Set the flow setpoint
4207 : }
4208 2005 : } break;
4209 :
4210 31366 : case SPMType::ChilledWaterReturnTemp:
4211 : case SPMType::HotWaterReturnTemp: {
4212 31366 : auto *spmRWT = dynamic_cast<SPMReturnWaterTemp *>(spm);
4213 31366 : assert(spmRWT != nullptr);
4214 31366 : if (spmRWT->plantSetPtNodeNum > 0) {
4215 31366 : state.dataLoopNodes->Node(spmRWT->plantSetPtNodeNum).TempSetPoint = spmRWT->currentSupplySetPt;
4216 : }
4217 31366 : } break;
4218 :
4219 : // MixedAir and OutsideAirPretreat SPMs have to be handled separately because they depend on other SPMs
4220 7101544 : case SPMType::MixedAir:
4221 : case SPMType::OutsideAirPretreat: {
4222 7101544 : } break;
4223 :
4224 0 : default:
4225 0 : break;
4226 : } // switch (sys->type)
4227 : } // for (spm)
4228 2832891 : } // UpdateSetPointManagers()
4229 :
4230 2832891 : void UpdateMixedAirSetPoints(EnergyPlusData &state)
4231 : {
4232 : // SUBROUTINE INFORMATION:
4233 : // AUTHOR Fred Buhl
4234 : // DATE WRITTEN May 2001
4235 :
4236 : // PURPOSE OF THIS SUBROUTINE
4237 : // Loop over all the Mixed Air Managers and use their output arrays
4238 : // to set the node setpoints.
4239 :
4240 20747344 : for (auto *spm : state.dataSetPointManager->spms) {
4241 17914453 : if (spm->type != SPMType::MixedAir) continue;
4242 7079405 : if (spm->ctrlVar != HVAC::CtrlVarType::Temp) continue;
4243 14931467 : for (int ctrlNodeNum : spm->ctrlNodeNums)
4244 14931467 : state.dataLoopNodes->Node(ctrlNodeNum).TempSetPoint = spm->setPt; // Set the setpoint
4245 : }
4246 2832891 : } // UpdateMixedAirSetPoints()
4247 :
4248 2832891 : void UpdateOAPretreatSetPoints(EnergyPlusData &state)
4249 : {
4250 : // SUBROUTINE INFORMATION:
4251 : // AUTHOR M. J. Witte based on UpdateMixedAirSetPoints by Fred Buhl,
4252 : // Work supported by ASHRAE research project 1254-RP
4253 : // DATE WRITTEN January 2005
4254 :
4255 : // PURPOSE OF THIS SUBROUTINE
4256 : // Loop over all the Outside Air Pretreat Managers and use their output arrays
4257 : // to set the node setpoints.
4258 :
4259 20747344 : for (auto *spm : state.dataSetPointManager->spms) {
4260 17914453 : if (spm->type != SPMType::OutsideAirPretreat) continue;
4261 44278 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4262 22139 : auto &node = state.dataLoopNodes->Node(ctrlNodeNum);
4263 22139 : switch (spm->ctrlVar) {
4264 11459 : case HVAC::CtrlVarType::Temp: {
4265 11459 : node.TempSetPoint = spm->setPt;
4266 11459 : } break;
4267 10680 : case HVAC::CtrlVarType::MaxHumRat: {
4268 10680 : node.HumRatMax = spm->setPt;
4269 10680 : } break;
4270 0 : case HVAC::CtrlVarType::MinHumRat: {
4271 0 : node.HumRatMin = spm->setPt;
4272 0 : } break;
4273 0 : case HVAC::CtrlVarType::HumRat: {
4274 0 : node.HumRatSetPoint = spm->setPt;
4275 0 : } break;
4276 0 : default:
4277 0 : break;
4278 : }
4279 22139 : }
4280 : }
4281 2832891 : } // UpdateOutsideAirSetPoints()
4282 :
4283 4 : int GetSetPointManagerIndexByNode(EnergyPlusData &state, int const NodeNum, HVAC::CtrlVarType const ctrlVar, SPMType const spmType, bool isRefNode)
4284 : {
4285 :
4286 4 : if (state.dataSetPointManager->GetInputFlag) {
4287 0 : GetSetPointManagerInputs(state);
4288 0 : state.dataSetPointManager->GetInputFlag = false;
4289 : }
4290 :
4291 4 : for (int iSPM = 1; iSPM < (int)state.dataSetPointManager->spms.size(); ++iSPM) {
4292 0 : auto *spm = state.dataSetPointManager->spms(iSPM);
4293 0 : if (spm->type != spmType) continue;
4294 0 : if (spm->ctrlVar != ctrlVar) continue;
4295 :
4296 0 : if (isRefNode) {
4297 0 : if (NodeNum == spm->refNodeNum) return iSPM;
4298 : } else {
4299 0 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4300 0 : if (NodeNum == ctrlNodeNum) return iSPM;
4301 0 : }
4302 : }
4303 : }
4304 :
4305 4 : return 0;
4306 : } // getSPMIndexByNode()
4307 :
4308 4592 : bool IsNodeOnSetPtManager(EnergyPlusData &state, int const NodeNum, HVAC::CtrlVarType const ctrlVar)
4309 : {
4310 : // FUNCTION INFORMATION:
4311 : // AUTHOR Sankaranarayanan K P
4312 : // DATE WRITTEN January 2007
4313 :
4314 : // PURPOSE OF THIS SUBROUTINE:
4315 : // Determines if a particular node is acted upon by a specific setpoint manager
4316 :
4317 : // METHODOLOGY EMPLOYED:
4318 : // Cycle through all setpoint managers and find if the node passed in has a setpoint manager of passed
4319 : // in type associated to it.
4320 : // Return value
4321 :
4322 : // First time called, get the input for all the setpoint managers
4323 4592 : if (state.dataSetPointManager->GetInputFlag) {
4324 0 : GetSetPointManagerInputs(state);
4325 0 : state.dataSetPointManager->GetInputFlag = false;
4326 : }
4327 :
4328 43115 : for (auto const *spm : state.dataSetPointManager->spms) {
4329 39661 : if (spm->ctrlVar != ctrlVar) continue;
4330 78524 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4331 41186 : if (NodeNum == ctrlNodeNum) return true;
4332 39614 : }
4333 : }
4334 :
4335 3454 : return false;
4336 : } // IsNodeOnSetPointManager()
4337 :
4338 4542 : bool NodeHasSPMCtrlVarType(EnergyPlusData &state, int const NodeNum, HVAC::CtrlVarType const ctrlVar)
4339 : {
4340 : // FUNCTION INFORMATION:
4341 : // AUTHOR Chandan Sharma
4342 : // DATE WRITTEN March 2013
4343 :
4344 : // PURPOSE OF THIS SUBROUTINE:
4345 : // Determines if a particular node is acted upon by a specific setpoint manager
4346 :
4347 : // METHODOLOGY EMPLOYED:
4348 : // Cycle through all setpoint managers and find if the node has a specific control type
4349 :
4350 : // First time called, get the input for all the setpoint managers
4351 4542 : if (state.dataSetPointManager->GetInputFlag) {
4352 0 : GetSetPointManagerInputs(state);
4353 0 : state.dataSetPointManager->GetInputFlag = false;
4354 : }
4355 :
4356 68132 : for (auto const *spm : state.dataSetPointManager->spms) {
4357 65878 : if (spm->ctrlVar != ctrlVar) continue;
4358 127122 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4359 65590 : if (NodeNum == ctrlNodeNum) return true;
4360 66108 : }
4361 : }
4362 :
4363 2254 : return false;
4364 : } // NodeHasSPMCtrlVarType()
4365 :
4366 45 : void ResetHumidityRatioCtrlVarType(EnergyPlusData &state, int const NodeNum)
4367 : {
4368 : // FUNCTION INFORMATION:
4369 : // AUTHOR Bereket Nigusse
4370 : // DATE WRITTEN August 2015
4371 :
4372 : // PURPOSE OF THIS SUBROUTINE:
4373 : // Resets setpoint control variable type to "Maximum Humidty Ratio" if control variable type
4374 : // is "Humidity Ratio".
4375 :
4376 : // METHODOLOGY EMPLOYED:
4377 : // Cycle through all setpoint managers and find if the node has a "Humidity Ratio" control
4378 : // variable type. This routine is called from "GetControllerInput" routine. This reset is
4379 : // just to stop false warning message due to control variable type mismatch.
4380 :
4381 : // First time called, get the input for all the setpoint managers
4382 45 : if (state.dataSetPointManager->GetInputFlag) {
4383 0 : GetSetPointManagerInputs(state);
4384 0 : state.dataSetPointManager->GetInputFlag = false;
4385 : }
4386 :
4387 1992 : for (auto *spm : state.dataSetPointManager->spms) {
4388 1947 : if (spm->ctrlVar != HVAC::CtrlVarType::HumRat) continue;
4389 0 : for (int ctrlNodeNum : spm->ctrlNodeNums) {
4390 0 : if (NodeNum != ctrlNodeNum) continue;
4391 :
4392 0 : spm->ctrlVar = HVAC::CtrlVarType::MaxHumRat;
4393 0 : ShowWarningError(state, format("ResetHumidityRatioCtrlVarType: {}=\"{}\". ", spmTypeNames[(int)spm->type], spm->Name));
4394 0 : ShowContinueError(state, " ..Humidity ratio control variable type specified is = HumidityRatio");
4395 0 : ShowContinueError(state, " ..Humidity ratio control variable type allowed with water coils is = MaximumHumidityRatio");
4396 0 : ShowContinueError(state, " ..Setpointmanager control variable type is reset to = MaximumHumidityRatio");
4397 0 : ShowContinueError(state, " ..Simulation continues. ");
4398 0 : return;
4399 0 : }
4400 : }
4401 : } // ResetHumidityRatioCtrlVarType()
4402 :
4403 796 : void CheckIfAnyIdealCondEntSetPoint(EnergyPlusData &state)
4404 : {
4405 : // SUBROUTINE INFORMATION:
4406 : // AUTHOR Heejin Cho, PNNL
4407 : // DATE WRITTEN March 2012
4408 :
4409 : // PURPOSE OF THIS SUBROUTINE:
4410 : // Determine if ideal condenser entering set point manager is used in model and set flag
4411 :
4412 1592 : state.dataGlobal->AnyIdealCondEntSetPointInModel =
4413 796 : (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SetpointManager:CondenserEnteringReset:Ideal") > 0);
4414 796 : } // CheckIfAnyIdealCondEntSetPoint()
4415 :
4416 0 : HVAC::CtrlVarType GetHumidityRatioVariableType(EnergyPlusData &state, int const NodeNum)
4417 : {
4418 : // SUBROUTINE INFORMATION:
4419 : // AUTHOR B. A. Nigusse
4420 : // DATE WRITTEN December 2013
4421 :
4422 : // PURPOSE OF THIS SUBROUTINE
4423 : // Loop over all the humidity setpoint Managers to determine the
4424 : // humidity ratio setpoint type
4425 :
4426 0 : if (state.dataSetPointManager->GetInputFlag) {
4427 0 : GetSetPointManagerInputs(state);
4428 0 : state.dataSetPointManager->GetInputFlag = false;
4429 : }
4430 :
4431 0 : for (auto const *spm : state.dataSetPointManager->spms) {
4432 0 : if (spm->type != SPMType::SZMaxHum && spm->type != SPMType::MZMaxHum && spm->type != SPMType::MZMaxHumAverage) continue;
4433 0 : if (std::find(spm->ctrlNodeNums.begin(), spm->ctrlNodeNums.end(), NodeNum) != spm->ctrlNodeNums.end()) {
4434 0 : return HVAC::CtrlVarType::MaxHumRat;
4435 : }
4436 : }
4437 :
4438 0 : for (auto const *spm : state.dataSetPointManager->spms) {
4439 0 : if (spm->type != SPMType::SZMinHum && spm->type != SPMType::MZMinHum && spm->type != SPMType::MZMinHumAverage) continue;
4440 0 : if (std::find(spm->ctrlNodeNums.begin(), spm->ctrlNodeNums.end(), NodeNum) != spm->ctrlNodeNums.end()) {
4441 0 : return HVAC::CtrlVarType::MaxHumRat;
4442 : }
4443 : }
4444 :
4445 0 : for (auto const *spm : state.dataSetPointManager->spms) {
4446 0 : if (spm->type != SPMType::Scheduled) continue;
4447 0 : if (std::find(spm->ctrlNodeNums.begin(), spm->ctrlNodeNums.end(), NodeNum) != spm->ctrlNodeNums.end()) {
4448 0 : if (spm->ctrlVar == HVAC::CtrlVarType::HumRat || spm->ctrlVar == HVAC::CtrlVarType::MaxHumRat) return spm->ctrlVar;
4449 : }
4450 : }
4451 :
4452 0 : return HVAC::CtrlVarType::HumRat;
4453 : } // GetHumidityRatioVariableType()
4454 :
4455 2 : void SetUpNewScheduledTESSetPtMgr(EnergyPlusData &state,
4456 : int const SchedPtr,
4457 : int const SchedPtrCharge,
4458 : Real64 NonChargeCHWTemp,
4459 : Real64 ChargeCHWTemp,
4460 : DataPlant::CtrlType CompOpType,
4461 : int const ControlNodeNum)
4462 : {
4463 : // SUBROUTINE INFORMATION:
4464 : // AUTHOR Rick Strand
4465 : // DATE WRITTEN August 2014
4466 :
4467 : // PURPOSE OF THIS SUBROUTINE
4468 : // Set up new scheduled TES setpoint managers based on plant control Simple TES
4469 :
4470 : // METHODOLOGY EMPLOYED:
4471 : // Set up internally created scheduled setpoint managers to control the setpoints
4472 : // of various ice storage equipment with the user having to do this manually. The
4473 : // point is to provide a simpler input description and take care of logic internally.
4474 :
4475 2 : auto *spm = new SPMTESScheduled;
4476 :
4477 : // Set up the scheduled TES setpoint manager information
4478 2 : spm->Name = format("TES Scheduled {}", state.dataSetPointManager->spms.size());
4479 2 : state.dataSetPointManager->spms.push_back(spm);
4480 2 : state.dataSetPointManager->spmMap.insert_or_assign(spm->Name, state.dataSetPointManager->spms.size());
4481 :
4482 2 : spm->schedNum = SchedPtr;
4483 2 : spm->schedNumCharge = SchedPtrCharge;
4484 2 : spm->nonChargeCHWTemp = NonChargeCHWTemp;
4485 2 : spm->chargeCHWTemp = ChargeCHWTemp;
4486 2 : spm->compOpType = CompOpType;
4487 2 : spm->ctrlNodeNum = ControlNodeNum;
4488 :
4489 : // Set up the all setpoint manager information for "verification" that no other setpoint manager controls the node that this new ones does
4490 2 : spm->ctrlNodeNums.push_back(spm->ctrlNodeNum);
4491 2 : spm->type = SPMType::TESScheduled;
4492 2 : spm->ctrlVar = HVAC::CtrlVarType::Temp;
4493 :
4494 : // Now verify that there is no overlap (no other SPM uses the node of the new setpoint manager)
4495 2 : bool ErrorsFoundinTESSchSetup = false;
4496 2 : VerifySetPointManagers(state, ErrorsFoundinTESSchSetup);
4497 2 : if (ErrorsFoundinTESSchSetup) {
4498 0 : ShowFatalError(state, "Errors found in verification step of SetUpNewScheduledTESSetPtMgr. Program terminates.");
4499 : }
4500 : // Since all of the other setpoint managers not only been read and verified but also initialized, simulated, and updated,
4501 : // we must now also initialize, simulate, and update the current SchTESStPtMgr that was just added. But the init and simulate
4502 : // steps are the same so we can call the simulate first.
4503 :
4504 2 : spm->calculate(state);
4505 :
4506 : // Now update reusing code from Update routine specialized to only doing the current (new) setpoint manager and then we are done
4507 2 : state.dataLoopNodes->Node(spm->ctrlNodeNum).TempSetPoint = spm->setPt;
4508 2 : } // end of SetUpNewScheduledTESSetPtMgr
4509 :
4510 10151 : bool GetCoilFreezingCheckFlag(EnergyPlusData &state, int const spmNum)
4511 : {
4512 : // SUBROUTINE INFORMATION:
4513 : // AUTHOR L. Gu
4514 : // DATE WRITTEN Nov. 2015
4515 :
4516 : // PURPOSE OF THIS SUBROUTINE
4517 : // Get freezing check status
4518 10151 : if (state.dataSetPointManager->GetInputFlag) {
4519 0 : GetSetPointManagerInputs(state);
4520 0 : state.dataSetPointManager->GetInputFlag = false;
4521 : }
4522 :
4523 10151 : auto *spmMA = dynamic_cast<SPMMixedAir *>(state.dataSetPointManager->spms(spmNum));
4524 10151 : assert(spmMA != nullptr);
4525 10151 : return spmMA->freezeCheckEnable;
4526 : } // GetCoilFreezingCheckFlag()
4527 :
4528 1055 : int GetMixedAirNumWithCoilFreezingCheck(EnergyPlusData &state, int const MixedAirNode)
4529 : {
4530 : // SUBROUTINE INFORMATION:
4531 : // AUTHOR L. Gu
4532 : // DATE WRITTEN Nov. 2015
4533 :
4534 : // PURPOSE OF THIS SUBROUTINE
4535 : // Loop over all the MixedAir setpoint Managers to find coil freezing check flag
4536 :
4537 1055 : if (state.dataSetPointManager->GetInputFlag) {
4538 0 : GetSetPointManagerInputs(state);
4539 0 : state.dataSetPointManager->GetInputFlag = false;
4540 : }
4541 :
4542 19423 : for (int iSPM = 1; iSPM <= state.dataSetPointManager->spms.size(); ++iSPM) {
4543 18369 : auto *const spm = state.dataSetPointManager->spms(iSPM);
4544 18369 : if (spm->type != SPMType::MixedAir) continue;
4545 :
4546 9144 : auto *spmMA = dynamic_cast<SPMMixedAir *>(spm);
4547 9144 : assert(spmMA != nullptr);
4548 :
4549 18288 : if (std::find(spmMA->ctrlNodeNums.begin(), spmMA->ctrlNodeNums.end(), MixedAirNode) != spmMA->ctrlNodeNums.end() &&
4550 18288 : spmMA->coolCoilInNodeNum > 0 && spmMA->coolCoilOutNodeNum > 0) {
4551 1 : return iSPM; // Is this really thing we are returning? Not the number of the SPM? Why?
4552 : }
4553 : }
4554 :
4555 1054 : return 0;
4556 : } // End of GetMixedAirNumWithCoilFreezingCheck()
4557 :
4558 : } // namespace EnergyPlus::SetPointManager
|