Пример #1
0
def _solar_projection_tangent(solar_zenith, solar_azimuth, surface_azimuth):
    """
    Tangent of the angle between the zenith vector and the sun vector
    projected to the plane defined by the zenith vector and surface_azimuth.

    .. math::
        \\tan \\phi = \\cos\\left(\\text{solar azimuth}-\\text{system azimuth}
        \\right)\\tan\\left(\\text{solar zenith}\\right)

    Parameters
    ----------
    solar_zenith : numeric
        Solar zenith angle. [degree].
    solar_azimuth : numeric
        Solar azimuth. [degree].
    surface_azimuth : numeric
        Azimuth of the module surface, i.e., North=0, East=90, South=180,
        West=270. [degree]

    Returns
    -------
    tan_phi : numeric
        Tangent of the angle between vertical and the projection of the
        sun direction onto the YZ plane.
    """
    rotation = solar_azimuth - surface_azimuth
    tan_phi = cosd(rotation) * tand(solar_zenith)
    return tan_phi
Пример #2
0
def calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth):
    """
    Calculate tracker axis tilt in the global reference frame when on a sloped
    plane.

    Parameters
    ----------
    slope_azimuth : float
        direction of normal to slope on horizontal [degrees]
    slope_tilt : float
        tilt of normal to slope relative to vertical [degrees]
    axis_azimuth : float
        direction of tracker axes on horizontal [degrees]

    Returns
    -------
    axis_tilt : float
        tilt of tracker [degrees]

    See also
    --------
    pvlib.tracking.singleaxis
    pvlib.tracking.calc_cross_axis_tilt

    Notes
    -----
    See [1]_ for derivation of equations.

    References
    ----------
    .. [1] Kevin Anderson and Mark Mikofski, "Slope-Aware Backtracking for
       Single-Axis Trackers", Technical Report NREL/TP-5K00-76626, July 2020.
       https://www.nrel.gov/docs/fy20osti/76626.pdf
    """
    delta_gamma = axis_azimuth - slope_azimuth
    # equations 18-19
    tan_axis_tilt = cosd(delta_gamma) * tand(slope_tilt)
    return np.degrees(np.arctan(tan_axis_tilt))
Пример #3
0
def physicaliam(K, L, n, aoi):
    '''
    Determine the incidence angle modifier using refractive 
    index, glazing thickness, and extinction coefficient

    physicaliam calculates the incidence angle modifier as described in
    De Soto et al. "Improvement and validation of a model for photovoltaic
    array performance", section 3. The calculation is based upon a physical
    model of absorbtion and transmission through a cover. Required
    information includes, incident angle, cover extinction coefficient,
    cover thickness

    Note: The authors of this function believe that eqn. 14 in [1] is
    incorrect. This function uses the following equation in its place:
    theta_r = arcsin(1/n * sin(theta))

    Parameters
    ----------
    K : float
        The glazing extinction coefficient in units of 1/meters. Reference
        [1] indicates that a value of  4 is reasonable for "water white"
        glass. K must be a numeric scalar or vector with all values >=0. If K
        is a vector, it must be the same size as all other input vectors.

    L : float
        The glazing thickness in units of meters. Reference [1] indicates
        that 0.002 meters (2 mm) is reasonable for most glass-covered
        PV panels. L must be a numeric scalar or vector with all values >=0. 
        If L is a vector, it must be the same size as all other input vectors.

    n : float
        The effective index of refraction (unitless). Reference [1]
        indicates that a value of 1.526 is acceptable for glass. n must be a 
        numeric scalar or vector with all values >=0. If n is a vector, it 
        must be the same size as all other input vectors.

    aoi : Series
        The angle of incidence between the module normal vector and the
        sun-beam vector in degrees. 

    Returns
    -------
    IAM : float or Series
        The incident angle modifier as specified in eqns. 14-16 of [1].
        IAM is a column vector with the same number of elements as the
        largest input vector.
        
        Theta must be a numeric scalar or vector.
        For any values of theta where abs(aoi)>90, IAM is set to 0. For any
        values of aoi where -90 < aoi < 0, theta is set to abs(aoi) and
        evaluated.

    References
    ----------
    [1] W. De Soto et al., "Improvement and validation of a model for
    photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
    2006.

    [2] Duffie, John A. & Beckman, William A.. (2006). Solar Engineering 
    of Thermal Processes, third edition. [Books24x7 version] Available 
    from http://common.books24x7.com/toc.aspx?bookid=17160. 

    See Also 
    --------
    getaoi   
    ephemeris   
    spa    
    ashraeiam
    '''
    thetar_deg = tools.asind(1.0 / n*(tools.sind(aoi)))

    tau = ( np.exp(- 1.0 * (K*L / tools.cosd(thetar_deg))) *
            ((1 - 0.5*((((tools.sind(thetar_deg - aoi)) ** 2) /
            ((tools.sind(thetar_deg + aoi)) ** 2) +
            ((tools.tand(thetar_deg - aoi)) ** 2) /
            ((tools.tand(thetar_deg + aoi)) ** 2))))) )
    
    zeroang = 1e-06
    
    thetar_deg0 = tools.asind(1.0 / n*(tools.sind(zeroang)))
    
    tau0 = ( np.exp(- 1.0 * (K*L / tools.cosd(thetar_deg0))) *
             ((1 - 0.5*((((tools.sind(thetar_deg0 - zeroang)) ** 2) /
             ((tools.sind(thetar_deg0 + zeroang)) ** 2) +
             ((tools.tand(thetar_deg0 - zeroang)) ** 2) /
             ((tools.tand(thetar_deg0 + zeroang)) ** 2))))) )
    
    IAM = tau / tau0
    
    IAM[abs(aoi) >= 90] = np.nan
    IAM[IAM < 0] = np.nan
    
    return IAM
