Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // ObjexxFCL Headers
49 : #include <ObjexxFCL/Array.functions.hh>
50 :
51 : // EnergyPlus Headers
52 : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
53 : #include <EnergyPlus/Data/EnergyPlusData.hh>
54 : #include <EnergyPlus/DataHVACGlobals.hh>
55 : #include <EnergyPlus/DataHeatBalFanSys.hh>
56 : #include <EnergyPlus/DataHeatBalSurface.hh>
57 : #include <EnergyPlus/DataHeatBalance.hh>
58 : #include <EnergyPlus/DataIPShortCuts.hh>
59 : #include <EnergyPlus/DataLoopNode.hh>
60 : #include <EnergyPlus/DataSizing.hh>
61 : #include <EnergyPlus/DataSurfaces.hh>
62 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
63 : #include <EnergyPlus/DataZoneEquipment.hh>
64 : #include <EnergyPlus/ElectricBaseboardRadiator.hh>
65 : #include <EnergyPlus/GeneralRoutines.hh>
66 : #include <EnergyPlus/GlobalNames.hh>
67 : #include <EnergyPlus/HeatBalanceIntRadExchange.hh>
68 : #include <EnergyPlus/HeatBalanceSurfaceManager.hh>
69 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
70 : #include <EnergyPlus/OutputProcessor.hh>
71 : #include <EnergyPlus/Psychrometrics.hh>
72 : #include <EnergyPlus/ScheduleManager.hh>
73 : #include <EnergyPlus/UtilityRoutines.hh>
74 :
75 : namespace EnergyPlus {
76 :
77 : namespace ElectricBaseboardRadiator {
78 :
79 : // Module ElectricBaseboardRadiator -- (ref: Object: ZoneHVAC:Baseboard:RadiantConvective:Electric)
80 :
81 : // MODULE INFORMATION:
82 : // AUTHOR Daeho Kang
83 : // DATE WRITTEN Feb 2010
84 :
85 : // PURPOSE OF THIS MODULE:
86 : // This module is to calculate the actual convective heat addition that an electrical baseboard heater
87 : // delivers to a space.
88 :
89 : // METHODOLOGY EMPLOYED:
90 : // Based on the convective-only electric baseboard module (Object: ZoneHVAC:Baseboard:Convective:Electric)
91 : // written by Richard Liesen in Nov 2001, this new electric baseboard module is to add the existing calculation
92 : // algorithm of radiant heat transfer in the high temperature radiant system module.
93 :
94 : // REFERENCES:
95 : // HighTempRadiantSystem module (ZoneHVAC:HighTemperatureRadiant)
96 : // Convective electric baseboard module (ZoneHVAC:Baseboard:Convective:Electric)
97 :
98 60 : void SimElecBaseboard(EnergyPlusData &state,
99 : std::string const &EquipName,
100 : int const ControlledZoneNum,
101 : bool const FirstHVACIteration,
102 : Real64 &PowerMet,
103 : int &CompIndex)
104 : {
105 :
106 : // SUBROUTINE INFORMATION:
107 : // AUTHOR Richard Liesen
108 : // DATE WRITTEN Nov 2001
109 : // MODIFIED Feb 2010 Daeho Kang for radiant component
110 :
111 : // PURPOSE OF THIS SUBROUTINE:
112 : // This subroutine simulates the Electric Baseboard units.
113 :
114 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
115 : int BaseboardNum; // Index of unit in baseboard array
116 60 : int NumElecBaseboards = state.dataElectBaseboardRad->NumElecBaseboards;
117 :
118 60 : if (state.dataElectBaseboardRad->GetInputFlag) {
119 4 : GetElectricBaseboardInput(state);
120 4 : state.dataElectBaseboardRad->GetInputFlag = false;
121 : }
122 :
123 : // Find the correct Baseboard Equipment
124 60 : if (CompIndex == 0) {
125 4 : BaseboardNum = Util::FindItemInList(EquipName, state.dataElectBaseboardRad->ElecBaseboard, &ElecBaseboardParams::EquipName);
126 4 : if (BaseboardNum == 0) {
127 0 : ShowFatalError(state, "SimElectricBaseboard: Unit not found=" + EquipName);
128 : }
129 4 : CompIndex = BaseboardNum;
130 : } else {
131 56 : BaseboardNum = CompIndex;
132 56 : if (BaseboardNum > NumElecBaseboards || BaseboardNum < 1) {
133 0 : ShowFatalError(state,
134 0 : format("SimElectricBaseboard: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
135 : BaseboardNum,
136 : NumElecBaseboards,
137 : EquipName));
138 : }
139 56 : if (state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum).CheckEquipName) {
140 4 : if (EquipName != state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum).EquipName) {
141 0 : ShowFatalError(state,
142 0 : format("SimElectricBaseboard: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
143 : BaseboardNum,
144 : EquipName,
145 0 : state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum).EquipName));
146 : }
147 4 : state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum).CheckEquipName = false;
148 : }
149 : }
150 :
151 60 : InitElectricBaseboard(state, BaseboardNum, ControlledZoneNum, FirstHVACIteration);
152 60 : CalcElectricBaseboard(state, BaseboardNum, ControlledZoneNum);
153 :
154 60 : PowerMet = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum).TotPower;
155 :
156 60 : UpdateElectricBaseboard(state, BaseboardNum);
157 60 : ReportElectricBaseboard(state, BaseboardNum);
158 60 : }
159 :
160 6 : void GetElectricBaseboardInput(EnergyPlusData &state)
161 : {
162 :
163 : // SUBROUTINE INFORMATION:
164 : // AUTHOR Richard Liesen
165 : // DATE WRITTEN Nov 2001
166 : // MODIFIED Feb 2010 Daeho Kang for radiant component
167 :
168 : // PURPOSE OF THIS SUBROUTINE:
169 : // This subroutine gets the input for the Baseboard units.
170 :
171 : // SUBROUTINE PARAMETER DEFINITIONS:
172 : static constexpr std::string_view RoutineName("GetElectricBaseboardInput: "); // include trailing blank space
173 : static constexpr std::string_view routineName = "GetElectricBaseboardInput"; // include trailing blank space
174 :
175 6 : Real64 constexpr MaxFraction(1.0); // Maximum limit of fractional values
176 6 : Real64 constexpr MinFraction(0.0); // Minimum limit of fractional values
177 : // INTEGER,PARAMETER :: MaxDistribSurfaces = 20 ! Maximum number of surfaces that a baseboard heater can radiate to
178 6 : int constexpr MinDistribSurfaces(1); // Minimum number of surfaces that a baseboard heater can radiate to
179 6 : int constexpr iHeatCAPMAlphaNum(3); // get input index to HW baseboard heating capacity sizing method
180 6 : int constexpr iHeatDesignCapacityNumericNum(1); // get input index to HW baseboard heating capacity
181 6 : int constexpr iHeatCapacityPerFloorAreaNumericNum(2); // get input index to HW baseboard heating capacity per floor area sizing
182 6 : int constexpr iHeatFracOfAutosizedCapacityNumericNum(
183 : 3); // get input index to HW baseboard heating capacity sizing as fraction of autosized heating capacity
184 :
185 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
186 : int NumAlphas;
187 : int NumNumbers;
188 : int IOStat;
189 6 : bool ErrorsFound(false); // If errors detected in input
190 :
191 6 : auto &s_ipsc = state.dataIPShortCut;
192 :
193 6 : s_ipsc->cCurrentModuleObject = state.dataElectBaseboardRad->cCMO_BBRadiator_Electric;
194 :
195 : // Update Num in state and make local convenience copy
196 12 : int NumElecBaseboards = state.dataElectBaseboardRad->NumElecBaseboards =
197 6 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
198 :
199 : // object is extensible, no max args needed as IPShortCuts being used
200 6 : auto &ElecBaseboardNumericFields = state.dataElectBaseboardRad->ElecBaseboardNumericFields;
201 :
202 6 : state.dataElectBaseboardRad->ElecBaseboard.allocate(NumElecBaseboards);
203 6 : ElecBaseboardNumericFields.allocate(NumElecBaseboards);
204 :
205 15 : for (int BaseboardNum = 1; BaseboardNum <= NumElecBaseboards; ++BaseboardNum) {
206 9 : auto &elecBaseboard = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum);
207 :
208 18 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
209 9 : s_ipsc->cCurrentModuleObject,
210 : BaseboardNum,
211 9 : s_ipsc->cAlphaArgs,
212 : NumAlphas,
213 9 : s_ipsc->rNumericArgs,
214 : NumNumbers,
215 : IOStat,
216 9 : s_ipsc->lNumericFieldBlanks,
217 9 : s_ipsc->lAlphaFieldBlanks,
218 9 : s_ipsc->cAlphaFieldNames,
219 9 : s_ipsc->cNumericFieldNames);
220 :
221 9 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
222 :
223 9 : ElecBaseboardNumericFields(BaseboardNum).FieldNames.allocate(NumNumbers);
224 9 : ElecBaseboardNumericFields(BaseboardNum).FieldNames = "";
225 9 : ElecBaseboardNumericFields(BaseboardNum).FieldNames = s_ipsc->cNumericFieldNames;
226 :
227 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
228 9 : GlobalNames::VerifyUniqueBaseboardName(
229 18 : state, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1), ErrorsFound, s_ipsc->cCurrentModuleObject + " Name");
230 :
231 9 : elecBaseboard.EquipName = s_ipsc->cAlphaArgs(1); // name of this baseboard
232 9 : elecBaseboard.Schedule = s_ipsc->cAlphaArgs(2);
233 9 : if (s_ipsc->lAlphaFieldBlanks(2)) {
234 4 : elecBaseboard.availSched = Sched::GetScheduleAlwaysOn(state);
235 5 : } else if ((elecBaseboard.availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
236 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
237 0 : ErrorsFound = true;
238 : }
239 :
240 : // Determine HW radiant baseboard heating design capacity sizing method
241 9 : if (Util::SameString(s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum), "HeatingDesignCapacity")) {
242 7 : elecBaseboard.HeatingCapMethod = DataSizing::HeatingDesignCapacity;
243 :
244 7 : if (!s_ipsc->lNumericFieldBlanks(iHeatDesignCapacityNumericNum)) {
245 7 : elecBaseboard.ScaledHeatingCapacity = s_ipsc->rNumericArgs(iHeatDesignCapacityNumericNum);
246 7 : if (elecBaseboard.ScaledHeatingCapacity < 0.0 && elecBaseboard.ScaledHeatingCapacity != DataSizing::AutoSize) {
247 0 : ShowSevereError(state, format("{} = {}", s_ipsc->cCurrentModuleObject, elecBaseboard.EquipName));
248 0 : ShowContinueError(state,
249 0 : format("Illegal {} = {:.7T}",
250 0 : s_ipsc->cNumericFieldNames(iHeatDesignCapacityNumericNum),
251 0 : s_ipsc->rNumericArgs(iHeatDesignCapacityNumericNum)));
252 0 : ErrorsFound = true;
253 : }
254 : } else {
255 0 : ShowSevereError(state, format("{} = {}", s_ipsc->cCurrentModuleObject, elecBaseboard.EquipName));
256 0 : ShowContinueError(state,
257 0 : "Input for " + s_ipsc->cAlphaFieldNames(iHeatCAPMAlphaNum) + " = " + s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum));
258 0 : ShowContinueError(state, "Blank field not allowed for " + s_ipsc->cNumericFieldNames(iHeatDesignCapacityNumericNum));
259 0 : ErrorsFound = true;
260 : }
261 2 : } else if (Util::SameString(s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum), "CapacityPerFloorArea")) {
262 1 : elecBaseboard.HeatingCapMethod = DataSizing::CapacityPerFloorArea;
263 1 : if (!s_ipsc->lNumericFieldBlanks(iHeatCapacityPerFloorAreaNumericNum)) {
264 1 : elecBaseboard.ScaledHeatingCapacity = s_ipsc->rNumericArgs(iHeatCapacityPerFloorAreaNumericNum);
265 1 : if (elecBaseboard.ScaledHeatingCapacity <= 0.0) {
266 0 : ShowSevereError(state, format("{} = {}", s_ipsc->cCurrentModuleObject, elecBaseboard.EquipName));
267 0 : ShowContinueError(state,
268 0 : "Input for " + s_ipsc->cAlphaFieldNames(iHeatCAPMAlphaNum) + " = " + s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum));
269 0 : ShowContinueError(state,
270 0 : format("Illegal {} = {:.7T}",
271 0 : s_ipsc->cNumericFieldNames(iHeatCapacityPerFloorAreaNumericNum),
272 0 : s_ipsc->rNumericArgs(iHeatCapacityPerFloorAreaNumericNum)));
273 0 : ErrorsFound = true;
274 1 : } else if (elecBaseboard.ScaledHeatingCapacity == DataSizing::AutoSize) {
275 0 : ShowSevereError(state, format("{} = {}", s_ipsc->cCurrentModuleObject, elecBaseboard.EquipName));
276 0 : ShowContinueError(state,
277 0 : "Input for " + s_ipsc->cAlphaFieldNames(iHeatCAPMAlphaNum) + " = " + s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum));
278 0 : ShowContinueError(state, "Illegal " + s_ipsc->cNumericFieldNames(iHeatCapacityPerFloorAreaNumericNum) + " = Autosize");
279 0 : ErrorsFound = true;
280 : }
281 : } else {
282 0 : ShowSevereError(state, format(s_ipsc->cCurrentModuleObject, elecBaseboard.EquipName));
283 0 : ShowContinueError(state,
284 0 : "Input for " + s_ipsc->cAlphaFieldNames(iHeatCAPMAlphaNum) + " = " + s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum));
285 0 : ShowContinueError(state, "Blank field not allowed for " + s_ipsc->cNumericFieldNames(iHeatCapacityPerFloorAreaNumericNum));
286 0 : ErrorsFound = true;
287 : }
288 1 : } else if (Util::SameString(s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum), "FractionOfAutosizedHeatingCapacity")) {
289 1 : elecBaseboard.HeatingCapMethod = DataSizing::FractionOfAutosizedHeatingCapacity;
290 1 : if (!s_ipsc->lNumericFieldBlanks(iHeatFracOfAutosizedCapacityNumericNum)) {
291 1 : elecBaseboard.ScaledHeatingCapacity = s_ipsc->rNumericArgs(iHeatFracOfAutosizedCapacityNumericNum);
292 1 : if (elecBaseboard.ScaledHeatingCapacity < 0.0) {
293 0 : ShowSevereError(state, s_ipsc->cCurrentModuleObject + " = " + elecBaseboard.EquipName);
294 0 : ShowContinueError(state,
295 0 : format("Illegal {} = {:.7T}",
296 0 : s_ipsc->cNumericFieldNames(iHeatFracOfAutosizedCapacityNumericNum),
297 0 : s_ipsc->rNumericArgs(iHeatFracOfAutosizedCapacityNumericNum)));
298 0 : ErrorsFound = true;
299 : }
300 : } else {
301 0 : ShowSevereError(state, s_ipsc->cCurrentModuleObject + " = " + elecBaseboard.EquipName);
302 0 : ShowContinueError(state,
303 0 : "Input for " + s_ipsc->cAlphaFieldNames(iHeatCAPMAlphaNum) + " = " + s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum));
304 0 : ShowContinueError(state, "Blank field not allowed for " + s_ipsc->cNumericFieldNames(iHeatFracOfAutosizedCapacityNumericNum));
305 0 : ErrorsFound = true;
306 : }
307 : } else {
308 0 : ShowSevereError(state, s_ipsc->cCurrentModuleObject + " = " + elecBaseboard.EquipName);
309 0 : ShowContinueError(state, "Illegal " + s_ipsc->cAlphaFieldNames(iHeatCAPMAlphaNum) + " = " + s_ipsc->cAlphaArgs(iHeatCAPMAlphaNum));
310 0 : ErrorsFound = true;
311 : }
312 :
313 9 : elecBaseboard.BaseboardEfficiency = s_ipsc->rNumericArgs(4);
314 9 : elecBaseboard.FracRadiant = s_ipsc->rNumericArgs(5);
315 9 : if (elecBaseboard.FracRadiant < MinFraction) {
316 0 : ShowWarningError(state,
317 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) + "\", " +
318 0 : s_ipsc->cNumericFieldNames(5) + " was lower than the allowable minimum.");
319 0 : ShowContinueError(state, format("...reset to minimum value=[{:.2R}].", MinFraction));
320 0 : elecBaseboard.FracRadiant = MinFraction;
321 : }
322 9 : if (elecBaseboard.FracRadiant > MaxFraction) {
323 0 : ShowWarningError(state,
324 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) + "\", " +
325 0 : s_ipsc->cNumericFieldNames(5) + " was higher than the allowable maximum.");
326 0 : ShowContinueError(state, format("...reset to maximum value=[{:.2R}].", MaxFraction));
327 0 : elecBaseboard.FracRadiant = MaxFraction;
328 : }
329 :
330 : // Remaining fraction is added to the zone as convective heat transfer
331 9 : if (elecBaseboard.FracRadiant > MaxFraction) {
332 0 : ShowWarningError(state,
333 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) +
334 : "\", Fraction Radiant was higher than the allowable maximum.");
335 0 : elecBaseboard.FracRadiant = MaxFraction;
336 0 : elecBaseboard.FracConvect = 0.0;
337 : } else {
338 9 : elecBaseboard.FracConvect = 1.0 - elecBaseboard.FracRadiant;
339 : }
340 :
341 9 : elecBaseboard.FracDistribPerson = s_ipsc->rNumericArgs(6);
342 9 : if (elecBaseboard.FracDistribPerson < MinFraction) {
343 0 : ShowWarningError(state,
344 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) + "\", " +
345 0 : s_ipsc->cNumericFieldNames(6) + " was lower than the allowable minimum.");
346 0 : ShowContinueError(state, format("...reset to minimum value=[{:.2R}].", MinFraction));
347 0 : elecBaseboard.FracDistribPerson = MinFraction;
348 : }
349 9 : if (elecBaseboard.FracDistribPerson > MaxFraction) {
350 0 : ShowWarningError(state,
351 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) + "\", " +
352 0 : s_ipsc->cNumericFieldNames(6) + " was higher than the allowable maximum.");
353 0 : ShowContinueError(state, format("...reset to maximum value=[{:.2R}].", MaxFraction));
354 0 : elecBaseboard.FracDistribPerson = MaxFraction;
355 : }
356 :
357 9 : elecBaseboard.TotSurfToDistrib = NumNumbers - 6;
358 :
359 9 : if ((elecBaseboard.TotSurfToDistrib < MinDistribSurfaces) && (elecBaseboard.FracRadiant > MinFraction)) {
360 0 : ShowSevereError(state,
361 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) +
362 : "\", the number of surface/radiant fraction groups entered was less than the allowable minimum.");
363 0 : ShowContinueError(state, format("...the minimum that must be entered=[{}].", MinDistribSurfaces));
364 0 : ErrorsFound = true;
365 0 : elecBaseboard.TotSurfToDistrib = 0; // error
366 : }
367 :
368 9 : elecBaseboard.SurfaceName.allocate(elecBaseboard.TotSurfToDistrib);
369 9 : elecBaseboard.SurfaceName = "";
370 9 : elecBaseboard.SurfacePtr.allocate(elecBaseboard.TotSurfToDistrib);
371 9 : elecBaseboard.SurfacePtr = 0;
372 9 : elecBaseboard.FracDistribToSurf.allocate(elecBaseboard.TotSurfToDistrib);
373 9 : elecBaseboard.FracDistribToSurf = 0.0;
374 :
375 9 : elecBaseboard.ZonePtr =
376 9 : DataZoneEquipment::GetZoneEquipControlledZoneNum(state, DataZoneEquipment::ZoneEquipType::BaseboardElectric, elecBaseboard.EquipName);
377 :
378 9 : Real64 AllFracsSummed = elecBaseboard.FracDistribPerson;
379 18 : for (int SurfNum = 1; SurfNum <= elecBaseboard.TotSurfToDistrib; ++SurfNum) {
380 9 : elecBaseboard.SurfaceName(SurfNum) = s_ipsc->cAlphaArgs(SurfNum + 3);
381 9 : elecBaseboard.SurfacePtr(SurfNum) = HeatBalanceIntRadExchange::GetRadiantSystemSurface(state,
382 9 : s_ipsc->cCurrentModuleObject,
383 9 : elecBaseboard.EquipName,
384 : elecBaseboard.ZonePtr,
385 9 : elecBaseboard.SurfaceName(SurfNum),
386 : ErrorsFound);
387 9 : elecBaseboard.FracDistribToSurf(SurfNum) = s_ipsc->rNumericArgs(SurfNum + 6);
388 9 : if (elecBaseboard.FracDistribToSurf(SurfNum) > MaxFraction) {
389 0 : ShowWarningError(state,
390 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) + "\", " +
391 0 : s_ipsc->cNumericFieldNames(SurfNum + 6) + "was greater than the allowable maximum.");
392 0 : ShowContinueError(state, format("...reset to maximum value=[{:.2R}].", MaxFraction));
393 0 : elecBaseboard.TotSurfToDistrib = MaxFraction;
394 : }
395 9 : if (elecBaseboard.FracDistribToSurf(SurfNum) < MinFraction) {
396 0 : ShowWarningError(state,
397 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) + "\", " +
398 0 : s_ipsc->cNumericFieldNames(SurfNum + 6) + "was less than the allowable minimum.");
399 0 : ShowContinueError(state, format("...reset to maximum value=[{:.2R}].", MinFraction));
400 0 : elecBaseboard.TotSurfToDistrib = MinFraction;
401 : }
402 9 : if (elecBaseboard.SurfacePtr(SurfNum) != 0) {
403 9 : state.dataSurface->surfIntConv(elecBaseboard.SurfacePtr(SurfNum)).getsRadiantHeat = true;
404 9 : state.dataSurface->allGetsRadiantHeatSurfaceList.emplace_back(elecBaseboard.SurfacePtr(SurfNum));
405 : }
406 :
407 9 : AllFracsSummed += elecBaseboard.FracDistribToSurf(SurfNum);
408 : } // Surfaces
409 :
410 9 : if (AllFracsSummed > (MaxFraction + 0.01)) {
411 0 : ShowSevereError(state,
412 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) +
413 : "\", Summed radiant fractions for people + surface groups > 1.0");
414 0 : ErrorsFound = true;
415 : }
416 9 : if ((AllFracsSummed < (MaxFraction - 0.01)) &&
417 0 : (elecBaseboard.FracRadiant > MinFraction)) { // User didn't distribute all of the | radiation warn that some will be lost
418 0 : ShowWarningError(state,
419 0 : std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "=\"" + s_ipsc->cAlphaArgs(1) +
420 : "\", Summed radiant fractions for people + surface groups < 1.0");
421 0 : ShowContinueError(state, "The rest of the radiant energy delivered by the baseboard heater will be lost");
422 : }
423 : }
424 :
425 6 : if (ErrorsFound) {
426 0 : ShowFatalError(state, std::string{RoutineName} + s_ipsc->cCurrentModuleObject + "Errors found getting input. Program terminates.");
427 : }
428 :
429 15 : for (auto &elecBaseboard : state.dataElectBaseboardRad->ElecBaseboard) {
430 : // Setup Report variables for the Electric Baseboards
431 : // CurrentModuleObject='ZoneHVAC:Baseboard:RadiantConvective:Electric'
432 18 : SetupOutputVariable(state,
433 : "Baseboard Total Heating Rate",
434 : Constant::Units::W,
435 9 : elecBaseboard.TotPower,
436 : OutputProcessor::TimeStepType::System,
437 : OutputProcessor::StoreType::Average,
438 9 : elecBaseboard.EquipName);
439 :
440 18 : SetupOutputVariable(state,
441 : "Baseboard Convective Heating Rate",
442 : Constant::Units::W,
443 9 : elecBaseboard.ConvPower,
444 : OutputProcessor::TimeStepType::System,
445 : OutputProcessor::StoreType::Average,
446 9 : elecBaseboard.EquipName);
447 18 : SetupOutputVariable(state,
448 : "Baseboard Radiant Heating Rate",
449 : Constant::Units::W,
450 9 : elecBaseboard.RadPower,
451 : OutputProcessor::TimeStepType::System,
452 : OutputProcessor::StoreType::Average,
453 9 : elecBaseboard.EquipName);
454 :
455 18 : SetupOutputVariable(state,
456 : "Baseboard Electricity Energy",
457 : Constant::Units::J,
458 9 : elecBaseboard.ElecUseLoad,
459 : OutputProcessor::TimeStepType::System,
460 : OutputProcessor::StoreType::Sum,
461 9 : elecBaseboard.EquipName,
462 : Constant::eResource::Electricity,
463 : OutputProcessor::Group::HVAC,
464 : OutputProcessor::EndUseCat::Heating);
465 18 : SetupOutputVariable(state,
466 : "Baseboard Electricity Rate",
467 : Constant::Units::W,
468 9 : elecBaseboard.ElecUseRate,
469 : OutputProcessor::TimeStepType::System,
470 : OutputProcessor::StoreType::Average,
471 9 : elecBaseboard.EquipName);
472 18 : SetupOutputVariable(state,
473 : "Baseboard Total Heating Energy",
474 : Constant::Units::J,
475 9 : elecBaseboard.TotEnergy,
476 : OutputProcessor::TimeStepType::System,
477 : OutputProcessor::StoreType::Sum,
478 9 : elecBaseboard.EquipName,
479 : Constant::eResource::EnergyTransfer,
480 : OutputProcessor::Group::HVAC,
481 : OutputProcessor::EndUseCat::Baseboard);
482 :
483 18 : SetupOutputVariable(state,
484 : "Baseboard Convective Heating Energy",
485 : Constant::Units::J,
486 9 : elecBaseboard.ConvEnergy,
487 : OutputProcessor::TimeStepType::System,
488 : OutputProcessor::StoreType::Sum,
489 9 : elecBaseboard.EquipName);
490 18 : SetupOutputVariable(state,
491 : "Baseboard Radiant Heating Energy",
492 : Constant::Units::J,
493 9 : elecBaseboard.RadEnergy,
494 : OutputProcessor::TimeStepType::System,
495 : OutputProcessor::StoreType::Sum,
496 9 : elecBaseboard.EquipName);
497 : }
498 6 : }
499 :
500 60 : void InitElectricBaseboard(EnergyPlusData &state, int const BaseboardNum, int const ControlledZoneNum, bool const FirstHVACIteration)
501 : {
502 :
503 : // SUBROUTINE INFORMATION:
504 : // AUTHOR Richard Liesen
505 : // DATE WRITTEN Nov 2001
506 : // MODIFIED Feb 2010 Daeho Kang for radiant component
507 :
508 : // PURPOSE OF THIS SUBROUTINE:
509 : // This subroutine initializes the Baseboard units during simulation.
510 :
511 60 : auto &elecBaseboard = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum);
512 :
513 60 : if (!state.dataGlobal->SysSizingCalc && elecBaseboard.MySizeFlag) {
514 : // for each coil, do the sizing once.
515 4 : SizeElectricBaseboard(state, BaseboardNum);
516 4 : elecBaseboard.MySizeFlag = false;
517 : }
518 :
519 : // Do the Begin Environment initializations
520 60 : if (state.dataGlobal->BeginEnvrnFlag && elecBaseboard.MyEnvrnFlag) {
521 : // Initialize
522 8 : elecBaseboard.ZeroBBSourceSumHATsurf = 0.0;
523 8 : elecBaseboard.QBBElecRadSource = 0.0;
524 8 : elecBaseboard.QBBElecRadSrcAvg = 0.0;
525 8 : elecBaseboard.LastQBBElecRadSrc = 0.0;
526 8 : elecBaseboard.LastSysTimeElapsed = 0.0;
527 8 : elecBaseboard.LastTimeStepSys = 0.0;
528 :
529 8 : elecBaseboard.MyEnvrnFlag = false;
530 : }
531 :
532 60 : if (!state.dataGlobal->BeginEnvrnFlag) {
533 36 : elecBaseboard.MyEnvrnFlag = true;
534 : }
535 :
536 60 : if (state.dataGlobal->BeginTimeStepFlag && FirstHVACIteration) {
537 16 : elecBaseboard.ZeroBBSourceSumHATsurf = state.dataHeatBal->Zone(ControlledZoneNum).sumHATsurf(state);
538 16 : elecBaseboard.QBBElecRadSrcAvg = 0.0;
539 16 : elecBaseboard.LastQBBElecRadSrc = 0.0;
540 16 : elecBaseboard.LastSysTimeElapsed = 0.0;
541 16 : elecBaseboard.LastTimeStepSys = 0.0;
542 : }
543 :
544 : // Do the every time step initializations
545 60 : int ZoneNode = state.dataZoneEquip->ZoneEquipConfig(ControlledZoneNum).ZoneNode;
546 60 : elecBaseboard.AirInletTemp = state.dataLoopNodes->Node(ZoneNode).Temp;
547 60 : elecBaseboard.AirInletHumRat = state.dataLoopNodes->Node(ZoneNode).HumRat;
548 :
549 : // Set the reporting variables to zero at each timestep.
550 60 : elecBaseboard.TotPower = 0.0;
551 60 : elecBaseboard.Power = 0.0;
552 60 : elecBaseboard.ConvPower = 0.0;
553 60 : elecBaseboard.RadPower = 0.0;
554 60 : elecBaseboard.TotEnergy = 0.0;
555 60 : elecBaseboard.Energy = 0.0;
556 60 : elecBaseboard.ConvEnergy = 0.0;
557 60 : elecBaseboard.RadEnergy = 0.0;
558 60 : elecBaseboard.ElecUseLoad = 0.0;
559 60 : elecBaseboard.ElecUseRate = 0.0;
560 60 : }
561 :
562 10 : void SizeElectricBaseboard(EnergyPlusData &state, int const BaseboardNum)
563 : {
564 :
565 : // SUBROUTINE INFORMATION:
566 : // AUTHOR Fred Buhl
567 : // DATE WRITTEN February 2002
568 : // MODIFIED August 2013 Daeho Kang, add component sizing table entries
569 : // July 2014, B. Nigusse, added scalable sizing
570 :
571 : // PURPOSE OF THIS SUBROUTINE:
572 : // This subroutine is for sizing electric baseboard components for which nominal capacities have not been
573 : // specified in the input.
574 :
575 : // METHODOLOGY EMPLOYED:
576 : // Obtains flow rates from the zone sizing arrays and plant sizing data. UAs are
577 : // calculated by numerically inverting the baseboard calculation routine.
578 :
579 : // SUBROUTINE PARAMETER DEFINITIONS:
580 : static constexpr std::string_view RoutineName("SizeElectricBaseboard");
581 :
582 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
583 : Real64 TempSize; // autosized value of coil input field
584 :
585 10 : if (state.dataSize->CurZoneEqNum > 0) {
586 10 : auto &zoneEqSizing = state.dataSize->ZoneEqSizing(state.dataSize->CurZoneEqNum);
587 10 : auto &elecBaseboard = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum);
588 10 : state.dataSize->DataScalableCapSizingON = false;
589 :
590 10 : std::string_view const CompType = state.dataElectBaseboardRad->cCMO_BBRadiator_Electric;
591 10 : std::string_view const CompName = elecBaseboard.EquipName;
592 10 : state.dataSize->DataFracOfAutosizedHeatingCapacity = 1.0;
593 10 : state.dataSize->DataZoneNumber = elecBaseboard.ZonePtr;
594 10 : int SizingMethod = HVAC::HeatingCapacitySizing; // Integer representation of sizing method name (e.g., CoolingAirflowSizing)
595 10 : int FieldNum = 1; // IDD numeric field number where input field description is found
596 : std::string const SizingString =
597 10 : format("{} [W]", state.dataElectBaseboardRad->ElecBaseboardNumericFields(BaseboardNum).FieldNames(FieldNum));
598 : // capacity sizing methods (e.g., HeatingDesignCapacity, CapacityPerFloorArea, FractionOfAutosizedCoolingCapacity)
599 10 : int CapSizingMethod = elecBaseboard.HeatingCapMethod;
600 10 : zoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
601 10 : if (CapSizingMethod == DataSizing::HeatingDesignCapacity || CapSizingMethod == DataSizing::CapacityPerFloorArea ||
602 : CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
603 10 : bool PrintFlag = true; // TRUE when sizing information is reported in the eio file
604 10 : if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
605 8 : if (elecBaseboard.ScaledHeatingCapacity == DataSizing::AutoSize) {
606 7 : CheckZoneSizing(state, CompType, CompName);
607 7 : zoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).NonAirSysDesHeatLoad;
608 : } else {
609 1 : zoneEqSizing.DesHeatingLoad = elecBaseboard.ScaledHeatingCapacity;
610 : }
611 8 : zoneEqSizing.HeatingCapacity = true;
612 8 : TempSize = elecBaseboard.ScaledHeatingCapacity;
613 2 : } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
614 1 : if (state.dataSize->ZoneSizingRunDone) {
615 1 : zoneEqSizing.HeatingCapacity = true;
616 1 : zoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).NonAirSysDesHeatLoad;
617 : }
618 1 : TempSize = elecBaseboard.ScaledHeatingCapacity * state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
619 1 : state.dataSize->DataScalableCapSizingON = true;
620 1 : } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
621 1 : CheckZoneSizing(state, CompType, CompName);
622 1 : zoneEqSizing.HeatingCapacity = true;
623 1 : state.dataSize->DataFracOfAutosizedHeatingCapacity = elecBaseboard.ScaledHeatingCapacity;
624 1 : zoneEqSizing.DesHeatingLoad = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).NonAirSysDesHeatLoad;
625 1 : Real64 FracOfAutoSzCap = DataSizing::AutoSize;
626 1 : bool ErrorsFound = false;
627 1 : HeatingCapacitySizer sizerHeatingCapacity;
628 1 : sizerHeatingCapacity.overrideSizingString(SizingString);
629 1 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
630 1 : FracOfAutoSzCap = sizerHeatingCapacity.size(state, FracOfAutoSzCap, ErrorsFound);
631 1 : TempSize = FracOfAutoSzCap;
632 1 : state.dataSize->DataFracOfAutosizedHeatingCapacity = 1.0;
633 1 : state.dataSize->DataScalableCapSizingON = true;
634 1 : } else {
635 0 : TempSize = elecBaseboard.ScaledHeatingCapacity;
636 : }
637 10 : bool errorsFound = false;
638 10 : HeatingCapacitySizer sizerHeatingCapacity;
639 10 : sizerHeatingCapacity.overrideSizingString(SizingString);
640 10 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
641 10 : elecBaseboard.NominalCapacity = sizerHeatingCapacity.size(state, TempSize, errorsFound);
642 10 : state.dataSize->DataScalableCapSizingON = false;
643 10 : }
644 10 : }
645 10 : }
646 :
647 60 : void CalcElectricBaseboard(EnergyPlusData &state, int const BaseboardNum, [[maybe_unused]] int const ControlledZoneNum)
648 : {
649 : // SUBROUTINE INFORMATION:
650 : // AUTHOR Richard Liesen
651 : // DATE WRITTEN Nov 2001
652 : // MODIFIED Feb 2010 Daeho Kang for radiant component
653 : // Sep 2011 LKL/BG - resimulate only zones needing it for Radiant systems
654 :
655 : // PURPOSE OF THIS SUBROUTINE:
656 : // This subroutine calculates the heat exchange rate in a Electric baseboard heater.
657 : // It includes radiant heat transfer to people and surfaces in a space, and the actual convective
658 : // system impact of a electric baseboard heater is determined after the radiant heat distribution.
659 :
660 : // METHODOLOGY EMPLOYED:
661 : // This is primarily modified from Convective Electric Baseboard. An existing algorithm of radiant
662 : // heat transfer calculation in the High Temperature Radiant System module is implemented.
663 :
664 : // SUBROUTINE PARAMETER DEFINITIONS:
665 60 : Real64 constexpr SimpConvAirFlowSpeed(0.5); // m/s
666 :
667 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
668 : Real64 QBBCap;
669 : Real64 RadHeat;
670 : Real64 LoadMet;
671 60 : auto &elecBaseboard = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum);
672 :
673 60 : int ZoneNum = elecBaseboard.ZonePtr;
674 60 : Real64 QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP;
675 60 : Real64 AirInletTemp = elecBaseboard.AirInletTemp;
676 60 : Real64 AirOutletTemp = AirInletTemp;
677 60 : Real64 CpAir = Psychrometrics::PsyCpAirFnW(elecBaseboard.AirInletHumRat);
678 60 : Real64 AirMassFlowRate = SimpConvAirFlowSpeed;
679 60 : Real64 CapacitanceAir = CpAir * AirMassFlowRate;
680 :
681 : // Currently only the efficiency is used to calculate the electric consumption. There could be some
682 : // thermal loss that could be accounted for with this efficiency input.
683 60 : Real64 Effic = elecBaseboard.BaseboardEfficiency;
684 :
685 68 : if (QZnReq > HVAC::SmallLoad && !state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum) &&
686 8 : elecBaseboard.availSched->getCurrentVal() > 0.0) {
687 :
688 : // If the load exceeds the capacity than the capacity is set to the BB limit.
689 8 : if (QZnReq > elecBaseboard.NominalCapacity) {
690 0 : QBBCap = elecBaseboard.NominalCapacity;
691 : } else {
692 8 : QBBCap = QZnReq;
693 : }
694 8 : RadHeat = QBBCap * elecBaseboard.FracRadiant;
695 8 : elecBaseboard.QBBElecRadSource = RadHeat;
696 :
697 8 : if (elecBaseboard.FracRadiant > 0.0) { // User defines radiant heat addition
698 : // Now, distribute the radiant energy of all systems to the appropriate surfaces, to people, and the air
699 8 : DistributeBBElecRadGains(state);
700 : // Now "simulate" the system by recalculating the heat balances
701 8 : HeatBalanceSurfaceManager::CalcHeatBalanceOutsideSurf(state, ZoneNum);
702 8 : HeatBalanceSurfaceManager::CalcHeatBalanceInsideSurf(state, ZoneNum);
703 : // Here an assumption is made regarding radiant heat transfer to people.
704 : // While the radiant heat transfer to people array will be used by the thermal comfort
705 : // routines, the energy transfer to people would get lost from the perspective
706 : // of the heat balance. So, to avoid this net loss of energy which clearly
707 : // gets added to the zones, we must account for it somehow. This assumption
708 : // that all energy radiated to people is converted to convective energy is
709 : // not very precise, but at least it conserves energy. The system impact to heat balance
710 : // should include this.
711 8 : LoadMet = (state.dataHeatBal->Zone(ZoneNum).sumHATsurf(state) - elecBaseboard.ZeroBBSourceSumHATsurf) +
712 8 : (QBBCap * elecBaseboard.FracConvect) + (RadHeat * elecBaseboard.FracDistribPerson);
713 :
714 8 : if (LoadMet < 0.0) {
715 : // This basically means that SumHATsurf is LESS than ZeroBBSourceSumHATsurf which
716 : // should not happen unless something unusual is happening like a fast change
717 : // in temperature or some sort of change in internal load. This is not a problem
718 : // normally, but when LoadMet goes negative the choice is to either zero out
719 : // the baseboard or give it another shot at getting an accurate reading on
720 : // what is happening in the zone. If it is still predicting a negative heating
721 : // load, then zero everything out.
722 : // First, turn off the baseboard:
723 0 : elecBaseboard.QBBElecRadSource = 0.0;
724 0 : DistributeBBElecRadGains(state);
725 0 : HeatBalanceSurfaceManager::CalcHeatBalanceOutsideSurf(state, ZoneNum);
726 0 : HeatBalanceSurfaceManager::CalcHeatBalanceInsideSurf(state, ZoneNum);
727 0 : Real64 TempZeroBBSourceSumHATsurf = state.dataHeatBal->Zone(ZoneNum).sumHATsurf(state);
728 : // Now, turn it back on:
729 0 : elecBaseboard.QBBElecRadSource = RadHeat;
730 0 : DistributeBBElecRadGains(state);
731 0 : HeatBalanceSurfaceManager::CalcHeatBalanceOutsideSurf(state, ZoneNum);
732 0 : HeatBalanceSurfaceManager::CalcHeatBalanceInsideSurf(state, ZoneNum);
733 : // Recalculate LoadMet with new ZeroBBSource... term and see if it is positive now. If not, shut it down.
734 0 : LoadMet = (state.dataHeatBal->Zone(ZoneNum).sumHATsurf(state) - TempZeroBBSourceSumHATsurf) +
735 0 : (QBBCap * elecBaseboard.FracConvect) + (RadHeat * elecBaseboard.FracDistribPerson);
736 0 : if (LoadMet < 0.0) {
737 : // LoadMet is still less than zero so shut everything down
738 0 : UpdateElectricBaseboardOff(
739 0 : LoadMet, QBBCap, RadHeat, elecBaseboard.QBBElecRadSource, elecBaseboard.ElecUseRate, AirOutletTemp, AirInletTemp);
740 : } else {
741 : // Corrected LoadMet is now positive so use this and move forward with system operating
742 0 : UpdateElectricBaseboardOn(AirOutletTemp, elecBaseboard.ElecUseRate, AirInletTemp, QBBCap, CapacitanceAir, Effic);
743 : }
744 : } else {
745 :
746 8 : UpdateElectricBaseboardOn(AirOutletTemp, elecBaseboard.ElecUseRate, AirInletTemp, QBBCap, CapacitanceAir, Effic);
747 : }
748 :
749 : } else { // zero radiant fraction, no need of recalculation of heat balances
750 :
751 0 : LoadMet = QBBCap;
752 0 : UpdateElectricBaseboardOn(AirOutletTemp, elecBaseboard.ElecUseRate, AirInletTemp, QBBCap, CapacitanceAir, Effic);
753 : }
754 :
755 : } else { // If there is an off condition the BB does nothing.
756 :
757 52 : UpdateElectricBaseboardOff(
758 52 : LoadMet, QBBCap, RadHeat, elecBaseboard.QBBElecRadSource, elecBaseboard.ElecUseRate, AirOutletTemp, AirInletTemp);
759 : }
760 :
761 : // Assign calculated ones
762 60 : elecBaseboard.AirOutletTemp = AirOutletTemp;
763 60 : elecBaseboard.Power = QBBCap;
764 60 : elecBaseboard.TotPower = LoadMet;
765 60 : elecBaseboard.RadPower = RadHeat;
766 60 : elecBaseboard.ConvPower = QBBCap - RadHeat;
767 60 : }
768 :
769 53 : void UpdateElectricBaseboardOff(Real64 &LoadMet,
770 : Real64 &QBBCap,
771 : Real64 &RadHeat,
772 : Real64 &QBBElecRadSrc,
773 : Real64 &ElecUseRate,
774 : Real64 &AirOutletTemp,
775 : Real64 const AirInletTemp)
776 : {
777 :
778 : // SUBROUTINE INFORMATION:
779 : // AUTHOR Rick Strand
780 : // DATE WRITTEN August 2017
781 :
782 : // PURPOSE OF THIS SUBROUTINE: Zero out appropriate system variables when it is off
783 :
784 53 : QBBCap = 0.0;
785 53 : LoadMet = 0.0;
786 53 : RadHeat = 0.0;
787 53 : AirOutletTemp = AirInletTemp;
788 53 : QBBElecRadSrc = 0.0;
789 53 : ElecUseRate = 0.0;
790 53 : }
791 :
792 9 : void UpdateElectricBaseboardOn(
793 : Real64 &AirOutletTemp, Real64 &ElecUseRate, Real64 const AirInletTemp, Real64 const QBBCap, Real64 const CapacitanceAir, Real64 const Effic)
794 : {
795 :
796 : // SUBROUTINE INFORMATION:
797 : // AUTHOR Rick Strand
798 : // DATE WRITTEN August 2017
799 :
800 : // PURPOSE OF THIS SUBROUTINE: System is on, so calculate some of the result variables
801 :
802 9 : AirOutletTemp = AirInletTemp + QBBCap / CapacitanceAir;
803 : // This could be utilized somehow or even reported so the data structures are left in place
804 : // The Baseboard electric Load is calculated using the efficiency
805 9 : ElecUseRate = QBBCap / Effic;
806 9 : }
807 :
808 60 : void UpdateElectricBaseboard(EnergyPlusData &state, int const BaseboardNum)
809 : {
810 :
811 : // SUBROUTINE INFORMATION:
812 : // AUTHOR Russ Taylor
813 : // Rick Strand
814 : // DATE WRITTEN Nov 1997
815 : // February 2001
816 : // MODIFIED Feb 2010 Daeho Kang for radiant component
817 :
818 : // Using/Aliasing
819 60 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
820 60 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
821 60 : auto &elecBaseboard = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum);
822 :
823 : // First, update the running average if necessary...
824 60 : if (elecBaseboard.LastSysTimeElapsed == SysTimeElapsed) {
825 56 : elecBaseboard.QBBElecRadSrcAvg -= elecBaseboard.LastQBBElecRadSrc * elecBaseboard.LastTimeStepSys / state.dataGlobal->TimeStepZone;
826 : }
827 : // Update the running average and the "last" values with the current values of the appropriate variables
828 60 : elecBaseboard.QBBElecRadSrcAvg += elecBaseboard.QBBElecRadSource * TimeStepSys / state.dataGlobal->TimeStepZone;
829 :
830 60 : elecBaseboard.LastQBBElecRadSrc = elecBaseboard.QBBElecRadSource;
831 60 : elecBaseboard.LastSysTimeElapsed = SysTimeElapsed;
832 60 : elecBaseboard.LastTimeStepSys = TimeStepSys;
833 60 : }
834 :
835 249945 : void UpdateBBElecRadSourceValAvg(EnergyPlusData &state, bool &ElecBaseboardSysOn) // .TRUE. if the radiant system has run this zone time step
836 : {
837 :
838 : // SUBROUTINE INFORMATION:
839 : // AUTHOR Rick Strand
840 : // DATE WRITTEN February 2001
841 : // MODIFIED Feb 2010 Daeho Kang for baseboard
842 :
843 : // PURPOSE OF THIS SUBROUTINE:
844 : // To transfer the average value of the heat source over the entire
845 : // zone time step back to the heat balance routines so that the heat
846 : // balance algorithms can simulate one last time with the average source
847 : // to maintain some reasonable amount of continuity and energy balance
848 : // in the temperature and flux histories.
849 :
850 : // METHODOLOGY EMPLOYED:
851 : // All of the record keeping for the average term is done in the Update
852 : // routine so the only other thing that this subroutine does is check to
853 : // see if the system was even on. If any average term is non-zero, then
854 : // one or more of the radiant systems was running.
855 :
856 249945 : ElecBaseboardSysOn = false;
857 :
858 : // If this was never allocated, then there are no radiant systems in this input file (just RETURN)
859 249945 : if (state.dataElectBaseboardRad->NumElecBaseboards == 0) return;
860 :
861 : // If it was allocated, then we have to check to see if this was running at all...
862 48 : for (auto &elecBaseboard : state.dataElectBaseboardRad->ElecBaseboard) {
863 24 : elecBaseboard.QBBElecRadSource = elecBaseboard.QBBElecRadSrcAvg;
864 24 : if (elecBaseboard.QBBElecRadSrcAvg != 0.0) {
865 4 : ElecBaseboardSysOn = true;
866 : }
867 : }
868 :
869 : // QBBElecRadSource has been modified so we need to redistribute gains
870 24 : DistributeBBElecRadGains(state);
871 : }
872 :
873 32 : void DistributeBBElecRadGains(EnergyPlusData &state)
874 : {
875 :
876 : // SUBROUTINE INFORMATION:
877 : // AUTHOR Rick Strand
878 : // DATE WRITTEN February 2001
879 : // MODIFIED Feb 2010 Daeho Kang for baseboard
880 : // April 2010 Brent Griffith, max limit to protect surface temperature calcs
881 :
882 : // PURPOSE OF THIS SUBROUTINE:
883 : // To distribute the gains from the electric baseboard heater
884 : // as specified in the user input file. This includes distribution
885 : // of long wavelength radiant gains to surfaces and "people."
886 :
887 : // METHODOLOGY EMPLOYED:
888 : // We must cycle through all of the radiant systems because each
889 : // surface could feel the effect of more than one radiant system.
890 : // Note that the energy radiated to people is assumed to affect them
891 : // but them it is assumed to be convected to the air.
892 :
893 : // SUBROUTINE PARAMETER DEFINITIONS:
894 32 : Real64 constexpr SmallestArea(0.001); // Smallest area in meters squared (to avoid a divide by zero)
895 :
896 : // Initialize arrays
897 64 : for (auto &elecBaseboard : state.dataElectBaseboardRad->ElecBaseboard) {
898 64 : for (int radSurfNum = 1; radSurfNum <= elecBaseboard.TotSurfToDistrib; ++radSurfNum) {
899 32 : int surfNum = elecBaseboard.SurfacePtr(radSurfNum);
900 32 : state.dataHeatBalFanSys->surfQRadFromHVAC(surfNum).ElecBaseboard = 0.0;
901 : }
902 : }
903 32 : state.dataHeatBalFanSys->ZoneQElecBaseboardToPerson = 0.0;
904 :
905 64 : for (auto &elecBaseboard : state.dataElectBaseboardRad->ElecBaseboard) {
906 32 : if (elecBaseboard.ZonePtr > 0) { // issue 5806 can be zero during first calls to baseboards, will be set after all are modeled
907 32 : int ZoneNum = elecBaseboard.ZonePtr;
908 32 : state.dataHeatBalFanSys->ZoneQElecBaseboardToPerson(ZoneNum) += elecBaseboard.QBBElecRadSource * elecBaseboard.FracDistribPerson;
909 :
910 64 : for (int RadSurfNum = 1; RadSurfNum <= elecBaseboard.TotSurfToDistrib; ++RadSurfNum) {
911 32 : int SurfNum = elecBaseboard.SurfacePtr(RadSurfNum);
912 32 : if (state.dataSurface->Surface(SurfNum).Area > SmallestArea) {
913 : Real64 ThisSurfIntensity =
914 32 : (elecBaseboard.QBBElecRadSource * elecBaseboard.FracDistribToSurf(RadSurfNum) / state.dataSurface->Surface(SurfNum).Area);
915 32 : state.dataHeatBalFanSys->surfQRadFromHVAC(SurfNum).ElecBaseboard += ThisSurfIntensity;
916 32 : if (ThisSurfIntensity > DataHeatBalFanSys::MaxRadHeatFlux) {
917 0 : ShowSevereError(state, "DistributeBBElecRadGains: excessive thermal radiation heat flux intensity detected");
918 0 : ShowContinueError(state, "Surface = " + state.dataSurface->Surface(SurfNum).Name);
919 0 : ShowContinueError(state, format("Surface area = {:.3R} [m2]", state.dataSurface->Surface(SurfNum).Area));
920 0 : ShowContinueError(state,
921 0 : "Occurs in " + state.dataElectBaseboardRad->cCMO_BBRadiator_Electric + " = " + elecBaseboard.EquipName);
922 0 : ShowContinueError(state, format("Radiation intensity = {:.2R} [W/m2]", ThisSurfIntensity));
923 0 : ShowContinueError(
924 0 : state, "Assign a larger surface area or more surfaces in " + state.dataElectBaseboardRad->cCMO_BBRadiator_Electric);
925 0 : ShowFatalError(state, "DistributeBBElecRadGains: excessive thermal radiation heat flux intensity detected");
926 : }
927 : } else {
928 0 : ShowSevereError(state, "DistributeBBElecRadGains: surface not large enough to receive thermal radiation heat flux");
929 0 : ShowContinueError(state, "Surface = " + state.dataSurface->Surface(SurfNum).Name);
930 0 : ShowContinueError(state, format("Surface area = {:.3R} [m2]", state.dataSurface->Surface(SurfNum).Area));
931 0 : ShowContinueError(state,
932 0 : "Occurs in " + state.dataElectBaseboardRad->cCMO_BBRadiator_Electric + " = " + elecBaseboard.EquipName);
933 0 : ShowContinueError(
934 0 : state, "Assign a larger surface area or more surfaces in " + state.dataElectBaseboardRad->cCMO_BBRadiator_Electric);
935 0 : ShowFatalError(state, "DistributeBBElecRadGains: surface not large enough to receive thermal radiation heat flux");
936 : }
937 : }
938 : }
939 : }
940 32 : }
941 :
942 60 : void ReportElectricBaseboard(EnergyPlusData &state, int const BaseboardNum)
943 : {
944 :
945 : // SUBROUTINE INFORMATION:
946 : // AUTHOR Daeho Kang
947 : // DATE WRITTEN Feb 2010
948 :
949 : // Using/Aliasing
950 60 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
951 60 : auto &elecBaseboard = state.dataElectBaseboardRad->ElecBaseboard(BaseboardNum);
952 60 : elecBaseboard.ElecUseLoad = elecBaseboard.ElecUseRate * TimeStepSysSec;
953 60 : elecBaseboard.TotEnergy = elecBaseboard.TotPower * TimeStepSysSec;
954 60 : elecBaseboard.Energy = elecBaseboard.Power * TimeStepSysSec;
955 60 : elecBaseboard.ConvEnergy = elecBaseboard.ConvPower * TimeStepSysSec;
956 60 : elecBaseboard.RadEnergy = elecBaseboard.RadPower * TimeStepSysSec;
957 60 : }
958 :
959 : } // namespace ElectricBaseboardRadiator
960 :
961 : } // namespace EnergyPlus
|