// Transposition of the global horizontal irradiance values to the transposed values void Transpose(SimMeteo SimMet) { SimSun.Calculate(SimMet.DayOfYear, HourOfDay); // Calculating the Surface Slope and Azimuth based on the Tracker Chosen SimTracker.Calculate(SimSun.Zenith, SimSun.Azimuth, SimMet.Year, SimMet.DayOfYear); SimTilter.itsSurfaceSlope = SimTracker.SurfSlope; SimTilter.itsSurfaceAzimuth = SimTracker.SurfAzimuth; SimTilterOpposite.itsSurfaceSlope = Math.PI - SimTracker.SurfSlope; SimTilterOpposite.itsSurfaceAzimuth = Math.PI + SimTracker.SurfAzimuth; if (double.IsNaN(SimMet.HDiff)) { // Split global into direct SimSplitter.Calculate(SimSun.Zenith, SimMet.HGlo, NExtra: SimSun.NExtra); } else { // Split global into direct and diffuse SimSplitter.Calculate(SimSun.Zenith, SimMet.HGlo, _HDif: SimMet.HDiff, NExtra: SimSun.NExtra); } // Calculate tilted irradiance SimTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); SimTilterOpposite.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); }
// Calculates AC power of system public void Calculate ( SimMeteo SimMet // Meteological data required for ACPower calculation ) { // Initializing to default ACPower = double.NaN; // Log errors if incorrect parameters are found // No check is made for radiation as it can be negative at night in some cases if (SimMet.MonthOfYear < 1 || SimMet.MonthOfYear > 12) { ErrorLogger.Log("ASTM E2848 Calculate: Month must be betwen 1 and 12", ErrLevel.WARNING); } else if (SimMet.TAmbient < -273.15) { ErrorLogger.Log("ASTM E2848 Calculate: Ambient temperature must be greater than 0K.", ErrLevel.WARNING); } else if (SimMet.WindSpeed < 0) { ErrorLogger.Log("ASTM E2848 Calculate: Windspeed must be greater than 0 m/s.", ErrLevel.WARNING); } else { // If inputs are valid continue with power calculation using the ASTM E2848 Equation (Ref 1) ACPower = Math.Min(SimMet.TGlo * (itsA1 + itsA2 * SimMet.TGlo + itsA3 * SimMet.TAmbient + itsA4 * SimMet.WindSpeed) * itsEAF[SimMet.MonthOfYear - 1], itsPmax); ACPower = Math.Max(ACPower, 0); } // Assigning Outputs for this class. AssignOutputs(); }
public void Calculate ( SimMeteo SimMet // Meteological data from inputfile ) { // Calculating Sun position // Calculate the Solar Azimuth, and Zenith angles [radians] SimSun.itsSurfaceSlope = SimTracker.SurfSlope; SimSun.Calculate(SimMet.DayOfYear, SimMet.HourOfDay); HourOfDay = SimMet.HourOfDay; // The time stamp must be adjusted for sunset and sunrise hours such that the position of the sun is only calculated // for the middle of the interval where the sun is above the horizon. if ((SimMet.TimeStepEnd > SimSun.TrueSunSetHour) && (SimMet.TimeStepBeg < SimSun.TrueSunSetHour)) { HourOfDay = SimMet.TimeStepBeg + (SimSun.TrueSunSetHour - SimMet.TimeStepBeg) / 2; } else if ((SimMet.TimeStepBeg < SimSun.TrueSunRiseHour) && (SimMet.TimeStepEnd > SimSun.TrueSunRiseHour)) { HourOfDay = SimSun.TrueSunRiseHour + (SimMet.TimeStepEnd - SimSun.TrueSunRiseHour) / 2; } // Based on the definition of Input file, use Tilted irradiance or transpose the horizontal irradiance if (ReadFarmSettings.UsePOA == true) { // Check if the meter tilt and surface tilt are equal, if not detranspose the pyranometer if (string.Compare(ReadFarmSettings.CASSYSCSYXVersion, "0.9.2") >= 0) { // Checking if the Meter and Panel Tilt are different: if ((pyranoTilter.itsSurfaceAzimuth != SimTracker.SurfAzimuth) || (pyranoTilter.itsSurfaceSlope != SimTracker.SurfSlope)) { if (SimMet.TGlo < 0) { SimMet.TGlo = 0; if (negativeIrradFlag == false) { ErrorLogger.Log("Global Plane of Array Irradiance contains negative values. CASSYS will set the value to 0.", ErrLevel.WARNING); negativeIrradFlag = true; } } PyranoDetranspose(SimMet); } else { if (SimMet.TGlo < 0) { SimMet.TGlo = 0; if (negativeIrradFlag == false) { ErrorLogger.Log("Global Plane of Array Irradiance contains negative values. CASSYS will set the value to 0.", ErrLevel.WARNING); negativeIrradFlag = true; } } Detranspose(SimMet); } } else { if (SimMet.TGlo < 0) { SimMet.TGlo = 0; if (negativeIrradFlag == false) { ErrorLogger.Log("Global Plane of Array Irradiance contains negative values. CASSYS will the value to 0.", ErrLevel.WARNING); negativeIrradFlag = true; } } Detranspose(SimMet); } } else { if (SimMet.HGlo < 0) { SimMet.HGlo = 0; if (negativeIrradFlag == false) { ErrorLogger.Log("Global Horizontal Irradiance is negative. CASSYS set the value to 0.", ErrLevel.WARNING); negativeIrradFlag = true; } } if (ReadFarmSettings.UseDiffMeasured == true) { if (SimMet.HDiff < 0) { if (negativeIrradFlag == false) { SimMet.HDiff = 0; ErrorLogger.Log("Horizontal Diffuse Irradiance is negative. CASSYS set the value to 0.", ErrLevel.WARNING); negativeIrradFlag = true; } } } else { SimMet.HDiff = double.NaN; } Transpose(SimMet); } // Calculate horizon shading effects SimHorizonShading.Calculate(SimSun.Zenith, SimSun.Azimuth, SimTracker.SurfSlope, SimTracker.SurfAzimuth, SimTilter.TDir, SimTilter.TDif, SimTilter.TRef, SimSplitter.HDir, SimSplitter.HDif, SimTracker.itsTrackMode); // Assigning outputs AssignOutputs(); }
// De-transposition method to the be used if the meter and panel tilt do not match void PyranoDetranspose(SimMeteo SimMet) { if (pyranoTilter.NoPyranoAnglesDefined) { SimTracker.Calculate(SimSun.Zenith, SimSun.Azimuth, SimMet.Year, SimMet.DayOfYear); pyranoTilter.itsSurfaceAzimuth = SimTracker.SurfAzimuth; pyranoTilter.itsSurfaceSlope = SimTracker.SurfSlope; pyranoTilter.IncidenceAngle = SimTracker.IncidenceAngle; } // Lower bound of bisection double HGloLo = 0; // Higher bound of bisection double HGloHi = SimSun.NExtra; // Calculating the Incidence Angle for the current setup double cosInc = Tilt.GetCosIncidenceAngle(SimSun.Zenith, SimSun.Azimuth, pyranoTilter.itsSurfaceSlope, pyranoTilter.itsSurfaceAzimuth); // Trivial case if (SimMet.TGlo <= 0) { SimSplitter.Calculate(SimSun.Zenith, 0, NExtra: SimSun.NExtra); pyranoTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); } else if ((SimSun.Zenith > 87.5 * Util.DTOR) || (cosInc <= Math.Cos(87.5 * Util.DTOR))) { SimMet.HGlo = SimMet.TGlo / ((1 + Math.Cos(pyranoTilter.itsSurfaceSlope)) / 2 + pyranoTilter.itsMonthlyAlbedo[SimMet.MonthOfYear] * (1 - Math.Cos(pyranoTilter.itsSurfaceSlope)) / 2); // Forcing the horizontal irradiance to be composed entirely of diffuse irradiance SimSplitter.HGlo = SimMet.HGlo; SimSplitter.HDif = SimMet.HGlo; SimSplitter.NDir = 0; SimSplitter.HDir = 0; //SimSplitter.Calculate(SimSun.Zenith, HGlo, NExtra: SimSun.NExtra); pyranoTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); } // Otherwise, bisection loop else { // Bisection loop while (Math.Abs(HGloHi - HGloLo) > 0.01) { // Use the central value between the domain to start the bisection, and then solve for TGlo, double HGloAv = (HGloLo + HGloHi) / 2; SimSplitter.Calculate(SimSun.Zenith, _HGlo: HGloAv, NExtra: SimSun.NExtra); pyranoTilter.Calculate(SimSplitter.NDir, SimSplitter.HDif, SimSun.NExtra, SimSun.Zenith, SimSun.Azimuth, SimSun.AirMass, SimMet.MonthOfYear, SimMet.Albedo); double TGloAv = pyranoTilter.TGlo; // Compare the TGloAv calculated from the Horizontal guess to the acutal TGlo and change the bounds for analysis // comparing the TGloAv and TGlo if (TGloAv < SimMet.TGlo) { HGloLo = HGloAv; } else { HGloHi = HGloAv; } } } SimMet.HGlo = SimSplitter.HGlo; // This value of the horizontal global should now be transposed to the tilt value from the array. Transpose(SimMet); }
// Gets the day of the year based on a given date public static void TSBreak(String TimeStamp, out int dayOfYear, out double hour, out int year, out int month, out double nextTimeStampHour, out double baseTimeStampHour, SimMeteo simMeteoParser) { try { CurrentTimeStamp = DateTime.ParseExact(TimeStamp, Util.timeFormat, null); // Checks ensure the time series is always progressing forward. if (ErrorLogger.iterationCount != 1) { if (CurrentTimeStamp != cachedTimeStamp) { // Check if the time stamps are going back in time if (DateTime.Compare(CurrentTimeStamp, cachedTimeStamp) < 0) { ErrorLogger.Log("Time stamps in the Input File go backwards in time. Please check your input file. CASSYS has ended.", ErrLevel.FATAL); } } } else { // Get the next expected time stamp cachedTimeStamp = CurrentTimeStamp; } // Next and Base time stamps are used to check if the sun-rise and sun-set event occurs in between the time stamps under consideration DateTime nextTimeStamp = DateTime.ParseExact(TimeStamp, Util.timeFormat, null); DateTime baseTimeStamp = DateTime.ParseExact(TimeStamp, Util.timeFormat, null); switch (Util.AveragedAt) { case "Beginning": baseTimeStamp = CurrentTimeStamp; nextTimeStamp = baseTimeStamp.AddMinutes(Util.timeStep); CurrentTimeStamp = CurrentTimeStamp.AddMinutes(Util.timeStep / 2D); break; case "End": nextTimeStamp = CurrentTimeStamp; baseTimeStamp = CurrentTimeStamp.AddMinutes(-Util.timeStep); CurrentTimeStamp = CurrentTimeStamp.AddMinutes(-Util.timeStep / 2D); break; default: baseTimeStamp = CurrentTimeStamp.AddMinutes(-Util.timeStep / 2D); nextTimeStamp = CurrentTimeStamp.AddMinutes(Util.timeStep / 2D); CurrentTimeStamp = CurrentTimeStamp.AddMinutes(0); break; } dayOfYear = CurrentTimeStamp.DayOfYear; // Allowing for Leap Years - Assumes February 29 as Feb 28 and all other days as their day number during a normal year if ((CurrentTimeStamp.Month > 2) && (DateTime.IsLeapYear(CurrentTimeStamp.Year))) { if (dayOfYear > 59) { dayOfYear = CurrentTimeStamp.DayOfYear - 1; } } hour = CurrentTimeStamp.Hour + CurrentTimeStamp.Minute / 60D + CurrentTimeStamp.Second / 3600D; year = CurrentTimeStamp.Year; month = CurrentTimeStamp.Month; baseTimeStampHour = baseTimeStamp.Hour + baseTimeStamp.Minute / 60D + baseTimeStamp.Second / 3600D; nextTimeStampHour = nextTimeStamp.Hour + nextTimeStamp.Minute / 60D + nextTimeStamp.Second / 3600D; } catch (FormatException) { dayOfYear = 0; hour = 0; year = 0; month = 0; baseTimeStampHour = 0; nextTimeStampHour = 0; ErrorLogger.Log(TimeStamp + " was not recognized a valid DateTime. The date-time was expected in " + Util.timeFormat + " format. Please check Site definition file. Row was skipped", ErrLevel.WARNING); simMeteoParser.inputRead = false; } }
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 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(); }