Пример #4
0
def physical(aoi, n=1.526, K=4., L=0.002):
    r"""
    Determine the incidence angle modifier using refractive index ``n``,
    extinction coefficient ``K``, and glazing thickness ``L``.

    ``iam.physical`` calculates the incidence angle modifier as described in
    [1]_, Section 3. The calculation is based on a physical model of absorbtion
    and transmission through a transparent cover.

    Parameters
    ----------
    aoi : numeric
        The angle of incidence between the module normal vector and the
        sun-beam vector in degrees. Angles of 0 are replaced with 1e-06
        to ensure non-nan results. Angles of nan will result in nan.

    n : numeric, default 1.526
        The effective index of refraction (unitless). Reference [1]_
        indicates that a value of 1.526 is acceptable for glass.

    K : numeric, default 4.0
        The glazing extinction coefficient in units of 1/meters.
        Reference [1] indicates that a value of 4 is reasonable for
        "water white" glass.

    L : numeric, default 0.002
        The glazing thickness in units of meters. Reference [1]_
        indicates that 0.002 meters (2 mm) is reasonable for most
        glass-covered PV panels.

    Returns
    -------
    iam : numeric
        The incident angle modifier

    Notes
    -----
    The pvlib python authors believe that Eqn. 14 in [1]_ is
    incorrect, which presents :math:`\theta_{r} = \arcsin(n \sin(AOI))`.
    Here, :math:`\theta_{r} = \arcsin(1/n \times \sin(AOI))`

    References
    ----------
    .. [1] W. De Soto et al., "Improvement and validation of a model for
       photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
       2006.

    .. [2] Duffie, John A. & Beckman, William A.. (2006). Solar Engineering
       of Thermal Processes, third edition. [Books24x7 version] Available
       from http://common.books24x7.com/toc.aspx?bookid=17160.

    See Also
    --------
    pvlib.iam.martin_ruiz
    pvlib.iam.ashrae
    pvlib.iam.interp
    pvlib.iam.sapm
    """
    zeroang = 1e-06

    # hold a new reference to the input aoi object since we're going to
    # overwrite the aoi reference below, but we'll need it for the
    # series check at the end of the function
    aoi_input = aoi

    aoi = np.where(aoi == 0, zeroang, aoi)

    # angle of reflection
    thetar_deg = asind(1.0 / n * (sind(aoi)))

    # reflectance and transmittance for normal incidence light
    rho_zero = ((1 - n) / (1 + n))**2
    tau_zero = np.exp(-K * L)

    # reflectance for parallel and perpendicular polarized light
    rho_para = (tand(thetar_deg - aoi) / tand(thetar_deg + aoi))**2
    rho_perp = (sind(thetar_deg - aoi) / sind(thetar_deg + aoi))**2

    # transmittance for non-normal light
    tau = np.exp(-K * L / cosd(thetar_deg))

    # iam is ratio of non-normal to normal incidence transmitted light
    # after deducting the reflected portion of each
    iam = ((1 - (rho_para + rho_perp) / 2) / (1 - rho_zero) * tau / tau_zero)

    with np.errstate(invalid='ignore'):
        # angles near zero produce nan, but iam is defined as one
        small_angle = 1e-06
        iam = np.where(np.abs(aoi) < small_angle, 1.0, iam)

        # angles at 90 degrees can produce tiny negative values,
        # which should be zero. this is a result of calculation precision
        # rather than the physical model
        iam = np.where(iam < 0, 0, iam)

        # for light coming from behind the plane, none can enter the module
        iam = np.where(aoi > 90, 0, iam)

    if isinstance(aoi_input, pd.Series):
        iam = pd.Series(iam, index=aoi_input.index)

    return iam
