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