# -*- coding: utf-8 -*-
"""
ViewFactor module - VF calculation helper files for bifacial-viewfactor
    
@author Bill Marion
@translated to python by sayala 06/09/17

"""

# ensure python3 compatible division and printing
from __future__ import division, print_function, absolute_import
import math
import numpy as np
from sun import solarPos, sunIncident, perezComp, aOIcorrection
import logging

# TODO: set level or add formatters if more advanced logging required
LOGGER = logging.getLogger(__name__)  # only used to raise errors
DTOR = math.pi / 180.0  # Factor for converting from degrees to radians


def getBackSurfaceIrradiances(rowType, maxShadow, PVbackSurface, beta, sazm,
                              dni, dhi, C, D, albedo, zen, azm, cellRows,
                              pvBackSH, rearGroundGHI, frontGroundGHI,
                              frontReflected, offset=0):      
    """
    This method calculates the AOI corrected irradiance on the back of the PV
    module/panel. 11/19/2015

    Added rowType and other changes to distinguish between types of rows.
    4/19/2016

    Added input of offset of reference cell from PV module back (in PV panel
    slope lengths) for modeling Sara's reference cell measurements, should be
    set to zero for PV module cell irradiances.

    Added while loop so projected Xs aren't too negative causing array index
    problems (<0) 12/13/2016::

        while (projectedX1 < -100.0 || projectedX2 < -100.0):
            # Offset so array indexes are >= -100.0  12/13/2016

            projectedX1 += 100.0;
            projectedX2 += 100.0;

    Parameters
    ----------
    rowType : str
        Type of row: "first", "interior", "last", or "single" 
    maxShadow
        Maximum shadow length projected to the front(-) or rear (+) from the
        front of the module 
    PVbackSurface
        PV module back surface material type, either "glass" or "ARglass"
    beta
        Tilt from horizontal of the PV modules/panels (deg) (for front surface)
    sazm
        Surface azimuth of PV panels (deg) (for front surface)
    dni
        Direct normal irradiance (W/m2)
    dhi
        Diffuse horizontal irradiance (W/m2)
    C
        Ground clearance of PV panel (in PV panel slope lengths)            
    D
        Horizontal distance between rows of PV panels (in PV panel slope
        lengths) 
    albedo
        Ground albedo
    zen
        Sun zenith (in radians)
    azm
        Sun azimuth (in radians)
    pvBackSH
        Decimal fraction of the back surface of the PV panel that is shaded,
        0.0 to 1.0
    rearGroundGHI : array of size [100]
        Global horizontal irradiance for each of 100 ground segments (W/m2)
    frontGroundGHI : array of size [100]
        Global horizontal irradiance for each of 100 ground segments (W/m2)
    frontReflected : array of size [cellRows]
        Irradiance reflected from the front of the PV module/panel (W/m2) in
        the row behind the one of interest
    offset
        Offset of reference cell from PV module back (in PV panel slope
        lengths), set to zero for PV module cell irradiances

    Returns
    -------
    backGTI : array of size [cellRows]
        AOI corrected irradiance on back side of PV module/panel, one for each
        cell row (W/m2)
    aveGroundGHI : numeric
        Average GHI on ground under PV array

    Notes
    -----
    1-degree hemispherical segment AOI correction factor for glass (index=0)
    and ARglass (index=1)
    """
    backGTI = []

    SegAOIcor = [
        [0.057563, 0.128570, 0.199651, 0.265024, 0.324661, 0.378968, 0.428391, 0.473670, 0.514788, 0.552454, 
         0.586857, 0.618484, 0.647076, 0.673762, 0.698029, 0.720118, 0.740726, 0.759671, 0.776946, 0.792833, 
         0.807374, 0.821010, 0.833534, 0.845241, 0.855524, 0.865562, 0.874567, 0.882831, 0.890769, 0.897939, 
         0.904373, 0.910646, 0.916297, 0.921589, 0.926512, 0.930906, 0.935179, 0.939074, 0.942627, 0.946009, 
         0.949096, 0.952030, 0.954555, 0.957157, 0.959669, 0.961500, 0.963481, 0.965353, 0.967387, 0.968580, 
         0.970311, 0.971567, 0.972948, 0.974114, 0.975264, 0.976287, 0.977213, 0.978142, 0.979057, 0.979662, 
         0.980460, 0.981100, 0.981771, 0.982459, 0.982837, 0.983199, 0.983956, 0.984156, 0.984682, 0.985026, 
         0.985364, 0.985645, 0.985954, 0.986241, 0.986484, 0.986686, 0.986895, 0.987043, 0.987287, 0.987388, 
         0.987541, 0.987669, 0.987755, 0.987877, 0.987903, 0.987996, 0.988022, 0.988091, 0.988104, 0.988114, 
         0.988114, 0.988104, 0.988091, 0.988022, 0.987996, 0.987903, 0.987877, 0.987755, 0.987669, 0.987541, 
         0.987388, 0.987287, 0.987043, 0.986895, 0.986686, 0.986484, 0.986240, 0.985954, 0.985645, 0.985364, 
         0.985020, 0.984676, 0.984156, 0.983956, 0.983199, 0.982837, 0.982459, 0.981771, 0.981100, 0.980460, 
         0.979662, 0.979057, 0.978142, 0.977213, 0.976287, 0.975264, 0.974114, 0.972947, 0.971567, 0.970311, 
         0.968580, 0.967387, 0.965353, 0.963481, 0.961501, 0.959671, 0.957157, 0.954555, 0.952030, 0.949096, 
         0.946009, 0.942627, 0.939074, 0.935179, 0.930906, 0.926512, 0.921589, 0.916297, 0.910646, 0.904373, 
         0.897939, 0.890769, 0.882831, 0.874567, 0.865562, 0.855524, 0.845241, 0.833534, 0.821010, 0.807374, 
         0.792833, 0.776946, 0.759671, 0.740726, 0.720118, 0.698029, 0.673762, 0.647076, 0.618484, 0.586857, 
         0.552454, 0.514788, 0.473670, 0.428391, 0.378968, 0.324661, 0.265024, 0.199651, 0.128570, 0.057563],
        [0.062742, 0.139913, 0.216842, 0.287226, 0.351055, 0.408796, 0.460966, 0.508397, 0.551116, 0.589915,
         0.625035, 0.657029, 0.685667, 0.712150, 0.735991, 0.757467, 0.777313, 0.795374, 0.811669, 0.826496, 
         0.839932, 0.852416, 0.863766, 0.874277, 0.883399, 0.892242, 0.900084, 0.907216, 0.914023, 0.920103, 
         0.925504, 0.930744, 0.935424, 0.939752, 0.943788, 0.947313, 0.950768, 0.953860, 0.956675, 0.959339, 
         0.961755, 0.964039, 0.965984, 0.967994, 0.969968, 0.971283, 0.972800, 0.974223, 0.975784, 0.976647, 
         0.977953, 0.978887, 0.979922, 0.980773, 0.981637, 0.982386, 0.983068, 0.983759, 0.984436, 0.984855, 
         0.985453, 0.985916, 0.986417, 0.986934, 0.987182, 0.987435, 0.988022, 0.988146, 0.988537, 0.988792, 
         0.989043, 0.989235, 0.989470, 0.989681, 0.989857, 0.990006, 0.990159, 0.990263, 0.990455, 0.990515, 
         0.990636, 0.990731, 0.990787, 0.990884, 0.990900, 0.990971, 0.990986, 0.991042, 0.991048, 0.991057, 
         0.991057, 0.991048, 0.991042, 0.990986, 0.990971, 0.990900, 0.990884, 0.990787, 0.990731, 0.990636, 
         0.990515, 0.990455, 0.990263, 0.990159, 0.990006, 0.989857, 0.989681, 0.989470, 0.989235, 0.989043, 
         0.988787, 0.988532, 0.988146, 0.988022, 0.987435, 0.987182, 0.986934, 0.986417, 0.985916, 0.985453, 
         0.984855, 0.984436, 0.983759, 0.983068, 0.982386, 0.981637, 0.980773, 0.979920, 0.978887, 0.977953, 
         0.976647, 0.975784, 0.974223, 0.972800, 0.971284, 0.969970, 0.967994, 0.965984, 0.964039, 0.961755, 
         0.959339, 0.956675, 0.953860, 0.950768, 0.947313, 0.943788, 0.939752, 0.935424, 0.930744, 0.925504, 
         0.920103, 0.914023, 0.907216, 0.900084, 0.892242, 0.883399, 0.874277, 0.863766, 0.852416, 0.839932, 
         0.826496, 0.811669, 0.795374, 0.777313, 0.757467, 0.735991, 0.712150, 0.685667, 0.657029, 0.625035, 
         0.589915, 0.551116, 0.508397, 0.460966, 0.408796, 0.351055, 0.287226, 0.216842, 0.139913, 0.062742]]

    # Tilt from horizontal of the PV modules/panels, in radians
    beta = beta * DTOR
    sazm = sazm * DTOR  # Surface azimuth of PV module/panels, in radians

    # 1. Calculate and assign various paramters to be used for modeling
    #    irradiances

    # For calling PerezComp to break diffuse into components for zero tilt
    # (horizontal)
    iso_dif = 0.0; circ_dif = 0.0; horiz_dif = 0.0; grd_dif = 0.0; beam = 0.0
    # Call to get iso_dif for horizontal surface
    ghi, iso_dif, circ_dif, horiz_dif, grd_dif, beam = perezComp(
        dni, dhi, albedo, zen, 0.0, zen)

    # Isotropic irradiance from sky on horizontal surface, used later for
    # determining isotropic sky component
    iso_sky_dif = iso_dif

    # For calling PerezComp to break diffuse into components for 90 degree tilt
    # (vertical)
    inc, tiltr, sazmr = sunIncident(0, 90.0, 180.0, 45.0, zen, azm)

    # Call to get horiz_dif for vertical surface
    vti, iso_dif, circ_dif, horiz_dif, grd_dif, beam = perezComp(
        dni, dhi, albedo, inc, tiltr, zen)

    # Horizon diffuse irradiance on a vertical surface, used later for
    # determining horizon brightening irradiance component
    F2DHI = horiz_dif

    index = -99
    n2 = -99.9
    if (PVbackSurface == "glass"):
        # Index to use with 1-degree hemispherical segment AOI correction
        # factor array
        index = 0
        n2 = 1.526  # Index of refraction for glass
    elif (PVbackSurface == "ARglass"):
        # Index to use with 1-degree hemispherical segment AOI correction
        # factor array
        index = 1
        n2 = 1.300  # Index of refraction for ARglass
    else:
        raise Exception(
            "Incorrect text input for PVbackSurface."
            " Must be glass or ARglass.")

    # Reflectance at normal incidence, Duffie and Beckman p217
    Ro = math.pow((n2 - 1.0) / (n2 + 1.0), 2.0)

    # Average GHI on ground under PV array for cases when x projection exceed
    # 2*rtr
    aveGroundGHI = 0.0
    for i in range(0,100):
        aveGroundGHI += rearGroundGHI[i] / 100.0

    # Calculate x,y coordinates of bottom and top edges of PV row in back of desired PV row so that portions of sky and ground viewed by the 
    # PV cell may be determined. Origin of x-y axis is the ground pobelow the lower front edge of the desired PV row. The row in back of 
    # the desired row is in the positive x direction.

    h = math.sin(beta);          # Vertical height of sloped PV panel (in PV panel slope lengths)                    
    x1 = math.cos(beta);         # Horizontal distance from front of panel to rear of panel (in PV panel slope lengths)
    rtr = D + x1;                # Row-to-row distance (in PV panel slope lengths)
    PbotX = rtr;                 # x value for poon bottom egde of PV module/panel of row in back of (in PV panel slope lengths)
    PbotY = C;                   # y value for poon bottom egde of PV module/panel of row in back of (in PV panel slope lengths)
    PtopX = rtr + x1;            # x value for poon top egde of PV module/panel of row in back of (in PV panel slope lengths)
    PtopY = h + C;               # y value for poon top egde of PV module/panel of row in back of (in PV panel slope lengths)

    # 2. Calculate diffuse and direct component irradiances for each cell row 
    for i in range (0, cellRows):
    
        # Calculate diffuse irradiances and reflected amounts for each cell row over it's field of view of 180 degrees, 
        # beginning with the angle providing the upper most view of the sky (j=0)
        #PcellX = x1 * (i + 0.5) / ((double)cellRows);                    # x value for location of PV cell
        #PcellY = C + h * (i + 0.5) / ((double)cellRows);                 # y value for location of PV cell
        PcellX = x1 * (i + 0.5) / (cellRows) + offset * math.sin(beta);    # x value for location of PV cell with OFFSET FOR SARA REFERENCE CELLS     4/26/2016
        PcellY = C + h * (i + 0.5) / (cellRows) - offset * math.cos(beta); # y value for location of PV cell with OFFSET FOR SARA REFERENCE CELLS     4/26/2016
        elvUP = math.atan((PtopY - PcellY) / (PtopX - PcellX));          # Elevation angle up from PV cell to top of PV module/panel, radians
        elvDOWN = math.atan((PcellY - PbotY) / (PbotX - PcellX));        # Elevation angle down from PV cell to bottom of PV module/panel, radians
        if (rowType == "last" or rowType == "single"):                         # 4/19/16 No array to the rear for these cases
        
            elvUP = 0.0;
            elvDOWN = 0.0;
        
        #Console.WriteLine("ElvUp = 0", elvUP / DTOR);
        #if (i == 0)
        #    Console.WriteLine("ElvDown = 0", elvDOWN / DTOR);

        #123
        #iStopIso = Convert.ToInt32((beta - elvUP) / DTOR);        # Last whole degree in arc range that sees sky, first is 0
        #Console.WriteLine("iStopIso = 0", iStopIso);
        #iHorBright = Convert.ToInt32(max(0.0, 6.0 - elvUP / DTOR));    # Number of whole degrees for which horizon brightening occurs
        #iStartGrd = Convert.ToInt32((beta + elvDOWN) / DTOR);               # First whole degree in arc range that sees ground, last is 180

        iStopIso = int(round((beta - elvUP) / DTOR));        # Last whole degree in arc range that sees sky, first is 0
        #Console.WriteLine("iStopIso = 0", iStopIso);
        iHorBright = int(round(max(0.0, 6.0 - elvUP / DTOR)));    # Number of whole degrees for which horizon brightening occurs
        iStartGrd = int(round((beta + elvDOWN) / DTOR));               # First whole degree in arc range that sees ground, last is 180

        backGTI.append(0.0)                                                      # Initialtize front GTI

        for j in range (0, iStopIso):                                      # Add sky diffuse component and horizon brightening if present
        
            backGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * iso_sky_dif;                               # Sky radiation
    #          backGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * iso_sky_dif;                               # Sky radiation

            if ((iStopIso - j) <= iHorBright):                                   # Add horizon brightening term if seen
            
                backGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * F2DHI / 0.052264;  # 0.052246 = 0.5 * [cos(84) - cos(90)]
            #backGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * F2DHI / 0.052264;  # 0.052246 = 0.5 * [cos(84) - cos(90)]
            
        

        if (rowType == "interior" or rowType == "first"):                          # 4/19/16 Only add reflections from PV modules for these cases
        

            for j in range (iStopIso, iStartGrd):      #j = iStopIso; j < iStartGrd; j++)                              # Add relections from PV module front surfaces
            
                L = (PbotX - PcellX) / math.cos(elvDOWN);                    # Diagonal distance from cell to bottom of module in row behind
                startAlpha = -(j - iStopIso) * DTOR + elvUP + elvDOWN;
                stopAlpha = -(j + 1 - iStopIso) * DTOR + elvUP + elvDOWN;
                m = L * math.sin(startAlpha);
                theta = math.pi - elvDOWN - (math.pi / 2.0 - startAlpha) - beta;
                projectedX2 = m / math.cos(theta);                           # Projected distance on sloped PV module
                m = L * math.sin(stopAlpha);
                theta = math.pi - elvDOWN - (math.pi / 2.0 - stopAlpha) - beta;
                projectedX1 = m / math.cos(theta);                           # Projected distance on sloped PV module
                projectedX1 = max(0.0, projectedX1);
                #Console.WriteLine("j= 0 projected X1 = 1,6:0.000  projected X2 = 2,6:0.000", j, projectedX1, projectedX2);

                PVreflectedIrr = 0.0;                                        # Irradiance from PV module front cover reflections
                deltaCell = 1.0 / cellRows;                          # Length of cell in sloped direction in module/panel units (dimensionless)
                for k in range (0, cellRows):                                  # Determine which cells in behind row are seen, and their reflected irradiance
                
                    cellBot = k * deltaCell;                                 # Position of bottom of cell along PV module/panel
                    cellTop = (k + 1) * deltaCell;                           # Position of top of cell along PV module/panel
                    cellLengthSeen = 0.0;                                    # Length of cell seen for this row, start with zero
                    if (cellBot >= projectedX1 and cellTop <= projectedX2):
                        cellLengthSeen = cellTop - cellBot;                         # Sees the whole cell
                    elif (cellBot <= projectedX1 and cellTop >= projectedX2):
                        cellLengthSeen = projectedX2 - projectedX1;                 # Sees portion in the middle of cell
                    elif (cellBot >= projectedX1 and projectedX2 > cellBot and cellTop >= projectedX2):
                        cellLengthSeen = projectedX2 - cellBot;                     # Sees bottom of cell
                    elif (cellBot <= projectedX1 and projectedX1 < cellTop and cellTop <= projectedX2):
                        cellLengthSeen = cellTop - projectedX1;                     # Sees top of cell
                    #Console.WriteLine("cell= 0 cellBot = 1,5:0.00 cellTop = 2,5:0.00  Cell length seen = 3,5:0.00", k, cellBot, cellTop, cellLengthSeen);
                    PVreflectedIrr += cellLengthSeen * frontReflected[k];           # Add reflected radiation for this PV cell, if seen, weight by cell length seen
                
                PVreflectedIrr /= projectedX2 - projectedX1;                        # Reflected irradiance from PV modules (W/m2)
                backGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * PVreflectedIrr;     # Radiation reflected from PV module surfaces onto back surface of module
            
            # End of adding reflections from PV module surfaces
        #Console.WriteLine("");
        #if (i == 0)
        #Console.WriteLine("iStartGrd = 0", iStartGrd);
        for j in range (iStartGrd, 180):                                  # Add ground reflected component
        
            startElvDown = (j - iStartGrd) * DTOR + elvDOWN;             # Start and ending down elevations for this j loop 
            stopElvDown = (j + 1 - iStartGrd) * DTOR + elvDOWN;
            projectedX2 = PcellX + np.float64(PcellY) / math.tan(startElvDown);      # Projection of ElvDown to ground in +x direction (X1 and X2 opposite nomenclature for front irradiance method)
            projectedX1 = PcellX + PcellY / math.tan(stopElvDown);
            actualGroundGHI = 0.0;                                       # Actuall ground GHI from summing array values
            #if (i == 0)
            #    Console.WriteLine("j= 0 projected X1 = 1,6:0.0", j, 100 * projectedX1 / rtr);
            if (abs(projectedX1 - projectedX2) > 0.99 * rtr):
            
                if (rowType == "last" or rowType == "single"):                  # 4/19/16 No array to rear for these cases
                
                    actualGroundGHI = ghi;                                      # Use total value if projection approximates the rtr
                
                else:
                    actualGroundGHI = aveGroundGHI;                                 # Use average value if projection approximates the rtr                        
            
            else:
            
                projectedX1 = 100.0 * projectedX1 / rtr;                        # Normalize projections and multiply by 100
                projectedX2 = 100.0 * projectedX2 / rtr;
                #Console.WriteLine("projectedX1 = 0 projectedX2 = 1", projectedX1, projectedX2);

                if ((rowType == "last" or rowType == "single") and (abs(projectedX1) > 99.0 or abs(projectedX2) > 99.0)):    #4/19/2016
                
                    actualGroundGHI = ghi;                                      # Use total value if projection > rtr for "last" or "single"
                
                else:
                
                    while (projectedX1 >= 100.0 or projectedX2 >= 100.0):            # Offset so array indexes are less than 100
                    
                        projectedX1 -= 100.0;
                        projectedX2 -= 100.0;
                    
                    while (projectedX1 < -100.0 or projectedX2 < -100.0):            # Offset so array indexes are >= -100.0  12/13/2016
                    
                        projectedX1 += 100.0;
                        projectedX2 += 100.0;
                    

                    #Console.WriteLine("projectedX1 = 0 projectedX2 = 1", projectedX1, projectedX2);
                    index1 = (int)(projectedX1 + 100.0) - 100;                  # Determine indexes for use with rearGroundGHI array and frontGroundGHI array(truncates values)
                    index2 = (int)(projectedX2 + 100.0) - 100;                  # (int)(1.9) = 1 and (int)(-1.9) = -1; (int)(1.9+100) - 100 = 1 and (int)(-1.9+100) - 100 = -2
                    #Console.WriteLine("index1=0 index2=1", index1, index2);
                    if (index1 == index2):
                    
                        if (index1 < 0):
                            actualGroundGHI = frontGroundGHI[index1 + 100];
                        #actualGroundGHI = 0.0;
                        else:
                            actualGroundGHI = rearGroundGHI[index1];                        # x projections in same groundGHI element THIS SEEMS TO ADD HICCUP 4/26/2016 ***************************
                        #actualGroundGHI = 0.0;
                    
                    else:
                    
                        for k in range (index1, index2+1):  #for (k = index1; k <= index2; k++)                      # Sum the irradiances on the ground if projections are in different groundGHI elements
                        
                            if (k == index1):
                            
                                if (k < 0):
                                    actualGroundGHI += frontGroundGHI[k + 100] * (k + 1.0 - projectedX1);
                                else:
                                    actualGroundGHI += rearGroundGHI[k] * (k + 1.0 - projectedX1);
                            
                            elif (k == index2):
                            
                                if (k < 0):
                                    actualGroundGHI += frontGroundGHI[k + 100] * (projectedX2 - k);
                                else:
                                    actualGroundGHI += rearGroundGHI[k] * (projectedX2 - k);
                            
                            else:
                            
                                if (k < 0):
                                    actualGroundGHI += frontGroundGHI[k + 100];
                                else:
                                    actualGroundGHI += rearGroundGHI[k];
                            
                        
                        actualGroundGHI /= projectedX2 - projectedX1;                # Irradiance on ground in the 1 degree field of view
                    
                

                #if (i == 0)
                #    Console.WriteLine("j=0 index1=1 index2=2 projectX1=3,5:0.0 projectX2=4,5:0.0 actualGrdGHI=5,6:0.0", j, index1, index2, projectedX1, projectedX2, actualGroundGHI);
                # End of if looping to determine actualGroundGHI

            
            backGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * actualGroundGHI * albedo;     # Add ground reflected component

            #Console.WriteLine("actualGroundGHI = 0,6:0.0 inputGHI = 1,6:0.0 aveArrayGroundGHI = 2,6:0.0", actualGroundGHI, dhi + dni * math.cos(zen), aveGroundGHI);
            
            # End of j loop for adding ground reflected componenet 

        # Calculate and add direct and circumsolar irradiance components
        inc, tiltr, sazmr = sunIncident(0, 180-beta / DTOR, sazm / DTOR - 180, 45.0, zen, azm)  # For calling PerezComp to break diffuse into components for downward facing tilt
        
        gtiAllpc, iso_dif, circ_dif, horiz_dif, grd_dif, beam = perezComp(dni, dhi, albedo, inc, tiltr, zen) # Call to get components for the tilt

        cellShade = pvBackSH * cellRows - i;
        if (cellShade > 1.0):    # Fully shaded if > 1, no shade if < 0, otherwise fractionally shaded
            cellShade = 1.0;
        elif (cellShade < 0.0):
            cellShade = 0.0;

        if (cellShade < 1.0 and inc < math.pi / 2.0):  # Cell not shaded entirely and inc < 90 deg
        
            cor = aOIcorrection(n2, inc);                # Get AOI correction for beam and circumsolar
            backGTI[i] += (1.0 - cellShade) * (beam + circ_dif) * cor; # Add beam and circumsolar radiation
        
        # End of for i = 0; i < cellRows loop
    
    return backGTI, aveGroundGHI;
    # End of GetBackSurfaceIrradiances


