Example #1
0
        // Computes the fraction of collectors arranged in rows that will be shaded on the back side at a particular sun position.
        public double GetBackShadedFraction
        (
            double SunZenith                // Zenith angle of sun [radians]
            , double SunAzimuth             // Azimuth angle of sun [radians]
            , double CollectorTilt          // Tilt of the module [radians]
        )
        {
            if (SunZenith > Math.PI / 2)
            {
                BackProfileAng = 0;
                return(0);                  // No shading possible as Sun is set or not risen
            }
            else if (Math.Abs(SunAzimuth - itsCollAzimuth) < Math.PI / 2)
            {
                BackProfileAng = 0;
                return(0);                 // No partial back shading possible as Sun is in front of the collectors
            }
            else
            {
                // Compute profile angle, relative to back of module (see Ref 1.)
                BackProfileAng = Tilt.GetProfileAngle(SunZenith, SunAzimuth, itsCollAzimuth + Math.PI);

                // NB: Added small tolerance since shading limit angle and profile angle are found through different methods and could have a small difference
                if (BackSLA - BackProfileAng <= 0.000001)
                {
                    return(0); // No shading possible as the light reaching the back of the panel is not limited by the proceeding row
                }
                else
                {
                    // Computes the fraction of collectors arranged in rows that will be shaded on the back side at a particular sun position.
                    double AC   = Math.Sin(CollectorTilt) * itsCollBW / Math.Sin(BackSLA);
                    double CAAp = Math.PI - CollectorTilt - BackSLA;
                    double CApA = Math.PI - CAAp - (BackSLA - BackProfileAng);

                    // Length of shaded section
                    double AAp = AC * Math.Sin(BackSLA - BackProfileAng) / Math.Sin(CApA);

                    // Using the Cell based shading model
                    if (useCellBasedShading)
                    {
                        double cellShaded = AAp / (CellSize);               // The number of cells shaded
                        double SF         = 1;                              // The resultant shading factor initialized to 1, modified later// Calculate the Shading fraction based on which cell numbers they are between

                        for (int i = 1; i <= itsNumModTransverseStrings; i++)
                        {
                            if ((cellShaded > cellSetup[i - 1]) && (cellShaded < cellSetup[i]))
                            {
                                SF = Math.Min(((shadingPC[i - 1] + (shadingPC[i] * (cellShaded - cellSetup[i - 1])))), shadingPC[i]);
                            }
                        }
                        return(SF);
                    }
                    else
                    {
                        // Return shaded fraction of the collector bandwidth
                        return(AAp / itsCollBW);
                    }
                }
            }
        }