Пример #5
0
def get_irradiance_poa(surface_tilt,
                       surface_azimuth,
                       solar_zenith,
                       solar_azimuth,
                       gcr,
                       height,
                       pitch,
                       ghi,
                       dhi,
                       dni,
                       albedo,
                       iam=1.0,
                       npoints=100):
    r"""
    Calculate plane-of-array (POA) irradiance on one side of a row of modules.

    The infinite sheds model [1] assumes the PV system comprises parallel,
    evenly spaced rows on a level, horizontal surface. Rows can be on fixed
    racking or single axis trackers. The model calculates irradiance at a
    location far from the ends of any rows, in effect, assuming that the
    rows (sheds) are infinitely long.

    POA irradiance components include direct, diffuse and global (total).
    Irradiance values are reduced to account for reflection of direct light,
    but are not adjusted for solar spectrum or reduced by a module's
    bifaciality factor.

    Parameters
    ----------
    surface_tilt : numeric
        Tilt of the surface from horizontal. Must be between 0 and 180. For
        example, for a fixed tilt module mounted at 30 degrees from
        horizontal, use ``surface_tilt=30`` to get front-side irradiance and
        ``surface_tilt=150`` to get rear-side irradiance. [degree]

    surface_azimuth : numeric
        Surface azimuth in decimal degrees east of north
        (e.g. North = 0, South = 180, East = 90, West = 270). [degree]

    solar_zenith : numeric
        Refraction-corrected solar zenith. [degree]

    solar_azimuth : numeric
        Solar azimuth. [degree]

    gcr : float
        Ground coverage ratio, ratio of row slant length to row spacing.
        [unitless]

    height : float
        Height of the center point of the row above the ground; must be in the
        same units as ``pitch``.

    pitch : float
        Distance between two rows; must be in the same units as ``height``.

    ghi : numeric
        Global horizontal irradiance. [W/m2]

    dhi : numeric
        Diffuse horizontal irradiance. [W/m2]

    dni : numeric
        Direct normal irradiance. [W/m2]

    albedo : numeric
        Surface albedo. [unitless]

    iam : numeric, default 1.0
        Incidence angle modifier, the fraction of direct irradiance incident
        on the surface that is not reflected away. [unitless]

    npoints : int, default 100
        Number of points used to discretize distance along the ground.

    Returns
    -------
    output : dict or DataFrame
        Output is a DataFrame when input ghi is a Series. See Notes for
        descriptions of content.

    Notes
    -----
    Input parameters ``height`` and ``pitch`` must have the same unit.

    ``output`` always includes:

    - ``poa_global`` : total POA irradiance. [W/m^2]
    - ``poa_diffuse`` : total diffuse POA irradiance from all sources. [W/m^2]
    - ``poa_direct`` : total direct POA irradiance. [W/m^2]
    - ``poa_sky_diffuse`` : total sky diffuse irradiance on the plane of array.
      [W/m^2]
    - ``poa_ground_diffuse`` : total ground-reflected diffuse irradiance on the
      plane of array. [W/m^2]

    References
    ----------
    .. [1] Mikofski, M., Darawali, R., Hamer, M., Neubert, A., and Newmiller,
       J. "Bifacial Performance Modeling in Large Arrays". 2019 IEEE 46th
       Photovoltaic Specialists Conference (PVSC), 2019, pp. 1282-1287.
       :doi:`10.1109/PVSC40753.2019.8980572`.

    See also
    --------
    get_irradiance
    """
    # Calculate some geometric quantities
    # rows to consider in front and behind current row
    # ensures that view factors to the sky are computed to within 5 degrees
    # of the horizon
    max_rows = np.ceil(height / (pitch * tand(5)))
    # fraction of ground between rows that is illuminated accounting for
    # shade from panels. [1], Eq. 4
    f_gnd_beam = utils._unshaded_ground_fraction(surface_tilt, surface_azimuth,
                                                 solar_zenith, solar_azimuth,
                                                 gcr)
    # integrated view factor from the ground to the sky, integrated between
    # adjacent rows interior to the array
    # method differs from [1], Eq. 7 and Eq. 8; height is defined at row
    # center rather than at row lower edge as in [1].
    vf_gnd_sky = _vf_ground_sky_integ(surface_tilt, surface_azimuth, gcr,
                                      height, pitch, max_rows, npoints)
    # fraction of row slant height that is shaded from direct irradiance
    f_x = _shaded_fraction(solar_zenith, solar_azimuth, surface_tilt,
                           surface_azimuth, gcr)

    # Integrated view factors to the sky from the shaded and unshaded parts of
    # the row slant height
    # Differs from [1] Eq. 15 and Eq. 16. Here, we integrate over each
    # interval (shaded or unshaded) rather than averaging values at each
    # interval's end points.
    vf_shade_sky, vf_noshade_sky = _vf_row_sky_integ(f_x, surface_tilt, gcr,
                                                     npoints)

    # view factors from the ground to shaded and unshaded portions of the row
    # slant height
    # Differs from [1] Eq. 17 and Eq. 18. Here, we integrate over each
    # interval (shaded or unshaded) rather than averaging values at each
    # interval's end points.
    f_gnd_pv_shade, f_gnd_pv_noshade = _vf_row_ground_integ(
        f_x, surface_tilt, gcr, npoints)

    # Total sky diffuse received by both shaded and unshaded portions
    poa_sky_pv = _poa_sky_diffuse_pv(f_x, dhi, vf_shade_sky, vf_noshade_sky)

    # irradiance reflected from the ground before accounting for shadows
    # and restricted views
    # this is a deviation from [1], because the row to ground view factor
    # is accounted for in a different manner
    ground_diffuse = ghi * albedo

    # diffuse fraction
    diffuse_fraction = np.clip(dhi / ghi, 0., 1.)
    # make diffuse fraction 0 when ghi is small
    diffuse_fraction = np.where(ghi < 0.0001, 0., diffuse_fraction)

    # Reduce ground-reflected irradiance because other rows in the array
    # block irradiance from reaching the ground.
    # [2], Eq. 9
    ground_diffuse = _poa_ground_shadows(ground_diffuse, f_gnd_beam,
                                         diffuse_fraction, vf_gnd_sky)

    # Ground-reflected irradiance on the row surface accounting for
    # the view to the ground. This deviates from [1], Eq. 10, 11 and
    # subsequent. Here, the row to ground view factor is computed. In [1],
    # the usual ground-reflected irradiance includes the single row to ground
    # view factor (1 - cos(tilt))/2, and Eq. 10, 11 and later multiply
    # this quantity by a ratio of view factors.
    poa_gnd_pv = _poa_ground_pv(f_x, ground_diffuse, f_gnd_pv_shade,
                                f_gnd_pv_noshade)

    # add sky and ground-reflected irradiance on the row by irradiance
    # component
    poa_diffuse = poa_gnd_pv + poa_sky_pv
    # beam on plane, make an array for consistency with poa_diffuse
    poa_beam = np.atleast_1d(
        beam_component(surface_tilt, surface_azimuth, solar_zenith,
                       solar_azimuth, dni))
    poa_direct = poa_beam * (1 - f_x) * iam  # direct only on the unshaded part
    poa_global = poa_direct + poa_diffuse

    output = {
        'poa_global': poa_global,
        'poa_direct': poa_direct,
        'poa_diffuse': poa_diffuse,
        'poa_ground_diffuse': poa_gnd_pv,
        'poa_sky_diffuse': poa_sky_pv
    }
    if isinstance(poa_global, pd.Series):
        output = pd.DataFrame(output)
    return output