def getFrontSurfaceIrradiances(rowType, maxShadow, PVfrontSurface, beta, sazm,
                               dni, dhi, C, D, albedo, zen, azm, cellRows,
                               pvFrontSH, frontGroundGHI):      
    """
    This method calculates the AOI corrected irradiance on the front of the PV
    module/panel and the irradiance reflected from the the front of the PV
    module/panel. 11/12/2015
    
    Added row type and MaxShadow and changed code to accommodate 4/19/2015
    
    Parameters
    ----------
    rowType : str
        Type of row: "first", "interior", "last", or "single" 
    maxShadow
        Maximum shadow length projected to the front (-) or rear (+) from the
        front of the module row (in PV panel slope lengths), only used for
        `rowTypes` other than "interior"
    PVfrontSurface
        PV module front surface material type, either "glass" or "ARglass"
    beta
        Tilt from horizontal of the PV modules/panels (deg)
    sazm
        Surface azimuth of PV panels (deg)
    dni
        Direct normal irradiance (W/m2)
    dhi
        Diffuse horizontal irradiance (W/m2)
    C
        Ground clearance of PV panel (in PV panel slope lengths)            
    D
        Horizontal distance between rows of PV panels (in PV panel slope
        lengths) 
    albedo
        Ground albedo
    zen
        Sun zenith (in radians)
    azm
        Sun azimuth (in radians)
    pvFrontSH
        Decimal fraction of the front surface of the PV panel that is shaded,
        0.0 to 1.0
    froutGroundGHI : array of size [100]
        Global horizontal irradiance for each of 100 ground segments in front
        of the module row
    
    Returns
    -------
    frontGTI : array of size [cellRows]
        AOI corrected irradiance on front side of PV module/panel, one for each
        cell row (W/m2)
    frontReflected : array of size [cellRows]
        Irradiance reflected from the front of the PV module/panel (W/m2)
    aveGroundGHI : numeric
        Average GHI on the ground (includes effects of shading by array) from
        the array frontGroundGHI[100]

    Notes
    -----
    1-degree hemispherical segment AOI correction factor for glass (index=0)
    and ARglass (index=1). Creates a list containing 5 lists, each of 8 items,
    all set to 0
    """
    frontGTI = []
    frontReflected = []
    #w, h = 2, 180;
    #SegAOIcor = [[0 for x in range(w)] for y in range(h)] 
    SegAOIcor = ([[0.057563, 0.128570, 0.199651, 0.265024, 0.324661, 0.378968, 0.428391, 0.473670, 0.514788, 0.552454, 
            0.586857, 0.618484, 0.647076, 0.673762, 0.698029, 0.720118, 0.740726, 0.759671, 0.776946, 0.792833, 
            0.807374, 0.821010, 0.833534, 0.845241, 0.855524, 0.865562, 0.874567, 0.882831, 0.890769, 0.897939, 
            0.904373, 0.910646, 0.916297, 0.921589, 0.926512, 0.930906, 0.935179, 0.939074, 0.942627, 0.946009, 
            0.949096, 0.952030, 0.954555, 0.957157, 0.959669, 0.961500, 0.963481, 0.965353, 0.967387, 0.968580, 
            0.970311, 0.971567, 0.972948, 0.974114, 0.975264, 0.976287, 0.977213, 0.978142, 0.979057, 0.979662, 
            0.980460, 0.981100, 0.981771, 0.982459, 0.982837, 0.983199, 0.983956, 0.984156, 0.984682, 0.985026, 
            0.985364, 0.985645, 0.985954, 0.986241, 0.986484, 0.986686, 0.986895, 0.987043, 0.987287, 0.987388, 
            0.987541, 0.987669, 0.987755, 0.987877, 0.987903, 0.987996, 0.988022, 0.988091, 0.988104, 0.988114, 
            0.988114, 0.988104, 0.988091, 0.988022, 0.987996, 0.987903, 0.987877, 0.987755, 0.987669, 0.987541, 
            0.987388, 0.987287, 0.987043, 0.986895, 0.986686, 0.986484, 0.986240, 0.985954, 0.985645, 0.985364, 
            0.985020, 0.984676, 0.984156, 0.983956, 0.983199, 0.982837, 0.982459, 0.981771, 0.981100, 0.980460, 
            0.979662, 0.979057, 0.978142, 0.977213, 0.976287, 0.975264, 0.974114, 0.972947, 0.971567, 0.970311, 
            0.968580, 0.967387, 0.965353, 0.963481, 0.961501, 0.959671, 0.957157, 0.954555, 0.952030, 0.949096, 
            0.946009, 0.942627, 0.939074, 0.935179, 0.930906, 0.926512, 0.921589, 0.916297, 0.910646, 0.904373, 
            0.897939, 0.890769, 0.882831, 0.874567, 0.865562, 0.855524, 0.845241, 0.833534, 0.821010, 0.807374, 
            0.792833, 0.776946, 0.759671, 0.740726, 0.720118, 0.698029, 0.673762, 0.647076, 0.618484, 0.586857, 
            0.552454, 0.514788, 0.473670, 0.428391, 0.378968, 0.324661, 0.265024, 0.199651, 0.128570, 0.057563],
            [0.062742, 0.139913, 0.216842, 0.287226, 0.351055, 0.408796, 0.460966, 0.508397, 0.551116, 0.589915,
            0.625035, 0.657029, 0.685667, 0.712150, 0.735991, 0.757467, 0.777313, 0.795374, 0.811669, 0.826496, 
            0.839932, 0.852416, 0.863766, 0.874277, 0.883399, 0.892242, 0.900084, 0.907216, 0.914023, 0.920103, 
            0.925504, 0.930744, 0.935424, 0.939752, 0.943788, 0.947313, 0.950768, 0.953860, 0.956675, 0.959339, 
            0.961755, 0.964039, 0.965984, 0.967994, 0.969968, 0.971283, 0.972800, 0.974223, 0.975784, 0.976647, 
            0.977953, 0.978887, 0.979922, 0.980773, 0.981637, 0.982386, 0.983068, 0.983759, 0.984436, 0.984855, 
            0.985453, 0.985916, 0.986417, 0.986934, 0.987182, 0.987435, 0.988022, 0.988146, 0.988537, 0.988792, 
            0.989043, 0.989235, 0.989470, 0.989681, 0.989857, 0.990006, 0.990159, 0.990263, 0.990455, 0.990515, 
            0.990636, 0.990731, 0.990787, 0.990884, 0.990900, 0.990971, 0.990986, 0.991042, 0.991048, 0.991057, 
            0.991057, 0.991048, 0.991042, 0.990986, 0.990971, 0.990900, 0.990884, 0.990787, 0.990731, 0.990636, 
            0.990515, 0.990455, 0.990263, 0.990159, 0.990006, 0.989857, 0.989681, 0.989470, 0.989235, 0.989043, 
            0.988787, 0.988532, 0.988146, 0.988022, 0.987435, 0.987182, 0.986934, 0.986417, 0.985916, 0.985453, 
            0.984855, 0.984436, 0.983759, 0.983068, 0.982386, 0.981637, 0.980773, 0.979920, 0.978887, 0.977953, 
            0.976647, 0.975784, 0.974223, 0.972800, 0.971284, 0.969970, 0.967994, 0.965984, 0.964039, 0.961755, 
            0.959339, 0.956675, 0.953860, 0.950768, 0.947313, 0.943788, 0.939752, 0.935424, 0.930744, 0.925504, 
            0.920103, 0.914023, 0.907216, 0.900084, 0.892242, 0.883399, 0.874277, 0.863766, 0.852416, 0.839932, 
            0.826496, 0.811669, 0.795374, 0.777313, 0.757467, 0.735991, 0.712150, 0.685667, 0.657029, 0.625035, 
            0.589915, 0.551116, 0.508397, 0.460966, 0.408796, 0.351055, 0.287226, 0.216842, 0.139913, 0.062742]]);

    beta = beta * DTOR                 # Tilt from horizontal of the PV modules/panels, in radians
    sazm = sazm * DTOR                 # Surface azimuth of PV module/panels, in radians

    # 1. Calculate and assign various paramters to be used for modeling irradiances
    iso_dif = 0.0; circ_dif = 0.0; horiz_dif = 0.0; grd_dif = 0.0; beam = 0.0;   # For calling PerezComp to break diffuse into components for zero tilt (horizontal)                           
    ghi, iso_dif, circ_dif, horiz_dif, grd_dif, beam = perezComp(dni, dhi, albedo, zen, 0.0, zen) # Call to get iso_dif for horizontal surface
    #            print "PEREZCOMP1 = "
    #            print "ghi = ", ghi
    #            print "iso_dif = ", iso_dif
    #            print "circ_dif = ", circ_dif
    #            print "horiz_dif = ", horiz_dif
    #            print "grd_dif = ", grd_dif
    #            print "beam = ", beam

    iso_sky_dif = iso_dif;       # Isotropic irradiance from sky on horizontal surface, used later for determining isotropic sky component

    inc, tiltr, sazmr = sunIncident(0, 90.0, 180.0, 45.0, zen, azm) # For calling PerezComp to break diffuse into components for 90 degree tilt (vertical)
    #            print "sunIncident 1."
    #            print "inc = ", inc
    #            print "tiltr = ", tiltr
    #            print "sazmr = ", sazmr

    vti, iso_dif, circ_dif, horiz_dif, grd_dif, beam = perezComp(dni, dhi, albedo, inc, tiltr, zen) # Call to get horiz_dif for vertical surface
    #            print "PEREZCOMP1 = "
    #            print "vti = ", vti
    #            print "iso_dif = ", iso_dif
    #            print "circ_dif = ", circ_dif
    #            print "horiz_dif = ", horiz_dif
    #            print "grd_dif = ", grd_dif
    #            print "beam = ", beam

    F2DHI = horiz_dif;           # Horizon diffuse irradiance on a vertical surface, used later for determining horizon brightening irradiance component

    index = -99;
    n2 = -99.9;
    if (PVfrontSurface == "glass"):
    
        index = 0;          # Index to use with 1-degree hemispherical segment AOI correction factor array
        n2 = 1.526;         # Index of refraction for glass
    
    elif (PVfrontSurface == "ARglass"):
    
        index = 1;          # Index to use with 1-degree hemispherical segment AOI correction factor array
        n2 = 1.300;         # Index of refraction for ARglass
    
    else:
        raise Exception("Incorrect text input for PVfrontSurface. Must be glass or ARglass.")
    
    Ro = math.pow((n2 - 1.0) / (n2 + 1.0), 2.0);     # Reflectance at normal incidence, Duffie and Beckman p217

    aveGroundGHI = 0.0;          # Average GHI on ground under PV array for cases when x projection exceed 2*rtr
    for i in range (0,100):
        aveGroundGHI += frontGroundGHI[i] / 100.0;

    # Calculate x,y coordinates of bottom and top edges of PV row in front of desired PV row so that portions of sky and ground viewed by the 
    # PV cell may be determined. Origin of x-y axis is the ground pobelow the lower front edge of the desired PV row. The row in front of 
    # the desired row is in the negative x direction.

    h = math.sin(beta);          # Vertical height of sloped PV panel (in PV panel slope lengths)                    
    x1 = math.cos(beta);         # Horizontal distance from front of panel to rear of panel (in PV panel slope lengths)
    rtr = D + x1;                # Row-to-row distance (in PV panel slope lengths)
    PbotX = -rtr;                # x value for poon bottom egde of PV module/panel of row in front of (in PV panel slope lengths)
    PbotY = C;                   # y value for poon bottom egde of PV module/panel of row in front of (in PV panel slope lengths)
    PtopX = -D;                  # x value for poon top egde of PV module/panel of row in front of (in PV panel slope lengths)
    PtopY = h + C;               # y value for poon top egde of PV module/panel of row in front of (in PV panel slope lengths)

    # 2. Calculate diffuse and direct component irradiances for each cell row 
    
    
    for i in range (0, cellRows):
    
        # Calculate diffuse irradiances and reflected amounts for each cell row over it's field of view of 180 degrees, 
        # beginning with the angle providing the upper most view of the sky (j=0)
        PcellX = x1 * (i + 0.5) / (cellRows);                    # x value for location of PV cell
        PcellY = C + h * (i + 0.5) / (cellRows);                 # y value for location of PV cell
        elvUP = math.atan((PtopY - PcellY) / (PcellX - PtopX));          # Elevation angle up from PV cell to top of PV module/panel, radians
        elvDOWN = math.atan((PcellY - PbotY) / (PcellX - PbotX));        # Elevation angle down from PV cell to bottom of PV module/panel, radians
        if (rowType == "first" or rowType == "single"):                         # 4/19/16 No array in front for these cases
        
            elvUP = 0.0;
            elvDOWN = 0.0;
        
        #Console.WriteLine("ElvUp = 0", elvUP / DTOR);
        #if (i == 0)
        #    Console.WriteLine("ElvDown = 0", elvDOWN / DTOR);

        if math.isnan(beta):
            print( "Beta is Nan")
        if math.isnan(elvUP):
            print( "elvUP is Nan")
        if math.isnan((math.pi - beta - elvUP) / DTOR):
            print( "division is Nan")
        
        
        iStopIso = int(round(np.float64((math.pi - beta - elvUP)) / DTOR)) # Last whole degree in arc range that sees sky, first is 0
        #Console.WriteLine("iStopIso = 0", iStopIso);
        iHorBright = int(round(max(0.0, 6.0 - elvUP / DTOR)));    # Number of whole degrees for which horizon brightening occurs
        iStartGrd = int(round((math.pi - beta + elvDOWN) / DTOR));     # First whole degree in arc range that sees ground, last is 180
    #                print "iStopIso = ", iStopIso
    #                print "iHorBright = ", iHorBright
    #                print "iStartGrd = ", iStartGrd

        frontGTI.append(0.0)                                                      # Initialtize front GTI
        frontReflected.append(0.0);                                                # Initialize reflected amount from front

        for j in range (0, iStopIso):                                        # Add sky diffuse component and horizon brightening if present
        #for (j = 0; j < iStopIso; j++)                                      
        
            frontGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * iso_sky_dif;                               # Sky radiation
            frontReflected[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * iso_sky_dif * (1.0 - SegAOIcor[index][j] * (1.0 - Ro));    # Reflected radiation from module
            if ((iStopIso - j) <= iHorBright):                                   # Add horizon brightening term if seen
            
                frontGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * F2DHI / 0.052264;  # 0.052246 = 0.5 * [cos(84) - cos(90)]
                frontReflected[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * (F2DHI / 0.052264) * (1.0 - SegAOIcor[index][j] * (1.0 - Ro));    # Reflected radiation from module
            
        

        #if (i == 0)
        #    Console.WriteLine("iStartGrd = 0", iStartGrd);
        for j in range (iStartGrd, 180):                                     # Add ground reflected component
        #(j = iStartGrd; j < 180; j++)                                   
            startElvDown = (j - iStartGrd) * DTOR + elvDOWN;             # Start and ending down elevations for this j loop 
            stopElvDown = (j + 1 - iStartGrd) * DTOR + elvDOWN;
            projectedX1 = PcellX - np.float64(PcellY) / math.tan(startElvDown);      # Projection of ElvDown to ground in -x direction
            projectedX2 = PcellX - PcellY / math.tan(stopElvDown);
            actualGroundGHI = 0.0;                                       # Actuall ground GHI from summing array values
            #if (i == 0)
            #    Console.WriteLine("j= 0 projected X1 = 1,6:0.0", j, 100 * projectedX1 / rtr);
            if (abs(projectedX1 - projectedX2) > 0.99 * rtr):
            
                if (rowType == "first" or rowType == "single"):                  # 4/19/16 No array in front for these cases
                
                    actualGroundGHI = ghi;                                      # Use total value if projection approximates the rtr
                
                else:
                    actualGroundGHI = aveGroundGHI;                                 # Use average value if projection approximates the rtr                        
            
            else:
            
                projectedX1 = 100.0 * projectedX1 / rtr;                        # Normalize projections and multiply by 100
                projectedX2 = 100.0 * projectedX2 / rtr;
                if ((rowType == "first" or rowType == "single") and (abs(projectedX1) > rtr or abs(projectedX2) > rtr)):    #4/19/2016
                
                    actualGroundGHI = ghi;                                      # Use total value if projection > rtr for "first" or "single"
                
                else:
                
                    while (projectedX1 < 0.0 or projectedX2 < 0.0):                  # Offset so array indexes are positive
                    
                        projectedX1 += 100.0;
                        projectedX2 += 100.0;
                    
                    index1 = int(projectedX1);                                  # Determine indexes for use with groundGHI array (truncates values)
                    index2 = int(projectedX2);
                    if (index1 == index2):
                    
                        actualGroundGHI = frontGroundGHI[index1];                        # x projections in same groundGHI element
                    
                    else:
                    
                        for k in range (index1, index2+1):                   # Sum the irradiances on the ground if projections are in different groundGHI elements
                        #for (k = index1; k <= index2; k++)
                        
                            #Console.WriteLine("index1=0 index2=1", index1,index2);
                            if (k == index1):
                                actualGroundGHI += frontGroundGHI[k] * (k + 1.0 - projectedX1);
                            elif (k == index2):
                            
                                if (k < 100):
                                    actualGroundGHI += frontGroundGHI[k] * (projectedX2 - k);
                                else:
                                    actualGroundGHI += frontGroundGHI[k - 100] * (projectedX2 - k);
                            
                            else:
                            
                                if (k < 100):
                                    actualGroundGHI += frontGroundGHI[k];
                                else:
                                    actualGroundGHI += frontGroundGHI[k - 100];
                            
                        
                        actualGroundGHI /= projectedX2 - projectedX1;                # Irradiance on ground in the 1 degree field of view
                    
                
                #if (i == 0)
                #    Console.WriteLine("j=0 index1=1 index2=2 projectX1=3,5:0.0 projectX2=4,5:0.0 actualGrdGHI=5,6:0.0", j, index1, index2, projectedX1, projectedX2, actualGroundGHI);
            
            frontGTI[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * SegAOIcor[index][j] * actualGroundGHI * albedo;     # Add ground reflected component
            frontReflected[i] += 0.5 * (math.cos(j * DTOR) - math.cos((j + 1) * DTOR)) * actualGroundGHI * albedo * (1.0 - SegAOIcor[index][j] * (1.0 - Ro));    # Reflected ground radiation from module
            #Console.WriteLine("actualGroundGHI = 0,6:0.0 inputGHI = 1,6:0.0 aveArrayGroundGHI = 2,6:0.0", actualGroundGHI, dhi + dni * math.cos(zen), aveGroundGHI);
            # End of j loop for adding ground reflected componenet 

        # Calculate and add direct and circumsolar irradiance components
        inc, tiltr, sazmr = sunIncident(0, beta / DTOR, sazm / DTOR, 45.0, zen, azm) # For calling PerezComp to break diffuse into components for 90 degree tilt (vertical)
    #                print "sunIncident 2."
    #                print "inc = ", inc
    #                print "tiltr = ", tiltr
    #                print "sazmr = ", sazmr

    #                print " INCIDENT REALY NEEDED for AOI ", inc
        gtiAllpc, iso_dif, circ_dif, horiz_dif, grd_dif, beam = perezComp(dni, dhi, albedo, inc, tiltr, zen) # Call to get components for the tilt
    #                print "PEREZCOMP 2 = "
    #                print "gtiAllpc = ", vti
    #                print "iso_dif = ", iso_dif
    #                print "circ_dif = ", circ_dif
    #                print "horiz_dif = ", horiz_dif
    #                print "grd_dif = ", grd_dif
    #                print "beam = ", beam

        cellShade = pvFrontSH * cellRows - i;
        if (cellShade > 1.0):    # Fully shaded if > 1, no shade if < 0, otherwise fractionally shaded
            cellShade = 1.0;
        elif (cellShade < 0.0):
            cellShade = 0.0;

        if (cellShade < 1.0 and inc < math.pi / 2.0):  # Cell not shaded entirely and inc < 90 deg
        
            cor = aOIcorrection(n2, inc);                # Get AOI correction for beam and circumsolar
            frontGTI[i] += (1.0 - cellShade) * (beam + circ_dif) * cor; # Add beam and circumsolar radiation
            #frontReflected[i] += (1.0 - cellShade) * (beam + circ_dif) * (1.0 - cor * (1.0 - Ro));    # Reflected beam and circumsolar radiation from module
        
        # End of for i = 0; i < cellRows loop
    return aveGroundGHI, frontGTI, frontReflected;
    # End of GetFrontSurfaceIrradiances

    
def getGroundShadeFactors(rowType, beta, C, D, elv, azm, sazm):
    """
    This method determines if the ground is shaded from direct beam radiation
    for points on the ground from the leading edge of one row of PV panels to
    the leading edge of the next row of PV panels behind it. This row-to-row
    dimension is divided into 100 ground segments and a ground shade factor is
    returned for each ground segment, with values of 1 for shaded segments and
    values of 0 for non shaded segments. The fractional amounts of shading of
    the front and back surfaces of the PV panel are also returned. 8/20/2015

    4/18/2016 - Modified to account for different row types. Because the ground
    factors may now be different depending on row, they are calculated for the
    row-to-row dimension to the rear of the leading module edge and to the
    front of the leading edge. Also returned is the maximum shadow length
    projected to the front or rear from the front of the module row

    Parameters
    ----------
    rowType : str
        "first", "interior", "last", or "single" 
    beta
        Tilt from horizontal of the PV modules/panels (deg)
    C
        Ground clearance of PV panel (in PV panel slope lengths)            
    D
        Horizontal distance between rows of PV panels (in PV panel slope
        lengths) 
    elv
        Sun elevation (in radians)
    azm
        Sun azimuth (in radians)
    sazm
        Surface azimuth of PV panels (deg)

    Returns
    -------
    pvFrontSH : numeric
        Decimal fraction of the front surface of the PV panel that is shaded,
        0.0 to 1.0
    pvBackSH : numeric
        Decimal fraction of the back surface of the PV panel that is shaded,
        0.0 to 1.0
    rearGroundSH : array of size [100]
        Ground shade factors for ground segments to the rear, 0 = not shaded,
        1 = shaded
    frontGroundSH : array of size [100]
        Ground shade factors for ground segments to the front, 0 = not shaded,
        1 = shaded
    maxShadow : numeric
        Maximum shadow length projected to the front(-) or rear (+) from the
        front of the module row (in PV panel slope lengths), only used later
        for rowTypes other than "interior"
    """
    rearGroundSH = []
    frontGroundSH = []
    
    beta = beta * DTOR  # Tilt from horizontal of the PV modules/panels, in radians
    sazm = sazm * DTOR  # Surface azimuth of PV module/pamels, in radians

    h = math.sin(beta);          # Vertical height of sloped PV panel (in PV panel slope lengths)                    
    x1 = math.cos(beta);         # Horizontal distance from front of panel to rear of panel (in PV panel slope lengths)
    rtr = D + x1;                # Row-to-row distance (in PV panel slope lengths)

    # Divide the row-to-row spacing into 100 intervals for calculating ground shade factors
    delta = rtr / 100.0;
    x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals

    Lh = (h / math.tan(elv)) * math.cos(sazm - azm); # Horizontal length of shadow perpindicular to row from top of module to bottom of module
    Lhc = ((h + C) / math.tan(elv)) * math.cos(sazm - azm); # Horizontal length of shadow perpindicular to row from top of module to ground level
    Lc = (C / math.tan(elv)) * math.cos(sazm - azm); # Horizontal length of shadow perpindicular to row from bottom of module to ground level

    ss1 = 0.0; se1 = 0.0; ss2 = 0.0; se2 = 0.0;  # Initialize shading start (s) and end (e) to zeros for two potential shading segments
    pvFrontSH = 0.0;
    pvBackSH = 0.0;

    if (rowType == "interior"):
    
        if (Lh > D): # Front side of PV module partially shaded, back completely shaded, ground completely shaded
        
            pvFrontSH = (Lh - D) / (Lh + x1);
            pvBackSH = 1.0;
            ss1 = 0.0;      # Ground shaded from 0.0 to rtr
            se1 = rtr;
        
        elif (Lh < -(rtr + x1)):  # Back side of PV module partially shaded, front completely shaded, ground completely shaded
        
            pvFrontSH = 1.0;
            pvBackSH = (Lh + rtr + x1) / (Lh + x1);
            ss1 = 0.0;      # Ground shaded from 0.0 to rtr
            se1 = rtr;
        
        else:        # Ground is partially shaded (I assume)            
        
            if (Lhc >= 0.0):     # Shadow to rear of row, module front unshaded, back shaded
            
                pvFrontSH = 0.0;
                pvBackSH = 1.0;
                Ss = Lc;         # Shadow starts at Lc
                Se = Lhc + x1;   # Shadow ends here
                while (Ss > rtr):
                
                    Ss -= rtr;          # Put shadow in correct rtr space if needed
                    Se -= rtr;
                
                ss1 = Ss;
                se1 = Se;
                if (se1 > rtr):          # then need to use two shade areas
                
                    se1 = rtr;
                    ss2 = 0.0;
                    se2 = Se - rtr;
                    if (se2 > ss1):
                        # This would mean ground completely shaded, does this occur?
                        ss1 = 0.0;      # Ground shaded from 0.0 to rtr
                        se1 = rtr;
                    
                
            
            else:                # Shadow to front of row, either front or back might be shaded, depending on tilt and other factors
            
                Ss = 0.0;         # Shadow starts at Lc, initialize
                Se = 0.0;         # Shadow ends here, initialize
                if (Lc < Lhc + x1):
                
                    pvFrontSH = 0.0;
                    pvBackSH = 1.0;
                    Ss = Lc;         # Shadow starts at Lc
                    Se = Lhc + x1;   # Shadow ends here
                
                else:
                
                    pvFrontSH = 1.0;
                    pvBackSH = 0.0;
                    Ss = Lhc + x1;  # Shadow starts at Lhc + x1
                    Se = Lc;        # Shadow ends here
                
                while (Ss < 0.0):
                
                    Ss += rtr;          # Put shadow in correct rtr space if needed
                    Se += rtr;
                
                ss1 = Ss;
                se1 = Se;
                if (se1 > rtr):         # then need to use two shade areas
                
                    se1 = rtr;
                    ss2 = 0.0;
                    se2 = Se - rtr;
                    if (se2 > ss1):
                        # This would mean ground completely shaded, does this occur?
                        ss1 = 0.0;      # Ground shaded from 0.0 to rtr
                        se1 = rtr;
                    
                
            
            # End of if (Lh > D) else branching

        delta = rtr / 100.0;
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals            
        #for (i = 0; i <= 99; i++)
        for i in range(0,100):
            
            x += delta;
            #if ((x >= ss1 && x < se1) || (x >= ss2 && x < se2)):
            if ((x >= ss1 and x < se1) or (x >= ss2 and x < se2)):
            
                rearGroundSH.append(1);        # x within a shaded interval, set groundSH to 1 to indicate shaded
                frontGroundSH.append(1);       # same for both front and rear
            
            else:
            
                rearGroundSH.append(0);        # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
                frontGroundSH.append(0);       # same for both front and rear
            
            #Console.WriteLine("x = 0,6:0.0000 groundSH = 1", x, groundSH[i]);
        
        # End of if row type == "interior"

    elif (rowType == "first"):
    
        if (Lh > 0.0):   # Sun is on front side of PV module
        
            pvFrontSH = 0.0;
            pvBackSH = 1.0;
            ss1 = Lc;           # Ground shaded from shadow of lower edge
            se1 = x1 + Lhc;     # to shadow of upper edge                                        
            # End of if sun on front side of PV module

        elif (Lh < -(rtr + x1)):  # Back side of PV module partially shaded from row to rear, front completely shaded, ground completely shaded
        
            pvFrontSH = 1.0;
            pvBackSH = (Lh + rtr + x1) / (Lh + x1);
            ss1 = -rtr;      # Ground shaded from -rtr to rtr
            se1 = rtr;

            # End of if back side of PV module partially shaded, front completely shaded, ground completely shaded

        else:   # Shadow to frontside of row, either front or back might be shaded, depending on tilt and other factors     
        
            if (Lc < Lhc + x1):
            
                pvFrontSH = 0.0;
                pvBackSH = 1.0;
                ss1 = Lc;         # Shadow starts at Lc
                se1 = Lhc + x1;   # Shadow ends here
            
            else:
            
                pvFrontSH = 1.0;
                pvBackSH = 0.0;
                ss1 = Lhc + x1;  # Shadow starts at Lhc + x1
                se1 = Lc;        # Shadow ends here
            

            # End of shadow to front of row 

        delta = rtr / 100.0;
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals            
        for i in range(0,100):
        
            x += delta;
            if (x >= ss1 and x < se1):
                rearGroundSH.append(1)         # x within a shaded interval, set groundSH to 1 to indicate shaded
            else:
                rearGroundSH.append(0)         # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
        

        x = -rtr - delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals for front interval           
        for i in range(0,100):
        
            x += delta;
            if (x >= ss1 and x < se1):
                frontGroundSH.append(1)        # x within a shaded interval, set groundSH to 1 to indicate shaded
            else:
                frontGroundSH.append(0)        # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
        
        # End of if row type == "first"

    elif (rowType == "last"):
    
        if (Lh > D): # Front side of PV module partially shaded, back completely shaded, ground completely shaded
        
            pvFrontSH = (Lh - D) / (Lh + x1);
            pvBackSH = 1.0;
            ss1 = -rtr;      # Ground shaded from -rtr to rtr
            se1 = rtr;
        
        else:   # Shadow to frontside of row, either front or back might be shaded, depending on tilt and other factors     
        
            if (Lc < Lhc + x1):
            
                pvFrontSH = 0.0;
                pvBackSH = 1.0;
                ss1 = Lc;         # Shadow starts at Lc
                se1 = Lhc + x1;   # Shadow ends here
            
            else:
            
                pvFrontSH = 1.0;
                pvBackSH = 0.0;
                ss1 = Lhc + x1;  # Shadow starts at Lhc + x1
                se1 = Lc;        # Shadow ends here
            

            # End of shadow to front of row 

        delta = rtr / 100.0;
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals            
        for i in range(0,100):
        
            x += delta;
            if (x >= ss1 and x < se1):
                rearGroundSH.append(1);        # x within a shaded interval, set groundSH to 1 to indicate shaded
            else:
                rearGroundSH.append(0);        # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
        

        x = -rtr - delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals for front interval           
        for i in range(0,100):
        
            x += delta;
            if (x >= ss1 and x < se1):
                frontGroundSH.append(1);        # x within a shaded interval, set groundSH to 1 to indicate shaded
            else:
                frontGroundSH.append(0);        # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
        

        # End of if row type == "last"

    elif (rowType == "single"):
    
        if (Lh > 0.0):   # Shadow to the rear
        
            pvFrontSH = 0.0;
            pvBackSH = 1.0;
            ss1 = Lc;           # Ground shaded from shadow of lower edge
            se1 = x1 + Lhc;     # to shadow of upper edge                                        
            # End of if sun on front side of PV module

        else:   # Shadow to frontside of row, either front or back might be shaded, depending on tilt and other factors     
        
            if (Lc < Lhc + x1):
            
                pvFrontSH = 0.0;
                pvBackSH = 1.0;
                ss1 = Lc;         # Shadow starts at Lc
                se1 = Lhc + x1;   # Shadow ends here
            
            else:
            
                pvFrontSH = 1.0;
                pvBackSH = 0.0;
                ss1 = Lhc + x1;  # Shadow starts at Lhc + x1
                se1 = Lc;        # Shadow ends here
            

            # End of shadow to front of row 

        delta = rtr / 100.0;
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals            
        for i in range(0,100):
        
            x += delta;
            if (x >= ss1 and x < se1):
                rearGroundSH.append(1);        # x within a shaded interval, set groundSH to 1 to indicate shaded
            else:
                rearGroundSH.append(0);        # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
        

        x = -rtr - delta / 2.0;    # Initialize horizontal dimension x to provide midpoof intervals for front interval           
        for i in range(0,100):
        
            x += delta;
            if (x >= ss1 and x < se1):
                frontGroundSH.append(1);        # x within a shaded interval, set groundSH to 1 to indicate shaded
            else:
                frontGroundSH.append(0);        # x not within a shaded interval, set groundSH to 0 to indicated not shaded, i.e. sunny
        

        # End of if row type == "single"
    else:
        print ("ERROR: Incorrect row type not passed to function GetGroundShadedFactors ");

    if (abs(ss1) > abs(se1)):      # Maximum shadow length projected from the front of the PV module row
        maxShadow = ss1;
    else:
        maxShadow = se1;

    #Console.WriteLine("elv = 0,6:0.00  azm = 1,6:0.00  sazm = 2,6:0.00", elv * 180.0 / math.pi, azm * 180.0 / math.pi, sazm * 180.0 / math.pi);
    #Console.WriteLine("ss1 = 0,6:0.0000 se1 = 1,6:0.0000 ss2 = 2,6:0.0000 se2 = 3,6:0.0000     rtr = 4,6:0.000", ss1, se1, ss2, se2, rtr);
    #Console.WriteLine("pvFrontSH = 0,6:0.00 pvBackSH = 1,6:0.00", pvFrontSH, pvBackSH);

    # End of GetGroundShadedFactors
    
    #print "rearGroundSH", rearGroundSH[0]
    return pvFrontSH, pvBackSH, maxShadow, rearGroundSH, frontGroundSH;
    # End of getGroundShadeFactors


def getSkyConfigurationFactors(rowType, beta, C, D):
    """
    This method determines the sky configuration factors for points on the
    ground from the leading edge of one row of PV panels to the leading edge of
    the next row of PV panels behind it. This row-to-row dimension is divided
    into 100 ground segments and a sky configuration factor is returned for
    each ground segment. The sky configuration factor represents the fraction
    of the isotropic diffuse sky radiation (unobstructed) that is present on
    the ground when partially obstructed by the rows of PV panels. The
    equations follow that on pages in the notebook dated 8/12/2015. 8/20/2015            

    4/15/2016 Modifed for calculations other than just the interior rows. Row
    type is identified with the string `rowType`, with the possilbe values:

    * first = first row of the array
    * interior = interior row of array
    * last = last row of the array
    * single = a single row array

    Because the sky configuration factors may now be different depending on
    row, they are calculated for the row-to-row dimension to the rear of the
    leading module edge and to the front of the leading edge.

    Parameters
    ----------
    rowType : str
        "first", "interior", "last", or "single"
    beta : float
        Tilt from horizontal of the PV modules/panels (deg)
    C : float
        Ground clearance of PV panel (in PV module/panel slope lengths)            
    D : float
        Horizontal distance between rows of PV panels (in PV module/panel slope
        lengths)

    Returns
    -------
    rearSkyConfigFactors : array of size [100]
        Sky configuration factors to rear of leading PVmodule edge (decimal
        fraction)
    frontSkyConfigFactors : array of size [100]
        Sky configuration factors to rear of leading PVmodule edge (decimal
        fraction)

    Notes
    -----
    The horizontal distance between rows, `D`, is from the back edge of one row
    to the front edge of the next, and it is not the row-to-row spacing.
    """

    rearSkyConfigFactors = []
    frontSkyConfigFactors = []
    
    # Tilt from horizontal of the PV modules/panels, in radians
    beta = beta * DTOR
    # Vertical height of sloped PV panel (in PV panel slope lengths)                    
    h = math.sin(beta)
    # Horizontal distance from front of panel to rear of panel (in PV panel
    # slope lengths)
    x1 = math.cos(beta)
    rtr = D + x1  # Row-to-row distance (in PV panel slope lengths)

    # Forced fix for case of C = 0
    # FIXME: for some reason the Config Factors go from 1 to 2 and not 0 to 1.
    # TODO: investigate why this is happening in the code.
    if C==0:
        C=0.0000000001
        
    if C < 0:
        LOGGER.error(
            "Height is below ground level. Function GetSkyConfigurationFactors"
            " will continue but results might be unreliable")
        
    # Divide the row-to-row spacing into 100 intervals and calculate
    # configuration factors
    delta = rtr / 100.0

    if (rowType == "interior"):
        # Initialize horizontal dimension x to provide midpoint of intervals
        x = -delta / 2.0

        for i in range(0,100):        
            x += delta
            #  <--rtr=x1+D--><--rtr=x1+D--><--rtr=x1+D-->
            # |\            |\            |\            |\ 
            # | \ `         | \           | \          /| \
            # h  \   `      h  \          h  \       /  h  \
            # |   \     `   |   \         |   \    /    |   \
            # |_x1_\____D__`|_x1_\____D___|_x1_\_/_D____|_x1_\_
            # |               `   <------x-----/|
            # C                  `           /
            # |              angA   `      /  angB
            # *------------------------`-/---------------------
            #                          x
            # use ATAN2: 4-quadrant tangent instead of ATAN
            # check 2 rows away
            angA = math.atan2(h + C, (2.0 * rtr + x1 - x))
            angB = math.atan2(C, (2.0 * rtr - x))
            beta1 = max(angA, angB)
            # check 1 rows away
            angA = math.atan2(h + C, (rtr + x1 - x))
            angB = math.atan2(C, (rtr - x))
            beta2 = min(angA, angB)
            # check 0 rows away
            beta3 = max(angA, angB)
            beta4 = math.atan2(h + C, (x1 - x))
            beta5 = math.atan2(C, (-x))
            beta6 = math.atan2(h + C, (-D - x))
            sky1 =0; sky2 =0; sky3 =0
            if (beta2 > beta1):
                sky1 = 0.5 * (math.cos(beta1) - math.cos(beta2))
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4))
            if (beta6 > beta5):
                sky3 = 0.5 * (math.cos(beta5) - math.cos(beta6))
            skyAll = sky1 + sky2 + sky3

            # Save as arrays of values, same for both to the rear and front
            rearSkyConfigFactors.append(skyAll)
            frontSkyConfigFactors.append(skyAll)        
        # End of if "interior"

    elif (rowType == "first"):
    
        # RearSkyConfigFactors don't have a row in front, calculation of sky3
        # changed, beta6 = 180 degrees
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoint of intervals

        for i in range(0,100):
        
            x += delta;
            angA = math.atan((h + C) / (2.0 * rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (2.0 * rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta1 = max(angA, angB);

            angA = math.atan((h + C) / (rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta2 = min(angA, angB);

            beta3 = max(angA, angB);

            beta4 = math.atan((h + C) / (x1 - x));
            if (beta4 < 0.0):
                beta4 += math.pi;

            beta5 = math.atan(C / (-x));
            if (beta5 < 0.0):
                beta5 += math.pi;

            beta6 = math.pi;

            sky1 = 0.0; sky2 = 0.0; sky3 = 0.0;
            if (beta2 > beta1):
                sky1 = 0.5 * (math.cos(beta1) - math.cos(beta2));
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4));
            if (beta6 > beta5):
                sky3 = 0.5 * (math.cos(beta5) - math.cos(beta6));
            skyAll = sky1 + sky2 + sky3;
            rearSkyConfigFactors.append(skyAll);    # Save as arrays of values                   
            #Console.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
            #sw.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
        

        #  frontSkyConfigFactors don't have a row in front, calculation of sky3 included as part of revised sky2,
        #  beta 4 set to 180 degrees
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoint of intervals

        for i in range(0,100):
        
            x += delta;
            angA = math.atan((h + C) / (2.0 * rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (2.0 * rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta1 = max(angA, angB);

            angA = math.atan((h + C) / (rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta2 = min(angA, angB);

            beta3 = max(angA, angB);

            beta4 = math.pi;

            sky1 = 0.0; sky2 = 0.0;
            if (beta2 > beta1):
                sky1 = 0.5 * (math.cos(beta1) - math.cos(beta2));
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4));

            skyAll = sky1 + sky2;
            frontSkyConfigFactors.append(skyAll);  # Save as arrays of values
            #Console.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
            #sw.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
        
        # End of if "first"

    elif (rowType == "last"):
    
        #  RearSkyConfigFactors don't have a row to the rear, combine sky1 into sky 2, set beta 3 = 0.0
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoint of intervals

        for i in range(0,100):
        
            x += delta;

            beta3 = 0.0;

            beta4 = math.atan((h + C) / (x1 - x));
            if (beta4 < 0.0):
                beta4 += math.pi;

            beta5 = math.atan(C / (-x));
            if (beta5 < 0.0):
                beta5 += math.pi;

            beta6 = math.atan((h + C) / (-D - x));
            if (beta6 < 0.0):
                beta6 += math.pi;

            sky2 = 0.0; sky3 = 0.0;
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4));
            if (beta6 > beta5):
                sky3 = 0.5 * (math.cos(beta5) - math.cos(beta6));
            skyAll = sky2 + sky3;
            rearSkyConfigFactors.append(skyAll);     # Save as arrays of values
            #Console.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
            #sw.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
        

        #  FrontSkyConfigFactors have beta1 = 0.0
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoint of intervals

        for i in range(0,100):
        
            x += delta;
            angA = math.atan((h + C) / (2.0 * rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (2.0 * rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta1 = max(angA, angB);
            beta1 = 0.0;

            angA = math.atan((h + C) / (rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta2 = min(angA, angB);

            beta3 = max(angA, angB);

            beta4 = math.atan((h + C) / (x1 - x));
            if (beta4 < 0.0):
                beta4 += math.pi;

            beta5 = math.atan(C / (-x));
            if (beta5 < 0.0):
                beta5 += math.pi;

            beta6 = math.atan((h + C) / (-D - x));
            if (beta6 < 0.0):
                beta6 += math.pi;

            sky1 = 0.0; sky2 = 0.0; sky3 = 0.0;
            if (beta2 > beta1):
                sky1 = 0.5 * (math.cos(beta1) - math.cos(beta2));
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4));
            if (beta6 > beta5):
                sky3 = 0.5 * (math.cos(beta5) - math.cos(beta6));
            skyAll = sky1 + sky2 + sky3;
            frontSkyConfigFactors.append(skyAll);      # Save as arrays of values,
            #Console.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
            #sw.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
        
        # End of if "last" row

    elif (rowType == "single"):
    
                    
        #  RearSkyConfigFactors don't have a row to the rear ir front, combine sky1 into sky 2, set beta 3 = 0.0,
        #  for sky3, beta6 = 180.0.
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoint of intervals

        for i in range(0,100):
        
            x += delta;

            beta3 = 0.0;

            beta4 = math.atan((h + C) / (x1 - x));
            if (beta4 < 0.0):
                beta4 += math.pi;

            beta5 = math.atan(C / (-x));
            if (beta5 < 0.0):
                beta5 += math.pi;

            beta6 = math.pi;

            sky2 = 0.0; sky3 = 0.0;
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4));
            if (beta6 > beta5):
                sky3 = 0.5 * (math.cos(beta5) - math.cos(beta6));
            skyAll = sky2 + sky3;
            rearSkyConfigFactors.append(skyAll);     # Save as arrays of values                    
            #Console.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
            #sw.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
        

        #  FrontSkyConfigFactors have only a row to the rear, combine sky3 into sky2, set beta1 = 0, beta4 = 180
        x = -delta / 2.0;    # Initialize horizontal dimension x to provide midpoint of intervals

        for i in range(0,100):
        
            x += delta;
            angA = math.atan((h + C) / (2.0 * rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (2.0 * rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta1 = max(angA, angB);
            beta1 = 0.0;

            angA = math.atan((h + C) / (rtr + x1 - x));
            if (angA < 0.0):
                angA += math.pi;
            angB = math.atan(C / (rtr - x));
            if (angB < 0.0):
                angB += math.pi;
            beta2 = min(angA, angB);

            beta3 = max(angA, angB);

            beta4 = math.pi;

            sky1 = 0.0; sky2 = 0.0;
            if (beta2 > beta1):
                sky1 = 0.5 * (math.cos(beta1) - math.cos(beta2));
            if (beta4 > beta3):
                sky2 = 0.5 * (math.cos(beta3) - math.cos(beta4));
            skyAll = sky1 + sky2;
            frontSkyConfigFactors.append(skyAll);  # Save as arrays of values
            #Console.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
            #sw.WriteLine("0,5:0.000,1,5:0.000,2,5:0.000,3,5:0.000,4,5:0.000", x, sky1, sky2, sky3, skyAll);
        
        # End of if "single"
    else:
        print("ERROR: Incorrect row type not passed to function GetSkyConfigurationFactors ");

    return rearSkyConfigFactors, frontSkyConfigFactors;
# End of GetSkyConfigurationFactors


def rowSpacing(beta, sazm, lat, lng, tz, hour, minute):
    """
    This method determines the horizontal distance D between rows of PV panels
    (in PV module/panel slope lengths) for no shading on December 21 (north
    hemisphere) June 21 (south hemisphere) for a module tilt angle beta and
    surface azimuth sazm, and a given latitude, longitude, and time zone and
    for the time passed to the method (typically 9 am).

    (Ref: the row-to-row spacing is then ``D + cos(beta)``)
    8/21/2015

    Parameters
    ----------
    beta : double
        Tilt from horizontal of the PV modules/panels (deg)
    sazm : double
        Surface azimuth of the PV modules/panels (deg)
    lat : double
        Site latitude (deg)
    lng : double
        Site longitude (deg)
    tz : double
        Time zone (hrs)
    hour : int
        hour for no shading criteria
    minute: double
        minute for no shading

    Returns
    -------
    D : numeric
        Horizontal distance between rows of PV panels (in PV panel slope
        lengths)
    """     
    beta = beta * DTOR  # Tilt from horizontal of the PV modules/panels, in radians
    sazm = sazm * DTOR  # Surface azimuth of PV module/pamels, in radians
    if lat >= 0:
        [azm, zen, elv, dec, sunrise, sunset, Eo, tst] = solarPos (2014, 12, 21, hour, minute, lat, lng, tz)
    else:
        [azm, zen, elv, dec, sunrise, sunset, Eo, tst] = solarPos (2014, 6, 21, hour, minute, lat, lng, tz)
    tst = 8.877  ##DLL Forced value
    minute -= 60.0 * (tst - hour);      # Adjust minute so sun position is calculated for a tst equal to the
      # time passed to the function

    if lat >= 0:
        [azm, zen, elv, dec, sunrise, sunset, Eo, tst] = solarPos(2014, 12, 21, hour, minute, lat, lng, tz)
    else:
        [azm, zen, elv, dec, sunrise, sunset, Eo, tst] = solarPos(2014, 6, 21, hour, minute, lat, lng, tz)
      
    # Console.WriteLine("tst = {0} azm = {1} elv = {2}", tst, azm * 180.0 / Math.PI, elv * 180.0 / Math.PI);
    D = math.cos(sazm - azm) * math.sin(beta) / math.tan(elv)
    return D
# End of RowSpacing  


def trackingBFvaluescalculator(beta, hub_height, r2r):
    '''
    1-axis tracking helper file

    Parameters
    ----------
    beta : float
        Tilt from horizontal of the PV modules/panels, in radians
    hub_height : float
        tracker hub height
    r2r : float
        Row-to-row distance (in PV panel slope lengths)

    Returns
    -------
    C : float
        ground clearance of PV panel
    D : float
        row-to-row distance (each in PV panel slope lengths)
    '''
    # Created on Tue Jun 13 08:01:56 2017
    # @author: sayala

    beta = beta * DTOR  # Tilt from horizontal of the PV modules/panels, in radians
    x1 = math.cos(beta);         # Horizontal distance from front of panel to rear of panel (in PV panel slope lengths)
    #rtr = D + x1;                # Row-to-row distance (in PV panel slope lengths)
    D = r2r - x1;                # Calculates D DistanceBetweenRows(panel slope lengths)
    hm = 0.5*math.sin(beta);        # vertical distance from bottom of panel to top of panel (in PV panel slope lengths)
    #C = 0.5+Cv-hm               # Ground clearance of PV panel (in PV panel slope lengths). 
    C = hub_height - hm          #Adding a 0.5 for half a panel slope length, since it is assumed the panel is rotating around its middle axis 

    return C, D

[(18, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (152, 'sun.perezComp', 'perezComp', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (161, 'sun.sunIncident', 'sunIncident', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (164, 'sun.perezComp', 'perezComp', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (189, 'math.pow', 'math.pow', False, 'import math\n'), (201, 'math.sin', 'math.sin', False, 'import math\n'), (202, 'math.cos', 'math.cos', False, 'import math\n'), (522, 'sun.perezComp', 'perezComp', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (533, 'sun.sunIncident', 'sunIncident', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (539, 'sun.perezComp', 'perezComp', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (565, 'math.pow', 'math.pow', False, 'import math\n'), (575, 'math.sin', 'math.sin', False, 'import math\n'), (576, 'math.cos', 'math.cos', False, 'import math\n'), (802, 'math.sin', 'math.sin', False, 'import math\n'), (803, 'math.cos', 'math.cos', False, 'import math\n'), (1158, 'math.sin', 'math.sin', False, 'import math\n'), (1161, 'math.cos', 'math.cos', False, 'import math\n'), (1557, 'math.cos', 'math.cos', False, 'import math\n'), (218, 'math.atan', 'math.atan', False, 'import math\n'), (219, 'math.atan', 'math.atan', False, 'import math\n'), (392, 'sun.sunIncident', 'sunIncident', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (394, 'sun.perezComp', 'perezComp', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (592, 'math.atan', 'math.atan', False, 'import math\n'), (593, 'math.atan', 'math.atan', False, 'import math\n'), (603, 'math.isnan', 'math.isnan', False, 'import math\n'), (605, 'math.isnan', 'math.isnan', False, 'import math\n'), (607, 'math.isnan', 'math.isnan', False, 'import math\n'), (710, 'sun.sunIncident', 'sunIncident', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (717, 'sun.perezComp', 'perezComp', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (810, 'math.cos', 'math.cos', False, 'import math\n'), (811, 'math.cos', 'math.cos', False, 'import math\n'), (812, 'math.cos', 'math.cos', False, 'import math\n'), (1515, 'sun.solarPos', 'solarPos', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (1517, 'sun.solarPos', 'solarPos', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (1523, 'sun.solarPos', 'solarPos', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (1525, 'sun.solarPos', 'solarPos', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (1528, 'math.tan', 'math.tan', False, 'import math\n'), (1560, 'math.sin', 'math.sin', False, 'import math\n'), (404, 'sun.aOIcorrection', 'aOIcorrection', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (734, 'sun.aOIcorrection', 'aOIcorrection', False, 'from sun import solarPos, sunIncident, perezComp, aOIcorrection\n'), (810, 'math.tan', 'math.tan', False, 'import math\n'), (811, 'math.tan', 'math.tan', False, 'import math\n'), (812, 'math.tan', 'math.tan', False, 'import math\n'), (1198, 'math.atan2', 'math.atan2', False, 'import math\n'), (1199, 'math.atan2', 'math.atan2', False, 'import math\n'), (1202, 'math.atan2', 'math.atan2', False, 'import math\n'), (1203, 'math.atan2', 'math.atan2', False, 'import math\n'), (1207, 'math.atan2', 'math.atan2', False, 'import math\n'), (1208, 'math.atan2', 'math.atan2', False, 'import math\n'), (1209, 'math.atan2', 'math.atan2', False, 'import math\n'), (1528, 'math.cos', 'math.cos', False, 'import math\n'), (1528, 'math.sin', 'math.sin', False, 'import math\n'), (216, 'math.sin', 'math.sin', False, 'import math\n'), (217, 'math.cos', 'math.cos', False, 'import math\n'), (1233, 'math.atan', 'math.atan', False, 'import math\n'), (1236, 'math.atan', 'math.atan', False, 'import math\n'), (1241, 'math.atan', 'math.atan', False, 'import math\n'), (1244, 'math.atan', 'math.atan', False, 'import math\n'), (1251, 'math.atan', 'math.atan', False, 'import math\n'), (1255, 'math.atan', 'math.atan', False, 'import math\n'), (1281, 'math.atan', 'math.atan', False, 'import math\n'), (1284, 'math.atan', 'math.atan', False, 'import math\n'), (1289, 'math.atan', 'math.atan', False, 'import math\n'), (1292, 'math.atan', 'math.atan', False, 'import math\n'), (259, 'math.cos', 'math.cos', False, 'import math\n'), (262, 'math.sin', 'math.sin', False, 'import math\n'), (264, 'math.cos', 'math.cos', False, 'import math\n'), (265, 'math.sin', 'math.sin', False, 'import math\n'), (267, 'math.cos', 'math.cos', False, 'import math\n'), (300, 'numpy.float64', 'np.float64', True, 'import numpy as np\n'), (300, 'math.tan', 'math.tan', False, 'import math\n'), (301, 'math.tan', 'math.tan', False, 'import math\n'), (611, 'numpy.float64', 'np.float64', True, 'import numpy as np\n'), (640, 'numpy.float64', 'np.float64', True, 'import numpy as np\n'), (640, 'math.tan', 'math.tan', False, 'import math\n'), (641, 'math.tan', 'math.tan', False, 'import math\n'), (1325, 'math.atan', 'math.atan', False, 'import math\n'), (1329, 'math.atan', 'math.atan', False, 'import math\n'), (1333, 'math.atan', 'math.atan', False, 'import math\n'), (1354, 'math.atan', 'math.atan', False, 'import math\n'), (1357, 'math.atan', 'math.atan', False, 'import math\n'), (1363, 'math.atan', 'math.atan', False, 'import math\n'), (1366, 'math.atan', 'math.atan', False, 'import math\n'), (1373, 'math.atan', 'math.atan', False, 'import math\n'), (1377, 'math.atan', 'math.atan', False, 'import math\n'), (1381, 'math.atan', 'math.atan', False, 'import math\n'), (1212, 'math.cos', 'math.cos', False, 'import math\n'), (1212, 'math.cos', 'math.cos', False, 'import math\n'), (1214, 'math.cos', 'math.cos', False, 'import math\n'), (1214, 'math.cos', 'math.cos', False, 'import math\n'), (1216, 'math.cos', 'math.cos', False, 'import math\n'), (1216, 'math.cos', 'math.cos', False, 'import math\n'), (1412, 'math.atan', 'math.atan', False, 'import math\n'), (1416, 'math.atan', 'math.atan', False, 'import math\n'), (1439, 'math.atan', 'math.atan', False, 'import math\n'), (1442, 'math.atan', 'math.atan', False, 'import math\n'), (1448, 'math.atan', 'math.atan', False, 'import math\n'), (1451, 'math.atan', 'math.atan', False, 'import math\n'), (244, 'math.cos', 'math.cos', False, 'import math\n'), (244, 'math.cos', 'math.cos', False, 'import math\n'), (625, 'math.cos', 'math.cos', False, 'import math\n'), (625, 'math.cos', 'math.cos', False, 'import math\n'), (626, 'math.cos', 'math.cos', False, 'import math\n'), (626, 'math.cos', 'math.cos', False, 'import math\n'), (1263, 'math.cos', 'math.cos', False, 'import math\n'), (1263, 'math.cos', 'math.cos', False, 'import math\n'), (1265, 'math.cos', 'math.cos', False, 'import math\n'), (1265, 'math.cos', 'math.cos', False, 'import math\n'), (1267, 'math.cos', 'math.cos', False, 'import math\n'), (1267, 'math.cos', 'math.cos', False, 'import math\n'), (1303, 'math.cos', 'math.cos', False, 'import math\n'), (1303, 'math.cos', 'math.cos', False, 'import math\n'), (1305, 'math.cos', 'math.cos', False, 'import math\n'), (1305, 'math.cos', 'math.cos', False, 'import math\n'), (290, 'math.cos', 'math.cos', False, 'import math\n'), (290, 'math.cos', 'math.cos', False, 'import math\n'), (385, 'math.cos', 'math.cos', False, 'import math\n'), (385, 'math.cos', 'math.cos', False, 'import math\n'), (630, 'math.cos', 'math.cos', False, 'import math\n'), (630, 'math.cos', 'math.cos', False, 'import math\n'), (704, 'math.cos', 'math.cos', False, 'import math\n'), (704, 'math.cos', 'math.cos', False, 'import math\n'), (705, 'math.cos', 'math.cos', False, 'import math\n'), (705, 'math.cos', 'math.cos', False, 'import math\n'), (1339, 'math.cos', 'math.cos', False, 'import math\n'), (1339, 'math.cos', 'math.cos', False, 'import math\n'), (1341, 'math.cos', 'math.cos', False, 'import math\n'), (1341, 'math.cos', 'math.cos', False, 'import math\n'), (1387, 'math.cos', 'math.cos', False, 'import math\n'), (1387, 'math.cos', 'math.cos', False, 'import math\n'), (1389, 'math.cos', 'math.cos', False, 'import math\n'), (1389, 'math.cos', 'math.cos', False, 'import math\n'), (1391, 'math.cos', 'math.cos', False, 'import math\n'), (1391, 'math.cos', 'math.cos', False, 'import math\n'), (249, 'math.cos', 'math.cos', False, 'import math\n'), (249, 'math.cos', 'math.cos', False, 'import math\n'), (629, 'math.cos', 'math.cos', False, 'import math\n'), (629, 'math.cos', 'math.cos', False, 'import math\n'), (1424, 'math.cos', 'math.cos', False, 'import math\n'), (1424, 'math.cos', 'math.cos', False, 'import math\n'), (1426, 'math.cos', 'math.cos', False, 'import math\n'), (1426, 'math.cos', 'math.cos', False, 'import math\n'), (1462, 'math.cos', 'math.cos', False, 'import math\n'), (1462, 'math.cos', 'math.cos', False, 'import math\n'), (1464, 'math.cos', 'math.cos', False, 'import math\n'), (1464, 'math.cos', 'math.cos', False, 'import math\n')]