Esempio n. 1
0
        /// <summary>
        /// Digraph of bypass diodes for calculation.
        /// </summary>
        /*private class BypassNode {
            public int minCell, maxCell;
            public List<BypassNode> childs = new List<BypassNode>();
        }
        private static BypassNode GetRoot(ArraySpec.CellString cellStr) {
            BypassNode root = new BypassNode();
            root.minCell = 0;
            root.maxCell = cellStr.Cells.Count;
            foreach (ArraySpec.BypassDiode diode in cellStr.BypassDiodes) {
                BypassNode node = root;
                while(true){
                    bool found = false;
                    foreach (BypassNode child in node.childs) {
                        if (diode.CellIxs.First >= node.minCell &&
                            diode.CellIxs.Second <= node.maxCell) {
                            node = child;
                            found = true;
                            break;
                        }
                    }
                    if (!found) break;
                }

                BypassNode newNode = new BypassNode();
                newNode.minCell = diode.CellIxs.First;
                newNode.maxCell = diode.CellIxs.Second;
                node.childs.Add(newNode);
            }
            return root;
        }*/
        public static IVTrace CalcStringIV(ArraySpec.CellString cellStr, IVTrace[] cellTraces, DiodeSpec bypassSpec)
        {
            Debug.Assert(cellTraces.Length != 0);
            Debug.Assert(cellTraces.Length == cellStr.Cells.Count);
            int ncells = cellTraces.Length;
            double strIsc = cellTraces[0].Isc;
            for (int i = 0; i < cellTraces.Length; i++) {
                strIsc = Math.Max(strIsc, cellTraces[i].Isc);
            }

            // which nodes connect to which others via bypass diode?
            // there's one node between each cell (subtotal ncells-1),
            // plus one at each end of the string, total ncells+1
            List<int>[] links = new List<int>[ncells+1];
            for(int i = 0; i <= ncells; i++){
                links[i] = new List<int>();
            }
            foreach (ArraySpec.BypassDiode diode in cellStr.BypassDiodes) {
                links[diode.CellIxs.Second + 1].Add(diode.CellIxs.First);
            }

            // sweep current this time, compute voltage
            int nsamples = 200;
            int ngoodsamples = nsamples;
            double[] veci = new double[nsamples];
            double[] vecv = new double[nsamples];
            for (int i = 0; i < nsamples; i++) {
                double current = i * strIsc / (nsamples-1);

                // what cells are in bypass?
                double[] nodevs = new double[ncells+1];
                nodevs[0] = 0; // string starts at ground, zero volts
                for (int j = 1; j <= ncells; j++) {
                    // first, voltage assuming no bypass
                    double nodev = nodevs[j - 1];
                    if (current < cellTraces[j - 1].Isc) {
                        nodev += cellTraces[j - 1].InterpV(current);
                    } else {
                        nodev = Double.NegativeInfinity;
                    }
                    // then, can we do better with bypass?
                    foreach (int linkIx in links[j]) {
                        nodev = Math.Max(nodev, nodevs[linkIx] - bypassSpec.VoltageDrop);
                    }
                    nodevs[j] = nodev;
                }
                veci[i] = current;
                vecv[i] = Math.Max(nodevs[ncells], 0);

                // cut off the part of the trace that's invalid (unachievable current)
                if (nodevs[ncells] >= 0) {
                    Debug.Assert(ngoodsamples == nsamples); // should not "bounce"
                } else if(ngoodsamples == nsamples){
                    ngoodsamples = i;
                }
            }

            // calculate summary info such as mppt power
            double vmp = 0, imp = 0;
            for (int i = 0; i < nsamples; i++) {
                if (veci[i] * vecv[i] > vmp * imp) {
                    vmp = vecv[i];
                    imp = veci[i];
                }
            }

            // return a trace. supports lin interp, etc
            IVTrace trace = new IVTrace();
            ngoodsamples = Math.Min(ngoodsamples + 1, nsamples);
            Debug.Assert(ngoodsamples > 0);
            trace.I = new double[ngoodsamples];
            trace.V = new double[ngoodsamples];
            Array.Copy(veci, trace.I, ngoodsamples);
            Array.Copy(vecv, trace.V, ngoodsamples);
            trace.Isc = veci[ngoodsamples - 1];
            trace.Voc = vecv[0];
            trace.Imp = imp;
            trace.Vmp = vmp;
            return trace;
        }
Esempio n. 2
0
        public static IVTrace CalcSweep(CellSpec cell, double insolationW, double tempC)
        {
            double voc = cell.CalcVoc(insolationW, tempC);
            double isc = cell.CalcIsc(insolationW, tempC);
            int n = 100;
            double[] vecv = new double[n + 1];
            for (int i = 0; i <= n; i++) {
                vecv[i] = voc * (double)i / n;
            }
            double[] veci = CalcIV(cell, vecv, insolationW, tempC);
            //Debug.Assert(Math.Abs(veci[0] - isc) < 0.001);
            //Debug.Assert(Math.Abs(veci[n]) < 0.001);
            double pmp = 0.0;
            double imp = 0.0, vmp = 0.0;
            for (int i = 0; i < n; i++) {
                double p = veci[i] * vecv[i];
                if (p > pmp) {
                    pmp = p;
                    vmp = vecv[i];
                    imp = veci[i];
                }
            }

            IVTrace trace = new IVTrace();
            trace.I = veci;
            trace.V = vecv;
            trace.Isc = isc;
            trace.Voc = voc;
            trace.Imp = imp;
            trace.Vmp = vmp;
            return trace;
        }
Esempio n. 3
0
        /// <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;
        }