Пример #6
0
def physicaliam(K, L, n, aoi):
    '''
    Determine the incidence angle modifier using refractive 
    index, glazing thickness, and extinction coefficient

    physicaliam calculates the incidence angle modifier as described in
    De Soto et al. "Improvement and validation of a model for photovoltaic
    array performance", section 3. The calculation is based upon a physical
    model of absorbtion and transmission through a cover. Required
    information includes, incident angle, cover extinction coefficient,
    cover thickness

    Note: The authors of this function believe that eqn. 14 in [1] is
    incorrect. This function uses the following equation in its place:
    theta_r = arcsin(1/n * sin(theta))

    Parameters
    ----------
    K : float
        The glazing extinction coefficient in units of 1/meters. Reference
        [1] indicates that a value of  4 is reasonable for "water white"
        glass. K must be a numeric scalar or vector with all values >=0. If K
        is a vector, it must be the same size as all other input vectors.

    L : float
        The glazing thickness in units of meters. Reference [1] indicates
        that 0.002 meters (2 mm) is reasonable for most glass-covered
        PV panels. L must be a numeric scalar or vector with all values >=0. 
        If L is a vector, it must be the same size as all other input vectors.

    n : float
        The effective index of refraction (unitless). Reference [1]
        indicates that a value of 1.526 is acceptable for glass. n must be a 
        numeric scalar or vector with all values >=0. If n is a vector, it 
        must be the same size as all other input vectors.

    aoi : Series
        The angle of incidence between the module normal vector and the
        sun-beam vector in degrees. 

    Returns
    -------
    IAM : float or Series
        The incident angle modifier as specified in eqns. 14-16 of [1].
        IAM is a column vector with the same number of elements as the
        largest input vector.
        
        Theta must be a numeric scalar or vector.
        For any values of theta where abs(aoi)>90, IAM is set to 0. For any
        values of aoi where -90 < aoi < 0, theta is set to abs(aoi) and
        evaluated.

    References
    ----------
    [1] W. De Soto et al., "Improvement and validation of a model for
    photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
    2006.

    [2] Duffie, John A. & Beckman, William A.. (2006). Solar Engineering 
    of Thermal Processes, third edition. [Books24x7 version] Available 
    from http://common.books24x7.com/toc.aspx?bookid=17160. 

    See Also 
    --------
    getaoi   
    ephemeris   
    spa    
    ashraeiam
    '''
    thetar_deg = tools.asind(1.0 / n*(tools.sind(aoi)))

    tau = ( np.exp(- 1.0 * (K*L / tools.cosd(thetar_deg))) *
            ((1 - 0.5*((((tools.sind(thetar_deg - aoi)) ** 2) /
            ((tools.sind(thetar_deg + aoi)) ** 2) +
            ((tools.tand(thetar_deg - aoi)) ** 2) /
            ((tools.tand(thetar_deg + aoi)) ** 2))))) )
    
    zeroang = 1e-06
    
    thetar_deg0 = tools.asind(1.0 / n*(tools.sind(zeroang)))
    
    tau0 = ( np.exp(- 1.0 * (K*L / tools.cosd(thetar_deg0))) *
             ((1 - 0.5*((((tools.sind(thetar_deg0 - zeroang)) ** 2) /
             ((tools.sind(thetar_deg0 + zeroang)) ** 2) +
             ((tools.tand(thetar_deg0 - zeroang)) ** 2) /
             ((tools.tand(thetar_deg0 + zeroang)) ** 2))))) )
    
    IAM = tau / tau0
    
    IAM[abs(aoi) >= 90] = np.nan
    IAM[IAM < 0] = np.nan
    
    return IAM