/// <summary> /// Renders the array from the sun's point of view, into several buffers: /// one for cell id (texture map), one for insolation, and one for area. /// /// After this step, results be read using OpenGL ReadBuffer and analyzed. /// </summary> public void ComputeRender(ArraySpec array, Vector3 sunDir) { Debug.WriteLine("rendering insolation+cells into a " + computeWidth + "x" + computeWidth + " fbo"); // gl state GL.UseProgram(shaderProg); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboWatts); GL.DrawBuffers(3, new DrawBuffersEnum[]{ (DrawBuffersEnum)FramebufferAttachment.ColorAttachment0Ext, (DrawBuffersEnum)FramebufferAttachment.ColorAttachment1Ext, (DrawBuffersEnum)FramebufferAttachment.ColorAttachment2Ext}); GL.BindTexture(TextureTarget.Texture2D, texArray); GL.Viewport(0, 0, computeWidth, computeHeight); GL.ClearColor(Color.White); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); Vector3 arrayCenter = ComputeArrayCenter(array); double arrayMaxDimension = ComputeArrayMaxDimension(array); SetCameraSunPOV(sunDir, arrayCenter, arrayMaxDimension); //render MeshSprite sprite = new MeshSprite(array.Mesh); sprite.PushTransform(); sprite.Render(); sprite.PopTransform(); }
public ArrayLayoutForm(ArraySpec spec) { Debug.Assert(spec != null); this.array = spec; InitializeComponent(); UpdateView(); }
private IList <TSpec> ReadTSpecs() { var tspecs = new List <TSpec>(); while (true) { SkipWhite(); switch (PeekChar()) { case '[': // SZArray, Array, or GenericInst ReadChar(); SkipWhite(); var peeked = PeekChar(); if (peeked == ']') { // SZ array Verify(ReadChar() == ']', "Expected ']'"); tspecs.Add(SZArraySpec.Instance); } else if (peeked == '*' || peeked == ',' || peeked == '-' || char.IsDigit((char)peeked)) { // Array var arraySpec = new ArraySpec(); arraySpec.rank = 0; while (true) { SkipWhite(); var c = PeekChar(); if (c == '*') { ReadChar(); } else if (c == ',' || c == ']') { } else if (c == '-' || char.IsDigit((char)c)) { var lower = ReadInt32(); uint?size; SkipWhite(); Verify(ReadChar() == '.', "Expected '.'"); Verify(ReadChar() == '.', "Expected '.'"); if (PeekChar() == '.') { ReadChar(); size = null; } else { SkipWhite(); if (PeekChar() == '-') { var upper = ReadInt32(); Verify(upper >= lower, "upper < lower"); size = (uint)(upper - lower + 1); Verify(size.Value != 0 && size.Value <= 0x1FFFFFFF, "Invalid size"); } else { var upper = ReadUInt32(); var lsize = upper - lower + 1; Verify(lsize > 0 && lsize <= 0x1FFFFFFF, "Invalid size"); size = (uint)lsize; } } if (arraySpec.lowerBounds.Count == arraySpec.rank) { arraySpec.lowerBounds.Add(lower); } if (size.HasValue && arraySpec.sizes.Count == arraySpec.rank) { arraySpec.sizes.Add(size.Value); } } else { Verify(false, "Unknown char"); } arraySpec.rank++; SkipWhite(); if (PeekChar() != ',') { break; } ReadChar(); } Verify(ReadChar() == ']', "Expected ']'"); tspecs.Add(arraySpec); } else { // Generic args var ginstSpec = new GenericInstSpec(); while (true) { SkipWhite(); peeked = PeekChar(); var needSeperators = peeked == '['; if (peeked == ']') { break; } Verify(!needSeperators || ReadChar() == '[', "Expected '['"); ginstSpec.args.Add(ReadType(needSeperators)); SkipWhite(); Verify(!needSeperators || ReadChar() == ']', "Expected ']'"); SkipWhite(); if (PeekChar() != ',') { break; } ReadChar(); } Verify(ReadChar() == ']', "Expected ']'"); tspecs.Add(ginstSpec); } break; case '&': // ByRef ReadChar(); tspecs.Add(ByRefSpec.Instance); break; case '*': // Ptr ReadChar(); tspecs.Add(PtrSpec.Instance); break; default: return(tspecs); } } }
/// <summary> /// Gets the centerpoint of each cell in the texture /// that has been wired in the current string, in order, /// in screen (layout control pixel, not texel) coordinates. /// </summary> private PointF[] ComputeCellCenterpoints(ArraySpec.CellString cellString) { if (cellString == null) { return new PointF[0]; } SizeF arraySize = GetScaledArraySize(); float scale = arraySize.Width / Array.LayoutTexture.Width; int n = cellString.Cells.Count; PointF[] points = new PointF[n]; for (int i = 0; i < n; i++) { ArraySpec.Cell cell = cellString.Cells[i]; int sx = 0, sy = 0; foreach (Pair<int> xy in cell.Pixels) { sx += xy.First; sy += xy.Second; } int m = cell.Pixels.Count; points[i] = new PointF( (float)sx / m * scale, (float)sy / m * scale); } return points; }
private void ClickCell(ArraySpec.Cell cell) { // either add it to the current string, or remove it if it's already there if (!CellString.Cells.Remove(cell)) { CellString.Cells.Add(cell); } else { // prune bypass diodes CellString.BypassDiodes.RemoveAll((diode) => { return diode.CellIxs.First >= CellString.Cells.Count || diode.CellIxs.Second >= CellString.Cells.Count; }); } }
void AddArray(ArraySpec array) { if (array_spec == null) array_spec = new List<ArraySpec>(); array_spec.Add(array); }
public static IEnumerable <TestCaseData> SpecificationExtensionsTestIsSameCaseSource() { // single spec types var specFirstNull = new FirstNameSpec(null); var specFirstNull2 = new FirstNameSpec(null); var specFirstBob = new FirstNameSpec("bob"); var specFirstBob2 = new FirstNameSpec("bob"); var specFirstBobbette = new FirstNameSpec("bobbette"); var specFirstAndLastBob = new FirstAndLastNameSpec("bob", "le"); var specFirstAndLastBob2 = new FirstAndLastNameSpec("bob", "le"); var specFirstAndLastBobbbette = new FirstAndLastNameSpec("bobbette", "le"); var specLastBob = new LastNameSpec("bob"); yield return(new TestCaseData("Check on null type", specFirstBob, null, false)); yield return(new TestCaseData("Check non reference type", specFirstBob, "dinge", false)); yield return(new TestCaseData("SameReference", specFirstBob, specFirstBob, true)); yield return(new TestCaseData("Different type", specFirstBob, specLastBob, false)); yield return(new TestCaseData("Same single content (null)", specFirstNull, specFirstNull2, true)); yield return(new TestCaseData("Same single content", specFirstBob, specFirstBob2, true)); yield return(new TestCaseData("Same multiple content", specFirstAndLastBob, specFirstAndLastBob2, true)); yield return(new TestCaseData("Different single content", specFirstBob, specFirstBobbette, false)); // operator specs var orSpecBob = new OrSpecification <Sample>(specFirstBob, specFirstBob2); var orSpecBob2 = new OrSpecification <Sample>(specFirstBob, specFirstBob2); var orSpecBob3 = new OrSpecification <Sample>(specFirstBob, specLastBob); var andSpecBob = new AndSpecification <Sample>(specFirstBob, specFirstBob); var andSpecBob2 = new AndSpecification <Sample>(specFirstBob, specFirstBob2); var andSpecBob3 = new AndSpecification <Sample>(specFirstBob, specLastBob); yield return(new TestCaseData("OR Same containing specs", orSpecBob, orSpecBob2, true)); yield return(new TestCaseData("Or Different containing specs", orSpecBob, orSpecBob3, false)); yield return(new TestCaseData("And Same containing specs", andSpecBob, andSpecBob2, true)); yield return(new TestCaseData("And Different containing specs", andSpecBob, andSpecBob3, false)); // array specs: value compares var array = new int[] { 1, 2, 3 }; var arraySpecNull = new ArraySpec <int>(null); var arraySpec = new ArraySpec <int>(array); var arraySpec2 = new ArraySpec <int>(array); var arraySpec3 = new ArraySpec <int>(new int[] { 1, 2, 3 }); var arraySpec4 = new ArraySpec <int>(new int[] { 1, 2, 3, 4 }); yield return(new TestCaseData("Array - same array", arraySpec, arraySpec2, true)); yield return(new TestCaseData("Array - different array - same content", arraySpec, arraySpec3, true)); yield return(new TestCaseData("Array - different array - different content", arraySpec, arraySpec4, false)); yield return(new TestCaseData("Array - different array - different content (null)", arraySpec, arraySpecNull, false)); yield return(new TestCaseData("Array(obj) - same array", arraySpec, arraySpec2, true)); // array specs: object compares var sample = new Sample("le", "bob"); var arraySpecSample = new ArraySpec <Sample>(new Sample[] { sample, sample }); var arraySpecSample2 = new ArraySpec <Sample>(new Sample[] { sample, sample }); var arraySpecSampleNull = new ArraySpec <Sample>(new Sample[] { sample, null, sample }); var arraySpecSampleNull2 = new ArraySpec <Sample>(new Sample[] { sample, null, sample }); var arraySpecObject = new ArraySpec <object>(new object[] { "a", 1 }); var arraySpecObject2 = new ArraySpec <object>(new object[] { "a", "b" }); var arraySpecObject3 = new ArraySpec <object>(new object[] { "a", "c" }); var arraySpecObjectnull = new ArraySpec <object>(null); yield return(new TestCaseData("Array(obj) - different array - same content", arraySpecSample, arraySpecSample2, true)); yield return(new TestCaseData("Array(obj) - different array - different content (null)", arraySpecSampleNull, arraySpecSampleNull2, false)); yield return(new TestCaseData("Array(obj) - different array - different content (type)", arraySpecObject, arraySpecObject2, false)); yield return(new TestCaseData("Array(obj) - different array - different content (value)", arraySpecObject3, arraySpecObject2, false)); yield return(new TestCaseData("Array(obj) - different array - one is null", arraySpecObject3, arraySpecObjectnull, false)); }
private static ArrayJsonSpec ToJson(ArraySpec arraySpec, string layoutFile, string meshFile, string relativeDir) { return new ArrayJsonSpec() { BypassDiode = ToJson(arraySpec.BypassDiodeSpec), Cell = ToJson(arraySpec.CellSpec), EncapsulationLoss = arraySpec.EncapsulationLoss, LayoutBounds = ToJson(arraySpec.LayoutBounds), LayoutFilename = GetRelative(relativeDir, layoutFile), MeshFilename = GetRelative(relativeDir, meshFile) }; }
/// <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; }
private void SetUniforms(ArraySpec array, double insolation) { GL.UseProgram(shaderProg); // array layout alignment GL.Uniform1(uniformX0, array.LayoutBoundsXZ.Left); GL.Uniform1(uniformX1, array.LayoutBoundsXZ.Right); GL.Uniform1(uniformZ0, array.LayoutBoundsXZ.Top); GL.Uniform1(uniformZ1, array.LayoutBoundsXZ.Bottom); // array layout texture. shows where each cell is located. GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, texArray); if (cacheSolarCells != array.LayoutTexture) { cacheSolarCells = array.LayoutTexture; GLUtils.LoadTexture(array.LayoutTexture, TextureUnit.Texture0); } GL.Uniform1(uniformSolarCells, (float)TextureUnit.Texture0); // solar insolation per pixel rendered // (since we are rendering orth projection from the sun's pov, this is a constant) double arrayDimM = ComputeArrayMaxDimension(array); double m2PerPixel = arrayDimM*arrayDimM/COMPUTE_TEX_SIZE/COMPUTE_TEX_SIZE; double wattsPerPixel = m2PerPixel * insolation; GL.Uniform1(uniformPixelWattsIn, (float)wattsPerPixel); Debug.WriteLine("uniforms set."); }
/// <summary> /// Computes the diameter of the array (ie, the maximum dimension) in meters. /// /// Approximates by finding the diagonal length of the bounding box, for simplicity. /// </summary> private double ComputeArrayMaxDimension(ArraySpec array) { Quad3 arrayBB = array.Mesh.BoundingBox; return (arrayBB.Max - arrayBB.Min).Length; }
/// <summary> /// Finds the center of the array mesh. Uses the middle of the bounding box, for simplicity. /// </summary> private Vector3 ComputeArrayCenter(ArraySpec array) { Quad3 arrayBB = array.Mesh.BoundingBox; return (arrayBB.Max + arrayBB.Min) * 0.5f; }
/// <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; }
public ArraySimulationStepOutput Simulate(ArraySpec array, Vector3 sunDir, double wPerM2Insolation, double wPerM2Indirect, double encapLoss, double cTemp) { // validate that we're gtg if (array == null) throw new ArgumentException("No array specified."); if (array.Mesh == null) throw new ArgumentException("No array shape (mesh) loaded."); if (array.LayoutTexture == null) throw new ArgumentException("No array layout (texture) loaded."); if (wPerM2Insolation < 0) throw new ArgumentException("Invalid (negative) insolation."); if (Math.Abs(sunDir.Length - 1.0) > 1e-3) throw new ArgumentException("Sun direction must be a unit vector."); ArraySimulationStepOutput output; lock (typeof(GL)) { DateTime dt1 = DateTime.Now; SetUniforms(array, wPerM2Insolation); ComputeRender(array, sunDir); DebugSaveBuffers(); output = AnalyzeComputeTex(array, wPerM2Insolation, wPerM2Indirect, encapLoss, cTemp); DateTime dt2 = DateTime.Now; Debug.WriteLine(string.Format("finished sim step! {0:0.000}s {1:0.0}/{2:0.0}W", (double)(dt2.Ticks - dt1.Ticks) * 0.0000001, output.WattsInsolation, output.WattsOutput)); } return output; }
private void AddArray(ArraySpec array) { if (ArraySpecs == null) ArraySpecs = new List<ArraySpec>(); ArraySpecs.Add(array); }