Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cassert>
50 : #include <cmath>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/Construction.hh>
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataEnvironment.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/DataHeatBalFanSys.hh>
62 : #include <EnergyPlus/DataHeatBalSurface.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataPhotovoltaics.hh>
66 : #include <EnergyPlus/DataPrecisionGlobals.hh>
67 : #include <EnergyPlus/DataSurfaces.hh>
68 : #include <EnergyPlus/General.hh>
69 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
70 : #include <EnergyPlus/OutputProcessor.hh>
71 : #include <EnergyPlus/PhotovoltaicThermalCollectors.hh>
72 : #include <EnergyPlus/Photovoltaics.hh>
73 : #include <EnergyPlus/ScheduleManager.hh>
74 : #include <EnergyPlus/TranspiredCollector.hh>
75 : #include <EnergyPlus/UtilityRoutines.hh>
76 :
77 : namespace EnergyPlus {
78 :
79 : namespace Photovoltaics {
80 : // MODULE INFORMATION:
81 : // AUTHOR David Bradley
82 : // DATE WRITTEN January 2003
83 : // MODIFIED B. Griffith, dec2003 - Jan2004
84 : // added Sandia PV model loosely based on G. Barker's implementation for TRNSYS type
85 : // added Simple PV efficiency model for early design phases
86 : // RE-ENGINEERED added case statement to allow selecting and mixing between different models
87 : // moved derived types to DataPhotovoltaics
88 : // B. Griffith, Aug. 2008, refactored PV data structures and input objects to
89 : // so that there is one Generator:Photovoltaics object with 3 different model options.
90 :
91 : // PURPOSE OF THIS MODULE:
92 : // This module collects routines used to simulate the timestep by timestep performance of a
93 : // photovoltaic arrays. The user can select between different models by choosing an a model and performance input object
94 : // Using the input object "PhotovoltaicPerformance:Simple" will lead to modeling the PV system using
95 : // crude model that just applies a power conversion efficiency factor, much simpler to specify
96 : // Using the input object "PhotovoltaicPerformance:EquivalentOne-Diode" will lead to modeling the PV system using
97 : // The PV model used as the basis for this module is Type180 from the HYDROGEMS library developed by
98 : // Oystein Ulleberg at the IFE Institute for Energy Technology in Norway and also work by Eckstein
99 :
100 : // Using the input object, "PhotovoltaicPerformance:SANDIA" will lead to modeling a PV array
101 : // using models developed by David King, Sandia National lab. These models appear to provide
102 : // improved prediction of PV performance at low radiance and incident angles.
103 :
104 : // METHODOLOGY EMPLOYED: This module contains routines to manage PV system models.
105 : // There are two options for what model to use and this duality of modeling approaches is
106 : // reflected in there being two groups of routines for each PV model, The original model is
107 : // referred to as Equivalent one-diode model and has origins as a TRNSYS type180 from the Hydrogems library
108 : // A newer model with more involved input has been developed by Sandia National Lab (SNL) by David King.
109 : // The TRNSYS type180 model include the use of numerical routines to minimize a multivariate function
110 :
111 : // Using/Aliasing
112 : using namespace DataPhotovoltaics;
113 :
114 : constexpr std::string_view cPVGeneratorObjectName = "Generator:Photovoltaic";
115 :
116 : constexpr std::array<std::string_view, (int)PVModel::Num> pvModelNames = {
117 : "PhotovoltaicPerformance:Simple", "PhotovoltaicPerformance:EquivalentOne-Diode", "PhotovoltaicPerformance:Sandia"};
118 : constexpr std::array<std::string_view, (int)PVModel::Num> pvModelNamesUC = {
119 : "PHOTOVOLTAICPERFORMANCE:SIMPLE", "PHOTOVOLTAICPERFORMANCE:EQUIVALENTONE-DIODE", "PHOTOVOLTAICPERFORMANCE:SANDIA"};
120 :
121 : constexpr std::array<std::string_view, (int)CellIntegration::Num> cellIntegrationNames = {"Decoupled",
122 : "DecoupledUllebergDynamic",
123 : "IntegratedSurfaceOutsideFace",
124 : "IntegratedTranspiredCollector",
125 : "IntegratedExteriorVentedCavity",
126 : "PhotovoltaicThermalSolarCollector"};
127 : constexpr std::array<std::string_view, (int)CellIntegration::Num> cellIntegrationNamesUC = {"DECOUPLED",
128 : "DECOUPLEDULLEBERGDYNAMIC",
129 : "INTEGRATEDSURFACEOUTSIDEFACE",
130 : "INTEGRATEDTRANSPIREDCOLLECTOR",
131 : "INTEGRATEDEXTERIORVENTEDCAVITY",
132 : "PHOTOVOLTAICTHERMALSOLARCOLLECTOR"};
133 :
134 : constexpr std::array<std::string_view, (int)Efficiency::Num> efficiencyNames = {"Fixed", "Scheduled"};
135 : constexpr std::array<std::string_view, (int)Efficiency::Num> efficiencyNamesUC = {"FIXED", "SCHEDULED"};
136 :
137 : constexpr std::array<std::string_view, (int)SiPVCells::Num> siPVCellsNames = {"CrystallineSilicon", "AmorphousSilicon"};
138 : constexpr std::array<std::string_view, (int)SiPVCells::Num> siPVCellsNamesUC = {"CRYSTALLINESILICON", "AMORPHOUSSILICON"};
139 :
140 0 : void SimPVGenerator(EnergyPlusData &state,
141 : [[maybe_unused]] GeneratorType const GeneratorType, // type of Generator !unused1208
142 : std::string const &GeneratorName, // user specified name of Generator
143 : int &GeneratorIndex,
144 : bool const RunFlag, // is PV ON or OFF as determined by schedules in ElecLoadCenter
145 : [[maybe_unused]] Real64 const PVLoad // electrical load on the PV (not really used... PV models assume "full on" !unused1208
146 : )
147 : {
148 :
149 : // SUBROUTINE INFORMATION:
150 : // AUTHOR David Bradley
151 : // DATE WRITTEN April 2003
152 : // MODIFIED B. Griffith Jan 2004
153 : // B. Griffith Aug. 2008 Rework for new structure
154 : // RE-ENGINEERED na
155 :
156 : // PURPOSE OF THIS SUBROUTINE:
157 : // This subroutine is in charge of all the rest of the subroutines contained
158 : // in this module. provides common entry point for all the models
159 :
160 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
161 : int PVnum; // index of unit in PV array for Equivalent one-diode model
162 :
163 : // Get PV data from input file
164 0 : if (state.dataPhotovoltaicState->GetInputFlag) {
165 0 : GetPVInput(state); // for all three types of models
166 0 : state.dataPhotovoltaicState->GetInputFlag = false;
167 : }
168 :
169 0 : if (GeneratorIndex == 0) {
170 0 : PVnum = Util::FindItemInList(GeneratorName, state.dataPhotovoltaic->PVarray);
171 0 : if (PVnum == 0) {
172 0 : ShowFatalError(state, format("SimPhotovoltaicGenerator: Specified PV not one of valid Photovoltaic Generators {}", GeneratorName));
173 : }
174 0 : GeneratorIndex = PVnum;
175 : } else {
176 0 : PVnum = GeneratorIndex;
177 0 : if (PVnum > state.dataPhotovoltaic->NumPVs || PVnum < 1) {
178 0 : ShowFatalError(state,
179 0 : format("SimPhotovoltaicGenerator: Invalid GeneratorIndex passed={}, Number of PVs={}, Generator name={}",
180 : PVnum,
181 0 : state.dataPhotovoltaic->NumPVs,
182 : GeneratorName));
183 : }
184 0 : if (state.dataPhotovoltaicState->CheckEquipName(PVnum)) {
185 0 : if (GeneratorName != state.dataPhotovoltaic->PVarray(PVnum).Name) {
186 0 : ShowFatalError(
187 : state,
188 0 : format("SimPhotovoltaicGenerator: Invalid GeneratorIndex passed={}, Generator name={}, stored PV Name for that index={}",
189 : PVnum,
190 : GeneratorName,
191 0 : state.dataPhotovoltaic->PVarray(PVnum).Name));
192 : }
193 0 : state.dataPhotovoltaicState->CheckEquipName(PVnum) = false;
194 : }
195 : }
196 :
197 0 : switch (state.dataPhotovoltaic->PVarray(PVnum).PVModelType) {
198 0 : case PVModel::Simple: {
199 0 : CalcSimplePV(state, PVnum);
200 0 : } break;
201 0 : case PVModel::TRNSYS: {
202 : // 'PhotovoltaicPerformance:EquivalentOne-Diode' (aka. 5-parameter TRNSYS type 180 model)
203 0 : InitTRNSYSPV(state, PVnum);
204 :
205 0 : CalcTRNSYSPV(state, PVnum, RunFlag);
206 0 : } break;
207 0 : case PVModel::Sandia: {
208 : // 'PhotovoltaicPerformance:Sandia' (aka. King model, Sandia Nat. Labs.)
209 0 : CalcSandiaPV(state, PVnum, RunFlag);
210 0 : } break;
211 0 : default: {
212 0 : ShowFatalError(state, format("Specified generator model type not found for PV generator = {}", GeneratorName));
213 0 : } break;
214 : }
215 :
216 0 : ReportPV(state, PVnum);
217 0 : }
218 :
219 0 : void GetPVGeneratorResults(EnergyPlusData &state,
220 : [[maybe_unused]] GeneratorType const GeneratorType, // type of Generator !unused1208
221 : int const GeneratorIndex,
222 : Real64 &GeneratorPower, // electrical power
223 : Real64 &GeneratorEnergy, // electrical energy
224 : Real64 &ThermalPower,
225 : Real64 &ThermalEnergy)
226 : {
227 :
228 : // SUBROUTINE INFORMATION:
229 : // AUTHOR B. Griffith
230 : // DATE WRITTEN Aug. 2008
231 : // MODIFIED na
232 : // RE-ENGINEERED na
233 :
234 : // PURPOSE OF THIS SUBROUTINE:
235 : // provide a "get" method to collect results for individual electric load centers.
236 :
237 : // Using/Aliasing
238 : using PhotovoltaicThermalCollectors::GetPVTThermalPowerProduction;
239 :
240 0 : GeneratorPower = state.dataPhotovoltaic->PVarray(GeneratorIndex).Report.DCPower;
241 0 : GeneratorEnergy = state.dataPhotovoltaic->PVarray(GeneratorIndex).Report.DCEnergy;
242 0 : auto const &thisPVarray = state.dataPhotovoltaic->PVarray(GeneratorIndex);
243 : // PVT may add thermal
244 0 : if (thisPVarray.CellIntegrationMode == CellIntegration::PVTSolarCollector) {
245 : // get result for thermal power generation
246 0 : GetPVTThermalPowerProduction(state, GeneratorIndex, ThermalPower, ThermalEnergy);
247 : } else {
248 0 : ThermalPower = 0.0;
249 0 : ThermalEnergy = 0.0;
250 : }
251 0 : }
252 :
253 : // *************
254 :
255 2 : void GetPVInput(EnergyPlusData &state)
256 : {
257 :
258 : // SUBROUTINE INFORMATION:
259 : // AUTHOR David Bradley
260 : // DATE WRITTEN January 2003
261 : // MODIFIED B.Griffith Dec. 2003 - Jan 2004 added input for Simple and Sandia PV model
262 : // B. Griffith Feb. 2008 - revised input for TRNSYS pv model for BIPV and inverter
263 : // B. Griffith Aug. 2008 - revised input for new organization and naming convention
264 : // RE-ENGINEERED na
265 :
266 : // PURPOSE OF THIS SUBROUTINE:
267 : // This subroutine gets the input for the Photovoltaic units saving it in
268 : // the data structures defined in DataPhotovoltaics.cc.
269 :
270 : // METHODOLOGY EMPLOYED:
271 : // subroutine structure taken from Beta2 BaseboardRadiator.cc
272 :
273 : // Using/Aliasing
274 : using namespace DataHeatBalance;
275 :
276 : using PhotovoltaicThermalCollectors::GetPVTmodelIndex;
277 : using TranspiredCollector::GetTranspiredCollectorIndex;
278 :
279 : static constexpr std::string_view routineName = "GetPVInput";
280 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
281 : int PVnum; // working variable for do loop through pv arrays
282 : int SurfNum; // working variable for surface id in Heat Balance domain
283 : int ModNum; // working variable for do loop through Sandia model parameter input
284 : int NumAlphas; // Number of PV Array parameter alpha names being passed
285 : int NumNums; // Number of PV Array numeric parameters are being passed
286 : int IOStat;
287 2 : bool ErrorsFound(false); // if errors detected in input
288 : int ThisParamObj;
289 : int dupPtr;
290 :
291 : // Object Data
292 2 : Array1D<SimplePVParamsStruct> tmpSimpleModuleParams; // temporary, for processing input data
293 2 : Array1D<TRNSYSPVModuleParamsStruct> tmpTRNSYSModuleParams; // temporary, for processing input data
294 2 : Array1D<SNLModuleParamsStuct> tmpSNLModuleParams; // temporary, for processing input data
295 :
296 2 : auto &s_ipsc = state.dataIPShortCut;
297 :
298 : // count how many photovoltaic arrays of different types are in the .idf
299 2 : state.dataPhotovoltaic->NumPVs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cPVGeneratorObjectName);
300 4 : state.dataPhotovoltaic->NumSimplePVModuleTypes =
301 2 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, pvModelNames[(int)PVModel::Simple]);
302 4 : state.dataPhotovoltaic->Num1DiodePVModuleTypes =
303 2 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, pvModelNames[(int)PVModel::TRNSYS]);
304 4 : state.dataPhotovoltaic->NumSNLPVModuleTypes =
305 2 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, pvModelNames[(int)PVModel::Sandia]);
306 :
307 2 : if (state.dataPhotovoltaic->NumPVs <= 0) {
308 0 : ShowSevereError(state, format("Did not find any {}", cPVGeneratorObjectName));
309 0 : return;
310 : }
311 :
312 2 : if (!allocated(state.dataPhotovoltaic->PVarray)) state.dataPhotovoltaic->PVarray.allocate(state.dataPhotovoltaic->NumPVs);
313 2 : state.dataPhotovoltaicState->CheckEquipName.dimension(state.dataPhotovoltaic->NumPVs, true);
314 :
315 2 : s_ipsc->cCurrentModuleObject = cPVGeneratorObjectName;
316 4 : for (PVnum = 1; PVnum <= state.dataPhotovoltaic->NumPVs; ++PVnum) {
317 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
318 2 : s_ipsc->cCurrentModuleObject,
319 : PVnum,
320 2 : s_ipsc->cAlphaArgs,
321 : NumAlphas,
322 2 : s_ipsc->rNumericArgs,
323 : NumNums,
324 : IOStat,
325 : _,
326 2 : s_ipsc->lAlphaFieldBlanks,
327 2 : s_ipsc->cAlphaFieldNames,
328 2 : s_ipsc->cNumericFieldNames);
329 :
330 2 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
331 :
332 2 : Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
333 2 : state.dataPhotovoltaic->PVarray(PVnum).Name = s_ipsc->cAlphaArgs(1);
334 :
335 2 : state.dataPhotovoltaic->PVarray(PVnum).SurfaceName = s_ipsc->cAlphaArgs(2);
336 2 : state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataSurface->Surface);
337 : // required-surface
338 2 : if (s_ipsc->lAlphaFieldBlanks(2)) {
339 0 : ShowSevereError(state, format("Invalid {} = {}", s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
340 0 : ShowContinueError(state, format("Entered in {} = {}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
341 0 : ShowContinueError(state, "Surface name cannot be blank");
342 0 : ErrorsFound = true;
343 : }
344 2 : if (state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr == 0) {
345 0 : ShowSevereError(state, format("Invalid {} = {}", s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
346 0 : ShowContinueError(state, format("Entered in {} = {}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
347 0 : ErrorsFound = true;
348 : } else {
349 : // Found one -- make sure has right parameters for PV
350 2 : SurfNum = state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr;
351 2 : state.dataSurface->SurfIsPV(SurfNum) = true;
352 :
353 2 : if (!state.dataSurface->Surface(SurfNum).ExtSolar) {
354 0 : ShowWarningError(state, format("Invalid {} = {}", s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
355 0 : ShowContinueError(state, format("Entered in {} = {}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
356 0 : ShowContinueError(state, "Surface is not exposed to solar, check surface boundary condition");
357 : }
358 2 : state.dataPhotovoltaic->PVarray(PVnum).Zone = GetPVZone(state, state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr);
359 :
360 : // check surface orientation, warn if upside down
361 2 : if ((state.dataSurface->Surface(SurfNum).Tilt < -95.0) || (state.dataSurface->Surface(SurfNum).Tilt > 95.0)) {
362 0 : ShowWarningError(state, format("Suspected input problem with {} = {}", s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
363 0 : ShowContinueError(state, format("Entered in {} = {}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
364 0 : ShowContinueError(state, "Surface used for solar collector faces down");
365 0 : ShowContinueError(
366 0 : state, format("Surface tilt angle (degrees from ground outward normal) = {:.2R}", state.dataSurface->Surface(SurfNum).Tilt));
367 : }
368 : }
369 :
370 2 : if (s_ipsc->lAlphaFieldBlanks(3)) {
371 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
372 0 : ErrorsFound = true;
373 2 : } else if ((state.dataPhotovoltaic->PVarray(PVnum).PVModelType =
374 4 : static_cast<PVModel>(getEnumValue(pvModelNamesUC, s_ipsc->cAlphaArgs(3)))) == PVModel::Invalid) {
375 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
376 0 : ErrorsFound = true;
377 : }
378 :
379 2 : state.dataPhotovoltaic->PVarray(PVnum).PerfObjName = s_ipsc->cAlphaArgs(4); // check later once perf objects are loaded
380 :
381 2 : if (s_ipsc->lAlphaFieldBlanks(5)) {
382 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
383 0 : ErrorsFound = true;
384 2 : } else if ((state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode =
385 4 : static_cast<CellIntegration>(getEnumValue(cellIntegrationNamesUC, s_ipsc->cAlphaArgs(5)))) == CellIntegration::Invalid) {
386 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
387 0 : ErrorsFound = true;
388 : }
389 :
390 2 : state.dataPhotovoltaic->PVarray(PVnum).NumSeriesNParall = s_ipsc->rNumericArgs(1);
391 2 : state.dataPhotovoltaic->PVarray(PVnum).NumModNSeries = s_ipsc->rNumericArgs(2);
392 :
393 : } // main PV array objects
394 :
395 : // search for duplicate PV arrays on integrated heat transfer surfaces, accumulating source terms across arrays is not supported
396 4 : for (PVnum = 1; PVnum <= state.dataPhotovoltaic->NumPVs; ++PVnum) {
397 2 : switch (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode) {
398 0 : case CellIntegration::SurfaceOutsideFace:
399 : case CellIntegration::TranspiredCollector:
400 : case CellIntegration::ExteriorVentedCavity: {
401 0 : dupPtr = Util::FindItemInList(state.dataPhotovoltaic->PVarray(PVnum).SurfaceName,
402 0 : state.dataPhotovoltaic->PVarray({PVnum + 1, state.dataPhotovoltaic->NumPVs}),
403 : &PVArrayStruct::SurfaceName);
404 0 : if (dupPtr != 0) dupPtr += PVnum; // to correct for shortened array in find item
405 0 : if (dupPtr != 0) {
406 0 : auto &thisPVarray = state.dataPhotovoltaic->PVarray(dupPtr);
407 0 : if (thisPVarray.CellIntegrationMode == CellIntegration::SurfaceOutsideFace) {
408 0 : ShowSevereError(state, format("{}: problem detected with multiple PV arrays.", s_ipsc->cCurrentModuleObject));
409 0 : ShowContinueError(state, "When using IntegratedSurfaceOutsideFace heat transfer mode, only one PV array can be coupled");
410 0 : ShowContinueError(state,
411 0 : format("Both {} and {} are using surface {}",
412 0 : state.dataPhotovoltaic->PVarray(PVnum).Name,
413 0 : thisPVarray.Name,
414 0 : state.dataPhotovoltaic->PVarray(PVnum).SurfaceName));
415 0 : ErrorsFound = true;
416 0 : } else if (thisPVarray.CellIntegrationMode == CellIntegration::TranspiredCollector) {
417 0 : ShowSevereError(state, format("{}: problem detected with multiple PV arrays.", s_ipsc->cCurrentModuleObject));
418 0 : ShowContinueError(state, "When using IntegratedTranspiredCollector heat transfer mode, only one PV array can be coupled");
419 0 : ShowContinueError(state,
420 0 : format("Both {} and {} are using UTSC surface = {}",
421 0 : state.dataPhotovoltaic->PVarray(PVnum).Name,
422 0 : thisPVarray.Name,
423 0 : state.dataPhotovoltaic->PVarray(PVnum).SurfaceName));
424 0 : ErrorsFound = true;
425 0 : } else if (thisPVarray.CellIntegrationMode == CellIntegration::ExteriorVentedCavity) {
426 0 : ShowSevereError(state, format("{}: problem detected with multiple PV arrays.", s_ipsc->cCurrentModuleObject));
427 0 : ShowContinueError(state, "When using IntegratedExteriorVentedCavity heat transfer mode, only one PV array can be coupled");
428 0 : ShowContinueError(state,
429 0 : format("Both {} and {} are using exterior vented surface = {}",
430 0 : state.dataPhotovoltaic->PVarray(PVnum).Name,
431 0 : thisPVarray.Name,
432 0 : state.dataPhotovoltaic->PVarray(PVnum).SurfaceName));
433 0 : ErrorsFound = true;
434 0 : } else if (thisPVarray.CellIntegrationMode == CellIntegration::PVTSolarCollector) {
435 0 : ShowSevereError(
436 : state,
437 0 : format("Problem detected with multiple PV arrays for={}. When using PhotovoltaicThermalSolarCollector heat transfer "
438 : "mode, only one PV array can be coupled. Both this PV array={} and this PV array={} are using PVT surface={}",
439 0 : s_ipsc->cCurrentModuleObject,
440 0 : state.dataPhotovoltaic->PVarray(PVnum).Name,
441 0 : thisPVarray.Name,
442 0 : state.dataPhotovoltaic->PVarray(PVnum).SurfaceName));
443 0 : ErrorsFound = true;
444 : }
445 : }
446 0 : } break;
447 2 : default:
448 2 : break;
449 : }
450 : }
451 :
452 2 : if (state.dataPhotovoltaic->NumSimplePVModuleTypes > 0) {
453 1 : tmpSimpleModuleParams.allocate(state.dataPhotovoltaic->NumSimplePVModuleTypes);
454 1 : s_ipsc->cCurrentModuleObject = pvModelNames[(int)PVModel::Simple];
455 2 : for (ModNum = 1; ModNum <= state.dataPhotovoltaic->NumSimplePVModuleTypes; ++ModNum) {
456 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
457 1 : s_ipsc->cCurrentModuleObject,
458 : ModNum,
459 1 : s_ipsc->cAlphaArgs,
460 : NumAlphas,
461 1 : s_ipsc->rNumericArgs,
462 : NumNums,
463 : IOStat,
464 : _,
465 1 : s_ipsc->lAlphaFieldBlanks,
466 1 : s_ipsc->cAlphaFieldNames,
467 1 : s_ipsc->cNumericFieldNames);
468 :
469 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
470 :
471 1 : if (Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound)) {
472 0 : continue;
473 : }
474 1 : tmpSimpleModuleParams(ModNum).Name = s_ipsc->cAlphaArgs(1);
475 1 : tmpSimpleModuleParams(ModNum).ActiveFraction = s_ipsc->rNumericArgs(1);
476 :
477 1 : if (s_ipsc->lAlphaFieldBlanks(2)) {
478 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
479 0 : ErrorsFound = true;
480 2 : } else if ((tmpSimpleModuleParams(ModNum).EfficencyInputMode =
481 1 : static_cast<Efficiency>(getEnumValue(efficiencyNamesUC, s_ipsc->cAlphaArgs(2)))) == Efficiency::Invalid) {
482 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
483 0 : ErrorsFound = true;
484 : }
485 1 : tmpSimpleModuleParams(ModNum).PVEfficiency = s_ipsc->rNumericArgs(2);
486 :
487 1 : if (tmpSimpleModuleParams(ModNum).EfficencyInputMode == Efficiency::Scheduled) {
488 0 : if (s_ipsc->lAlphaFieldBlanks(3)) {
489 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
490 0 : ErrorsFound = true;
491 0 : } else if ((tmpSimpleModuleParams(ModNum).effSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
492 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
493 0 : ErrorsFound = true;
494 : }
495 : }
496 : }
497 : }
498 :
499 2 : if (state.dataPhotovoltaic->Num1DiodePVModuleTypes > 0) {
500 1 : tmpTRNSYSModuleParams.allocate(state.dataPhotovoltaic->Num1DiodePVModuleTypes);
501 1 : s_ipsc->cCurrentModuleObject = pvModelNames[(int)PVModel::TRNSYS];
502 2 : for (ModNum = 1; ModNum <= state.dataPhotovoltaic->Num1DiodePVModuleTypes; ++ModNum) {
503 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
504 1 : s_ipsc->cCurrentModuleObject,
505 : ModNum,
506 1 : s_ipsc->cAlphaArgs,
507 : NumAlphas,
508 1 : s_ipsc->rNumericArgs,
509 : NumNums,
510 : IOStat,
511 : _,
512 1 : s_ipsc->lAlphaFieldBlanks,
513 1 : s_ipsc->cAlphaFieldNames,
514 1 : s_ipsc->cNumericFieldNames);
515 :
516 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
517 1 : if (Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound)) {
518 0 : continue;
519 : }
520 1 : tmpTRNSYSModuleParams(ModNum).Name = s_ipsc->cAlphaArgs(1);
521 :
522 1 : if (s_ipsc->lAlphaFieldBlanks(2)) {
523 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
524 0 : ErrorsFound = true;
525 1 : } else if ((tmpTRNSYSModuleParams(ModNum).CellType = static_cast<SiPVCells>(getEnumValue(siPVCellsNamesUC, s_ipsc->cAlphaArgs(2)))) ==
526 : SiPVCells::Invalid) {
527 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
528 0 : ErrorsFound = true;
529 : }
530 :
531 1 : tmpTRNSYSModuleParams(ModNum).CellsInSeries = int(s_ipsc->rNumericArgs(1));
532 1 : tmpTRNSYSModuleParams(ModNum).Area = s_ipsc->rNumericArgs(2);
533 1 : tmpTRNSYSModuleParams(ModNum).TauAlpha = s_ipsc->rNumericArgs(3);
534 1 : tmpTRNSYSModuleParams(ModNum).SemiConductorBandgap = s_ipsc->rNumericArgs(4);
535 1 : tmpTRNSYSModuleParams(ModNum).ShuntResistance = s_ipsc->rNumericArgs(5);
536 1 : tmpTRNSYSModuleParams(ModNum).RefIsc = s_ipsc->rNumericArgs(6);
537 1 : tmpTRNSYSModuleParams(ModNum).RefVoc = s_ipsc->rNumericArgs(7);
538 1 : tmpTRNSYSModuleParams(ModNum).RefTemperature = s_ipsc->rNumericArgs(8) + Constant::Kelvin;
539 1 : tmpTRNSYSModuleParams(ModNum).RefInsolation = s_ipsc->rNumericArgs(9);
540 1 : tmpTRNSYSModuleParams(ModNum).Imp = s_ipsc->rNumericArgs(10);
541 1 : tmpTRNSYSModuleParams(ModNum).Vmp = s_ipsc->rNumericArgs(11);
542 1 : tmpTRNSYSModuleParams(ModNum).TempCoefIsc = s_ipsc->rNumericArgs(12);
543 1 : tmpTRNSYSModuleParams(ModNum).TempCoefVoc = s_ipsc->rNumericArgs(13);
544 1 : tmpTRNSYSModuleParams(ModNum).NOCTAmbTemp = s_ipsc->rNumericArgs(14) + Constant::Kelvin;
545 1 : tmpTRNSYSModuleParams(ModNum).NOCTCellTemp = s_ipsc->rNumericArgs(15) + Constant::Kelvin;
546 1 : tmpTRNSYSModuleParams(ModNum).NOCTInsolation = s_ipsc->rNumericArgs(16);
547 1 : tmpTRNSYSModuleParams(ModNum).HeatLossCoef = s_ipsc->rNumericArgs(17);
548 1 : tmpTRNSYSModuleParams(ModNum).HeatCapacity = s_ipsc->rNumericArgs(18);
549 : }
550 : }
551 :
552 2 : if (state.dataPhotovoltaic->NumSNLPVModuleTypes > 0) {
553 0 : tmpSNLModuleParams.allocate(state.dataPhotovoltaic->NumSNLPVModuleTypes);
554 0 : s_ipsc->cCurrentModuleObject = pvModelNames[(int)PVModel::Sandia];
555 0 : for (ModNum = 1; ModNum <= state.dataPhotovoltaic->NumSNLPVModuleTypes; ++ModNum) {
556 :
557 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
558 0 : s_ipsc->cCurrentModuleObject,
559 : ModNum,
560 0 : s_ipsc->cAlphaArgs,
561 : NumAlphas,
562 0 : s_ipsc->rNumericArgs,
563 : NumNums,
564 : IOStat,
565 : _,
566 0 : s_ipsc->lAlphaFieldBlanks,
567 0 : s_ipsc->cAlphaFieldNames,
568 0 : s_ipsc->cNumericFieldNames);
569 0 : if (Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound)) {
570 0 : continue;
571 : }
572 :
573 0 : tmpSNLModuleParams(ModNum).name = s_ipsc->cAlphaArgs(1);
574 0 : tmpSNLModuleParams(ModNum).Acoll = s_ipsc->rNumericArgs(1);
575 0 : tmpSNLModuleParams(ModNum).NcellSer = s_ipsc->rNumericArgs(2);
576 0 : tmpSNLModuleParams(ModNum).NparSerCells = s_ipsc->rNumericArgs(3);
577 0 : tmpSNLModuleParams(ModNum).Isc0 = s_ipsc->rNumericArgs(4);
578 0 : tmpSNLModuleParams(ModNum).Voc0 = s_ipsc->rNumericArgs(5);
579 0 : tmpSNLModuleParams(ModNum).Imp0 = s_ipsc->rNumericArgs(6);
580 0 : tmpSNLModuleParams(ModNum).Vmp0 = s_ipsc->rNumericArgs(7);
581 0 : tmpSNLModuleParams(ModNum).aIsc = s_ipsc->rNumericArgs(8);
582 0 : tmpSNLModuleParams(ModNum).aImp = s_ipsc->rNumericArgs(9);
583 0 : tmpSNLModuleParams(ModNum).c_0 = s_ipsc->rNumericArgs(10);
584 0 : tmpSNLModuleParams(ModNum).c_1 = s_ipsc->rNumericArgs(11);
585 0 : tmpSNLModuleParams(ModNum).BVoc0 = s_ipsc->rNumericArgs(12);
586 0 : tmpSNLModuleParams(ModNum).mBVoc = s_ipsc->rNumericArgs(13);
587 0 : tmpSNLModuleParams(ModNum).BVmp0 = s_ipsc->rNumericArgs(14);
588 0 : tmpSNLModuleParams(ModNum).mBVmp = s_ipsc->rNumericArgs(15);
589 0 : tmpSNLModuleParams(ModNum).DiodeFactor = s_ipsc->rNumericArgs(16);
590 0 : tmpSNLModuleParams(ModNum).c_2 = s_ipsc->rNumericArgs(17);
591 0 : tmpSNLModuleParams(ModNum).c_3 = s_ipsc->rNumericArgs(18);
592 0 : tmpSNLModuleParams(ModNum).a_0 = s_ipsc->rNumericArgs(19);
593 0 : tmpSNLModuleParams(ModNum).a_1 = s_ipsc->rNumericArgs(20);
594 0 : tmpSNLModuleParams(ModNum).a_2 = s_ipsc->rNumericArgs(21);
595 0 : tmpSNLModuleParams(ModNum).a_3 = s_ipsc->rNumericArgs(22);
596 0 : tmpSNLModuleParams(ModNum).a_4 = s_ipsc->rNumericArgs(23);
597 0 : tmpSNLModuleParams(ModNum).b_0 = s_ipsc->rNumericArgs(24);
598 0 : tmpSNLModuleParams(ModNum).b_1 = s_ipsc->rNumericArgs(25);
599 0 : tmpSNLModuleParams(ModNum).b_2 = s_ipsc->rNumericArgs(26);
600 0 : tmpSNLModuleParams(ModNum).b_3 = s_ipsc->rNumericArgs(27);
601 0 : tmpSNLModuleParams(ModNum).b_4 = s_ipsc->rNumericArgs(28);
602 0 : tmpSNLModuleParams(ModNum).b_5 = s_ipsc->rNumericArgs(29);
603 0 : tmpSNLModuleParams(ModNum).DT0 = s_ipsc->rNumericArgs(30);
604 0 : tmpSNLModuleParams(ModNum).fd = s_ipsc->rNumericArgs(31);
605 0 : tmpSNLModuleParams(ModNum).a = s_ipsc->rNumericArgs(32);
606 0 : tmpSNLModuleParams(ModNum).b = s_ipsc->rNumericArgs(33);
607 0 : tmpSNLModuleParams(ModNum).c_4 = s_ipsc->rNumericArgs(34);
608 0 : tmpSNLModuleParams(ModNum).c_5 = s_ipsc->rNumericArgs(35);
609 0 : tmpSNLModuleParams(ModNum).Ix0 = s_ipsc->rNumericArgs(36);
610 0 : tmpSNLModuleParams(ModNum).Ixx0 = s_ipsc->rNumericArgs(37);
611 0 : tmpSNLModuleParams(ModNum).c_6 = s_ipsc->rNumericArgs(38);
612 0 : tmpSNLModuleParams(ModNum).c_7 = s_ipsc->rNumericArgs(39);
613 : }
614 : }
615 :
616 : // now fill collector performance data into main PV structure
617 4 : for (PVnum = 1; PVnum <= state.dataPhotovoltaic->NumPVs; ++PVnum) {
618 :
619 2 : switch (state.dataPhotovoltaic->PVarray(PVnum).PVModelType) {
620 1 : case PVModel::Simple: {
621 1 : ThisParamObj = Util::FindItemInList(state.dataPhotovoltaic->PVarray(PVnum).PerfObjName, tmpSimpleModuleParams);
622 1 : if (ThisParamObj > 0) {
623 1 : state.dataPhotovoltaic->PVarray(PVnum).SimplePVModule = tmpSimpleModuleParams(ThisParamObj); // entire structure assignment
624 :
625 : // do one-time setups on input data
626 1 : state.dataPhotovoltaic->PVarray(PVnum).SimplePVModule.AreaCol =
627 1 : state.dataSurface->Surface(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr).Area *
628 1 : state.dataPhotovoltaic->PVarray(PVnum).SimplePVModule.ActiveFraction;
629 : } else {
630 0 : ShowSevereError(state, format("Invalid PV performance object name of {}", state.dataPhotovoltaic->PVarray(PVnum).PerfObjName));
631 0 : ShowContinueError(state, format("Entered in {} = {}", cPVGeneratorObjectName, state.dataPhotovoltaic->PVarray(PVnum).Name));
632 0 : ErrorsFound = true;
633 : }
634 1 : } break;
635 1 : case PVModel::TRNSYS: {
636 1 : ThisParamObj = Util::FindItemInList(state.dataPhotovoltaic->PVarray(PVnum).PerfObjName, tmpTRNSYSModuleParams);
637 1 : if (ThisParamObj > 0) {
638 1 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule = tmpTRNSYSModuleParams(ThisParamObj); // entire structure assignment
639 : } else {
640 0 : ShowSevereError(state, format("Invalid PV performance object name of {}", state.dataPhotovoltaic->PVarray(PVnum).PerfObjName));
641 0 : ShowContinueError(state, format("Entered in {} = {}", cPVGeneratorObjectName, state.dataPhotovoltaic->PVarray(PVnum).Name));
642 0 : ErrorsFound = true;
643 : }
644 1 : } break;
645 0 : case PVModel::Sandia: {
646 : ThisParamObj =
647 0 : Util::FindItemInList(state.dataPhotovoltaic->PVarray(PVnum).PerfObjName, tmpSNLModuleParams, &SNLModuleParamsStuct::name);
648 0 : if (ThisParamObj > 0) {
649 0 : state.dataPhotovoltaic->PVarray(PVnum).SNLPVModule = tmpSNLModuleParams(ThisParamObj); // entire structure assignment
650 : } else {
651 0 : ShowSevereError(state, format("Invalid PV performance object name of {}", state.dataPhotovoltaic->PVarray(PVnum).PerfObjName));
652 0 : ShowContinueError(state, format("Entered in {} = {}", cPVGeneratorObjectName, state.dataPhotovoltaic->PVarray(PVnum).Name));
653 0 : ErrorsFound = true;
654 : }
655 0 : } break;
656 0 : default:
657 0 : break;
658 : }
659 :
660 : // set up report variables CurrentModuleObject='Photovoltaics'
661 4 : SetupOutputVariable(state,
662 : "Generator Produced DC Electricity Rate",
663 : Constant::Units::W,
664 2 : state.dataPhotovoltaic->PVarray(PVnum).Report.DCPower,
665 : OutputProcessor::TimeStepType::System,
666 : OutputProcessor::StoreType::Average,
667 2 : state.dataPhotovoltaic->PVarray(PVnum).Name);
668 4 : SetupOutputVariable(state,
669 : "Generator Produced DC Electricity Energy",
670 : Constant::Units::J,
671 2 : state.dataPhotovoltaic->PVarray(PVnum).Report.DCEnergy,
672 : OutputProcessor::TimeStepType::System,
673 : OutputProcessor::StoreType::Sum,
674 2 : state.dataPhotovoltaic->PVarray(PVnum).Name,
675 : Constant::eResource::ElectricityProduced,
676 : OutputProcessor::Group::Plant,
677 : OutputProcessor::EndUseCat::Photovoltaic);
678 4 : SetupOutputVariable(state,
679 : "Generator PV Array Efficiency",
680 : Constant::Units::None,
681 2 : state.dataPhotovoltaic->PVarray(PVnum).Report.ArrayEfficiency,
682 : OutputProcessor::TimeStepType::System,
683 : OutputProcessor::StoreType::Average,
684 2 : state.dataPhotovoltaic->PVarray(PVnum).Name);
685 :
686 : // CurrentModuleObject='Equiv1Diode or Sandia Photovoltaics'
687 3 : if ((state.dataPhotovoltaic->PVarray(PVnum).PVModelType == PVModel::TRNSYS) ||
688 1 : (state.dataPhotovoltaic->PVarray(PVnum).PVModelType == PVModel::Sandia)) {
689 2 : SetupOutputVariable(state,
690 : "Generator PV Cell Temperature",
691 : Constant::Units::C,
692 1 : state.dataPhotovoltaic->PVarray(PVnum).Report.CellTemp,
693 : OutputProcessor::TimeStepType::System,
694 : OutputProcessor::StoreType::Average,
695 1 : state.dataPhotovoltaic->PVarray(PVnum).Name);
696 2 : SetupOutputVariable(state,
697 : "Generator PV Short Circuit Current",
698 : Constant::Units::A,
699 1 : state.dataPhotovoltaic->PVarray(PVnum).Report.ArrayIsc,
700 : OutputProcessor::TimeStepType::System,
701 : OutputProcessor::StoreType::Average,
702 1 : state.dataPhotovoltaic->PVarray(PVnum).Name);
703 2 : SetupOutputVariable(state,
704 : "Generator PV Open Circuit Voltage",
705 : Constant::Units::V,
706 1 : state.dataPhotovoltaic->PVarray(PVnum).Report.ArrayVoc,
707 : OutputProcessor::TimeStepType::System,
708 : OutputProcessor::StoreType::Average,
709 1 : state.dataPhotovoltaic->PVarray(PVnum).Name);
710 : }
711 :
712 : // do some checks and setup
713 2 : if (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode == CellIntegration::SurfaceOutsideFace) {
714 : // check that surface is HeatTransfer and a Construction with Internal Source was used
715 0 : if (!state.dataSurface->Surface(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr).HeatTransSurf) {
716 0 : ShowSevereError(state,
717 0 : format("Must use a surface with heat transfer for IntegratedSurfaceOutsideFace mode in {}",
718 0 : state.dataPhotovoltaic->PVarray(PVnum).Name));
719 0 : ErrorsFound = true;
720 0 : } else if (!state.dataConstruction
721 0 : ->Construct(state.dataSurface->Surface(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr).Construction)
722 0 : .SourceSinkPresent) {
723 0 : ShowSevereError(state,
724 0 : format("Must use a surface with internal source construction for IntegratedSurfaceOutsideFace mode in {}",
725 0 : state.dataPhotovoltaic->PVarray(PVnum).Name));
726 0 : ErrorsFound = true;
727 : }
728 : }
729 :
730 2 : if (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode == CellIntegration::TranspiredCollector) {
731 0 : GetTranspiredCollectorIndex(state, state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr, state.dataPhotovoltaic->PVarray(PVnum).UTSCPtr);
732 : }
733 :
734 2 : if (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode == CellIntegration::ExteriorVentedCavity) {
735 0 : GetExtVentedCavityIndex(
736 0 : state, state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr, state.dataPhotovoltaic->PVarray(PVnum).ExtVentCavPtr);
737 : }
738 :
739 2 : if (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode == CellIntegration::PVTSolarCollector) {
740 2 : state.dataPhotovoltaic->PVarray(PVnum).PVTPtr = GetPVTmodelIndex(state, state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr);
741 : }
742 : }
743 :
744 2 : if (ErrorsFound) {
745 0 : ShowFatalError(state, "Errors found in getting photovoltaic input");
746 : }
747 2 : }
748 :
749 5 : int GetPVZone(EnergyPlusData &state, int const SurfNum)
750 : {
751 : // SUBROUTINE INFORMATION:
752 : // AUTHOR Rick Strand
753 : // DATE WRITTEN Sept 2017
754 :
755 : // PURPOSE OF THIS SUBROUTINE:
756 : // Get the zone number for this PV array for use when zone multipliers are applied
757 :
758 5 : int GetPVZone(0);
759 :
760 5 : if (SurfNum > 0) {
761 5 : GetPVZone = state.dataSurface->Surface(SurfNum).Zone;
762 5 : if (GetPVZone == 0) { // might need to get the zone number from the name
763 3 : GetPVZone = Util::FindItemInList(state.dataSurface->Surface(SurfNum).ZoneName, state.dataHeatBal->Zone, state.dataGlobal->NumOfZones);
764 : }
765 : }
766 :
767 5 : return GetPVZone;
768 : }
769 :
770 : // **************************************
771 :
772 0 : void CalcSimplePV(EnergyPlusData &state, int const thisPV)
773 : {
774 :
775 : // SUBROUTINE INFORMATION:
776 : // AUTHOR B. Griffith
777 : // DATE WRITTEN Jan. 2004
778 : // MODIFIED B. Griffith, Aug. 2008
779 : // RE-ENGINEERED na
780 :
781 : // PURPOSE OF THIS SUBROUTINE:
782 : // calculate the electricity production using a simple PV model
783 :
784 : // Using/Aliasing
785 0 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
786 :
787 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
788 : int ThisSurf; // working index ptr to Surface arrays
789 : Real64 Eff; // working variable for solar electric efficiency
790 :
791 0 : ThisSurf = state.dataPhotovoltaic->PVarray(thisPV).SurfacePtr;
792 :
793 0 : if (state.dataHeatBal->SurfQRadSWOutIncident(ThisSurf) > DataPhotovoltaics::MinIrradiance) {
794 :
795 : // get efficiency
796 0 : switch (state.dataPhotovoltaic->PVarray(thisPV).SimplePVModule.EfficencyInputMode) {
797 0 : case Efficiency::Fixed: {
798 0 : Eff = state.dataPhotovoltaic->PVarray(thisPV).SimplePVModule.PVEfficiency;
799 0 : } break;
800 0 : case Efficiency::Scheduled: { // get from schedule
801 0 : Eff = state.dataPhotovoltaic->PVarray(thisPV).SimplePVModule.effSched->getCurrentVal();
802 0 : state.dataPhotovoltaic->PVarray(thisPV).SimplePVModule.PVEfficiency = Eff;
803 0 : } break;
804 0 : default: {
805 0 : Eff = 0.0; // Suppress uninitialized warning
806 0 : ShowSevereError(state, "caught bad Mode in Generator:Photovoltaic:Simple use FIXED or SCHEDULED efficiency mode");
807 0 : } break;
808 : }
809 :
810 0 : state.dataPhotovoltaic->PVarray(thisPV).Report.DCPower =
811 0 : state.dataPhotovoltaic->PVarray(thisPV).SimplePVModule.AreaCol * Eff *
812 0 : state.dataHeatBal->SurfQRadSWOutIncident(
813 : ThisSurf); // active solar cellsurface net area | solar conversion efficiency | solar incident
814 :
815 : // store sink term in appropriate place for surface heat transfer integration
816 0 : state.dataPhotovoltaic->PVarray(thisPV).SurfaceSink = state.dataPhotovoltaic->PVarray(thisPV).Report.DCPower;
817 :
818 : // array energy, power * timestep
819 0 : state.dataPhotovoltaic->PVarray(thisPV).Report.DCEnergy = state.dataPhotovoltaic->PVarray(thisPV).Report.DCPower * TimeStepSysSec;
820 0 : state.dataPhotovoltaic->PVarray(thisPV).Report.ArrayEfficiency = Eff;
821 : } else { // not enough incident solar, zero things out
822 :
823 0 : state.dataPhotovoltaic->PVarray(thisPV).SurfaceSink = 0.0;
824 0 : state.dataPhotovoltaic->PVarray(thisPV).Report.DCEnergy = 0.0;
825 0 : state.dataPhotovoltaic->PVarray(thisPV).Report.DCPower = 0.0;
826 0 : state.dataPhotovoltaic->PVarray(thisPV).Report.ArrayEfficiency = 0.0;
827 : }
828 0 : }
829 :
830 3 : void ReportPV(EnergyPlusData &state, int const PVnum)
831 : {
832 :
833 : // SUBROUTINE INFORMATION:
834 : // AUTHOR B. Griffith
835 : // DATE WRITTEN Jan. 2004
836 : // MODIFIED B. Griffith, Aug. 2008
837 :
838 : // PURPOSE OF THIS SUBROUTINE:
839 : // collect statements that assign to variables tied to output variables
840 :
841 : // Using/Aliasing
842 3 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
843 : using TranspiredCollector::SetUTSCQdotSource;
844 :
845 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
846 : int thisZone; // working index for zones
847 :
848 3 : state.dataPhotovoltaic->PVarray(PVnum).Report.DCEnergy = state.dataPhotovoltaic->PVarray(PVnum).Report.DCPower * TimeStepSysSec;
849 :
850 : // add check for multiplier. if surface is attached to a zone that is on a multiplier
851 : // then PV production should be multiplied out as well
852 :
853 3 : thisZone = state.dataPhotovoltaic->PVarray(PVnum).Zone;
854 3 : if (thisZone != 0) { // might need to apply multiplier
855 2 : state.dataPhotovoltaic->PVarray(PVnum).Report.DCEnergy *=
856 2 : (state.dataHeatBal->Zone(thisZone).Multiplier * state.dataHeatBal->Zone(thisZone).ListMultiplier);
857 2 : state.dataPhotovoltaic->PVarray(PVnum).Report.DCPower *=
858 2 : (state.dataHeatBal->Zone(thisZone).Multiplier * state.dataHeatBal->Zone(thisZone).ListMultiplier);
859 : }
860 :
861 3 : switch (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode) {
862 : // SurfaceSink is not multiplied...
863 0 : case CellIntegration::SurfaceOutsideFace: {
864 0 : state.dataHeatBalFanSys->QPVSysSource(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) =
865 0 : -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink;
866 0 : } break;
867 0 : case CellIntegration::TranspiredCollector: {
868 0 : SetUTSCQdotSource(state, state.dataPhotovoltaic->PVarray(PVnum).UTSCPtr, -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink);
869 0 : } break;
870 0 : case CellIntegration::ExteriorVentedCavity: {
871 0 : SetVentedModuleQdotSource(
872 0 : state, state.dataPhotovoltaic->PVarray(PVnum).ExtVentCavPtr, -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink);
873 0 : } break;
874 0 : case CellIntegration::PVTSolarCollector: {
875 0 : PhotovoltaicThermalCollectors::SetPVTQdotSource(
876 0 : state, state.dataPhotovoltaic->PVarray(PVnum).PVTPtr, -1.0 * state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink);
877 0 : } break;
878 3 : default:
879 3 : break;
880 : }
881 3 : }
882 :
883 : // *************
884 :
885 0 : void CalcSandiaPV(EnergyPlusData &state,
886 : int const PVnum, // ptr to current PV system
887 : bool const RunFlag // controls if generator is scheduled *ON*
888 : )
889 : {
890 :
891 : // SUBROUTINE INFORMATION:
892 : // AUTHOR B. Griffith , (derived from Greg Barker's TRNSYS type101 for SANDIA PV model)
893 : // DATE WRITTEN Jan 2004
894 : // MODIFIED B. Griffith, Aug. 2008 reworked for new, single-PV-generator data structure
895 : // RE-ENGINEERED na
896 :
897 : // PURPOSE OF THIS SUBROUTINE:
898 : // Calculate various PV system performance indices at the current timestep
899 :
900 : // METHODOLOGY EMPLOYED:
901 : // adapted code from a set of F77 routines by G. Barker that implement the model
902 : // This routines works on a single photovoltaic object of the type 'GENERATOR:PV:SANDIA'
903 : // Each major model equation has its own function (in this module)
904 :
905 : // REFERENCES:
906 : // King, David L. . Photovoltaic module and array performance characterization methods for all
907 : // system operating conditions. Pro. NREL/SNL Photovoltaics Program Review, AIP Press, Lakewood CO
908 : // Sandia National Laboratories
909 :
910 : // Davis, M.W., A.H. Fanney, and B.P. Dougherty. Measured versus predicted performance of Building
911 : // integrated photovoltaics. Solar 2002, Sunrise on the Reliable Energy Economy, June 15-19, 2002 Reno, NV
912 :
913 : // Using/Aliasing
914 : using PhotovoltaicThermalCollectors::GetPVTTsColl;
915 : using TranspiredCollector::GetUTSCTsColl;
916 :
917 : int ThisSurf; // working variable for indexing surfaces
918 : Real64 Ee;
919 :
920 0 : ThisSurf = state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr;
921 0 : auto &thisPVarray = state.dataPhotovoltaic->PVarray(PVnum);
922 :
923 : // get input from elsewhere in Energyplus for the current point in the simulation
924 0 : thisPVarray.SNLPVinto.IcBeam = state.dataHeatBal->SurfQRadSWOutIncidentBeam(ThisSurf); //(W/m2)from DataHeatBalance
925 0 : thisPVarray.SNLPVinto.IcDiffuse =
926 0 : state.dataHeatBal->SurfQRadSWOutIncident(ThisSurf) - state.dataHeatBal->SurfQRadSWOutIncidentBeam(ThisSurf); //(W/ m2)(was kJ/hr m2)
927 0 : thisPVarray.SNLPVinto.IncidenceAngle =
928 0 : std::acos(state.dataHeatBal->SurfCosIncidenceAngle(ThisSurf)) / Constant::DegToRad; // (deg) from dataHeatBalance
929 0 : thisPVarray.SNLPVinto.ZenithAngle = std::acos(state.dataEnvrn->SOLCOS(3)) / Constant::DegToRad; //(degrees),
930 0 : thisPVarray.SNLPVinto.Tamb = state.dataSurface->SurfOutDryBulbTemp(ThisSurf); //(deg. C)
931 0 : thisPVarray.SNLPVinto.WindSpeed = state.dataSurface->SurfOutWindSpeed(ThisSurf); // (m/s)
932 0 : thisPVarray.SNLPVinto.Altitude = state.dataEnvrn->Elevation; // from DataEnvironment via USE
933 :
934 0 : if (((thisPVarray.SNLPVinto.IcBeam + thisPVarray.SNLPVinto.IcDiffuse) > DataPhotovoltaics::MinIrradiance) && (RunFlag)) {
935 :
936 : // first determine PV cell temperatures depending on model
937 0 : switch (thisPVarray.CellIntegrationMode) {
938 0 : case CellIntegration::Decoupled: { // Sandia module temperature model for rack mounted PVs
939 : // Calculate back-of-module temperature:
940 0 : thisPVarray.SNLPVCalc.Tback = SandiaModuleTemperature(thisPVarray.SNLPVinto.IcBeam,
941 : thisPVarray.SNLPVinto.IcDiffuse,
942 : thisPVarray.SNLPVinto.WindSpeed,
943 : thisPVarray.SNLPVinto.Tamb,
944 : thisPVarray.SNLPVModule.fd,
945 : thisPVarray.SNLPVModule.a,
946 : thisPVarray.SNLPVModule.b);
947 :
948 : // Calculate cell temperature:
949 0 : thisPVarray.SNLPVCalc.Tcell = SandiaTcellFromTmodule(thisPVarray.SNLPVCalc.Tback,
950 : thisPVarray.SNLPVinto.IcBeam,
951 : thisPVarray.SNLPVinto.IcDiffuse,
952 : thisPVarray.SNLPVModule.fd,
953 : thisPVarray.SNLPVModule.DT0);
954 :
955 0 : } break;
956 0 : case CellIntegration::SurfaceOutsideFace: {
957 : // get back-of-module temperature from elsewhere in EnergyPlus
958 0 : thisPVarray.SNLPVCalc.Tback = state.dataHeatBalSurf->SurfTempOut(thisPVarray.SurfacePtr);
959 :
960 0 : thisPVarray.SNLPVCalc.Tcell = SandiaTcellFromTmodule(thisPVarray.SNLPVCalc.Tback,
961 : thisPVarray.SNLPVinto.IcBeam,
962 : thisPVarray.SNLPVinto.IcDiffuse,
963 : thisPVarray.SNLPVModule.fd,
964 : thisPVarray.SNLPVModule.DT0);
965 :
966 0 : } break;
967 0 : case CellIntegration::TranspiredCollector: {
968 0 : GetUTSCTsColl(state, thisPVarray.UTSCPtr, thisPVarray.SNLPVCalc.Tback);
969 :
970 0 : thisPVarray.SNLPVCalc.Tcell = SandiaTcellFromTmodule(thisPVarray.SNLPVCalc.Tback,
971 : thisPVarray.SNLPVinto.IcBeam,
972 : thisPVarray.SNLPVinto.IcDiffuse,
973 : thisPVarray.SNLPVModule.fd,
974 : thisPVarray.SNLPVModule.DT0);
975 :
976 0 : } break;
977 0 : case CellIntegration::ExteriorVentedCavity: {
978 0 : GetExtVentedCavityTsColl(state, thisPVarray.ExtVentCavPtr, thisPVarray.SNLPVCalc.Tback);
979 :
980 0 : thisPVarray.SNLPVCalc.Tcell = SandiaTcellFromTmodule(thisPVarray.SNLPVCalc.Tback,
981 : thisPVarray.SNLPVinto.IcBeam,
982 : thisPVarray.SNLPVinto.IcDiffuse,
983 : thisPVarray.SNLPVModule.fd,
984 : thisPVarray.SNLPVModule.DT0);
985 :
986 0 : } break;
987 0 : case CellIntegration::PVTSolarCollector: {
988 0 : GetPVTTsColl(state, thisPVarray.PVTPtr, thisPVarray.SNLPVCalc.Tback);
989 0 : thisPVarray.SNLPVCalc.Tcell = SandiaTcellFromTmodule(thisPVarray.SNLPVCalc.Tback,
990 : thisPVarray.SNLPVinto.IcBeam,
991 : thisPVarray.SNLPVinto.IcDiffuse,
992 : thisPVarray.SNLPVModule.fd,
993 : thisPVarray.SNLPVModule.DT0);
994 0 : } break;
995 0 : default: {
996 0 : ShowSevereError(state, format("Sandia PV Simulation Temperature Modeling Mode Error in {}", thisPVarray.Name));
997 0 : } break;
998 : }
999 :
1000 : // Calculate Air Mass function
1001 0 : thisPVarray.SNLPVCalc.AMa = AbsoluteAirMass(thisPVarray.SNLPVinto.ZenithAngle, thisPVarray.SNLPVinto.Altitude);
1002 :
1003 : // Calculate F1 polynomial function:
1004 0 : thisPVarray.SNLPVCalc.F1 = SandiaF1(thisPVarray.SNLPVCalc.AMa,
1005 : thisPVarray.SNLPVModule.a_0,
1006 : thisPVarray.SNLPVModule.a_1,
1007 : thisPVarray.SNLPVModule.a_2,
1008 : thisPVarray.SNLPVModule.a_3,
1009 : thisPVarray.SNLPVModule.a_4);
1010 :
1011 : // Calculate F2 polynomial function:
1012 0 : thisPVarray.SNLPVCalc.F2 = SandiaF2(thisPVarray.SNLPVinto.IncidenceAngle,
1013 : thisPVarray.SNLPVModule.b_0,
1014 : thisPVarray.SNLPVModule.b_1,
1015 : thisPVarray.SNLPVModule.b_2,
1016 : thisPVarray.SNLPVModule.b_3,
1017 : thisPVarray.SNLPVModule.b_4,
1018 : thisPVarray.SNLPVModule.b_5);
1019 :
1020 : // Calculate short-circuit current function:
1021 0 : thisPVarray.SNLPVCalc.Isc = SandiaIsc(thisPVarray.SNLPVCalc.Tcell,
1022 : thisPVarray.SNLPVModule.Isc0,
1023 : thisPVarray.SNLPVinto.IcBeam,
1024 : thisPVarray.SNLPVinto.IcDiffuse,
1025 : thisPVarray.SNLPVCalc.F1,
1026 : thisPVarray.SNLPVCalc.F2,
1027 : thisPVarray.SNLPVModule.fd,
1028 : thisPVarray.SNLPVModule.aIsc);
1029 :
1030 : // Calculate effective irradiance function:
1031 0 : Ee = SandiaEffectiveIrradiance(
1032 : thisPVarray.SNLPVCalc.Tcell, thisPVarray.SNLPVCalc.Isc, thisPVarray.SNLPVModule.Isc0, thisPVarray.SNLPVModule.aIsc);
1033 : // Calculate Imp function:
1034 0 : thisPVarray.SNLPVCalc.Imp = SandiaImp(thisPVarray.SNLPVCalc.Tcell,
1035 : Ee,
1036 : thisPVarray.SNLPVModule.Imp0,
1037 : thisPVarray.SNLPVModule.aImp,
1038 : thisPVarray.SNLPVModule.c_0,
1039 : thisPVarray.SNLPVModule.c_1);
1040 :
1041 : // Calculate Voc function:
1042 0 : thisPVarray.SNLPVCalc.Voc = SandiaVoc(thisPVarray.SNLPVCalc.Tcell,
1043 : Ee,
1044 : thisPVarray.SNLPVModule.Voc0,
1045 : thisPVarray.SNLPVModule.NcellSer,
1046 : thisPVarray.SNLPVModule.DiodeFactor,
1047 : thisPVarray.SNLPVModule.BVoc0,
1048 : thisPVarray.SNLPVModule.mBVoc);
1049 :
1050 : // Calculate Vmp: voltage at maximum powerpoint
1051 0 : thisPVarray.SNLPVCalc.Vmp = SandiaVmp(thisPVarray.SNLPVCalc.Tcell,
1052 : Ee,
1053 : thisPVarray.SNLPVModule.Vmp0,
1054 : thisPVarray.SNLPVModule.NcellSer,
1055 : thisPVarray.SNLPVModule.DiodeFactor,
1056 : thisPVarray.SNLPVModule.BVmp0,
1057 : thisPVarray.SNLPVModule.mBVmp,
1058 : thisPVarray.SNLPVModule.c_2,
1059 : thisPVarray.SNLPVModule.c_3);
1060 :
1061 : // Calculate Ix function:
1062 0 : thisPVarray.SNLPVCalc.Ix = SandiaIx(thisPVarray.SNLPVCalc.Tcell,
1063 : Ee,
1064 : thisPVarray.SNLPVModule.Ix0,
1065 : thisPVarray.SNLPVModule.aIsc,
1066 : thisPVarray.SNLPVModule.aImp,
1067 : thisPVarray.SNLPVModule.c_4,
1068 : thisPVarray.SNLPVModule.c_5);
1069 :
1070 : // Calculate Vx function:
1071 0 : thisPVarray.SNLPVCalc.Vx = thisPVarray.SNLPVCalc.Voc / 2.0;
1072 :
1073 : // Calculate Ixx function:
1074 0 : thisPVarray.SNLPVCalc.Ixx = SandiaIxx(thisPVarray.SNLPVCalc.Tcell,
1075 : Ee,
1076 : thisPVarray.SNLPVModule.Ixx0,
1077 : thisPVarray.SNLPVModule.aImp,
1078 : thisPVarray.SNLPVModule.c_6,
1079 : thisPVarray.SNLPVModule.c_7);
1080 : // Calculate Vxx :
1081 0 : thisPVarray.SNLPVCalc.Vxx = 0.5 * (thisPVarray.SNLPVCalc.Voc + thisPVarray.SNLPVCalc.Vmp);
1082 :
1083 : // Calculate Pmp, single module: power at maximum powerpoint
1084 0 : thisPVarray.SNLPVCalc.Pmp = thisPVarray.SNLPVCalc.Imp * thisPVarray.SNLPVCalc.Vmp; // W
1085 :
1086 : // Calculate PV efficiency at maximum power point
1087 0 : thisPVarray.SNLPVCalc.EffMax =
1088 0 : thisPVarray.SNLPVCalc.Pmp / (thisPVarray.SNLPVinto.IcBeam + thisPVarray.SNLPVinto.IcDiffuse) / thisPVarray.SNLPVModule.Acoll;
1089 :
1090 : // Scale to NumStrings and NumSeries:
1091 0 : thisPVarray.SNLPVCalc.Pmp *= thisPVarray.NumSeriesNParall * thisPVarray.NumModNSeries;
1092 0 : thisPVarray.SNLPVCalc.Imp *= thisPVarray.NumModNSeries;
1093 0 : thisPVarray.SNLPVCalc.Vmp *= thisPVarray.NumModNSeries;
1094 0 : thisPVarray.SNLPVCalc.Isc *= thisPVarray.NumSeriesNParall;
1095 0 : thisPVarray.SNLPVCalc.Voc *= thisPVarray.NumModNSeries;
1096 0 : thisPVarray.SNLPVCalc.Ix *= thisPVarray.NumSeriesNParall;
1097 0 : thisPVarray.SNLPVCalc.Ixx *= thisPVarray.NumSeriesNParall;
1098 0 : thisPVarray.SNLPVCalc.Vx *= thisPVarray.NumModNSeries;
1099 0 : thisPVarray.SNLPVCalc.Vxx *= thisPVarray.NumModNSeries;
1100 0 : thisPVarray.SNLPVCalc.SurfaceSink = thisPVarray.SNLPVCalc.Pmp;
1101 : } else { // Ibeam+Idiff < MaxIrradiance or not RunFlag
1102 : // so zero things.
1103 0 : thisPVarray.SNLPVCalc.Vmp = 0.0;
1104 0 : thisPVarray.SNLPVCalc.Imp = 0.0;
1105 0 : thisPVarray.SNLPVCalc.Pmp = 0.0;
1106 0 : thisPVarray.SNLPVCalc.EffMax = 0.0;
1107 0 : thisPVarray.SNLPVCalc.Isc = 0.0;
1108 0 : thisPVarray.SNLPVCalc.Voc = 0.0;
1109 0 : thisPVarray.SNLPVCalc.Tcell = thisPVarray.SNLPVinto.Tamb;
1110 0 : thisPVarray.SNLPVCalc.Tback = thisPVarray.SNLPVinto.Tamb;
1111 0 : thisPVarray.SNLPVCalc.AMa = 999.0;
1112 0 : thisPVarray.SNLPVCalc.F1 = 0.0;
1113 0 : thisPVarray.SNLPVCalc.F2 = 0.0;
1114 0 : thisPVarray.SNLPVCalc.Ix = 0.0;
1115 0 : thisPVarray.SNLPVCalc.Vx = 0.0;
1116 0 : thisPVarray.SNLPVCalc.Ixx = 0.0;
1117 0 : thisPVarray.SNLPVCalc.Vxx = 0.0;
1118 0 : thisPVarray.SNLPVCalc.SurfaceSink = 0.0;
1119 : } // Ibeam+Idiff > MinIrradiance and runflag
1120 :
1121 : // update calculations to report variables
1122 0 : thisPVarray.Report.DCPower = thisPVarray.SNLPVCalc.Pmp;
1123 0 : thisPVarray.Report.ArrayIsc = thisPVarray.SNLPVCalc.Isc;
1124 0 : thisPVarray.Report.ArrayVoc = thisPVarray.SNLPVCalc.Voc;
1125 0 : thisPVarray.Report.CellTemp = thisPVarray.SNLPVCalc.Tcell;
1126 0 : thisPVarray.Report.ArrayEfficiency = thisPVarray.SNLPVCalc.EffMax;
1127 0 : thisPVarray.SurfaceSink = thisPVarray.SNLPVCalc.SurfaceSink;
1128 0 : }
1129 :
1130 : // ********************
1131 : // begin routines for Equivalent one-diode model by Bradley/Ulleberg
1132 :
1133 0 : void InitTRNSYSPV(EnergyPlusData &state, int const PVnum) // the number of the GENERATOR:PHOTOVOLTAICS (passed in)
1134 : {
1135 :
1136 : // SUBROUTINE INFORMATION:
1137 : // AUTHOR David Bradley
1138 : // DATE WRITTEN April 2003
1139 : // MODIFIED BG March 2007 reworked for CR7109 (reverse DD testing)
1140 : // B. Griffith, Aug. 2008 reworked for new, single-PV-generator data structure
1141 : // RE-ENGINEERED na
1142 :
1143 : // PURPOSE OF THIS SUBROUTINE:
1144 : // This subroutine initializes the PV arrays during simulation. It performs both start of
1145 : // simulation initializations and start of timestep initializations. The structure of the
1146 : // subroutine was taken from InitBaseboard.
1147 :
1148 : // Using/Aliasing
1149 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1150 : Real64 TimeElapsed; // Fraction of the current hour that has elapsed (h)
1151 :
1152 : // perform the one time initializations
1153 0 : if (state.dataPhotovoltaicState->MyOneTimeFlag) {
1154 : // initialize the environment and sizing flags
1155 0 : state.dataPhotovoltaicState->MyEnvrnFlag.dimension(state.dataPhotovoltaic->NumPVs, true);
1156 0 : state.dataPhotovoltaicState->MyOneTimeFlag = false;
1157 : }
1158 :
1159 : // Do the Begin Environment initializations
1160 0 : if (state.dataGlobal->BeginEnvrnFlag && state.dataPhotovoltaicState->MyEnvrnFlag(PVnum)) {
1161 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.CellTempK =
1162 0 : state.dataSurface->SurfOutDryBulbTemp(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) + Constant::Kelvin;
1163 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.LastCellTempK =
1164 0 : state.dataSurface->SurfOutDryBulbTemp(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) + Constant::Kelvin;
1165 0 : state.dataPhotovoltaicState->MyEnvrnFlag(PVnum) = false;
1166 : }
1167 :
1168 0 : if (!state.dataGlobal->BeginEnvrnFlag) {
1169 0 : state.dataPhotovoltaicState->MyEnvrnFlag(PVnum) = true;
1170 : }
1171 :
1172 : // Do the beginning of every time step initializations
1173 0 : TimeElapsed =
1174 0 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
1175 0 : if (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.TimeElapsed != TimeElapsed) {
1176 : // The simulation has advanced to the next system timestep. Save conditions from the end of the previous system
1177 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.LastCellTempK = state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.CellTempK;
1178 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.TimeElapsed = TimeElapsed;
1179 : }
1180 :
1181 0 : if (any_gt(state.dataHeatBal->SurfQRadSWOutIncident, 0.0)) {
1182 : // Determine the amount of radiation incident on each PV
1183 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation =
1184 0 : state.dataHeatBal->SurfQRadSWOutIncident(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr); //[W/m2]
1185 : } else {
1186 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation = 0.0;
1187 : }
1188 0 : }
1189 :
1190 : // *************
1191 :
1192 0 : void CalcTRNSYSPV(EnergyPlusData &state,
1193 : int const PVnum, // BTG added intent
1194 : bool const RunFlag // BTG added intent !flag tells whether the PV is ON or OFF
1195 : )
1196 : {
1197 :
1198 : // SUBROUTINE INFORMATION:
1199 : // AUTHOR D. Bradley
1200 : // DATE WRITTEN April 2003
1201 : // MODIFIED B. Griffith, February 2008-- added support for inverter
1202 : // multipliers, and building integrated heat transfer
1203 : // B. Griffith, Aug. 2008 reworked for new, single-PV-generator data structure
1204 : // RE-ENGINEERED na
1205 :
1206 : // PURPOSE OF THIS SUBROUTINE:
1207 : // This subroutine simulates the PV performance.
1208 :
1209 : using PhotovoltaicThermalCollectors::GetPVTTsColl;
1210 : using TranspiredCollector::GetUTSCTsColl;
1211 :
1212 0 : Real64 constexpr EPS(0.001);
1213 0 : Real64 constexpr ERR(0.001);
1214 0 : Real64 constexpr MinInsolation(30.0);
1215 0 : int constexpr KMAX(100);
1216 0 : Real64 constexpr EtaIni(0.10); // initial value of eta
1217 : Real64 DummyErr;
1218 : Real64 ETA;
1219 : Real64 Tambient;
1220 : Real64 EtaOld;
1221 : Real64 ILRef;
1222 : Real64 AARef;
1223 : Real64 IORef;
1224 : Real64 SeriesResistance;
1225 : Real64 IL;
1226 : Real64 AA;
1227 : Real64 IO;
1228 : Real64 ISCG1;
1229 : Real64 ISC;
1230 : Real64 VOCG1;
1231 : Real64 VOC;
1232 : Real64 VLEFT;
1233 : Real64 VRIGHT;
1234 : Real64 VM;
1235 : Real64 IM;
1236 : Real64 PM;
1237 : Real64 IA;
1238 : Real64 ISCA;
1239 : Real64 VA;
1240 : Real64 VOCA;
1241 : Real64 PA;
1242 0 : Real64 CellTemp(0.0); // cell temperature in Kelvin
1243 : Real64 CellTempC; // cell temperature in degrees C
1244 :
1245 : // if the cell temperature mode is 2, convert the timestep to seconds
1246 0 : if (state.dataPhotovoltaicState->firstTime &&
1247 0 : state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode == CellIntegration::DecoupledUllebergDynamic) {
1248 0 : state.dataPhotovoltaicState->PVTimeStep = double(state.dataGlobal->MinutesInTimeStep) * 60.0; // Seconds per time step
1249 : }
1250 0 : state.dataPhotovoltaicState->firstTime = false;
1251 :
1252 : // place the shunt resistance into its common block
1253 0 : state.dataPhotovoltaic->ShuntResistance = state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.ShuntResistance;
1254 :
1255 : // convert ambient temperature from C to K
1256 0 : Tambient = state.dataSurface->SurfOutDryBulbTemp(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) + Constant::Kelvin;
1257 :
1258 0 : auto const &thisPVarray = state.dataPhotovoltaic->PVarray(PVnum);
1259 :
1260 0 : if ((state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation > MinInsolation) && (RunFlag)) {
1261 :
1262 : // set initial values for eta iteration loop
1263 0 : DummyErr = 2.0 * ERR;
1264 0 : EtaOld = EtaIni;
1265 : int K;
1266 :
1267 : // Begin DO WHILE loop - until the error tolerance is reached.
1268 0 : ETA = 0.0;
1269 0 : while (DummyErr > ERR) {
1270 0 : switch (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode) {
1271 0 : case CellIntegration::Decoupled: {
1272 : // cell temperature based on energy balance
1273 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatLossCoef =
1274 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TauAlpha *
1275 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.NOCTInsolation /
1276 0 : (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.NOCTCellTemp -
1277 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.NOCTAmbTemp);
1278 0 : CellTemp = Tambient + (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation *
1279 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TauAlpha /
1280 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatLossCoef) *
1281 0 : (1.0 - ETA / state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TauAlpha);
1282 0 : } break;
1283 0 : case CellIntegration::DecoupledUllebergDynamic: {
1284 : // cell temperature based on energy balance with thermal capacity effects
1285 0 : CellTemp =
1286 0 : Tambient +
1287 0 : (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.LastCellTempK - Tambient) *
1288 0 : std::exp(-state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatLossCoef /
1289 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatCapacity * state.dataPhotovoltaicState->PVTimeStep) +
1290 0 : (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TauAlpha - ETA) *
1291 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation /
1292 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatLossCoef *
1293 0 : (1.0 -
1294 0 : std::exp(-state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatLossCoef /
1295 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatCapacity * state.dataPhotovoltaicState->PVTimeStep));
1296 0 : } break;
1297 0 : case CellIntegration::SurfaceOutsideFace: {
1298 0 : CellTemp = state.dataHeatBalSurf->SurfTempOut(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) + Constant::Kelvin;
1299 0 : } break;
1300 0 : case CellIntegration::TranspiredCollector: {
1301 0 : GetUTSCTsColl(state, state.dataPhotovoltaic->PVarray(PVnum).UTSCPtr, CellTemp);
1302 0 : CellTemp += Constant::Kelvin;
1303 0 : } break;
1304 0 : case CellIntegration::ExteriorVentedCavity: {
1305 0 : GetExtVentedCavityTsColl(state, state.dataPhotovoltaic->PVarray(PVnum).ExtVentCavPtr, CellTemp);
1306 0 : CellTemp += Constant::Kelvin;
1307 0 : } break;
1308 0 : case CellIntegration::PVTSolarCollector: {
1309 0 : GetPVTTsColl(state, thisPVarray.PVTPtr, CellTemp);
1310 0 : CellTemp += Constant::Kelvin;
1311 0 : } break;
1312 0 : default:
1313 0 : break;
1314 : }
1315 :
1316 : // reference parameters
1317 0 : ILRef = state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefIsc;
1318 0 : AARef = (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TempCoefVoc *
1319 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefTemperature -
1320 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefVoc +
1321 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.SemiConductorBandgap *
1322 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.CellsInSeries) /
1323 0 : (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TempCoefIsc *
1324 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefTemperature / ILRef -
1325 : 3.0);
1326 0 : IORef = ILRef * std::exp(-state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefVoc / AARef);
1327 :
1328 : // series resistance
1329 0 : SeriesResistance =
1330 0 : (AARef * std::log(1.0 - state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.Imp / ILRef) -
1331 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.Vmp + state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefVoc) /
1332 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.Imp;
1333 :
1334 : // temperature dependence
1335 0 : IL = state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation /
1336 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefInsolation *
1337 0 : (ILRef + state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.TempCoefIsc *
1338 0 : (CellTemp - state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefTemperature));
1339 0 : Real64 const cell_temp_ratio(CellTemp / state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefTemperature);
1340 0 : AA = AARef * cell_temp_ratio;
1341 0 : IO = IORef * pow_3(cell_temp_ratio) *
1342 0 : std::exp(state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.SemiConductorBandgap *
1343 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.CellsInSeries / AARef *
1344 0 : (1.0 - state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.RefTemperature / CellTemp));
1345 :
1346 : // compute short circuit current and open circuit voltage
1347 :
1348 : // NEWTON --> ISC (STARTVALUE: ISCG1 - BASED ON IL=ISC)
1349 0 : ISCG1 = IL;
1350 0 : NEWTON(state, ISC, FUN, FI, ISC, DataPrecisionGlobals::constant_zero, IO, IL, SeriesResistance, AA, ISCG1, EPS);
1351 :
1352 : // NEWTON --> VOC (STARTVALUE: VOCG1 - BASED ON IM=0.0)
1353 0 : VOCG1 = (std::log(IL / IO) + 1.0) * AA;
1354 0 : NEWTON(state, VOC, FUN, FV, DataPrecisionGlobals::constant_zero, VOC, IO, IL, SeriesResistance, AA, VOCG1, EPS);
1355 :
1356 : // maximum power point tracking
1357 :
1358 : // SEARCH --> VM AT MAXIMUM POWER POINT
1359 0 : VLEFT = 0.0;
1360 0 : VRIGHT = VOC;
1361 0 : SEARCH(state, VLEFT, VRIGHT, VM, K, IO, IL, SeriesResistance, AA, EPS, KMAX);
1362 :
1363 : // POWER --> IM & PM AT MAXIMUM POWER POINT
1364 0 : POWER(state, IO, IL, SeriesResistance, AA, EPS, IM, VM, PM);
1365 :
1366 : // calculate overall PV module efficiency
1367 0 : ETA =
1368 0 : PM / state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation / state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.Area;
1369 0 : DummyErr = std::abs((ETA - EtaOld) / EtaOld);
1370 0 : EtaOld = ETA;
1371 :
1372 : } // while
1373 :
1374 : } else {
1375 : // if there is no incident radiation or if the control switch is 'Off'
1376 0 : switch (state.dataPhotovoltaic->PVarray(PVnum).CellIntegrationMode) {
1377 0 : case CellIntegration::Decoupled: {
1378 0 : CellTemp = Tambient;
1379 0 : } break;
1380 0 : case CellIntegration::DecoupledUllebergDynamic: {
1381 0 : CellTemp = Tambient +
1382 0 : (state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.LastCellTempK - Tambient) *
1383 0 : std::exp(-state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatLossCoef /
1384 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVModule.HeatCapacity * state.dataPhotovoltaicState->PVTimeStep);
1385 0 : } break;
1386 0 : case CellIntegration::SurfaceOutsideFace: {
1387 0 : CellTemp = state.dataHeatBalSurf->SurfTempOut(state.dataPhotovoltaic->PVarray(PVnum).SurfacePtr) + Constant::Kelvin;
1388 0 : } break;
1389 0 : case CellIntegration::TranspiredCollector: {
1390 0 : GetUTSCTsColl(state, state.dataPhotovoltaic->PVarray(PVnum).UTSCPtr, CellTemp);
1391 0 : CellTemp += Constant::Kelvin;
1392 0 : } break;
1393 0 : case CellIntegration::ExteriorVentedCavity: {
1394 0 : GetExtVentedCavityTsColl(state, state.dataPhotovoltaic->PVarray(PVnum).ExtVentCavPtr, CellTemp);
1395 0 : CellTemp += Constant::Kelvin;
1396 0 : } break;
1397 0 : case CellIntegration::PVTSolarCollector: {
1398 0 : GetPVTTsColl(state, thisPVarray.PVTPtr, CellTemp);
1399 0 : CellTemp += Constant::Kelvin;
1400 0 : } break;
1401 0 : default: {
1402 0 : assert(false);
1403 : } break;
1404 : }
1405 :
1406 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.Insolation = 0.0;
1407 0 : IM = 0.0; // module current
1408 0 : VM = 0.0; // module voltage
1409 0 : PM = 0.0; // module power
1410 0 : ETA = 0.0; // module efficiency
1411 0 : ISC = 0.0;
1412 0 : VOC = 0.0;
1413 : }
1414 :
1415 : // convert cell temperature back to C
1416 0 : CellTempC = CellTemp - Constant::Kelvin;
1417 :
1418 : // calculate array based outputs (so far, the outputs are module based
1419 0 : IA = state.dataPhotovoltaic->PVarray(PVnum).NumSeriesNParall * IM;
1420 0 : ISCA = state.dataPhotovoltaic->PVarray(PVnum).NumSeriesNParall * ISC;
1421 0 : VA = state.dataPhotovoltaic->PVarray(PVnum).NumModNSeries * VM;
1422 0 : VOCA = state.dataPhotovoltaic->PVarray(PVnum).NumModNSeries * VOC;
1423 0 : PA = IA * VA;
1424 :
1425 : // Place local variables into the reporting structure
1426 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.ArrayCurrent = IA;
1427 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.ArrayVoltage = VA;
1428 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.ArrayPower = PA;
1429 0 : state.dataPhotovoltaic->PVarray(PVnum).Report.DCPower = PA;
1430 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.ArrayEfficiency = ETA;
1431 0 : state.dataPhotovoltaic->PVarray(PVnum).Report.ArrayEfficiency = ETA;
1432 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.CellTemp = CellTempC;
1433 0 : state.dataPhotovoltaic->PVarray(PVnum).Report.CellTemp = CellTempC;
1434 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.CellTempK = CellTemp;
1435 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.ArrayIsc = ISCA;
1436 0 : state.dataPhotovoltaic->PVarray(PVnum).Report.ArrayIsc = ISCA;
1437 0 : state.dataPhotovoltaic->PVarray(PVnum).TRNSYSPVcalc.ArrayVoc = VOCA;
1438 0 : state.dataPhotovoltaic->PVarray(PVnum).Report.ArrayVoc = VOCA;
1439 0 : state.dataPhotovoltaic->PVarray(PVnum).SurfaceSink = PA;
1440 0 : }
1441 :
1442 0 : void POWER(EnergyPlusData &state,
1443 : Real64 const IO, // passed in from CalcPV
1444 : Real64 const IL, // passed in from CalcPV
1445 : Real64 const RSER, // passed in from CalcPV
1446 : Real64 const AA, // passed in from CalcPV
1447 : Real64 const EPS, // passed in from CalcPV
1448 : Real64 &II, // current [A]
1449 : Real64 const VV, // voltage [V]
1450 : Real64 &PP // power [W]
1451 : )
1452 : {
1453 :
1454 : // SUBROUTINE INFORMATION:
1455 : // AUTHOR O. Ulleberg, IFE Norway for Hydrogems
1456 : // DATE WRITTEN March 2001
1457 : // MODIFIED D. Bradley for use with EnergyPlus
1458 : // RE-ENGINEERED na
1459 :
1460 : // PURPOSE OF THIS SUBROUTINE:
1461 : // This subroutine calculates the power produced by the PV.
1462 :
1463 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1464 : Real64 IG1;
1465 :
1466 : // NEWTON --> II (STARTVALUE: IG1 BASED ON SIMPLIFIED I(I,V) EQUATION)
1467 0 : IG1 = IL - IO * std::exp(VV / AA - 1.0);
1468 0 : NEWTON(state, II, FUN, FI, II, VV, IO, IL, RSER, AA, IG1, EPS);
1469 0 : PP = II * VV;
1470 0 : }
1471 :
1472 0 : void NEWTON(EnergyPlusData &state,
1473 : Real64 &XX,
1474 : std::function<Real64(EnergyPlusData &state, Real64 const, Real64 const, Real64 const, Real64 const, Real64 const, Real64 const)> FXX,
1475 : std::function<Real64(EnergyPlusData &state, Real64 const, Real64 const, Real64 const, Real64 const, Real64 const)> DER,
1476 : Real64 const &II, // Autodesk Aliased to XX in some calls
1477 : Real64 const &VV, // Autodesk Aliased to XX in some calls
1478 : Real64 const IO,
1479 : Real64 const IL,
1480 : Real64 const RSER,
1481 : Real64 const AA,
1482 : Real64 const XS,
1483 : Real64 const EPS)
1484 : {
1485 :
1486 : // SUBROUTINE INFORMATION:
1487 : // AUTHOR O. Ulleberg, IFE Norway for Hydrogems
1488 : // DATE WRITTEN March 2001
1489 : // MODIFIED D. Bradley for use with EnergyPlus
1490 : // RE-ENGINEERED na
1491 :
1492 : // PURPOSE OF THIS SUBROUTINE:
1493 : // This subroutine uses the Newton-Raphson method to solve a non linear equation with one variable.
1494 :
1495 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1496 : int COUNT;
1497 : Real64 ERR;
1498 : Real64 X0;
1499 :
1500 0 : COUNT = 0;
1501 0 : XX = XS;
1502 0 : ERR = 1.0;
1503 0 : while ((ERR > EPS) && (COUNT <= 10)) {
1504 0 : X0 = XX;
1505 0 : XX -= FXX(state, II, VV, IL, IO, RSER, AA) / DER(state, II, VV, IO, RSER, AA);
1506 0 : ++COUNT;
1507 0 : ERR = std::abs((XX - X0) / X0);
1508 : }
1509 0 : }
1510 :
1511 0 : void SEARCH(EnergyPlusData &state,
1512 : Real64 &A,
1513 : Real64 &B,
1514 : Real64 &P,
1515 : int &K,
1516 : Real64 const IO,
1517 : Real64 const IL,
1518 : Real64 const RSER,
1519 : Real64 const AA,
1520 : Real64 const EPS,
1521 : int const KMAX)
1522 : {
1523 :
1524 : // SUBROUTINE INFORMATION:
1525 : // AUTHOR O. Ulleberg, IFE Norway for Hydrogems
1526 : // DATE WRITTEN March 2001
1527 : // MODIFIED D. Bradley for use with EnergyPlus
1528 : // RE-ENGINEERED na
1529 :
1530 : // PURPOSE OF THIS SUBROUTINE:
1531 : // This subroutine minimum of an unimodal function with one variable. The algorithm was
1532 : // adapted to find the maximum power point of a PV module. The changes to the original
1533 : // algorithm are the following:
1534 : // 1. a subroutine "POWER" is called in order to calculate the power output of the PV module
1535 : // 2. the negative of the power of the PV module is taken so that the optimum can be found.
1536 :
1537 : // REFERENCES:
1538 : // /1/ MATHEWS, JOHN H. NUMERICAL METHODS: FORTRAN PROGRAMS. 1992, PP 413.
1539 : // /2/ NUMERICAL METHODS FOR MATHEMATICS, SCIENCE AND ENGINEERING, 2ND EDITION,
1540 : // PRENTICE HALL, NEW JERSEY, 1992.
1541 :
1542 : // SUBROUTINE PARAMETER DEFINITIONS:
1543 0 : Real64 constexpr DELTA(1.e-3);
1544 0 : Real64 constexpr EPSILON(1.e-3);
1545 : static Real64 const RONE((std::sqrt(5.0) - 1.0) / 2.0);
1546 : static Real64 const RTWO(RONE * RONE);
1547 :
1548 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1549 : Real64 C;
1550 : Real64 D;
1551 : Real64 H;
1552 : Real64 YP;
1553 : Real64 YA;
1554 : Real64 YB;
1555 : Real64 YC;
1556 : Real64 YD;
1557 : Real64 IM;
1558 : Real64 PM;
1559 :
1560 0 : H = B - A;
1561 0 : POWER(state, IO, IL, RSER, AA, EPS, IM, A, PM);
1562 0 : YA = -1.0 * PM;
1563 0 : POWER(state, IO, IL, RSER, AA, EPS, IM, B, PM);
1564 0 : YB = -1.0 * PM;
1565 0 : C = A + RTWO * H;
1566 0 : D = A + RONE * H;
1567 0 : POWER(state, IO, IL, RSER, AA, EPS, IM, C, PM);
1568 0 : YC = -1.0 * PM;
1569 0 : POWER(state, IO, IL, RSER, AA, EPS, IM, D, PM);
1570 0 : YD = -1.0 * PM;
1571 0 : K = 1;
1572 0 : while (std::abs(YB - YA) > EPSILON || H > DELTA) {
1573 0 : if (YC < YD) {
1574 0 : B = D;
1575 0 : YB = YD;
1576 0 : D = C;
1577 0 : YD = YC;
1578 0 : H = B - A;
1579 0 : C = A + RTWO * H;
1580 0 : POWER(state, IO, IL, RSER, AA, EPS, IM, C, PM);
1581 0 : YC = -1.0 * PM;
1582 : } else {
1583 0 : A = C;
1584 0 : YA = YC;
1585 0 : C = D;
1586 0 : YC = YD;
1587 0 : H = B - A;
1588 0 : D = A + RONE * H;
1589 0 : POWER(state, IO, IL, RSER, AA, EPS, IM, D, PM);
1590 0 : YD = -1.0 * PM;
1591 : }
1592 0 : ++K;
1593 : }
1594 0 : if (K < KMAX) {
1595 0 : P = A;
1596 0 : YP = YA;
1597 0 : if (YB < YA) {
1598 0 : P = B;
1599 0 : YP = YB;
1600 : }
1601 0 : return;
1602 : } else {
1603 0 : return;
1604 : }
1605 : }
1606 :
1607 0 : Real64 FUN(EnergyPlusData &state, Real64 const II, Real64 const VV, Real64 const IL, Real64 const IO, Real64 const RSER, Real64 const AA)
1608 : {
1609 :
1610 : // FUNCTION INFORMATION:
1611 : // AUTHOR O. Ulleberg, IFE Norway for Hydrogems
1612 : // DATE WRITTEN March 2001
1613 : // MODIFIED D. Bradley for EnergyPlus
1614 : // RE-ENGINEERED
1615 :
1616 : // PURPOSE OF THIS FUNCTION:
1617 : // This function is based on the current-voltage characteristic of the PV module and is of the
1618 : // form f(I,V)=0
1619 :
1620 : // Return value
1621 0 : Real64 FUN(0.0);
1622 :
1623 0 : if (((VV + II * RSER) / AA) < 700.0) {
1624 0 : FUN = II - IL + IO * (std::exp((VV + II * RSER) / AA) - 1.0) - ((VV + II * RSER) / state.dataPhotovoltaic->ShuntResistance);
1625 : } else {
1626 0 : ShowSevereError(state, "EquivalentOneDiode Photovoltaic model failed to find maximum power point");
1627 0 : ShowContinueError(state, "Numerical solver failed trying to take exponential of too large a number");
1628 0 : ShowContinueError(state, format("Check input data in {}", pvModelNames[(int)PVModel::TRNSYS]));
1629 0 : ShowContinueError(state, format("VV (voltage) = {:.5R}", VV));
1630 0 : ShowContinueError(state, format("II (current) = {:.5R}", II));
1631 0 : ShowFatalError(state, "FUN: EnergyPlus terminates because of numerical problem in EquivalentOne-Diode PV model");
1632 : }
1633 :
1634 0 : return FUN;
1635 : }
1636 :
1637 0 : Real64 FI(EnergyPlusData &state, Real64 const II, Real64 const VV, Real64 const IO, Real64 const RSER, Real64 const AA)
1638 : {
1639 :
1640 : // FUNCTION INFORMATION:
1641 : // AUTHOR O. Ulleberg, IFE Norway for Hydrogems
1642 : // DATE WRITTEN March 2001
1643 : // MODIFIED D. Bradley for EnergyPlus
1644 : // RE-ENGINEERED
1645 :
1646 : // PURPOSE OF THIS FUNCTION:
1647 : // partial differential of I=I(I,V)
1648 :
1649 : // METHODOLOGY EMPLOYED:
1650 : // the function is based on the current voltage characteristic of the PV module and is of
1651 : // the form dF(I,V)/dI=0
1652 :
1653 : // Return value
1654 0 : Real64 FI(0.0);
1655 :
1656 0 : if (((VV + II * RSER) / AA) < 700.0) {
1657 0 : FI = 1.0 + IO * std::exp((VV + II * RSER) / AA) * RSER / AA + (RSER / state.dataPhotovoltaic->ShuntResistance);
1658 : } else {
1659 0 : ShowSevereError(state, "EquivalentOneDiode Photovoltaic model failed to find maximum power point");
1660 0 : ShowContinueError(state, "Numerical solver failed trying to take exponential of too large a number");
1661 0 : ShowContinueError(state, format("Check input data in {}", pvModelNames[(int)PVModel::TRNSYS]));
1662 0 : ShowContinueError(state, format("VV (voltage) = {:.5R}", VV));
1663 0 : ShowContinueError(state, format("II (current) = {:.5R}", II));
1664 0 : ShowFatalError(state, "FI: EnergyPlus terminates because of numerical problem in EquivalentOne-Diode PV model");
1665 : }
1666 :
1667 0 : return FI;
1668 : }
1669 :
1670 0 : Real64 FV(EnergyPlusData &state, Real64 const II, Real64 const VV, Real64 const IO, Real64 const RSER, Real64 const AA)
1671 : {
1672 :
1673 : // FUNCTION INFORMATION:
1674 : // AUTHOR O. Ulleberg, IFE Norway for Hydrogems
1675 : // DATE WRITTEN March 2001
1676 : // MODIFIED D. Bradley for EnergyPlus
1677 : // RE-ENGINEERED
1678 :
1679 : // PURPOSE OF THIS FUNCTION:
1680 : // partial differential of V=I(I,V)
1681 :
1682 : // METHODOLOGY EMPLOYED:
1683 : // the function is based on the current voltage characteristic of the PV module and is of
1684 : // the form dF(I,V)/dV=0
1685 :
1686 : // Return value
1687 0 : Real64 FV(0.0);
1688 :
1689 0 : if (((VV + II * RSER) / AA) < 700.0) {
1690 0 : FV = IO * std::exp((VV + II * RSER) / AA) / AA + (1.0 / state.dataPhotovoltaic->ShuntResistance);
1691 : } else {
1692 0 : ShowSevereError(state, "EquivalentOneDiode Photovoltaic model failed to find maximum power point");
1693 0 : ShowContinueError(state, "Numerical solver failed trying to take exponential of too large a number");
1694 0 : ShowContinueError(state, format("Check input data in {}", pvModelNames[(int)PVModel::TRNSYS]));
1695 0 : ShowContinueError(state, format("VV (voltage) = {:.5R}", VV));
1696 0 : ShowContinueError(state, format("II (current) = {:.5R}", II));
1697 0 : ShowFatalError(state, "FI: EnergyPlus terminates because of numerical problem in EquivalentOne-Diode PV model");
1698 : }
1699 :
1700 0 : return FV;
1701 : }
1702 :
1703 : // End routines for Equivalent One-Diode model as implemented by Bradley
1704 : //************************************************************************
1705 :
1706 : // Begin supporting routines for Sandia PV model
1707 : // -------------------------------------------------------------------------------
1708 :
1709 0 : Real64 SandiaModuleTemperature(Real64 const Ibc, // beam radiation on collector plane, W/m2
1710 : Real64 const Idc, // Diffuse radiation on collector plane, W/m2
1711 : Real64 const Ws, // wind speed, m/s
1712 : Real64 const Ta, // ambient temperature, degC
1713 : Real64 const fd, // fraction of Idc used (empirical constant)
1714 : Real64 const a, // empirical constant
1715 : Real64 const b // empirical constant
1716 : )
1717 : {
1718 : // FUNCTION INFORMATION:
1719 : // AUTHOR G. Barker
1720 : // DATE WRITTEN unknown
1721 : // MODIFIED na
1722 : // RE-ENGINEERED B.Griffith December 2003
1723 :
1724 : // PURPOSE OF THIS FUNCTION:
1725 : // Returns back-of-module temperature, deg C
1726 :
1727 : // METHODOLOGY EMPLOYED:
1728 : // apply sandia temperature model, This is module temp or back of
1729 : // of the panel. A separate correction handles delta T for actual cell
1730 :
1731 : // REFERENCES:
1732 : // from G. Barker's TRNSYS implementation
1733 : // Equations (10) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
1734 : // predicted performance of building integrated photovoltaics,
1735 : // Solar 2002, Sunrise on the Reliable Energy Economy,
1736 : // June 15-19, 2002, Reno, NV.
1737 :
1738 : // Return value
1739 : Real64 SandiaModuleTemperature;
1740 :
1741 : Real64 E; // total irradiance working variable
1742 :
1743 0 : E = Ibc + fd * Idc;
1744 :
1745 0 : SandiaModuleTemperature = E * std::exp(a + b * Ws) + Ta;
1746 :
1747 0 : return SandiaModuleTemperature;
1748 : }
1749 :
1750 : // -------------------------------------------------------------------------------
1751 : // -------------------------------------------------------------------------------
1752 :
1753 0 : Real64 SandiaTcellFromTmodule(Real64 const Tm, // module temperature (deg C)
1754 : Real64 const Ibc, // beam radiation on collector plane, W/m2
1755 : Real64 const Idc, // Diffuse radiation on collector plane, W/m2
1756 : Real64 const fd, // fraction of Idc used (empirical constant)
1757 : Real64 const DT0 // (Tc-Tm) at E=1000 W/m2 (empirical constant known as delta T), deg C
1758 : )
1759 : {
1760 : // FUNCTION INFORMATION:
1761 : // AUTHOR G. Barker
1762 : // DATE WRITTEN unknown
1763 : // MODIFIED na
1764 : // RE-ENGINEERED B. Griffith Jan 2004 F77 -> f90
1765 :
1766 : // PURPOSE OF THIS FUNCTION:
1767 : // Returns cell temperature, deg C
1768 :
1769 : // METHODOLOGY EMPLOYED:
1770 : // This is for the Sandia model method of determining cell temperatures
1771 : // module temperature differs from solar cell temperature
1772 : // because panel temperatures are not uniform
1773 :
1774 : // REFERENCES:
1775 : // Equations (11) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
1776 : // predicted performance of building integrated photovoltaics,
1777 : // Solar 2002, Sunrise on the Reliable Energy Economy,
1778 : // June 15-19, 2002, Reno, NV.
1779 :
1780 : // Return value
1781 : Real64 SandiaTcellFromTmodule;
1782 :
1783 : Real64 E; // total irradiance working variable
1784 :
1785 0 : E = Ibc + fd * Idc;
1786 :
1787 0 : SandiaTcellFromTmodule = Tm + (E / 1000.0) * DT0;
1788 :
1789 0 : return SandiaTcellFromTmodule;
1790 : }
1791 :
1792 : // -------------------------------------------------------------------------------
1793 :
1794 0 : Real64 SandiaCellTemperature(Real64 const Ibc, // beam radiation on collector plane W/m2
1795 : Real64 const Idc, // Diffuse radiation on collector plane W/m2
1796 : Real64 const Ws, // wind speed, m/s
1797 : Real64 const Ta, // ambient temperature, degC
1798 : Real64 const fd, // fraction of Idc used (empirical constant)
1799 : Real64 const a, // empirical constant
1800 : Real64 const b, // empirical constant
1801 : Real64 const DT0 // (Tc-Tm) at E=1000 W/m2 (empirical constant known as dTc), deg C
1802 : )
1803 : {
1804 : // FUNCTION INFORMATION:
1805 : // AUTHOR G. Barker
1806 : // DATE WRITTEN unknown
1807 : // MODIFIED
1808 : // RE-ENGINEERED B. Griffith, Jan 2004 F77-> f90
1809 :
1810 : // PURPOSE OF THIS FUNCTION:
1811 : // Returns cell temperature, deg C
1812 : // METHODOLOGY EMPLOYED:
1813 : // is this even used? duplicates separate functions above.
1814 : // combines function SandiaTcellFromTmodule with
1815 : // SandiaModuleTemperature
1816 :
1817 : // REFERENCES:
1818 : // Equations (10) and (11) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
1819 : // predicted performance of building integrated photovoltaics,
1820 : // Solar 2002, Sunrise on the Reliable Energy Economy,
1821 : // June 15-19, 2002, Reno, NV.
1822 :
1823 : // Return value
1824 : Real64 SandiaCellTemperature;
1825 :
1826 : Real64 E; // irradiance working variable
1827 : Real64 Tm;
1828 :
1829 0 : E = Ibc + fd * Idc;
1830 :
1831 0 : Tm = E * std::exp(a + b * Ws) + Ta;
1832 :
1833 0 : SandiaCellTemperature = Tm + (E / 1000.0) * DT0; // E0=1000.0 W/m2
1834 :
1835 0 : return SandiaCellTemperature;
1836 : }
1837 :
1838 : // -------------------------------------------------------------------------------
1839 :
1840 0 : Real64 SandiaEffectiveIrradiance(Real64 const Tc, // cell temperature (deg C)
1841 : Real64 const Isc, // short-circuit current under operating conditions (A)
1842 : Real64 const Isc0, // reference Isc at Tc=25 C, Ic=1000 W/m2 (A)
1843 : Real64 const aIsc // Isc temperature coefficient (degC^-1)
1844 : )
1845 : {
1846 : // FUNCTION INFORMATION:
1847 : // AUTHOR G. Barker
1848 : // DATE WRITTEN <unknown>
1849 : // MODIFIED na
1850 : // RE-ENGINEERED B. Griffith Jan 2004, F77 to f90
1851 :
1852 : // PURPOSE OF THIS FUNCTION:
1853 : // Returns "effective irradiance", used in calculation of Imp, Voc, Ix, Ixx
1854 :
1855 : // Return value
1856 : Real64 SandiaEffectiveIrradiance;
1857 :
1858 0 : SandiaEffectiveIrradiance = Isc / (1.0 + aIsc * (Tc - 25.0)) / Isc0;
1859 :
1860 0 : return SandiaEffectiveIrradiance;
1861 : }
1862 :
1863 : // -------------------------------------------------------------------------------
1864 :
1865 2 : Real64 AbsoluteAirMass(Real64 const SolZen, // solar zenith angle (deg)
1866 : Real64 const Altitude // site altitude (m)
1867 : )
1868 : {
1869 : // FUNCTION INFORMATION:
1870 : // AUTHOR G. Barker
1871 : // DATE WRITTEN <unknown>
1872 : // MODIFIED na
1873 : // RE-ENGINEERED B. Griffith Jan 2004 F77 -> f90
1874 :
1875 : // PURPOSE OF THIS FUNCTION:
1876 : // Returns absolute air mass
1877 :
1878 : // Return value
1879 : Real64 AbsoluteAirMass;
1880 :
1881 2 : if (SolZen < 89.9) {
1882 1 : Real64 const AM(1.0 / (std::cos(SolZen * Constant::DegToRad) + 0.5057 * std::pow(96.08 - SolZen, -1.634)));
1883 1 : AbsoluteAirMass = std::exp(-0.0001184 * Altitude) * AM;
1884 : } else {
1885 1 : Real64 constexpr AM(36.32); // evaluated above at SolZen = 89.9 issue #5528
1886 1 : AbsoluteAirMass = std::exp(-0.0001184 * Altitude) * AM;
1887 : }
1888 :
1889 2 : return AbsoluteAirMass;
1890 : }
1891 :
1892 : // -------------------------------------------------------------------------------
1893 :
1894 0 : Real64 SandiaF1(Real64 const AMa, // absolute air mass
1895 : Real64 const a0, // empirical constant, module-specific
1896 : Real64 const a1, // empirical constant, module-specific
1897 : Real64 const a2, // empirical constant, module-specific
1898 : Real64 const a3, // empirical constant, module-specific
1899 : Real64 const a4 // empirical constant, module-specific
1900 : )
1901 : {
1902 : // FUNCTION INFORMATION:
1903 : // AUTHOR G. Barker
1904 : // DATE WRITTEN <unknown>
1905 : // MODIFIED na
1906 : // RE-ENGINEERED B. Griffith F77-> f90
1907 :
1908 : // PURPOSE OF THIS FUNCTION:
1909 : // Returns the result of Sandia Air Mass function
1910 : // "AMa-Function" for solar spectral influence
1911 :
1912 : // METHODOLOGY EMPLOYED:
1913 : // <description>
1914 :
1915 : // REFERENCES:
1916 : // Equation (8) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
1917 : // predicted performance of building integrated photovoltaics,
1918 : // Solar 2002, Sunrise on the Reliable Energy Economy,
1919 : // June 15-19, 2002, Reno, NV.
1920 :
1921 : // Return value
1922 : Real64 SandiaF1;
1923 :
1924 0 : Real64 const F1(a0 + a1 * AMa + a2 * pow_2(AMa) + a3 * pow_3(AMa) + a4 * pow_4(AMa));
1925 :
1926 0 : if (F1 > 0.0) {
1927 0 : SandiaF1 = F1;
1928 : } else {
1929 0 : SandiaF1 = 0.0;
1930 : }
1931 :
1932 0 : return SandiaF1;
1933 : }
1934 :
1935 : // -------------------------------------------------------------------------------
1936 :
1937 0 : Real64 SandiaF2(Real64 const IncAng, // incidence angle (deg)
1938 : Real64 const b0, // empirical module-specific constants
1939 : Real64 const b1, // empirical module-specific constants
1940 : Real64 const b2, // empirical module-specific constants
1941 : Real64 const b3, // empirical module-specific constants
1942 : Real64 const b4, // empirical module-specific constants
1943 : Real64 const b5 // empirical module-specific constants
1944 : )
1945 : {
1946 : // FUNCTION INFORMATION:
1947 : // AUTHOR G. Barker
1948 : // DATE WRITTEN <unknown>
1949 : // MODIFIED na
1950 : // RE-ENGINEERED B. Griffith Jan 2004 F77-> f90
1951 :
1952 : // PURPOSE OF THIS FUNCTION:
1953 : // C Returns Sandia F2 function
1954 :
1955 : // REFERENCES:
1956 : // Equation (9) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
1957 : // predicted performance of building integrated photovoltaics,
1958 : // Solar 2002, Sunrise on the Reliable Energy Economy,
1959 : // June 15-19, 2002, Reno, NV.
1960 :
1961 : // Return value
1962 : Real64 SandiaF2;
1963 :
1964 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1965 : Real64 F2; // working variable for function result
1966 :
1967 0 : F2 = b0 + b1 * IncAng + b2 * pow_2(IncAng) + b3 * pow_3(IncAng) + b4 * pow_4(IncAng) + b5 * pow_5(IncAng);
1968 :
1969 0 : if (F2 > 0.0) {
1970 0 : SandiaF2 = F2;
1971 : } else {
1972 0 : SandiaF2 = 0.0;
1973 : }
1974 :
1975 0 : return SandiaF2;
1976 : }
1977 :
1978 : // -------------------------------------------------------------------------------
1979 :
1980 0 : Real64 SandiaImp(Real64 const Tc, // cell temperature (degC)
1981 : Real64 const Ee, // effective irradiance (W/m2)
1982 : Real64 const Imp0, // current at MPP at SRC (1000 W/m2, 25 C) (A)
1983 : Real64 const aImp, // Imp temperature coefficient (degC^-1)
1984 : Real64 const C0, // empirical module-specific constants
1985 : Real64 const C1 // empirical module-specific constants
1986 : )
1987 : {
1988 : // FUNCTION INFORMATION:
1989 : // AUTHOR G. Barker
1990 : // DATE WRITTEN <unknown>
1991 : // MODIFIED na
1992 : // RE-ENGINEERED B. Griffith F77 -> f90
1993 :
1994 : // PURPOSE OF THIS FUNCTION:
1995 : // Returns current at maximum power point (A)
1996 :
1997 : // REFERENCES:
1998 : // Equation (3) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
1999 : // predicted performance of building integrated photovoltaics,
2000 : // Solar 2002, Sunrise on the Reliable Energy Economy,
2001 : // June 15-19, 2002, Reno, NV.
2002 :
2003 : // Return value
2004 : Real64 SandiaImp;
2005 :
2006 0 : SandiaImp = Imp0 * (C0 * Ee + C1 * pow_2(Ee)) * (1.0 + aImp * (Tc - 25));
2007 : // why hardwire T0 at 25.0? can this change? seems okay, fewer args
2008 0 : return SandiaImp;
2009 : }
2010 :
2011 : // -------------------------------------------------------------------------------
2012 :
2013 0 : Real64 SandiaIsc(Real64 const Tc, // cell temperature (deg C)
2014 : Real64 const Isc0, // Isc at Tc=25 C, Ic=1000 W/m2 (A)
2015 : Real64 const Ibc, // beam radiation on collector plane (W/m2)
2016 : Real64 const Idc, // Diffuse radiation on collector plane (W/m2)
2017 : Real64 const F1, // Sandia F1 function for air mass effects
2018 : Real64 const F2, // Sandia F2 function of incidence angle
2019 : Real64 const fd, // module-specific empirical constant
2020 : Real64 const aIsc // Isc temperature coefficient (degC^-1)
2021 : )
2022 : {
2023 : // FUNCTION INFORMATION:
2024 : // AUTHOR G. Barker
2025 : // DATE WRITTEN <date_written>
2026 : // MODIFIED na
2027 : // RE-ENGINEERED B. Griffith Jan 2004 F77 -> f90
2028 :
2029 : // PURPOSE OF THIS FUNCTION:
2030 : // Returns Short-Circuit Current
2031 :
2032 : // REFERENCES:
2033 : // Equation (1) in Davis, M.W., A.H. Fanney, B.P. Dougherty. Measured versus
2034 : // predicted performance of building integrated photovoltaics,
2035 : // Solar 2002, Sunrise on the Reliable Energy Economy,
2036 : // June 15-19, 2002, Reno, NV.
2037 :
2038 : // Return value
2039 : Real64 SandiaIsc;
2040 :
2041 : // SandiaIsc=Isc0*((Ibc*F1*F2+fd*Idc)/1000.0)*(1.0+aIsc*(Tc-25.0))
2042 : // Barkers original (above) changed to match publish eq. (1) in reference
2043 0 : SandiaIsc = Isc0 * F1 * ((Ibc * F2 + fd * Idc) / 1000.0) * (1.0 + aIsc * (Tc - 25.0));
2044 :
2045 : // why hardwire E0 at 1000.0 ?, can this change? seems okay
2046 :
2047 0 : return SandiaIsc;
2048 : }
2049 :
2050 : // -------------------------------------------------------------------------------
2051 :
2052 0 : Real64 SandiaIx(Real64 const Tc, // cell temperature (deg C)
2053 : Real64 const Ee, // effective irradiance
2054 : Real64 const Ix0, // Ix at SRC (1000 W/m2, 25 C) (A)
2055 : Real64 const aIsc, // Isc temp coefficient (/C)
2056 : Real64 const aImp, // Imp temp coefficient (/C)
2057 : Real64 const C4, // empirical module-specific constants
2058 : Real64 const C5 // empirical module-specific constants
2059 : )
2060 : {
2061 : // FUNCTION INFORMATION:
2062 : // AUTHOR G. Barker
2063 : // DATE WRITTEN <unknown>
2064 : // MODIFIED na
2065 : // RE-ENGINEERED B. Griffith, Jan 2004 F77 -> f90
2066 :
2067 : // PURPOSE OF THIS FUNCTION:
2068 : // Returns current "Ix" at V=0.5*Voc (A)
2069 :
2070 : // REFERENCES:
2071 : // Equation 9 in King et al. nov 20003
2072 :
2073 : // Return value
2074 : Real64 SandiaIx;
2075 :
2076 0 : SandiaIx = Ix0 * (C4 * Ee + C5 * pow_2(Ee)) * (1.0 + ((aIsc + aImp) / 2.0 * (Tc - 25.0)));
2077 :
2078 0 : return SandiaIx;
2079 : }
2080 :
2081 : // -------------------------------------------------------------------------------
2082 :
2083 0 : Real64 SandiaIxx(Real64 const Tc, // cell temperature (deg C)
2084 : Real64 const Ee, // effective irradiance (W/m2 ?)
2085 : Real64 const Ixx0, // Ixx at SRC (1000 W/m2, 25 C) (A)
2086 : Real64 const aImp, // Imp temp coefficient (/C)
2087 : Real64 const C6, // empirical module-specific constants
2088 : Real64 const C7 // empirical module-specific constants
2089 : )
2090 : {
2091 : // FUNCTION INFORMATION:
2092 : // AUTHOR G. Barker
2093 : // DATE WRITTEN <unknown>
2094 : // MODIFIED na
2095 : // RE-ENGINEERED B. Griffith Jan2004 F77 to f90
2096 :
2097 : // PURPOSE OF THIS FUNCTION:
2098 : // Returns current "Ix" at V=0.5*(Voc+Vmp) (A)
2099 :
2100 : // REFERENCES:
2101 : // Equation 10 in King et al nov. 2003
2102 :
2103 : // Return value
2104 : Real64 SandiaIxx;
2105 :
2106 0 : SandiaIxx = Ixx0 * (C6 * Ee + C7 * pow_2(Ee)) * (1.0 + aImp * (Tc - 25.0));
2107 :
2108 0 : return SandiaIxx;
2109 : }
2110 :
2111 : // -------------------------------------------------------------------------------
2112 :
2113 0 : Real64 SandiaVmp(Real64 const Tc, // cell temperature (deg C)
2114 : Real64 const Ee, // effective irradiance
2115 : Real64 const Vmp0, // Vmp at SRC (1000 W/m2, 25 C) (V)
2116 : Real64 const NcellSer, // # cells in series
2117 : Real64 const DiodeFactor, // module-specIFic empirical constant
2118 : Real64 const BVmp0, // Vmp temperature coefficient (V/C)
2119 : Real64 const mBVmp, // change in BVmp with irradiance
2120 : Real64 const C2, // empirical module-specific constants
2121 : Real64 const C3 // empirical module-specific constants
2122 : )
2123 : {
2124 : // FUNCTION INFORMATION:
2125 : // AUTHOR G. Barker
2126 : // DATE WRITTEN <unknown>
2127 : // MODIFIED na
2128 : // RE-ENGINEERED B. Griffith, Jan 2004, F77 -> f90
2129 :
2130 : // PURPOSE OF THIS FUNCTION:
2131 : // Returns Voltage at Max. Power Point (V)
2132 :
2133 : // METHODOLOGY EMPLOYED:
2134 : // <description>
2135 :
2136 : // REFERENCES:
2137 : // Equation 4 in King et al Nov. 2003
2138 :
2139 : // Return value
2140 : Real64 SandiaVmp;
2141 :
2142 : Real64 dTc;
2143 : Real64 BVmpEe;
2144 :
2145 0 : if (Ee > 0.0) {
2146 : // following is equation 8 in King et al. nov. 2003
2147 0 : dTc = DiodeFactor * ((1.38066e-23 * (Tc + Constant::Kelvin)) / 1.60218e-19);
2148 :
2149 0 : BVmpEe = BVmp0 + mBVmp * (1.0 - Ee);
2150 :
2151 0 : SandiaVmp = Vmp0 + C2 * NcellSer * dTc * std::log(Ee) + C3 * NcellSer * pow_2(dTc * std::log(Ee)) + BVmpEe * (Tc - 25.0);
2152 : } else {
2153 0 : SandiaVmp = 0.0;
2154 : }
2155 :
2156 0 : return SandiaVmp;
2157 : }
2158 :
2159 : // -------------------------------------------------------------------------------
2160 :
2161 0 : Real64 SandiaVoc(Real64 const Tc, // cell temperature (deg C)
2162 : Real64 const Ee, // effective irradiance
2163 : Real64 const Voc0, // Voc at SRC (1000 W/m2, 25 C) (V)
2164 : Real64 const NcellSer, // # cells in series
2165 : Real64 const DiodeFactor, // module-specIFic empirical constant
2166 : Real64 const BVoc0, // Voc temperature coefficient (V/C)
2167 : Real64 const mBVoc // change in BVoc with irradiance
2168 : )
2169 : {
2170 : // FUNCTION INFORMATION:
2171 : // AUTHOR G Barker
2172 : // DATE WRITTEN <unknown>
2173 : // MODIFIED na
2174 : // RE-ENGINEERED B Griffith Jan 2004 F77 -> f90
2175 :
2176 : // PURPOSE OF THIS FUNCTION:
2177 : // Returns Open-Circuit Voltage (V)
2178 :
2179 : // Return value
2180 : Real64 SandiaVoc;
2181 :
2182 : Real64 dTc; // working variable
2183 : Real64 BVocEe; // working variable
2184 :
2185 0 : if (Ee > 0.0) {
2186 0 : dTc = DiodeFactor * ((1.38066e-23 * (Tc + Constant::Kelvin)) / 1.60218e-19);
2187 0 : BVocEe = BVoc0 + mBVoc * (1.0 - Ee);
2188 :
2189 0 : SandiaVoc = Voc0 + NcellSer * dTc * std::log(Ee) + BVocEe * (Tc - 25.0);
2190 : } else {
2191 0 : SandiaVoc = 0.0;
2192 : }
2193 :
2194 0 : return SandiaVoc;
2195 : }
2196 :
2197 0 : void SetVentedModuleQdotSource(EnergyPlusData &state,
2198 : int const VentModNum,
2199 : Real64 const QSource // source term in Watts
2200 : )
2201 : {
2202 :
2203 : // SUBROUTINE INFORMATION:
2204 : // AUTHOR B. Griffith
2205 : // DATE WRITTEN January 2004
2206 : // MODIFIED na
2207 : // RE-ENGINEERED na
2208 :
2209 : // PURPOSE OF THIS SUBROUTINE:
2210 : // object oriented "Set" routine for updating sink term without exposing variables
2211 :
2212 : // METHODOLOGY EMPLOYED:
2213 : // update derived type with new data , turn power into W/m2
2214 :
2215 : // Using/Aliasing
2216 : using namespace DataSurfaces;
2217 :
2218 0 : state.dataHeatBal->ExtVentedCavity(VentModNum).QdotSource = QSource / state.dataHeatBal->ExtVentedCavity(VentModNum).ProjArea;
2219 0 : }
2220 :
2221 0 : void GetExtVentedCavityIndex(EnergyPlusData &state, int const SurfacePtr, int &VentCavIndex)
2222 : {
2223 :
2224 : // SUBROUTINE INFORMATION:
2225 : // AUTHOR B. Griffith
2226 : // DATE WRITTEN January 2004
2227 : // MODIFIED na
2228 : // RE-ENGINEERED na
2229 :
2230 : // PURPOSE OF THIS SUBROUTINE:
2231 : // object oriented "Get" routine for establishing correct integer index from outside this module
2232 :
2233 : // METHODOLOGY EMPLOYED:
2234 : // mine Surface derived type for correct index/number of surface
2235 : // mine ExtVentedCavity derived type that has the surface.
2236 :
2237 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2238 : int CavNum; // temporary
2239 : int ThisSurf; // temporary
2240 : int thisCav;
2241 : bool Found;
2242 :
2243 0 : if (SurfacePtr == 0) {
2244 : // should be trapped already
2245 0 : ShowFatalError(state, "Invalid surface passed to GetExtVentedCavityIndex");
2246 : }
2247 :
2248 0 : CavNum = 0;
2249 0 : Found = false;
2250 0 : for (thisCav = 1; thisCav <= state.dataSurface->TotExtVentCav; ++thisCav) {
2251 0 : for (ThisSurf = 1; ThisSurf <= state.dataHeatBal->ExtVentedCavity(thisCav).NumSurfs; ++ThisSurf) {
2252 0 : if (SurfacePtr == state.dataHeatBal->ExtVentedCavity(thisCav).SurfPtrs(ThisSurf)) {
2253 0 : Found = true;
2254 0 : CavNum = thisCav;
2255 : }
2256 : }
2257 : }
2258 :
2259 0 : if (!Found) {
2260 0 : ShowFatalError(state,
2261 0 : format("Did not find surface in Exterior Vented Cavity description in GetExtVentedCavityIndex, Surface name = {}",
2262 0 : state.dataSurface->Surface(SurfacePtr).Name));
2263 : } else {
2264 :
2265 0 : VentCavIndex = CavNum;
2266 : }
2267 0 : }
2268 :
2269 0 : void GetExtVentedCavityTsColl(EnergyPlusData &state, int const VentModNum, Real64 &TsColl)
2270 : {
2271 :
2272 : // SUBROUTINE INFORMATION:
2273 : // AUTHOR <author>
2274 : // DATE WRITTEN <date_written>
2275 : // MODIFIED na
2276 : // RE-ENGINEERED na
2277 :
2278 : // PURPOSE OF THIS SUBROUTINE:
2279 : // object oriented "Get" routine for collector surface temperature
2280 :
2281 : // METHODOLOGY EMPLOYED:
2282 : // access derived type
2283 :
2284 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2285 0 : TsColl = state.dataHeatBal->ExtVentedCavity(VentModNum).Tbaffle;
2286 0 : }
2287 :
2288 : // -------------------------------------------------------------------------------
2289 :
2290 : // EnergyPlus V1.2 and beyond include models for photovoltaic calculations called
2291 : // Generator:Photovoltaic:Simple and Generator:PV:Sandia implemented by the Center for
2292 : // Buildings and Thermal Systems, National Renewable Energy Laboratory, 1617 Cole Blvd
2293 : // MS 2722, Golden, CO, 80401
2294 :
2295 : // EnergyPlus v1.1.1 and beyond includes model for Photovoltaic calculations, now
2296 : // referred to as the Generator:PV:Equivalent One-Diode model developed by Thermal Energy
2297 : // System Specialists, 2916 Marketplace Drive, Suite 104, Madison, WI 53719;
2298 : // Tel: (608) 274-2577
2299 :
2300 : } // namespace Photovoltaics
2301 :
2302 : } // namespace EnergyPlus
|