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