private void TimeAveragedSim() { // input time range; all other inputs come from simInput DateTime utcStart = dateTimePicker1.Value.AddHours(-simInput.TimezoneOffsetHours); DateTime utcEnd = dateTimePicker2.Value.AddHours(-simInput.TimezoneOffsetHours); // step-by-step output TextWriter csv = new StreamWriter("../../../../output.csv"); csv.WriteLine("time_utc,insolation_w,output_w"); // average output var simAvg = new ArraySimulationStepOutput(); // simulate in 10-minute intervals InitSimulator(); int nsim = 0; for (DateTime time = utcStart; time <= utcEnd; time = time.AddMinutes(10), nsim++) { simInput.Utc = time; simInputControls.UpdateView(); ArraySimulationStepOutput simOutput = simulator.Simulate(simInput); // averate the outputs if (nsim > 0) { Debug.Assert(simAvg.ArrayArea == simOutput.ArrayArea); } simAvg.ArrayArea = simOutput.ArrayArea; simAvg.ArrayLitArea += simOutput.ArrayLitArea; simAvg.WattsInsolation += simOutput.WattsInsolation; simAvg.WattsOutputByCell += simOutput.WattsOutputByCell; simAvg.WattsOutput += simOutput.WattsOutput; // debug output csv.WriteLine(time + "," + simOutput.WattsInsolation + "," + simOutput.WattsOutput); } csv.Close(); // show the average output simAvg.ArrayLitArea /= nsim; simAvg.WattsInsolation /= nsim; simAvg.WattsOutputByCell /= nsim; simAvg.WattsOutput /= nsim; Debug.WriteLine("Array time-averaged simulation output"); Debug.WriteLine(" ... " + simAvg.ArrayArea + " m^2 total cell area"); Debug.WriteLine(" ... " + simAvg.ArrayLitArea + " m^2 exposed to sunlight"); Debug.WriteLine(" ... " + simAvg.WattsInsolation + " W insolation"); Debug.WriteLine(" ... " + simAvg.WattsOutputByCell + " W output (assuming mppt per cell)"); Debug.WriteLine(" ... " + simAvg.WattsOutput + " W output"); }
/// <summary> /// Reads the compute textures from OpenGL. /// This gives insolation for each cell. /// /// Uses this to calculate IV curves, etc, and ultimately array power. /// </summary> private ArraySimulationStepOutput AnalyzeComputeTex(ArraySpec array, double wPerM2Insolation, double wPerM2Iindirect, double encapLoss, double cTemp) { Color[] texColors = ReadColorTexture(FramebufferAttachment.ColorAttachment0); float[] texWattsIn = ReadFloatTexture(FramebufferAttachment.ColorAttachment1, 0.0001); double arrayDimM = ComputeArrayMaxDimension(array); double m2PerPixel = arrayDimM * arrayDimM / COMPUTE_TEX_SIZE / COMPUTE_TEX_SIZE; float[] texArea = ReadFloatTexture(FramebufferAttachment.ColorAttachment2, m2PerPixel / 4); double dbgmin = texArea[0], dbgmax = texArea[0], dbgavg = 0; for (int i = 0; i < texArea.Length; i++) { dbgmin = Math.Min(dbgmin, texArea[i]); dbgmax = Math.Max(dbgmax, texArea[i]); dbgavg += texArea[i]; } dbgavg /= texArea.Length; // find the cell at each fragment... int ncells = 0; var cells = new List<ArraySpec.Cell>(); var colorToId = new Dictionary<Color, int>(); foreach (ArraySpec.CellString cellStr in array.Strings) { foreach (ArraySpec.Cell cell in cellStr.Cells) { cells.Add(cell); colorToId.Add(cell.Color, ncells); ncells++; } } // finally, find the area and insolation for each cell double[] wattsIn = new double[ncells]; double[] areas = new double[ncells]; double wattsInUnlinked = 0, areaUnlinked = 0; for (int i = 0; i < computeWidth * computeHeight; i++) { Color color = texColors[i]; if (ColorUtils.IsGrayscale(color)) continue; if (colorToId.ContainsKey(color)) { int id = colorToId[color]; wattsIn[id] += texWattsIn[i]; areas[id] += texArea[i]; } else { wattsInUnlinked += texWattsIn[i]; areaUnlinked += texArea[i]; } } if (areaUnlinked > 0 || wattsInUnlinked > 0) { Logger.warn("Found texels that are not grayscale, " + "but also doesn't correspond to any cell. Have you finished your layout?" + "\n\tTotal of {0}m^2 cell area not in any string, with {1}W insolation.", areaUnlinked,wattsInUnlinked); } // add indirect insolation, encapsulation loss for (int i = 0; i < ncells; i++) { wattsIn[i] += array.CellSpec.Area * wPerM2Iindirect; wattsIn[i] *= (1.0 - encapLoss); } // find totals double totalArea = 0, totalWattsIn = 0; for (int i = 0; i < ncells; i++) { totalWattsIn += wattsIn[i]; totalArea += areas[i]; Debug.WriteLine("cell {0}: {1}W, {2}m^2", i, wattsIn[i], areas[i]); } Debug.WriteLine("total: {0}W, {1}m^2", totalWattsIn, totalArea); // MPPT sweeps, for each cell and each string. // Inputs: CellSpec cellSpec = array.CellSpec; int nstrings = array.Strings.Count; // Outputs: double totalWattsOutByCell = 0; double totalWattsOutByString = 0; var strings = new ArraySimStringOutput[nstrings]; int cellIx = 0; for(int i = 0; i < nstrings; i++){ var cellStr = array.Strings[i]; double stringWattsIn = 0, stringWattsOutByCell = 0, stringLitArea = 0; // per-cell sweeps var cellSweeps = new IVTrace[cellStr.Cells.Count]; for(int j = 0; j < cellStr.Cells.Count; j++){ double cellWattsIn = wattsIn[cellIx++]; double cellInsolation = cellWattsIn / cellSpec.Area; IVTrace cellSweep = CellSimulator.CalcSweep(cellSpec, cellInsolation, cTemp); cellSweeps[j] = cellSweep; stringWattsIn += cellWattsIn; stringWattsOutByCell += cellSweep.Pmp; totalWattsOutByCell += cellSweep.Pmp; // shading stats stringLitArea += areas[i]; } // string sweep strings[i] = new ArraySimStringOutput(); strings[i].WattsIn = stringWattsIn; strings[i].WattsOutputByCell = stringWattsOutByCell; IVTrace stringSweep = StringSimulator.CalcStringIV(cellStr, cellSweeps, array.BypassDiodeSpec); strings[i].WattsOutput = stringSweep.Pmp; strings[i].IVTrace = stringSweep; // higher-level string info strings[i].String = cellStr; strings[i].Area = cellStr.Cells.Count*cellSpec.Area; strings[i].AreaShaded = strings[i].Area - stringLitArea; IVTrace cellSweepIdeal = CellSimulator.CalcSweep(cellSpec,wPerM2Insolation,cTemp); strings[i].WattsOutputIdeal = cellSweepIdeal.Pmp * cellStr.Cells.Count; // total array power totalWattsOutByString += stringSweep.Pmp; } ArraySimulationStepOutput output = new ArraySimulationStepOutput(); output.ArrayArea = ncells * cellSpec.Area; output.ArrayLitArea = totalArea; output.WattsInsolation = totalWattsIn; output.WattsOutputByCell = totalWattsOutByCell; output.WattsOutput = totalWattsOutByString; output.Strings = strings; return output; }