// Calculate manages calculations that need to be run for each time step public void Calculate ( double PanelTilt // The angle between the surface tilt of the module and the ground [radians] , double Clearance // Array ground clearance [m] , double HDif // Diffuse horizontal irradiance [W/m2] , double TDifRef // Front reflected diffuse irradiance [W/m2] , double HGlo // Horizontal global irradiance [W/m2] , double[] frontGroundGHI // Global irradiance for each of the ground segments to front of the row [W/m2] , double[] rearGroundGHI // Global irradiance for each of the ground segments to rear of the row [W/m2] , double aveGroundGHI // Average global irradiance on ground segment to the rear of row [W/m2] , int month // Current month, used for getting albedo value [#] , double backSH // Fraction of the back surface of the PV array that is unshaded [#] , double TDir // Back tilted beam irradiance [W/m2] , DateTime ts // Time stamp analyzed, used for printing .csv files ) { // For tracking systems, panel tilt and ground clearance will change at each time step itsPanelTilt = PanelTilt; itsClearance = Clearance / itsArrayBW; // Convert to panel slope lengths int numGroundSegs = Util.NUM_GROUND_SEGS; double h = Math.Sin(itsPanelTilt); // Vertical height of sloped PV panel [panel slope lengths] double b = Math.Cos(itsPanelTilt); // Horizontal distance from front of panel to back of panel [panel slope lengths] // Calculate x, y coordinates of bottom and top edges of PV row behind the current PV row so that portions of sky and ground viewed by // the PV cell may be determined. Coordinates are relative to (0,0) being the ground point below the lower front edge of current PV row. // The row behind the current row is in the positive x direction. double bottomX = itsPitch; // x value for point on bottom edge of PV panel behind current row double bottomY = itsClearance; // y value for point on bottom edge of PV panel behind current row double topX = bottomX + b; // x value for point on top edge of PV panel behind current row double topY = bottomY + h; // y value for point on top edge of PV panel behind current row // Get albedo value for the month Albedo = itsMonthlyAlbedo[month]; // Accumulate diffuse, reflected, and beam irradiance components for each cell row over its field of view of PI radians for (int i = 0; i < numCellRows; i++) { double cellX = b * (i + 0.5) / numCellRows; // x value for location of cell double cellY = itsClearance + h * (i + 0.5) / numCellRows; // y value for location of cell double elevUp = 0.0; // Elevation angle from PV cell to top of PV panel double elevDown = 0.0; // Elevation angle from PV cell to bottom of PV panel if (itsRowType == RowType.INTERIOR) { elevUp = Math.Atan((topY - cellY) / (topX - cellX)); elevDown = Math.Atan((cellY - bottomY) / (bottomX - cellX)); } int stopSky = Convert.ToInt32((itsPanelTilt - elevUp) * Util.RTOD); // Last whole degree in arc range that sees sky; first is 0 [degrees] int startGround = Convert.ToInt32((itsPanelTilt + elevDown) * Util.RTOD); // First whole degree in arc range that sees ground; last is 180 [degrees] // Compute sky diffuse component backDif[i] = RadiationProc.GetViewFactor(0, stopSky * Util.DTOR) * HDif; // Compute front surface reflected component if (itsRowType == RowType.INTERIOR) { backFroRef[i] = RadiationProc.GetViewFactor(stopSky * Util.DTOR, startGround * Util.DTOR) * TDifRef; } backGroRef[i] = 0; // Add ground reflected component for (int j = startGround; j < 180; j++) { // Get start and ending elevations for this (j, j + 1) pair double startElevDown = elevDown + (j - startGround) * Util.DTOR; double stopElevDown = elevDown + (j + 1 - startGround) * Util.DTOR; // Projection onto ground in positive x direction double projX2 = cellX + cellY / Math.Tan(startElevDown); double projX1 = cellX + cellY / Math.Tan(stopElevDown); // Initialize and get actualGroundGHI value double actualGroundGHI = 0; if (Math.Abs(projX1 - projX2) > 0.99 * itsPitch) { if (itsRowType == RowType.SINGLE) { // Use measured GHI if projection approximates or exceeds the pitch actualGroundGHI = HGlo; } else { // Use average GHI if projection approximates or exceeds the pitch actualGroundGHI = aveGroundGHI; } } else { // Normalize projections and multiply by n projX1 = numGroundSegs * projX1 / itsPitch; projX2 = numGroundSegs * projX2 / itsPitch; if (itsRowType == RowType.SINGLE && ((Math.Abs(projX1) > numGroundSegs - 1) || (Math.Abs(projX2) > numGroundSegs - 1))) { // Use measured GHI if projection exceeds the pitch actualGroundGHI = HGlo; } else { while (projX1 < -numGroundSegs || projX2 < -numGroundSegs) { projX1 += numGroundSegs; projX2 += numGroundSegs; } while (projX1 >= numGroundSegs || projX2 >= numGroundSegs) { projX1 -= numGroundSegs; projX2 -= numGroundSegs; } // Determine indices (truncate values) for use with groundGHI arrays int index1 = Convert.ToInt32(Math.Floor(projX1 + numGroundSegs) - numGroundSegs); int index2 = Convert.ToInt32(Math.Floor(projX2 + numGroundSegs) - numGroundSegs); if (index1 == index2) { // Use single value if projection falls within a single segment of ground if (index1 < 0) { actualGroundGHI = frontGroundGHI[index1 + numGroundSegs]; } else { actualGroundGHI = rearGroundGHI[index1]; } } else { // Sum the irradiances on the ground if the projection falls across multiple segments for (int k = index1; k <= index2; k++) { if (k == index1) { if (k < 0) { actualGroundGHI += frontGroundGHI[k + numGroundSegs] * (k + 1.0 - projX1); } else { actualGroundGHI += rearGroundGHI[k] * (k + 1.0 - projX1); } } else if (k == index2) { if (k < 0) { actualGroundGHI += frontGroundGHI[k + numGroundSegs] * (projX2 - k); } else { actualGroundGHI += rearGroundGHI[k] * (projX2 - k); } } else { if (k < 0) { actualGroundGHI += frontGroundGHI[k + numGroundSegs]; } else { actualGroundGHI += rearGroundGHI[k]; } } } // Get irradiance on ground in the 1 degree field of view actualGroundGHI /= projX2 - projX1; } } } backGroRef[i] += RadiationProc.GetViewFactor(j * Util.DTOR, (j + 1) * Util.DTOR) * actualGroundGHI * Albedo; } double cellShade = 0.0; if (itsRowType == RowType.INTERIOR) { // Cell is fully shaded if >= 1, fully unshaded if <= 0, otherwise fractionally shaded cellShade = (1.0 - backSH) * numCellRows - i; cellShade = Math.Min(cellShade, 1.0); cellShade = Math.Max(cellShade, 0.0); } // Compute beam component, corrected for back shading backDir[i] = TDir * (1.0 - cellShade); // Correct each component for AOI and structure shading losses backDif[i] = backDif[i] * IAMDif * (1.0 - structLossFactor); backFroRef[i] = backFroRef[i] * IAMRef * (1.0 - structLossFactor); backGroRef[i] = backGroRef[i] * IAMRef * (1.0 - structLossFactor); backDir[i] = backDir[i] * IAMDir * (1.0 - structLossFactor); // Sum all components to get global back irradiance backGlo[i] = backDif[i] + backFroRef[i] + backGroRef[i] + backDir[i]; } BDif = 0; BFroRef = 0; BGroRef = 0; BDir = 0; double maxGlo = backGlo[0]; double minGlo = backGlo[0]; for (int i = 0; i < numCellRows; i++) { // Calculate mean irradiance components BDif += backDif[i] / numCellRows; BFroRef += backFroRef[i] / numCellRows; BGroRef += backGroRef[i] / numCellRows; BDir += backDir[i] / numCellRows; // Find the max and min global irradiance values maxGlo = Math.Max(maxGlo, backGlo[i]); minGlo = Math.Min(minGlo, backGlo[i]); } BGlo = BDif + BFroRef + BGroRef + BDir; // Calculate the homogeneity of values as the range normalized by the sum IrrInhomogeneity = (BGlo > 0) ? (maxGlo - minGlo) / BGlo : 0; // Option to print details of the model in .csv files. Only recommended for single day simulations. // PrintModel(ts); }
double farmACMinVoltageLoss = 0; // Loss of power when voltage of the array is too small and forces the inverters to 'shut off' and when inverter is not operating at MPP [W] // Calculate method public void Calculate( RadiationProc RadProc, // Radiation related data SimMeteo SimMet // Meteological data from inputfile ) { // Reset Losses for (int i = 0; i < SimPVA.Length; i++) { SimInv[i].LossPMinThreshold = 0; SimInv[i].LossClipping = 0; SimInv[i].LossLowVoltage = 0; SimInv[i].LossHighVoltage = 0; } // Calculating solar panel shading SimShading.Calculate(RadProc.SimSun.Zenith, RadProc.SimSun.Azimuth, RadProc.SimHorizonShading.TDir, RadProc.SimHorizonShading.TDif, RadProc.SimHorizonShading.TRef, RadProc.SimTracker.SurfSlope, RadProc.SimTracker.SurfAzimuth); // Calculating spectral model effects SimSpectral.Calculate(SimMet.HGlo, RadProc.SimSun.NExtra, RadProc.SimSun.Zenith); try { // Calculate PV Array Output for inputs read in this loop for (int j = 0; j < ReadFarmSettings.SubArrayCount; j++) { // Adjust the IV Curve based on based on Temperature and Irradiance SimPVA[j].CalcIVCurveParameters(SimMet.TGlo, SimShading.ShadTDir, SimShading.ShadTDif, SimShading.ShadTRef, RadProc.SimTilter.IncidenceAngle, SimMet.TAmbient, SimMet.WindSpeed, SimMet.TModMeasured, SimMet.MonthOfYear, SimSpectral.clearnessCorrection); // Check Inverter status to determine if the Inverter is ON or OFF GetInverterStatus(j); // If inverter is off set appropriate variables to 0 and recalculate array in open circuit voltage if (!SimInv[j].isON) { SimInv[j].ACPwrOut = 0; SimInv[j].IOut = 0; SimPVA[j].CalcAtOpenCircuit(); SimPVA[j].Calculate(false, SimPVA[j].Voc); } //performing AC wiring calculations SimACWiring[j].Calculate(SimInv[j]); // Assigning the outputs to the dictionary ReadFarmSettings.Outputlist["SubArray_Current" + (j + 1).ToString()] = SimPVA[j].IOut; ReadFarmSettings.Outputlist["SubArray_Voltage" + (j + 1).ToString()] = SimPVA[j].VOut; ReadFarmSettings.Outputlist["SubArray_Power" + (j + 1).ToString()] = SimPVA[j].POut / 1000; ReadFarmSettings.Outputlist["SubArray_Current_Inv" + (j + 1).ToString()] = SimInv[j].IOut; ReadFarmSettings.Outputlist["SubArray_Voltage_Inv" + (j + 1).ToString()] = SimInv[j].itsOutputVoltage; ReadFarmSettings.Outputlist["SubArray_Power_Inv" + (j + 1).ToString()] = SimInv[j].ACPwrOut / 1000; } //Calculating total farm output and total ohmic loss farmACOutput = 0; farmACOhmicLoss = 0; for (int i = 0; i < SimInv.Length; i++) { farmACOutput += SimInv[i].ACPwrOut; farmACOhmicLoss += SimACWiring[i].ACWiringLoss; } SimTransformer.Calculate(farmACOutput - farmACOhmicLoss); // Calculating outputs that will be assigned for this interval // Shading each component of the Tilted radiaton // Using horizon affected tilted radiation ShadGloLoss = (RadProc.SimTilter.TGlo - SimShading.ShadTGlo) - RadProc.SimHorizonShading.LossGlo; ShadGloFactor = (RadProc.SimTilter.TGlo > 0 ? SimShading.ShadTGlo / RadProc.SimTilter.TGlo : 1); ShadBeamLoss = RadProc.SimHorizonShading.TDir - SimShading.ShadTDir; ShadDiffLoss = RadProc.SimTilter.TDif > 0 ? RadProc.SimHorizonShading.TDif - SimShading.ShadTDif : 0; ShadRefLoss = RadProc.SimTilter.TRef > 0 ? RadProc.SimHorizonShading.TRef - SimShading.ShadTRef : 0; //Calculating total farm level variables. Cleaning them so they are non-cumulative. farmDC = 0; farmDCCurrent = 0; farmDCMismatchLoss = 0; farmDCModuleQualityLoss = 0; farmDCOhmicLoss = 0; farmDCSoilingLoss = 0; farmDCTemp = 0; farmTotalModules = 0; farmPNomDC = 0; farmPNomAC = 0; farmACPMinThreshLoss = 0; farmACClippingPower = 0; farmACMaxVoltageLoss = 0; farmACMinVoltageLoss = 0; farmPnom = 0; farmTempLoss = 0; farmRadLoss = 0; for (int i = 0; i < SimPVA.Length; i++) { farmDC += SimPVA[i].POut; farmDCCurrent += SimPVA[i].IOut; farmDCMismatchLoss += Math.Max(0, SimPVA[i].MismatchLoss); farmDCModuleQualityLoss += SimPVA[i].ModuleQualityLoss; farmDCOhmicLoss += SimPVA[i].OhmicLosses; farmDCSoilingLoss += SimPVA[i].SoilingLoss; farmDCTemp += SimPVA[i].TModule * SimPVA[i].itsNumModules; farmTotalModules += SimPVA[i].itsNumModules; farmPNomDC += SimPVA[i].itsPNomDCArray; farmPNomAC += SimInv[i].itsPNomArrayAC; farmACPMinThreshLoss += SimInv[i].LossPMinThreshold; farmACClippingPower += SimInv[i].LossClipping; farmACMaxVoltageLoss += SimInv[i].LossHighVoltage; farmACMinVoltageLoss += SimInv[i].LossLowVoltage; farmPnom += (SimPVA[i].itsPNom * SimPVA[i].itsNumModules) * SimPVA[i].TGloEff / 1000; farmTempLoss += SimPVA[i].tempLoss; farmRadLoss += SimPVA[i].radLoss; } // Averages all PV Array temperature values farmDCTemp /= farmTotalModules; farmModuleTempAndAmbientTempDiff = farmDCTemp - SimMet.TAmbient; farmDCEfficiency = (RadProc.SimTilter.TGlo > 0 ? farmDC / (RadProc.SimTilter.TGlo * farmArea) : 0) * 100; farmPNomDC = Utilities.ConvertWtokW(farmPNomDC); farmPNomAC = Utilities.ConvertWtokW(farmPNomAC); farmOverAllEff = (RadProc.SimTilter.TGlo > 0 && SimTransformer.POut > 0 ? SimTransformer.POut / (RadProc.SimTilter.TGlo * farmArea) : 0) * 100; farmPR = RadProc.SimTilter.TGlo > 0 && farmPNomDC > 0 && SimTransformer.POut > 0 ? SimTransformer.POut / RadProc.SimTilter.TGlo / farmPNomDC : 0; farmSysIER = (SimTransformer.itsPNom - SimTransformer.POut) / (RadProc.SimTilter.TGlo * 1000); } catch (Exception ce) { ErrorLogger.Log(ce, ErrLevel.FATAL); } // Assigning Outputs for this class. AssignOutputs(); }
// Method to start the simulation software run. public void Simulate(XmlDocument SiteSettings, String _Input = null, String _Output = null) { // Adding timer to calculate elapsed time for the simulation. Stopwatch timeTaken = new Stopwatch(); timeTaken.Start(); // Delete error log just before simulation if (File.Exists(Application.StartupPath + "/ErrorLog.txt")) { File.Delete(Application.StartupPath + "/ErrorLog.txt"); } // ReadFarmSettings reads the overall configuration of the farm ReadFarmSettings.doc = SiteSettings; // Obtain IO File names, either from the command prompt, or from .CSYX if ((_Input == null) && (_Output == null)) { ReadFarmSettings.AssignIOFileNames(); } else { ReadFarmSettings.SimInputFile = _Input; ReadFarmSettings.SimOutputFile = _Output; } // Collecting input and output file locations and Input/Output file location ReadFarmSettings.GetSimulationMode(); // Inform user about site configuration: Console.WriteLine("Status: Configuring parameters"); // Creating the SimMeteo Object to process the input file, and streamWriter to Write to the Output File try { // Reading and assigning the input file schema before configuring the inputs ReadFarmSettings.AssignInputFileSchema(); // Instantiating the relevant simulation class based on the simulation mode switch (ReadFarmSettings.SystemMode) { case "ASTME2848Regression": SimASTM = new ASTME2848(); SimASTM.Config(); break; case "GridConnected": // Assign the Sub-Array Count for grid connected systems. ReadFarmSettings.AssignSubArrayCount(); SimGridSys = new GridConnectedSystem(); SimGridSys.Config(); goto case "Radiation"; case "Radiation": SimRadProc = new RadiationProc(); SimRadProc.Config(); break; default: ErrorLogger.Log("Invalid Simulation mode found. CASSYS will exit.", ErrLevel.FATAL); break; } // Reading and assigning the input file schema and the output file schema ReadFarmSettings.AssignOutputFileSchema(); // Notifying user of the Configuration status if (ErrorLogger.numWarnings == 0) { Console.WriteLine("Status: Configuration OK."); } else { Console.WriteLine("Status: There were problems encountered during configuration."); Console.WriteLine(" Please see the error log file for details."); Console.WriteLine(" CASSYS has configured parameters with default values."); } try { // Read through the input file and perform calculations Console.Write("Status: Simulation Running"); // Creating the input file parser SimMeteo SimMeteoParser = new SimMeteo(); SimMeteoParser.Config(); // Create StreamWriter if (ReadFarmSettings.outputMode == "str") { // Assigning Headers to output String OutputString = ReadFarmSettings.OutputHeader.TrimEnd(',') + '\n'; } // Create StreamWriter and write output to .csv file else if (ReadFarmSettings.outputMode == "csv") { // If in batch mode, use the appending overload of StreamWriter if ((File.Exists(ReadFarmSettings.SimOutputFile)) && (ReadFarmSettings.batchMode)) { OutputFileWriter = new StreamWriter(ReadFarmSettings.SimOutputFile, true); } else { // Writing the headers to the new output file. OutputFileWriter = new StreamWriter(ReadFarmSettings.SimOutputFile); // Writing the headers to the new output file. OutputFileWriter.WriteLine(ReadFarmSettings.OutputHeader); } } // The following is executed if using iterative mode else { // Row for output header created only during first simulation run if (ReadFarmSettings.runNumber == 1) { ReadFarmSettings.outputTable.Rows.InsertAt(ReadFarmSettings.outputTable.NewRow(), 0); } // Placing output header in data table ReadFarmSettings.outputTable.Rows[0][ReadFarmSettings.runNumber - 1] = ReadFarmSettings.OutputHeader.TrimEnd(','); } // Read through the Input File and perform the relevant simulation while (!SimMeteoParser.InputFileReader.EndOfData) { SimMeteoParser.Calculate(); // If input file line could not be read, go to next line if (!SimMeteoParser.inputRead) { continue; } // running required calculations based on simulation mode switch (ReadFarmSettings.SystemMode) { case "ASTME2848Regression": SimASTM.Calculate(SimMeteoParser); break; case "GridConnected": SimRadProc.Calculate(SimMeteoParser); SimGridSys.Calculate(SimRadProc, SimMeteoParser); LossDiagram.Calculate(); break; case "Radiation": SimRadProc.Calculate(SimMeteoParser); break; default: ErrorLogger.Log("Invalid Simulation mode found. CASSYS will exit.", ErrLevel.FATAL); break; } // Only create output string in DLL mode, as creating string causes significant lag if (ReadFarmSettings.outputMode == "str") { OutputString += String.Join(",", GetOutputLine()) + "\n"; } // Write to output file else if (ReadFarmSettings.outputMode == "csv") { try { // Assembling and writing the line containing all output values OutputFileWriter.WriteLine(String.Join(",", GetOutputLine())); } catch (IOException ex) { ErrorLogger.Log(ex, ErrLevel.WARNING); } } // Using Iterative Mode: Writes output to a datatable else { // Create row if this is the first simulation run if (ReadFarmSettings.runNumber == 1) { ReadFarmSettings.outputTable.Rows.InsertAt(ReadFarmSettings.outputTable.NewRow(), ReadFarmSettings.outputTable.Rows.Count); } // Write output values to data table ReadFarmSettings.outputTable.Rows[ErrorLogger.iterationCount][ReadFarmSettings.runNumber - 1] = String.Join(",", GetOutputLine()); } } if (ReadFarmSettings.outputMode == "str") { // Trim newline character from the end of output OutputString = OutputString.TrimEnd('\r', '\n'); } // Write output to .csv file else if (ReadFarmSettings.outputMode == "csv") { try { // Clean out the buffer of the writer to ensure all entries are written to the output file. OutputFileWriter.Flush(); OutputFileWriter.Dispose(); SimMeteoParser.InputFileReader.Dispose(); } catch (IOException ex) { ErrorLogger.Log(ex, ErrLevel.WARNING); } } // If simulation done for Grid connected mode then write the calculated loss values to temp output file if (ReadFarmSettings.SystemMode == "GridConnected") { LossDiagram.AssignLossOutputs(); } timeTaken.Stop(); Console.WriteLine(""); Console.WriteLine("Status: Complete. Simulation took " + timeTaken.ElapsedMilliseconds / 1000D + " seconds."); } catch (IOException) { ErrorLogger.Log("The output file name " + ReadFarmSettings.SimOutputFile + " is not accesible or is not available at the location provided.", ErrLevel.FATAL); } } catch (Exception ex) { ErrorLogger.Log("CASSYS encountered an unexpected error: " + ex.Message, ErrLevel.FATAL); } }
double CalcSkyViewDirection ( double x // Horizontal dimension in the row-to-row ground area , RowType rowType // The position of the row being calculated relative to others [unitless] , double direction // The direction in which to move along the x-axis [-1, 0, 1] ) { double h = Math.Sin(itsPanelTilt); // Vertical height of sloped PV panel [panel slope lengths] double b = Math.Cos(itsPanelTilt); // Horizontal distance from front of panel to back of panel [panel slope lengths] double offset = direction; // Initialize offset to begin at first unit of given direction double skyPatch = 0; // View factor for view of sky in single row-to-row area double skySum = 0; // View factor for all sky views in given direction double angA = 0; double angB = 0; double angC = 0; double angD = 0; double beta1 = 0; // Start of ground's field of view that sees the sky segment double beta2 = 0; // End of ground's field of view that sees the sky segment // Sum sky view factors until sky view factor contributes <= 1% of sum // Only loop the calculation for rows extending forward or backward, so break loop when direction = 0. do { // Set back limiting angle to 0 since there is no row behind. if (rowType == RowType.LAST) { beta1 = 0; } else { // Angle from ground point to top of panel P angA = Math.Atan2(h + itsClearance, (offset + 1) * itsPitch + b - x); // Angle from ground point to bottom of panel P angB = Math.Atan2(itsClearance, (offset + 1) * itsPitch - x); beta1 = Math.Max(angA, angB); } // Set front limiting angle to PI since there is no row ahead. if (rowType == RowType.FIRST) { beta2 = Math.PI; } else { // Angle from ground point to top of panel Q angC = Math.Atan2(h + itsClearance, offset * itsPitch + b - x); // Angle from ground point to bottom of panel Q angD = Math.Atan2(itsClearance, offset * itsPitch - x); beta2 = Math.Min(angC, angD); } // If there is an opening in the sky through which the sun is seen, calculate view factor of sky patch skyPatch = (beta2 > beta1) ? RadiationProc.GetViewFactor(beta1, beta2) : 0; skySum += skyPatch; offset += direction; } while (offset != 0 && skyPatch > (0.01 * skySum)); return(skySum); }
double YearsSinceStart; // Number of entire years since the beginning of the simulation // Calculate method public void Calculate ( RadiationProc RadProc, // Radiation related data SimMeteo SimMet // Meteological data from inputfile ) { // Record number of years since start of simulation // This is used to calculate module ageing // Update StartTimeStamp if it has never been initialized if (StartTimeStamp == new DateTime()) { StartTimeStamp = RadProc.TimeStampAnalyzed; } TimeSpan SimDelta = RadProc.TimeStampAnalyzed.Subtract(StartTimeStamp); YearsSinceStart = Math.Floor(SimDelta.Days / 365.25); // Reset Losses for (int i = 0; i < SimPVA.Length; i++) { SimInv[i].LossPMinThreshold = 0; SimInv[i].LossClipping = 0; SimInv[i].LossLowVoltage = 0; SimInv[i].LossHighVoltage = 0; } // Calculating solar panel shading SimShading.Calculate(RadProc.SimSun.Zenith, RadProc.SimSun.Azimuth, RadProc.SimHorizonShading.TDir, RadProc.SimHorizonShading.TDif, RadProc.SimHorizonShading.TRef, RadProc.SimTracker.SurfSlope, RadProc.SimTracker.SurfAzimuth); if (RadProc.SimTracker.useBifacial) { // Calculating shading of ground beneath solar panels SimGround.Calculate(RadProc.SimSun.Zenith, RadProc.SimSun.Azimuth, RadProc.SimTracker.SurfSlope, RadProc.SimTracker.SurfAzimuth, RadProc.SimTracker.SurfClearance, RadProc.SimSplitter.HDir, RadProc.SimSplitter.HDif, RadProc.TimeStampAnalyzed); // Get front reflected diffuse irradiance, since it contributes to the back SimPVA[0].CalcEffectiveIrradiance(SimShading.ShadTDir, SimShading.ShadTDif, SimShading.ShadTRef, SimBackTilter.BGlo, RadProc.SimTilter.IncidenceAngle, SimMet.MonthOfYear, SimSpectral.clearnessCorrection); // Get incidence angle modifiers for components of irradiance SimPVA[0].CalcIAM(out SimBackTilter.IAMDir, out SimBackTilter.IAMDif, out SimBackTilter.IAMRef, RadProc.SimTilterOpposite.IncidenceAngle); // Calculate back side global irradiance SimBackTilter.Calculate(RadProc.SimTracker.SurfSlope, RadProc.SimTracker.SurfClearance, RadProc.SimSplitter.HDif, SimPVA[0].TDifRef, SimMet.HGlo, SimGround.frontGroundGHI, SimGround.rearGroundGHI, SimGround.aveGroundGHI, SimMet.MonthOfYear, SimShading.BackBeamSF, RadProc.SimTilterOpposite.TDir, RadProc.TimeStampAnalyzed); } else { SimBackTilter.BGlo = 0; } // Calculating spectral model effects SimSpectral.Calculate(SimMet.HGlo, RadProc.SimSun.NExtra, RadProc.SimSun.Zenith); try { // Calculate PV Array Output for inputs read in this loop for (int j = 0; j < ReadFarmSettings.SubArrayCount; j++) { // Adjust the IV Curve based on Temperature and Irradiance. SimPVA[j].CalcIVCurveParameters(SimMet.TGlo, SimShading.ShadTDir, SimShading.ShadTDif, SimShading.ShadTRef, SimBackTilter.BGlo, RadProc.SimTilter.IncidenceAngle, SimMet.TAmbient, SimMet.WindSpeed, SimMet.TModMeasured, SimMet.MonthOfYear, SimSpectral.clearnessCorrection); // Check Inverter status to determine if the Inverter is ON or OFF GetInverterStatus(j); // If inverter is off set appropriate variables to 0 and recalculate array in open circuit voltage if (!SimInv[j].isON) { SimInv[j].ACPwrOut = 0; SimInv[j].IOut = 0; SimPVA[j].CalcAtOpenCircuit(); SimPVA[j].Calculate(false, SimPVA[j].Voc, YearsSinceStart); } //performing AC wiring calculations SimACWiring[j].Calculate(SimInv[j]); // Assigning the outputs to the dictionary ReadFarmSettings.Outputlist["SubArray_Current" + (j + 1).ToString()] = SimPVA[j].IOut; ReadFarmSettings.Outputlist["SubArray_Voltage" + (j + 1).ToString()] = SimPVA[j].VOut; ReadFarmSettings.Outputlist["SubArray_Power" + (j + 1).ToString()] = SimPVA[j].POut / 1000; ReadFarmSettings.Outputlist["SubArray_Current_Inv" + (j + 1).ToString()] = SimInv[j].IOut; ReadFarmSettings.Outputlist["SubArray_Voltage_Inv" + (j + 1).ToString()] = SimInv[j].itsOutputVoltage; ReadFarmSettings.Outputlist["SubArray_Power_Inv" + (j + 1).ToString()] = SimInv[j].ACPwrOut / 1000; } // Calculating total farm output and total ohmic loss farmACOutput = 0; farmACOhmicLoss = 0; for (int i = 0; i < SimInv.Length; i++) { farmACOutput += SimInv[i].ACPwrOut; farmACOhmicLoss += SimACWiring[i].ACWiringLoss; } SimTransformer.Calculate(farmACOutput - farmACOhmicLoss); // Calculating outputs that will be assigned for this interval // Shading each component of the Tilted radiaton // Using horizon affected tilted radiation ShadGloLoss = (RadProc.SimTilter.TGlo - SimShading.ShadTGlo) - RadProc.SimHorizonShading.LossGlo; ShadGloFactor = (RadProc.SimTilter.TGlo > 0 ? SimShading.ShadTGlo / RadProc.SimTilter.TGlo : 1); ShadBeamLoss = RadProc.SimHorizonShading.TDir - SimShading.ShadTDir; ShadDiffLoss = RadProc.SimTilter.TDif > 0 ? RadProc.SimHorizonShading.TDif - SimShading.ShadTDif : 0; ShadRefLoss = RadProc.SimTilter.TRef > 0 ? RadProc.SimHorizonShading.TRef - SimShading.ShadTRef : 0; //Calculating total farm level variables. Cleaning them so they are non-cumulative. farmDC = 0; farmDCCurrent = 0; farmDCMismatchLoss = 0; farmDCModuleQualityLoss = 0; farmDCModuleLIDLoss = 0; farmDCModuleAgeingLoss = 0; farmDCOhmicLoss = 0; farmDCSoilingLoss = 0; farmDCTemp = 0; farmTotalModules = 0; farmPNomDC = 0; farmPNomAC = 0; farmACPMinThreshLoss = 0; farmACClippingPower = 0; farmACMaxVoltageLoss = 0; farmACMinVoltageLoss = 0; farmPnom = 0; farmTempLoss = 0; farmRadLoss = 0; for (int i = 0; i < SimPVA.Length; i++) { farmDC += SimPVA[i].POut; farmDCCurrent += SimPVA[i].IOut; farmDCMismatchLoss += Math.Max(0, SimPVA[i].MismatchLoss); farmDCModuleQualityLoss += SimPVA[i].ModuleQualityLoss; farmDCModuleLIDLoss += SimPVA[i].ModuleLIDLoss; farmDCModuleAgeingLoss += SimPVA[i].ModuleAgeingLoss; farmDCOhmicLoss += SimPVA[i].OhmicLosses; farmDCSoilingLoss += SimPVA[i].SoilingLoss; farmDCTemp += SimPVA[i].TModule * SimPVA[i].itsNumModules; farmTotalModules += SimPVA[i].itsNumModules; farmPNomDC += SimPVA[i].itsPNomDCArray; farmPNomAC += SimInv[i].itsPNomArrayAC; farmACPMinThreshLoss += SimInv[i].LossPMinThreshold; farmACClippingPower += SimInv[i].LossClipping; farmACMaxVoltageLoss += SimInv[i].LossHighVoltage; farmACMinVoltageLoss += SimInv[i].LossLowVoltage; farmPnom += (SimPVA[i].itsPNom * SimPVA[i].itsNumModules) * SimPVA[i].RadEff / 1000; farmTempLoss += SimPVA[i].tempLoss; farmRadLoss += SimPVA[i].radLoss; } // Averages all PV Array temperature values farmDCTemp /= farmTotalModules; farmModuleTempAndAmbientTempDiff = farmDCTemp - SimMet.TAmbient; farmDCEfficiency = (RadProc.SimTilter.TGlo > 0 ? farmDC / (RadProc.SimTilter.TGlo * farmArea) : 0) * 100; farmPNomDC = Utilities.ConvertWtokW(farmPNomDC); farmPNomAC = Utilities.ConvertWtokW(farmPNomAC); farmOverAllEff = (RadProc.SimTilter.TGlo > 0 && SimTransformer.POut > 0 ? SimTransformer.POut / (RadProc.SimTilter.TGlo * farmArea) : 0) * 100; farmPR = RadProc.SimTilter.TGlo > 0 && farmPNomDC > 0 && SimTransformer.POut > 0 ? SimTransformer.POut / RadProc.SimTilter.TGlo / farmPNomDC : 0; farmSysIER = (SimTransformer.itsPNom - SimTransformer.POut) / (RadProc.SimTilter.TGlo * 1000); } catch (Exception ce) { ErrorLogger.Log(ce, ErrLevel.FATAL); } // Assigning Outputs for this class. AssignOutputs(); }