r'''
    Determine in-plane irradiance components.

    Combines DNI with sky diffuse and ground-reflected irradiance to calculate
    total, direct and diffuse irradiance components in the plane of array.

    Parameters
    ----------
    aoi : numeric
        Angle of incidence of solar rays with respect to the module
        surface, from :func:`aoi`.

    dni : numeric
        Direct normal irradiance (W/m^2), as measured from a TMY file or
        calculated with a clearsky model.

    poa_sky_diffuse : numeric
        Diffuse irradiance (W/m^2) in the plane of the modules, as
        calculated by a diffuse irradiance translation function

    poa_ground_diffuse : numeric
        Ground reflected irradiance (W/m^2) in the plane of the modules,
        as calculated by an albedo model (eg. :func:`grounddiffuse`)

    Returns
    -------
    irrads : OrderedDict or DataFrame
        Contains the following keys:

        * ``poa_global`` : Total in-plane irradiance (W/m^2)
        * ``poa_direct`` : Total in-plane beam irradiance (W/m^2)
        * ``poa_diffuse`` : Total in-plane diffuse irradiance (W/m^2)
        * ``poa_sky_diffuse`` : In-plane diffuse irradiance from sky (W/m^2)
        * ``poa_ground_diffuse`` : In-plane diffuse irradiance from ground
          (W/m^2)

    Notes
    ------
    Negative beam irradiation due to aoi :math:`> 90^{\circ}` or AOI
    :math:`< 0^{\circ}` is set to zero.

def poa_components(aoi, dni, poa_sky_diffuse, poa_ground_diffuse):
    r'''
    Determine in-plane irradiance components.

    Combines DNI with sky diffuse and ground-reflected irradiance to calculate
    total, direct and diffuse irradiance components in the plane of array.

    Parameters
    ----------
    aoi : numeric
        Angle of incidence of solar rays with respect to the module
        surface, from :func:`aoi`.

    dni : numeric
        Direct normal irradiance (W/m^2), as measured from a TMY file or
        calculated with a clearsky model.

    poa_sky_diffuse : numeric
        Diffuse irradiance (W/m^2) in the plane of the modules, as
        calculated by a diffuse irradiance translation function

    poa_ground_diffuse : numeric
        Ground reflected irradiance (W/m^2) in the plane of the modules,
        as calculated by an albedo model (eg. :func:`grounddiffuse`)

    Returns
    -------
    irrads : OrderedDict or DataFrame
        Contains the following keys:

        * ``poa_global`` : Total in-plane irradiance (W/m^2)
        * ``poa_direct`` : Total in-plane beam irradiance (W/m^2)
        * ``poa_diffuse`` : Total in-plane diffuse irradiance (W/m^2)
        * ``poa_sky_diffuse`` : In-plane diffuse irradiance from sky (W/m^2)
        * ``poa_ground_diffuse`` : In-plane diffuse irradiance from ground
          (W/m^2)

    Notes
    ------
    Negative beam irradiation due to aoi :math:`> 90^{\circ}` or AOI
    :math:`< 0^{\circ}` is set to zero.
    '''

    poa_direct = np.maximum(dni * np.cos(np.radians(aoi)), 0)
    poa_diffuse = poa_sky_diffuse + poa_ground_diffuse
    poa_global = poa_direct + poa_diffuse

    irrads = OrderedDict()
    irrads['poa_global'] = poa_global
    irrads['poa_direct'] = poa_direct
    irrads['poa_diffuse'] = poa_diffuse
    irrads['poa_sky_diffuse'] = poa_sky_diffuse
    irrads['poa_ground_diffuse'] = poa_ground_diffuse

    if isinstance(poa_direct, pd.Series):
        irrads = pd.DataFrame(irrads)

    return irrads