Example #2
0
        // Divides the ground between two PV rows into n segments and determines direct beam shading (0 = not shaded, 1 = shaded) for each segment
        void CalcGroundShading
        (
            double SunZenith                                    // The zenith position of the sun with 0 being normal to the earth [radians]
            , double SunAzimuth                                 // The azimuth position of the sun relative to 0 being true south. Positive if west, negative if east [radians]
        )
        {
            // When sun is below horizon, set everything to shaded
            if (SunZenith > (Math.PI / 2))
            {
                for (int i = 0; i < numGroundSegs; i++)
                {
                    frontGroundSH[i] = 1;
                    rearGroundSH[i]  = 1;
                }
            }
            else
            {
                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]

                double FrontPA = Tilt.GetProfileAngle(SunZenith, SunAzimuth, itsPanelAzimuth);

                double Lh  = h / Math.Tan(FrontPA);                  // Base of triangle formed by beam of sun and height of module top from bottom
                double Lc  = itsClearance / Math.Tan(FrontPA);       // Base of triangle formed by beam of sun and height of module bottom from ground
                double Lhc = (h + itsClearance) / Math.Tan(FrontPA); // Base of triangle formed by beam of sun and height of module top from ground

                double s1Start = 0;                                  // Shading start position for first potential shading segment
                double s1End   = 0;                                  // Shading end position for first potential shading segment
                double s2Start = 0;                                  // Shading start position for second potential shading segment
                double s2End   = 0;                                  // Shading end position for second potential shading segment
                double SStart  = 0;                                  // Shading start position for placeholder segment
                double SEnd    = 0;                                  // Shading start position for placeholder segment

                // Divide the row-to-row spacing into n intervals for calculating ground shade factors
                double delta = itsPitch / numGroundSegs;
                // Initialize horizontal dimension x to provide midpoint intervals
                double x = 0;

                if (itsRowType == RowType.SINGLE)
                {
                    // Calculate front ground shading
                    // Front side of PV module completely sunny, ground partially shaded
                    if (Lh > 0.0)
                    {
                        s1Start = Lc;
                        s1End   = Lhc + b;
                    }
                    // Front side of PV module completely shaded, ground completely shaded
                    else if (Lh < -(itsPitch + b))
                    {
                        s1Start = -itsPitch;
                        s1End   = itsPitch;
                    }
                    // Shadow to front of row - either front or back might be shaded, depending on tilt and other factors
                    else
                    {
                        // Sun hits front of module. Shadow cast by bottom of module extends further forward than shadow cast by top
                        if (Lc < Lhc + b)
                        {
                            s1Start = Lc;
                            s1End   = Lhc + b;
                        }
                        // Sun hits back of module. Shadow cast by top of module extends further forward than shadow cast by bottom
                        else
                        {
                            s1Start = Lhc + b;
                            s1End   = Lc;
                        }
                    }

                    // Determine whether shaded or sunny for each n ground segments
                    // TODO: improve accuracy (especially for n < 100) by setting 1 only if > 50% of segment is shaded
                    for (int i = 0; i < numGroundSegs; i++)
                    {
                        // Offset x coordinate by -itsPitch because row ahead is being measured
                        x = (i + 0.5) * delta - itsPitch;
                        if (x >= s1Start && x < s1End)
                        {
                            // x within a shaded interval, so set to 1 to indicate shaded
                            frontGroundSH[i] = 1;
                        }
                        else
                        {
                            // x not within a shaded interval, so set to 0 to indicate sunny
                            frontGroundSH[i] = 0;
                        }
                    }

                    // Calculate rear ground shading
                    // Back side of PV module completely shaded, ground completely shaded
                    if (Lh > itsPitch - b)
                    {
                        s1Start = 0.0;
                        s1End   = itsPitch;
                    }
                    // Shadow to front of row - either front or back might be shaded, depending on tilt and other factors
                    else
                    {
                        // Sun hits front of module. Shadow cast by bottom of module extends further forward than shadow cast by top
                        if (Lc < Lhc + b)
                        {
                            s1Start = Lc;
                            s1End   = Lhc + b;
                        }
                        // Sun hits back of module. Shadow cast by top of module extends further forward than shadow cast by bottom
                        else
                        {
                            s1Start = Lhc + b;
                            s1End   = Lc;
                        }
                    }

                    // Determine whether shaded or sunny for each n ground segments
                    // TODO: improve accuracy (especially for n < 100) by setting 1 only if > 50% of segment is shaded
                    for (int i = 0; i < numGroundSegs; i++)
                    {
                        x = (i + 0.5) * delta;
                        if (x >= s1Start && x < s1End)
                        {
                            // x within a shaded interval, so set to 1 to indicate shaded
                            rearGroundSH[i] = 1;
                        }
                        else
                        {
                            // x not within a shaded interval, so set to 0 to indicate sunny
                            rearGroundSH[i] = 0;
                        }
                    }
                }
                else
                {
                    // Calculate interior ground shading
                    // Front side of PV module partially shaded, back completely shaded, ground completely shaded
                    if (Lh > itsPitch - b)
                    {
                        s1Start = 0.0;
                        s1End   = itsPitch;
                    }
                    // Front side of PV module completely shaded, back partially shaded, ground completely shaded
                    else if (Lh < -(itsPitch + b))
                    {
                        s1Start = 0.0;
                        s1End   = itsPitch;
                    }
                    // Assume ground is partially shaded
                    else
                    {
                        // Shadow to back of row - module front unshaded, back shaded
                        if (Lhc >= 0.0)
                        {
                            SStart = Lc;
                            SEnd   = Lhc + b;
                            // Put shadow in correct row-to-row space if needed
                            while (SStart > itsPitch)
                            {
                                SStart -= itsPitch;
                                SEnd   -= itsPitch;
                            }
                            s1Start = SStart;
                            s1End   = SEnd;
                            // Need to use two shade areas. Transpose the area that extends beyond itsPitch to the front of the row-to-row space
                            if (s1End > itsPitch)
                            {
                                s1End   = itsPitch;
                                s2Start = 0.0;
                                s2End   = SEnd - itsPitch;
                                if (s2End - s1Start > 0.000001)
                                {
                                    ErrorLogger.Log("Unexpected shading coordinates encountered.", ErrLevel.FATAL);
                                }
                            }
                        }
                        // Shadow to front of row - either front or back might be shaded, depending on tilt and other factors
                        else
                        {
                            // Sun hits front of module. Shadow cast by bottom of module extends further forward than shadow cast by top
                            if (Lc < Lhc + b)
                            {
                                SStart = Lc;
                                SEnd   = Lhc + b;
                            }
                            // Sun hits back of module. Shadow cast by top of module extends further forward than shadow cast by bottom
                            else
                            {
                                SStart = Lhc + b;
                                SEnd   = Lc;
                            }
                            // Put shadow in correct row-to-row space if needed
                            while (SStart < 0.0)
                            {
                                SStart += itsPitch;
                                SEnd   += itsPitch;
                            }
                            s1Start = SStart;
                            s1End   = SEnd;
                            // Need to use two shade areas. Transpose the area that extends beyond itsPitch to the front of the row-to-row space
                            if (s1End > itsPitch)
                            {
                                s1End   = itsPitch;
                                s2Start = 0.0;
                                s2End   = SEnd - itsPitch;
                                if (s2End - s1Start > 0.000001)
                                {
                                    ErrorLogger.Log("Unexpected shading coordinates encountered.", ErrLevel.FATAL);
                                }
                            }
                        }
                    }

                    // Determine whether shaded or sunny for each n ground segments
                    // TODO: improve accuracy (especially for n < 100) by setting 1 only if > 50% of segment is shaded
                    for (int i = 0; i < numGroundSegs; i++)
                    {
                        x = (i + 0.5) * delta;

                        // Assume homogeneity to the front and rear of interior rows
                        if ((x >= s1Start && x < s1End) || (x >= s2Start && x < s2End))
                        {
                            // x within a shaded interval, so set to 1 to indicate shaded
                            frontGroundSH[i] = 1;
                            rearGroundSH[i]  = 1;
                        }
                        else
                        {
                            // x not within a shaded interval, so set to 0 to indicate sunny
                            frontGroundSH[i] = 0;
                            rearGroundSH[i]  = 0;
                        }
                    }
                }
            }
        }