// Config the albedo value based on monthly/yearly values defined on file public void ConfigAlbedo() { // Getting the Albedo values either at a monthly or yearly level if (ReadFarmSettings.GetAttribute("Albedo", "Frequency", ErrLevel.WARNING) == "Monthly") { // Initializing the expected list itsMonthlyAlbedo = new double[13]; // Using the month number as the index, populate the Albedo vales from each correspodning node itsMonthlyAlbedo[1] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Jan", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[2] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Feb", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[3] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Mar", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[4] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Apr", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[5] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "May", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[6] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Jun", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[7] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Jul", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[8] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Aug", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[9] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Sep", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[10] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Oct", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[11] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Nov", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[12] = double.Parse(ReadFarmSettings.GetInnerText("Albedo", "Dec", ErrLevel.WARNING, _default: "0.2")); } else { // Initializing the expected list itsMonthlyAlbedo = new double[13]; itsMonthlyAlbedo[1] = Convert.ToDouble(ReadFarmSettings.GetInnerText("Albedo", "Yearly", ErrLevel.WARNING, _default: "0.2")); // Initializing the expected list for (int i = 3; i < itsMonthlyAlbedo.Length + 1; i++) { itsMonthlyAlbedo[i - 1] = itsMonthlyAlbedo[1]; } } }
// Config takes values from the xml, which only needs to be done once public void Config ( ) { // Loads the spectral model from the .csyx document spectralModelUsed = Convert.ToBoolean(ReadFarmSettings.GetInnerText("Spectral", "UseSpectralModel", ErrLevel.WARNING, _default: "false")); if (spectralModelUsed == true) { // Getting the spectral model information from the .csyx file SpectralClearnessIndexStr = ReadFarmSettings.GetInnerText("Spectral", "ClearnessIndex/kt", ErrLevel.WARNING, "0.9", _default: "1"); SpectralClearnessCorrectionStr = ReadFarmSettings.GetInnerText("Spectral", "ClearnessIndex/ktCorrection", ErrLevel.WARNING, "0.9", _default: "0"); // Converts the spectral model imported from the .csyx file into an array of doubles ClearnessIndexArr = SpectralCSVStringtoArray(SpectralClearnessIndexStr); ClearnessCorrectionArr = SpectralCSVStringtoArray(SpectralClearnessCorrectionStr); // If user inputs spectral index/correction data of two different lengths if (ClearnessIndexArr.Length != ClearnessCorrectionArr.Length) { ErrorLogger.Log("The number of clearness correction values was not equal to the number of clearness index values.", ErrLevel.FATAL); } } else { ClearnessIndexArr = new double[] { 1 }; ClearnessCorrectionArr = new double[] { 0 }; } }
// Config takes values from the xml as well as manages calculations that need only to be run once public void Config ( double PanelTiltFixed // The tilt of the panel, used in config if no tracking is selected [radians] , double PanelAzimFixed // The azimuth position of the panel, used in config if no tracking is selected [radians] , TrackMode ArrayTrackMode // The tracking mode, used to determine whether the diffuse fraction can be calculated in config ) { // Loads the Horizon Profile from the .csyx document horizonDefined = Convert.ToBoolean(ReadFarmSettings.GetInnerText("O&S", "DefineHorizonProfile", ErrLevel.WARNING, _default: "false")); if (horizonDefined == true) { // Getting the horizon information from the .csyx file HorizonDefinitionAzimStr = ReadFarmSettings.GetInnerText("O&S", "HorizonAzi", ErrLevel.WARNING, "0.9", _default: "0"); HorizonDefinitionElevStr = ReadFarmSettings.GetInnerText("O&S", "HorizonElev", ErrLevel.WARNING, "0.9", _default: "0"); // Converts the Horizon Profile imported from the.csyx document into an array of doubles HorizonAzim = HorizonCSVStringtoArray(HorizonDefinitionAzimStr); HorizonElev = HorizonCSVStringtoArray(HorizonDefinitionElevStr); // If user inputs horizon azimuth/elevation data of two different lengths if (HorizonAzim.Length != HorizonElev.Length) { ErrorLogger.Log("The number of horizon azimuth values is not equal to the number of horizon elevation values.", ErrLevel.FATAL); } // Extends the Horizon Profile by duplicating the first and last values and transposing them 360 degrees forward and backwards, respectively CalcExtendedHorizon(HorizonAzim, HorizonElev, out HorizonAzimExtended, out HorizonElevExtended); // The full azimuthal range of the Horizon Profile is filled in using Interpolation // The horizon profile must be calculated here both for the non-tracking case, as well as for the ground reflected diffuse factor for all tracking cases // It is calculated separately in the Calculate method for tracking cases HorizonAzimInterpolated = InitializeHorizonProfile(PanelAzimFixed); HorizonElevInterpolated = GetInterpolatedElevationProfile(HorizonAzimExtended, HorizonElevExtended, HorizonAzimInterpolated); // If there is no tracking implemented the diffuse horizon factor only needs to be calculated once, it is otherwise calculated in the Calculate method if (ArrayTrackMode == TrackMode.NOAT && ReadFarmSettings.UsePOA != true) { // Calculates the limiting angle array LimitingAngle = GetLimitingAngleArray(PanelTiltFixed, PanelAzimFixed, HorizonAzimInterpolated); // The shading factor is calculated via numerical computation using the mathematical models described in the CASSYS documentation DiffFactor = GetHorizonDiffuseFactor(PanelTiltFixed, PanelAzimFixed, HorizonAzimInterpolated, HorizonElevInterpolated, LimitingAngle); } // Creating the limiting angle array for the ground, all values are PI/2 RefLimitingAngle = new double[361]; for (int i = 0; i < RefLimitingAngle.Length; i++) { RefLimitingAngle[i] = Math.PI / 2; } // Diffuse part of ground reflected factor only needs to be calculated once. 0s are used for azimuth and tilt of surface, as it represents the ground. RefDiffFactor = GetHorizonDiffuseFactor(0, 0, HorizonAzimInterpolated, HorizonElevInterpolated, RefLimitingAngle); } }
// Config will assign parameter variables their values as obtained from the .CSYX file public void Config ( int ArrayNum , double OperatingVoltage , double Phases , double MaximumInputPower ) { itsACWiringLossPC = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "LossFraction", _ArrayNum: ArrayNum)); itsACWiringRes = itsACWiringLossPC * OperatingVoltage / (MaximumInputPower / (OperatingVoltage * Math.Sqrt(Phases))); }
// Config will assign parameter variables their values as obtained from the .CSYX file public void Config() { // Gathering the parameters for the Sun Class //itsSurfaceSlope = Util.DTOR * double.Parse(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt")); TODO: Re-evaluate. itsSLat = double.Parse(ReadFarmSettings.GetInnerText("Site", "Latitude")); itsSLong = double.Parse(ReadFarmSettings.GetInnerText("Site", "Longitude")); itsLSTFlag = Convert.ToBoolean(ReadFarmSettings.GetInnerText("Site", "UseLocTime")); // If Local Standard Time is to be used, get the reference meridian for the "Standard Time" of the region if (itsLSTFlag) { itsMLong = double.Parse(ReadFarmSettings.GetInnerText("Site", "RefMer")); } }
// Calculation method public void Calculate ( double NDir // normal direct irradiance [W/m2] , double HDif // horizontal diffuse irradiance [W/m2] , double NExtra // normal extraterrestrial irradiance [W/m2] , double SunZenith // zenith angle of sun [radians] , double SunAzimuth // azimuth angle of sun [radians] , double AirMass // air mass [#] , int MonthNum // The month number of the time stamp [1->12] , double MeasAlbedo // albedo read from climate file, if available (NaN otherwise) ) { // Calculate albedo // Read from climate file if (ReadFarmSettings.GetAttribute("Albedo", "Frequency", ErrLevel.WARNING) == "From Climate File") { Albedo = MeasAlbedo; } // Otherwise, read from monthly array else { Albedo = itsMonthlyAlbedo[MonthNum]; } // Calculate direct horizontal if direct normal is provided double HDir = NDir * Math.Cos(SunZenith); // call Perez et al. algorithm or Hay algorithm switch (itsTiltAlgorithm) { case TiltAlgorithm.PEREZ: TGlo = GetTiltCompIrradPerez(out TDir, out TDif, out TRef, HDir, HDif, NExtra, SunZenith, SunAzimuth, AirMass, MonthNum); break; case TiltAlgorithm.HAY: TGlo = GetTiltCompIrradHay(out TDir, out TDif, out TRef, HDir, HDif, NExtra, SunZenith, SunAzimuth, MonthNum); break; default: itsTiltAlgorithm = TiltAlgorithm.HAY; break; } // Compute the incidence angle from the Tilt class IncidenceAngle = Tilt.GetIncidenceAngle(SunZenith, SunAzimuth, itsSurfaceSlope, itsSurfaceAzimuth); }
// Config will assign parameter variables their values as obtained from the .CSYX file public void ConfigPyranometer() { try { // Getting the parameter values itsSurfaceSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("InputFile", "MeterTilt", ErrLevel.WARNING, _default: "N/A")); itsSurfaceAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("InputFile", "MeterAzimuth", ErrLevel.WARNING, _default: "N/A")); NoPyranoAnglesDefined = false; } catch { ErrorLogger.Log("Irradiance Meter Tilt or Azimuth were not specified. CASSYS will assume these values are the same as the Array Tilt and Azimuth. This can be changed in the Climate File Sheet in the interface.", ErrLevel.WARNING); NoPyranoAnglesDefined = true; } // Assign the albedo parameters from the .CSYX file ConfigAlbedo(); }
// Config the albedo value based on monthly/yearly values defined on file void ConfigAlbedo() { // Determine whether to use site albedo values, or inter-row specific albedo values string whichAlbedo; if (Convert.ToBoolean(ReadFarmSettings.GetInnerText("BifAlbedo", "UseBifAlb", ErrLevel.FATAL))) { whichAlbedo = "BifAlbedo"; } else { whichAlbedo = "Albedo"; } // Initializing the expected list itsMonthlyAlbedo = new double[13]; if (ReadFarmSettings.GetAttribute(whichAlbedo, "Frequency", ErrLevel.WARNING) == "Monthly") { // Using the month number as the index, populate the albedo vales from each corresponding node itsMonthlyAlbedo[1] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Jan", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[2] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Feb", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[3] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Mar", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[4] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Apr", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[5] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "May", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[6] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Jun", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[7] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Jul", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[8] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Aug", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[9] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Sep", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[10] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Oct", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[11] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Nov", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[12] = double.Parse(ReadFarmSettings.GetInnerText(whichAlbedo, "Dec", ErrLevel.WARNING, _default: "0.2")); } else if (ReadFarmSettings.GetAttribute(whichAlbedo, "Frequency", ErrLevel.WARNING) == "Yearly") { itsMonthlyAlbedo[1] = Convert.ToDouble(ReadFarmSettings.GetInnerText(whichAlbedo, "Yearly", ErrLevel.WARNING, _default: "0.2")); // Apply the same albedo to all months for (int i = 3; i < itsMonthlyAlbedo.Length + 1; i++) { itsMonthlyAlbedo[i - 1] = itsMonthlyAlbedo[1]; } } }
// Config will assign parameter variables their values as obtained from the XML file public void Config() { // Getting the Tilt Algorithm for the Simulation if (ReadFarmSettings.GetInnerText("SiteDef", "TransEnum", ErrLevel.WARNING) == "0") { itsTiltAlgorithm = TiltAlgorithm.HAY; } else if (ReadFarmSettings.GetInnerText("SiteDef", "TransEnum", ErrLevel.WARNING) == "1") { itsTiltAlgorithm = TiltAlgorithm.PEREZ; } else { ErrorLogger.Log("Tilter: Invalid tilt algorithm chosen by User. CASSYS uses Hay as default.", ErrLevel.WARNING); itsTiltAlgorithm = TiltAlgorithm.HAY; } // Assign the albedo parameters from the .CSYX file ConfigAlbedo(); }
// Config the albedo value based on monthly/yearly values defined on file void ConfigAlbedo() { // Initializing the expected list itsMonthlyAlbedo = new double[13]; if (ReadFarmSettings.GetAttribute("BifAlbedo", "Frequency", ErrLevel.WARNING) == "Monthly") { // Using the month number as the index, populate the albedo vales from each corresponding node itsMonthlyAlbedo[1] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Jan", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[2] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Feb", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[3] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Mar", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[4] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Apr", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[5] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "May", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[6] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Jun", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[7] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Jul", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[8] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Aug", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[9] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Sep", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[10] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Oct", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[11] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Nov", ErrLevel.WARNING, _default: "0.2")); itsMonthlyAlbedo[12] = double.Parse(ReadFarmSettings.GetInnerText("BifAlbedo", "Dec", ErrLevel.WARNING, _default: "0.2")); } else if (ReadFarmSettings.GetAttribute("BifAlbedo", "Frequency", ErrLevel.WARNING) == "Yearly") { itsMonthlyAlbedo[1] = Convert.ToDouble(ReadFarmSettings.GetInnerText("BifAlbedo", "Yearly", ErrLevel.WARNING, _default: "0.2")); // Apply the same albedo to all months for (int i = 2; i < 13; i++) { itsMonthlyAlbedo[i] = itsMonthlyAlbedo[1]; } } else // read from file or equal to site albedo - set monthly valus to NaN for safety { for (int i = 1; i < 13; i++) { itsMonthlyAlbedo[i] = Double.NaN; } } }
// Config will assign parameter variables their values as obtained from the .CSYX file public void Config() { try { // Gathering all the parameters from ASTM E2848 Element itsPmax = double.Parse(ReadFarmSettings.GetInnerText("ASTM", "SystemPmax", _Error: ErrLevel.FATAL)); itsA1 = double.Parse(ReadFarmSettings.GetInnerText("ASTM/Coeffs", "ASTM1", _Error: ErrLevel.FATAL)); itsA2 = double.Parse(ReadFarmSettings.GetInnerText("ASTM/Coeffs", "ASTM2", _Error: ErrLevel.FATAL)); itsA3 = double.Parse(ReadFarmSettings.GetInnerText("ASTM/Coeffs", "ASTM3", _Error: ErrLevel.FATAL)); itsA4 = double.Parse(ReadFarmSettings.GetInnerText("ASTM/Coeffs", "ASTM4", _Error: ErrLevel.FATAL)); // Looping through and assigning all EAF parameters from EAF Element string[] months = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; for (int i = 0; i < 12; i++) { itsEAF[i] = double.Parse(ReadFarmSettings.GetInnerText("ASTM/EAF", months[i], _Error: ErrLevel.FATAL)); } } catch (Exception e) { ErrorLogger.Log("ASTM E2848 Config: " + e.Message, ErrLevel.FATAL); } }
//Config will determine and assign values for the losses at the transformer using an .CSYX file public void Config() { //call variables from ReadFarmSettings to determine version of CASSYS new ReadFarmSettings(); string version = ReadFarmSettings.doc.SelectSingleNode("/Site/Version").InnerXml; // Config will find the Iron Losses, and Global losses from the file. The resistive loss, etc are calculated by the program from these two values. itsPIronLoss = Convert.ToDouble(ReadFarmSettings.GetInnerText("Transformer", "PIronLossTrf", ErrLevel.WARNING)) * 1000; itsPNom = Convert.ToDouble(ReadFarmSettings.GetInnerText("Transformer", "PNomTrf", ErrLevel.WARNING)) * 1000; itsPResLss = Convert.ToDouble(ReadFarmSettings.GetInnerText("Transformer", "PResLssTrf", ErrLevel.WARNING)) * 1000; if (string.CompareOrdinal("1.2.0", version) > 0) { itsPGlobLoss = Convert.ToDouble(ReadFarmSettings.GetInnerText("Transformer", "PGlobLossTrf", ErrLevel.WARNING)) * 1000; } else { itsPGlobLoss = Convert.ToDouble(ReadFarmSettings.GetInnerText("Transformer", "PFullLoadLss", ErrLevel.WARNING)) * 1000; } // Parameters that determine if the transformer remains ON at night, and initializing the disconnection of the transformer. isNightlyDisconnected = Convert.ToBoolean(ReadFarmSettings.GetInnerText("Transformer", "NightlyDisconnect", ErrLevel.WARNING, _default: "False")); }
// Finding and assigning the number of CellRows public static void AssignCellRowsNum() { if (string.Compare(SystemMode, "GridConnected") == 0) { // Getting the number of back cell rows for this file if (Convert.ToBoolean(ReadFarmSettings.GetInnerText("Bifacial", "UseBifacialModel", ErrLevel.FATAL))) { switch (ReadFarmSettings.GetAttribute("O&S", "ArrayType", ErrLevel.FATAL)) { case "Fixed Tilted Plane": numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWid", ErrLevel.WARNING, _default: "1")); break; case "Unlimited Rows": numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWid", ErrLevel.WARNING, _default: "1")); break; case "Single Axis Elevation Tracking (E-W)": numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWidSAET", ErrLevel.WARNING, _default: "1")); break; case "Single Axis Horizontal Tracking (N-S)": numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWidSAST", ErrLevel.WARNING, _default: "1")); break; default: ErrorLogger.Log("Bifacial is not supported for the selected orientation and shading.", ErrLevel.FATAL); break; } } else { numCellRows = Util.NUM_CELLS_PANEL; } } }
public static void Main(string[] args) { // Showing the Header of the Simulation Program in the Console if (ReadFarmSettings.outputMode != "str") { ReadFarmSettings.ShowHeader(); } // Declaring a new simulation object PVPlant = new Simulation(); SiteSettings = new XmlDocument(); // Load CSYX file try { if (args.Length == 0) { // Set batch mode to true, and Ask user for all the arguments Console.WriteLine("Status: You are running CASSYS in batch mode."); Console.WriteLine("Status: You will need to provide a CASSYS Site File (.CSYX), Input File, and Output File Path."); Console.WriteLine("Enter CASSYS Site (.CSYX) File Path: "); String SimFilePath = Console.ReadLine(); Console.WriteLine("Enter Input file path (.csv file): "); SimInputFilePath = Console.ReadLine(); Console.WriteLine("Enter an Output file path (.csv file, Note: if the file exists this will append your results to the file): "); SimOutputFilePath = Console.ReadLine(); SiteSettings.Load(SimFilePath.Replace("\\", "/")); ErrorLogger.RunFileName = SimFilePath; ErrorLogger.Clean(); } else { // Get .CSYX file name from CMDPrompt, and Load document SiteSettings.Load(args[0]); ErrorLogger.RunFileName = args[0]; } // Assigning Xml document to ReadFarmSettings to allow reading of XML document. Check CSYX version ReadFarmSettings.doc = SiteSettings; ReadFarmSettings.CheckCSYXVersion(); // Variable Parameter mode if (ReadFarmSettings.doc.SelectSingleNode("/Site/Iterations/Iteration1") != null) { variableParameters(args); } else if (args.Length == 1) { // Running the Simulation based on the CASSYS Configuration File provided ReadFarmSettings.batchMode = false; PVPlant.Simulate(SiteSettings); // Show the end of simulation message, window should be kept open for longer ReadFarmSettings.ShowFooter(); } else if (args.Length == 3) { // Set batch mode to true, and Run from command prompt arguments directly ReadFarmSettings.batchMode = true; PVPlant.Simulate(SiteSettings, _Input: args[1], _Output: args[2]); } else if (args.Length == 0) { PVPlant.Simulate(SiteSettings, _Input: SimInputFilePath.Replace("\\", "/"), _Output: SimOutputFilePath.Replace("\\", "/")); } else { // CASSYS needs a site file name to run, so warn the user and exit the program. ErrorLogger.Log("No site file provided for CASSYS to simulate. Please select a valid .CSYX file.", ErrLevel.FATAL); } } catch (Exception) { ErrorLogger.Log("CASSYS was unable to access or load the Site XML file. Simulation has ended.", ErrLevel.FATAL); } }
static void variableParameters(string[] args) { ReadFarmSettings.outputMode = "var"; ReadFarmSettings.batchMode = true; // Assign input and output file path for simulation run. // File paths have already been assigned in case of no input arguments if (args.Length == 1) { SimInputFilePath = null; SimOutputFilePath = null; } else if (args.Length == 3) { SimInputFilePath = args[1]; SimOutputFilePath = args[2]; } // Gathering information on parameter to be changed paramPath = ReadFarmSettings.GetAttribute("Iteration1", "ParamPath", _Error: ErrLevel.FATAL); start = double.Parse(ReadFarmSettings.GetAttribute("Iteration1", "Start", _Error: ErrLevel.FATAL)); end = double.Parse(ReadFarmSettings.GetAttribute("Iteration1", "End", _Error: ErrLevel.FATAL)); interval = double.Parse(ReadFarmSettings.GetAttribute("Iteration1", "Interval", _Error: ErrLevel.FATAL)); // Number of simulations ran ReadFarmSettings.runNumber = 1; // Data table to hold output for various runs ReadFarmSettings.outputTable = new DataTable(); // Row used to hold parameter information ReadFarmSettings.outputTable.Rows.InsertAt(ReadFarmSettings.outputTable.NewRow(), 0); // Loop through values of variable parameter for (double value = start; value <= end; value = value + interval) { // iterationCount used to determine current row in data base ErrorLogger.iterationCount = 0; // Reset outputheader between runs ReadFarmSettings.OutputHeader = null; // Vary parameter in XML object SiteSettings.SelectSingleNode(paramPath).InnerText = Convert.ToString(value); // Creating column to store data from run // A single column stores the all data from the simulation run ReadFarmSettings.outputTable.Columns.Add(paramPath + "=" + value, typeof(String)); PVPlant.Simulate(SiteSettings, SimInputFilePath, SimOutputFilePath); ReadFarmSettings.runNumber++; } try { // Write data table output to csv file OutputFileWriter = new StreamWriter(ReadFarmSettings.SimOutputFile); WriteDataTable(ReadFarmSettings.outputTable, OutputFileWriter); OutputFileWriter.Dispose(); } catch (IOException ex) { ErrorLogger.Log("Error occured while creating to output file. Error: " + ex, ErrLevel.FATAL); } }
// Config manages calculations and initializations that need only to be run once public void Config() { useBifacial = Convert.ToBoolean(ReadFarmSettings.GetInnerText("Bifacial", "UseBifacialModel", ErrLevel.FATAL)); if (useBifacial) { switch (ReadFarmSettings.GetAttribute("O&S", "ArrayType", ErrLevel.FATAL)) { // In all cases, pitch must be normalized to panel slope lengths case "Fixed Tilted Plane": if (String.Compare(ReadFarmSettings.CASSYSCSYXVersion, "0.9.3") >= 0) { itsPanelTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTiltFix", ErrLevel.FATAL)); } else { itsPanelTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); } // itsPitch will be assigned in the below (numRows == 1) conditional itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CollBandWidthFix", ErrLevel.FATAL)); itsClearance = Convert.ToDouble(ReadFarmSettings.GetInnerText("Bifacial", "GroundClearance", ErrLevel.FATAL)) / itsArrayBW; // Find number of cell rows on back of array [#] numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWid", ErrLevel.WARNING, _default: "1")); numRows = 1; break; case "Unlimited Rows": itsPanelTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CollBandWidth", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "Pitch", ErrLevel.FATAL)) / itsArrayBW; itsClearance = Convert.ToDouble(ReadFarmSettings.GetInnerText("Bifacial", "GroundClearance", ErrLevel.FATAL)) / itsArrayBW; // Find number of cell rows on back of array [#] numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWid", ErrLevel.WARNING, _default: "1")); numRows = int.Parse(ReadFarmSettings.GetInnerText("O&S", "RowsBlock", ErrLevel.FATAL)); break; case "Single Axis Elevation Tracking (E-W)": itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAET", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAET", ErrLevel.FATAL)) / itsArrayBW; // Find number of cell rows on back of array [#] numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWidSAET", ErrLevel.WARNING, _default: "1")); numRows = int.Parse(ReadFarmSettings.GetInnerText("O&S", "RowsBlockSAET", ErrLevel.FATAL)); break; case "Single Axis Horizontal Tracking (N-S)": itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAST", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAST", ErrLevel.FATAL)) / itsArrayBW; // Find number of cell rows on back of array [#] numCellRows = Util.NUM_CELLS_PANEL * int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWidSAST", ErrLevel.WARNING, _default: "1")); numRows = int.Parse(ReadFarmSettings.GetInnerText("O&S", "RowsBlockSAST", ErrLevel.FATAL)); break; default: ErrorLogger.Log("Bifacial is not supported for the selected orientation and shading.", ErrLevel.FATAL); break; } structLossFactor = Convert.ToDouble(ReadFarmSettings.GetInnerText("Bifacial", "StructBlockingFactor", ErrLevel.FATAL)); if (numRows == 1) { // Pitch is needed for a single row because of ground patch calculations and geometry. Take value 100x greater than array bandwidth. itsPitch = 100; itsRowType = RowType.SINGLE; } else { itsRowType = RowType.INTERIOR; } // Initialize arrays backGlo = new double[numCellRows]; backDir = new double[numCellRows]; backDif = new double[numCellRows]; backFroRef = new double[numCellRows]; backGroRef = new double[numCellRows]; ConfigAlbedo(); } else { // Allows back irradiance profile output even when bifacial is disabled numCellRows = Util.NUM_CELLS_PANEL; backGlo = new double[numCellRows]; } }
// 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); } }
// Config will assign parameter variables their values as obtained from the .CSYX file public void Config(int ArrayNum) { itsNomOutputPwr = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "PNomAC", _ArrayNum: ArrayNum)) * 1000; itsMinVoltage = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Min.V", _ArrayNum: ArrayNum)); itsMaxVoltage = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Max.V", _ArrayNum: ArrayNum, _default: itsMppWindowMax.ToString())); itsNumInverters = int.Parse(ReadFarmSettings.GetInnerText("Inverter", "NumInverters", _ArrayNum: ArrayNum)); itsOutputVoltage = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Output", _ArrayNum: ArrayNum)); itsPNomArrayAC = itsNomOutputPwr * itsNumInverters; itsThresholdPwr = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Threshold", _ArrayNum: ArrayNum)); // Assigning the number of output phases; if (ReadFarmSettings.GetInnerText("Inverter", "Type", _ArrayNum: ArrayNum) == "Tri") { // Tri-phase Inverter outputPhases = 3; } else if (ReadFarmSettings.GetInnerText("Inverter", "Type", _ArrayNum: ArrayNum) == "Bi") { // Bi-phase inverter outputPhases = 2; } else if (ReadFarmSettings.GetInnerText("Inverter", "Type", _ArrayNum: ArrayNum) == "Mono") { outputPhases = 1; } else { ErrorLogger.Log("The Inverter output phase definition was not found. Please check the Inverter in the Inverter database.", ErrLevel.FATAL); } // Assigning the operation type of the Inverter if (ReadFarmSettings.GetInnerText("Inverter", "Oper.", _ArrayNum: ArrayNum) == "MPPT") { itsMPPTracking = true; itsMppWindowMin = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "MinMPP", _ArrayNum: ArrayNum)); itsMppWindowMax = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "MaxMPP", _ArrayNum: ArrayNum)); } else { ErrorLogger.Log("The Inverter does not operate in MPPT according to the configurations. CASSYS does not support these inverters at this time. Simulation has ended.", ErrLevel.FATAL); itsMPPTracking = false; } // Assigning if the Inverter is Bipolar or not if ((ReadFarmSettings.GetInnerText("Inverter", "BipolarInput", _ArrayNum: ArrayNum, _Error: ErrLevel.WARNING, _default: "false") == "Yes") || (ReadFarmSettings.GetInnerText("Inverter", "BipolarInput", _ArrayNum: ArrayNum, _Error: ErrLevel.WARNING, _default: "false") == "True") || (ReadFarmSettings.GetInnerText("Inverter", "BipolarInput", _ArrayNum: ArrayNum, _Error: ErrLevel.WARNING, _default: "false") == "Bipolar inputs")) { isBipolar = true; // itsThresholdPwr = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Threshold", _ArrayNum: ArrayNum)); } else { isBipolar = false; // itsThresholdPwr = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Threshold", _ArrayNum: ArrayNum)); } // Inverter Efficiency Curve Configuration threeCurves = Convert.ToBoolean(ReadFarmSettings.GetInnerText("Inverter", "MultiCurve", _ArrayNum: ArrayNum)); // Initialization of efficiency curve arrays itsMedEff[0] = new double[8]; itsMedEff[1] = new double[8]; itsHighEff[0] = new double[8]; itsHighEff[1] = new double[8]; // Initialization of efficiency curve arrays itsOnlyEff[0] = new double[8]; itsOnlyEff[1] = new double[8]; // Configuration of the Efficiency Curves for the Inverter ConfigEffCurves(ArrayNum, threeCurves); if (string.Compare(ReadFarmSettings.CASSYSCSYXVersion, "1.2.0") >= 0 && Convert.ToBoolean(ReadFarmSettings.GetInnerText("System", "ACWiringLossAtSTC", _Error: ErrLevel.FATAL))) { itsMaxSubArrayACEff = 1; } else if (threeCurves) { itsMaxSubArrayACEff = itsMedEff[1][itsMedEff[1].Length - 1] / itsMedEff[0][itsMedEff[0].Length - 1]; } else { itsMaxSubArrayACEff = itsOnlyEff[1][itsOnlyEff[1].Length - 1] / itsOnlyEff[0][itsOnlyEff[0].Length - 1]; } }
public void Config() { // Determine the type of Array layout switch (ReadFarmSettings.GetAttribute("O&S", "ArrayType", ErrLevel.FATAL)) { case "Unlimited Rows": itsShadModel = ShadModel.UR; break; case "Fixed Tilted Plane": itsShadModel = ShadModel.FT; break; case "Fixed Tilted Plane Seasonal Adjustment": itsShadModel = ShadModel.FT; break; case "Single Axis Elevation Tracking (E-W)": itsShadModel = ShadModel.TE; break; case "Single Axis Horizontal Tracking (N-S)": itsShadModel = ShadModel.TS; break; default: itsShadModel = ShadModel.None; break; } // reading and assigning values to the parameters for the Shading class based on the Array layout switch (itsShadModel) { case ShadModel.UR: // Defining all the parameters for the shading of a unlimited row array configuration itsCollTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); itsCollAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "Azimuth", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "Pitch", ErrLevel.FATAL)); itsCollBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CollBandWidth", ErrLevel.FATAL)); itsRowsBlock = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "RowsBlock", ErrLevel.FATAL)); itsRowBlockFactor = (itsRowsBlock - 1) / itsRowsBlock; // Collecting definitions for cell based shading models or preparing for its absence useCellBasedShading = Convert.ToBoolean(ReadFarmSettings.GetInnerText("O&S", "UseCellVal", ErrLevel.WARNING, _default: "false")); // Set up the arrays to allow for shading calculations according to electrical effect if (useCellBasedShading) { CellSize = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CellSize", ErrLevel.FATAL)) / 100D; itsNumModTransverseStrings = int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWid", ErrLevel.FATAL)); itsRowBlockFactor = 1; // No row related shading adjustments should be applied. // Use cell based shading to calculate the effect on the beam shading factor // The shading factor gets worse in steps based on how much of the collector bandwidth is currently under shadowed length cellSetup = new double[itsNumModTransverseStrings + 1]; shadingPC = new double[itsNumModTransverseStrings + 1]; // Defining the arrays needed for Number of cells in each string (transverse) and shading % for (int i = 1; i <= itsNumModTransverseStrings; i++) { cellSetup[i] = (double)i / (double)itsNumModTransverseStrings * (itsCollBW / CellSize); shadingPC[i] = (double)i / (double)itsNumModTransverseStrings; } } break; case ShadModel.TE: itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAET", ErrLevel.FATAL)); itsCollBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAET", ErrLevel.FATAL)); itsRowsBlock = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "RowsBlockSAET", ErrLevel.FATAL)); itsRowBlockFactor = (itsRowsBlock - 1) / itsRowsBlock; // NB: Using same formula as Unlimited Rows, with SAET added to variable names // Collecting definitions for cell based shading models or preparing for its absence useCellBasedShading = Convert.ToBoolean(ReadFarmSettings.GetInnerText("O&S", "UseCellValSAET", ErrLevel.WARNING, _default: "false")); // Set up the arrays to allow for shading calculations according to electrical effect if (useCellBasedShading) { CellSize = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CellSizeSAET", ErrLevel.FATAL)) / 100D; itsNumModTransverseStrings = int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWidSAET", ErrLevel.FATAL)); itsRowBlockFactor = 1; // No row related shading adjustments should be applied. // Use cell based shading to calculate the effect on the beam shading factor // The shading factor gets worse in steps based on how much of the collector bandwidth is currently under shadowed length cellSetup = new double[itsNumModTransverseStrings + 1]; shadingPC = new double[itsNumModTransverseStrings + 1]; // Defining the arrays needed for Number of cells in each string (transverse) and shading % for (int i = 1; i <= itsNumModTransverseStrings; i++) { cellSetup[i] = (double)i / (double)itsNumModTransverseStrings * (itsCollBW / CellSize); shadingPC[i] = (double)i / (double)itsNumModTransverseStrings; } } break; case ShadModel.TS: itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAST", ErrLevel.FATAL)); itsCollBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAST", ErrLevel.FATAL)); itsRowsBlock = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "RowsBlockSAST", ErrLevel.FATAL)); itsRowBlockFactor = (itsRowsBlock - 1) / itsRowsBlock; // NB: Using same formula as Unlimited Rows, with SAST added to variable names // Collecting definitions for cell based shading models or preparing for its absence useCellBasedShading = Convert.ToBoolean(ReadFarmSettings.GetInnerText("O&S", "UseCellValSAST", ErrLevel.WARNING, _default: "false")); // Set up the arrays to allow for shading calculations according to electrical effect if (useCellBasedShading) { CellSize = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CellSizeSAST", ErrLevel.FATAL)) / 100D; itsNumModTransverseStrings = int.Parse(ReadFarmSettings.GetInnerText("O&S", "StrInWidSAST", ErrLevel.FATAL)); itsRowBlockFactor = 1; // No row related shading adjustments should be applied. // Use cell based shading to calculate the effect on the beam shading factor // The shading factor gets worse in steps based on how much of the collector bandwidth is currently under shadowed length cellSetup = new double[itsNumModTransverseStrings + 1]; shadingPC = new double[itsNumModTransverseStrings + 1]; // Defining the arrays needed for Number of cells in each string (transverse) and shading % for (int i = 1; i <= itsNumModTransverseStrings; i++) { cellSetup[i] = (double)i / (double)itsNumModTransverseStrings * (itsCollBW / CellSize); shadingPC[i] = (double)i / (double)itsNumModTransverseStrings; } } break; case ShadModel.FT: // Defining the parameters for the shading for a fixed tilt configuration FrontSLA = 0; BackSLA = 0; // Running one-time only methods - the shading factors applied to diffuse and ground reflected component are constant and 1. DiffuseSF = 1; ReflectedSF = 1; break; case ShadModel.None: // No shading applies. DiffuseSF = 1; ReflectedSF = 1; BeamSF = 1; break; } }
// Configuring the required elements for a grid connected system public void Config() { SimShading.Config(); SimTransformer.Config(); SimSpectral.Config(); // Array of PVArray, Inverter and Wiring objects based on the number of Sub-Arrays SimPVA = new PVArray[ReadFarmSettings.SubArrayCount]; SimInv = new Inverter[ReadFarmSettings.SubArrayCount]; SimACWiring = new ACWiring[ReadFarmSettings.SubArrayCount]; // Initialize and Configure PVArray and Inverter Objects through their .CSYX file for (int SubArrayCount = 0; SubArrayCount < ReadFarmSettings.SubArrayCount; SubArrayCount++) { SimInv[SubArrayCount] = new Inverter(); SimInv[SubArrayCount].Config(SubArrayCount + 1); SimPVA[SubArrayCount] = new PVArray(); SimPVA[SubArrayCount].Config(SubArrayCount + 1); SimACWiring[SubArrayCount] = new ACWiring(); // If 'at Pnom' specified for AC Loss Fraction in version 1.2.0 if (string.Compare(ReadFarmSettings.CASSYSCSYXVersion, "1.2.0") >= 0 && ReadFarmSettings.GetInnerText("System", "ACWiringLossAtSTC", _Error: ErrLevel.WARNING) == "False") { SimACWiring[SubArrayCount].Config(SubArrayCount + 1, SimInv[SubArrayCount].itsOutputVoltage, SimInv[SubArrayCount].outputPhases, SimInv[SubArrayCount].itsPNomArrayAC); } else { SimACWiring[SubArrayCount].Config(SubArrayCount + 1, SimInv[SubArrayCount].itsOutputVoltage, SimInv[SubArrayCount].outputPhases, SimInv[SubArrayCount].itsMaxSubArrayACEff * SimPVA[SubArrayCount].itsPNomDCArray); } farmArea += SimPVA[SubArrayCount].itsRoughArea; } }
// Gathering the tracker mode, and relevant operational limits, and tracking axis characteristics. public void Config() { switch (ReadFarmSettings.GetAttribute("O&S", "ArrayType", ErrLevel.FATAL)) { case "Fixed Tilted Plane": itsTrackMode = TrackMode.NOAT; if (String.Compare(ReadFarmSettings.CASSYSCSYXVersion, "0.9.3") >= 0) { SurfSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTiltFix", ErrLevel.FATAL)); SurfAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AzimuthFix", ErrLevel.FATAL)); } else { SurfSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); SurfAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "Azimuth", ErrLevel.FATAL)); } break; case "Fixed Tilted Plane Seasonal Adjustment": itsTrackMode = TrackMode.FTSA; SurfAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AzimuthSeasonal", ErrLevel.FATAL)); itsSummerMonth = DateTime.ParseExact(ReadFarmSettings.GetInnerText("O&S", "SummerMonth", _Error: ErrLevel.FATAL), "MMM", CultureInfo.CurrentCulture).Month; itsWinterMonth = DateTime.ParseExact(ReadFarmSettings.GetInnerText("O&S", "WinterMonth", _Error: ErrLevel.FATAL), "MMM", CultureInfo.CurrentCulture).Month; itsSummerDay = int.Parse(ReadFarmSettings.GetInnerText("O&S", "SummerDay", _Error: ErrLevel.FATAL)); itsWinterDay = int.Parse(ReadFarmSettings.GetInnerText("O&S", "WinterDay", _Error: ErrLevel.FATAL)); itsPlaneTiltSummer = Util.DTOR * double.Parse(ReadFarmSettings.GetInnerText("O&S", "PlaneTiltSummer", _Error: ErrLevel.FATAL)); itsPlaneTiltWinter = Util.DTOR * double.Parse(ReadFarmSettings.GetInnerText("O&S", "PlaneTiltWinter", _Error: ErrLevel.FATAL)); // Assume the simualtion will begin when the array is in the summer tilt SurfSlope = itsPlaneTiltSummer; break; case "Unlimited Rows": itsTrackMode = TrackMode.NOAT; // Defining all the parameters for the shading of a unlimited row array configuration SurfSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); SurfAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "Azimuth", ErrLevel.FATAL)); break; case "Single Axis Elevation Tracking (E-W)": // Tracker Parameters itsTrackMode = TrackMode.SAXT; itsTrackerAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AxisAzimuthSAET", ErrLevel.FATAL)); itsTrackerSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AxisTiltSAET", ErrLevel.FATAL)); // Operational Limits itsMinTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MinTiltSAET", ErrLevel.FATAL)); itsMaxTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MaxTiltSAET", ErrLevel.FATAL)); // Backtracking Options useBackTracking = Convert.ToBoolean(ReadFarmSettings.GetInnerText("O&S", "BacktrackOptSAET", ErrLevel.WARNING, _default: "false")); if (useBackTracking) { itsTrackerPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAET", ErrLevel.FATAL)); itsTrackerBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAET", ErrLevel.FATAL)); } break; case "Single Axis Horizontal Tracking (N-S)": // Tracker Parameters itsTrackMode = TrackMode.SAXT; itsTrackerSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AxisTiltSAST", ErrLevel.FATAL)); itsTrackerAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AxisAzimuthSAST", ErrLevel.FATAL)); // Operational Limits itsMaxTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "RotationMaxSAST", ErrLevel.FATAL)); // Backtracking Options useBackTracking = Convert.ToBoolean(ReadFarmSettings.GetInnerText("O&S", "BacktrackOptSAST", ErrLevel.WARNING, _default: "false")); if (useBackTracking) { itsTrackerPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAST", ErrLevel.FATAL)); itsTrackerBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAST", ErrLevel.FATAL)); } break; case "Tilt and Roll Tracking": // Tracker Parameters itsTrackMode = TrackMode.SAXT; itsTrackerSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AxisTiltTART", ErrLevel.FATAL)); itsTrackerAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AxisAzimuthTART", ErrLevel.FATAL)); // Operational Limits itsMinRotationAngle = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "RotationMinTART", ErrLevel.FATAL)); itsMaxRotationAngle = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "RotationMaxTART", ErrLevel.FATAL)); break; case "Two Axis Tracking": itsTrackMode = TrackMode.TAXT; // Operational Limits itsMinTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MinTiltTAXT", ErrLevel.FATAL)); itsMaxTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MaxTiltTAXT", ErrLevel.FATAL)); itsAzimuthRef = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AzimuthRefTAXT", ErrLevel.FATAL)); itsMinAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MinAzimuthTAXT", ErrLevel.FATAL)); itsMaxAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MaxAzimuthTAXT", ErrLevel.FATAL)); break; case "Azimuth (Vertical Axis) Tracking": itsTrackMode = TrackMode.AVAT; // Surface Parameters SurfSlope = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTiltAVAT", ErrLevel.FATAL)); // Operational Limits itsAzimuthRef = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "AzimuthRefAVAT", ErrLevel.FATAL)); itsMinAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MinAzimuthAVAT", ErrLevel.FATAL)); itsMaxAzimuth = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "MaxAzimuthAVAT", ErrLevel.FATAL)); break; default: ErrorLogger.Log("No orientation and shading was specified by the user.", ErrLevel.FATAL); break; } }
// Config manages calculations and initializations that need only to be run once public void Config() { bool useBifacial = Convert.ToBoolean(ReadFarmSettings.GetInnerText("Bifacial", "UseBifacialModel", ErrLevel.FATAL)); if (useBifacial) { // Number of segments into which to divide up the ground [#] numGroundSegs = Util.NUM_GROUND_SEGS; switch (ReadFarmSettings.GetAttribute("O&S", "ArrayType", ErrLevel.FATAL)) { // In all cases, pitch and clearance must be normalized to panel slope lengths case "Fixed Tilted Plane": itsTrackMode = TrackMode.NOAT; if (String.Compare(ReadFarmSettings.CASSYSCSYXVersion, "0.9.3") >= 0) { itsPanelTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTiltFix", ErrLevel.FATAL)); } else { itsPanelTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); } // itsPitch will be assigned in the below (numRows == 1) conditional itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CollBandWidthFix", ErrLevel.FATAL)); itsClearance = Convert.ToDouble(ReadFarmSettings.GetInnerText("Bifacial", "GroundClearance", ErrLevel.FATAL)) / itsArrayBW; numRows = 1; break; case "Unlimited Rows": itsTrackMode = TrackMode.NOAT; itsPanelTilt = Util.DTOR * Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PlaneTilt", ErrLevel.FATAL)); itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "CollBandWidth", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "Pitch", ErrLevel.FATAL)) / itsArrayBW; itsClearance = Convert.ToDouble(ReadFarmSettings.GetInnerText("Bifacial", "GroundClearance", ErrLevel.FATAL)) / itsArrayBW; numRows = int.Parse(ReadFarmSettings.GetInnerText("O&S", "RowsBlock", ErrLevel.FATAL)); break; case "Single Axis Elevation Tracking (E-W)": itsTrackMode = TrackMode.SAXT; itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAET", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAET", ErrLevel.FATAL)) / itsArrayBW; numRows = int.Parse(ReadFarmSettings.GetInnerText("O&S", "RowsBlockSAET", ErrLevel.FATAL)); break; case "Single Axis Horizontal Tracking (N-S)": itsTrackMode = TrackMode.SAXT; itsArrayBW = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "WActiveSAST", ErrLevel.FATAL)); itsPitch = Convert.ToDouble(ReadFarmSettings.GetInnerText("O&S", "PitchSAST", ErrLevel.FATAL)) / itsArrayBW; numRows = int.Parse(ReadFarmSettings.GetInnerText("O&S", "RowsBlockSAST", ErrLevel.FATAL)); break; default: ErrorLogger.Log("Bifacial is not supported for the selected orientation and shading.", ErrLevel.FATAL); break; } transFactor = Convert.ToDouble(ReadFarmSettings.GetInnerText("Bifacial", "PanelTransFactor", ErrLevel.FATAL)); if (numRows == 1) { // Pitch is needed for a single row because of ground patch calculations and geometry. Take value 100x greater than array bandwidth. itsPitch = 100; itsRowType = RowType.SINGLE; } else { itsRowType = RowType.INTERIOR; } // Initialize arrays frontGroundSH = new int[numGroundSegs]; rearGroundSH = new int[numGroundSegs]; frontSkyViewFactors = new double[numGroundSegs]; rearSkyViewFactors = new double[numGroundSegs]; frontGroundGHI = new double[numGroundSegs]; rearGroundGHI = new double[numGroundSegs]; // Calculate sky view factors for diffuse shading. Stays constant for non-tracking systems, so done here in Config() if (itsTrackMode == TrackMode.NOAT) { CalcSkyViewFactors(); } } }
// Obtaining and Setting efficiency curve values from .CSYX file public void ConfigEffCurves(int ArrayNum, bool threeCurves) { // Configuration begins with a check if the Inverter has three efficiency curves or just one // Once determined, relevant values are collected from the .CSYX file if (threeCurves) { // Initiating an Array to hold Interpolated Values and the three voltage levels itsPresentEfficiencies[0] = new double[3]; // To hold the three voltage levels [Array, V] itsPresentEfficiencies[1] = new double[3]; // To hold the three efficiencies from interpolation [Array, %, Computed in Calculate Method] // Getting the Low, Medium and High Voltage Values used in the Curve itsLowVoltage = double.Parse(ReadFarmSettings.GetAttribute("Inverter", "Voltage", _Adder: "/Efficiency/Low", _ArrayNum: ArrayNum)); itsPresentEfficiencies[0][0] = itsLowVoltage; itsMedVoltage = double.Parse(ReadFarmSettings.GetAttribute("Inverter", "Voltage", _Adder: "/Efficiency/Med", _ArrayNum: ArrayNum)); itsPresentEfficiencies[0][1] = itsMedVoltage; itsHighVoltage = double.Parse(ReadFarmSettings.GetAttribute("Inverter", "Voltage", _Adder: "/Efficiency/High", _ArrayNum: ArrayNum)); itsPresentEfficiencies[0][2] = itsHighVoltage; // Setting the lowest efficiency (i.e. at Threshold Power) List <double> lowEffIn = new List <double>(); List <double> lowEffFrac = new List <double>(); List <double> medEffIn = new List <double>(); List <double> medEffFrac = new List <double>(); List <double> highEffIn = new List <double>(); List <double> highEffFrac = new List <double>(); double effInPlaceholder; double effFracPlaceholder; lowEffIn.Add(itsThresholdPwr * itsNumInverters); lowEffFrac.Add(0); medEffIn.Add(itsThresholdPwr * itsNumInverters); medEffFrac.Add(0); highEffIn.Add(itsThresholdPwr * itsNumInverters); highEffFrac.Add(0); // Get Low Voltage Efficiency Curve for (int eff = 1; eff < 8; eff++) { effIn = double.TryParse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/Low/IN" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: null), out effInPlaceholder); effFrac = double.TryParse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/Low/Effic" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: null), out effFracPlaceholder); if (effIn && effFrac) { ErrorLogger.Assert("The Inverter Efficiency Curve is Incorrectly defined. Check Inverter in Sub-Array " + ArrayNum + ".", double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/Low/Effic" + (eff + 1).ToString(), _ArrayNum: ArrayNum)) < 100, ErrLevel.FATAL); lowEffIn.Add(effInPlaceholder * itsNumInverters * 1000); lowEffFrac.Add(effInPlaceholder * effFracPlaceholder * itsNumInverters * 10); } } itsLowEff[0] = lowEffIn.ToArray(); itsLowEff[1] = lowEffFrac.ToArray(); // Get Med Voltage Efficiency Curve for (int eff = 1; eff < 8; eff++) { effIn = double.TryParse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/Med/IN" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: null), out effInPlaceholder); effFrac = double.TryParse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/Med/Effic" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: null), out effFracPlaceholder); if (effIn && effFrac) { ErrorLogger.Assert("The Inverter Efficiency Curve is Incorrectly defined. Check Inverter in Sub-Array " + ArrayNum + ".", double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/Med/Effic" + (eff + 1).ToString(), _ArrayNum: ArrayNum)) < 100, ErrLevel.FATAL); medEffIn.Add(effInPlaceholder * itsNumInverters * 1000); medEffFrac.Add(effInPlaceholder * effFracPlaceholder * itsNumInverters * 10); } } itsMedEff[0] = medEffIn.ToArray(); itsMedEff[1] = medEffFrac.ToArray(); // Get High Voltage Efficiency Curve for (int eff = 1; eff < 8; eff++) { effIn = double.TryParse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/High/IN" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: null), out effInPlaceholder); effFrac = double.TryParse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/High/Effic" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: null), out effFracPlaceholder); if (effIn && effFrac) { ErrorLogger.Assert("The Inverter Efficiency Curve is Incorrectly defined. Check Inverter in Sub-Array " + ArrayNum + ".", double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/High/Effic" + (eff + 1).ToString(), _ArrayNum: ArrayNum)) < 100, ErrLevel.FATAL); highEffIn.Add(effInPlaceholder * itsNumInverters * 1000); highEffFrac.Add(effInPlaceholder * effFracPlaceholder * itsNumInverters * 10); } } itsHighEff[0] = highEffIn.ToArray(); itsHighEff[1] = highEffFrac.ToArray(); } else { // The Inverter only has one Efficiency Curve // Setting the lowest efficiency (i.e. at Threshold Power) itsOnlyEff[0][0] = itsThresholdPwr * itsNumInverters; itsOnlyEff[1][0] = 0; // Go through all .CSYX Nodes with Efficiency values and assign them into Array for (int eff = 1; eff < itsOnlyEff[0].Length; eff++) { ErrorLogger.Assert("The Inverter Efficiency Curve is Incorrectly defined. Check Inverter in Sub-Array " + ArrayNum + ".", double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/EffCurve/Effic." + (eff + 1).ToString(), _ArrayNum: ArrayNum)) < 100, ErrLevel.FATAL); itsOnlyEff[0][eff] = double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/EffCurve/IN" + (eff + 1).ToString(), _ArrayNum: ArrayNum, _default: itsNomOutputPwr.ToString())) * itsNumInverters * 1000; itsOnlyEff[1][eff] = itsOnlyEff[0][eff] * double.Parse(ReadFarmSettings.GetInnerText("Inverter", "Efficiency/EffCurve/Effic." + (eff + 1).ToString(), _ArrayNum: ArrayNum, _Error: ErrLevel.FATAL)) / 100; } } }
// 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 siteAlbedo // Site albedo (used only if inter-row albedo is set to site albedo) [#] , double measAlbedo // Measured albedo read from climate file, if available [#] , 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 if (ReadFarmSettings.GetAttribute("BifAlbedo", "Frequency", ErrLevel.WARNING) == "From Climate File") { Albedo = measAlbedo; } else if (ReadFarmSettings.GetAttribute("BifAlbedo", "Frequency", ErrLevel.WARNING) == "Site") { Albedo = siteAlbedo; } else { 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); }