Determine DNI from GHI and DHI.

    When calculating the DNI from GHI and DHI the calculated DNI may be
    unreasonably high or negative for zenith angles close to 90 degrees
    (sunrise/sunset transitions). This function identifies unreasonable DNI
    values and sets them to NaN. If the clearsky DNI is given unreasonably high
    values are cut off.

    Parameters
    ----------
    ghi : Series
        Global horizontal irradiance.

    dhi : Series
        Diffuse horizontal irradiance.

    zenith : Series
        True (not refraction-corrected) zenith angles in decimal
        degrees. Angles must be >=0 and <=180.

    clearsky_dni : None or Series, default None
        Clearsky direct normal irradiance.

    clearsky_tolerance : float, default 1.1
        If 'clearsky_dni' is given this parameter can be used to allow a
        tolerance by how much the calculated DNI value can be greater than
        the clearsky value before it is identified as an unreasonable value.

    zenith_threshold_for_zero_dni : float, default 88.0
        Non-zero DNI values for zenith angles greater than or equal to
        'zenith_threshold_for_zero_dni' will be set to NaN.

    zenith_threshold_for_clearsky_limit : float, default 80.0
        DNI values for zenith angles greater than or equal to
        'zenith_threshold_for_clearsky_limit' and smaller the
        'zenith_threshold_for_zero_dni' that are greater than the clearsky DNI
        (times allowed tolerance) will be corrected. Only applies if
        'clearsky_dni' is not None.

    Returns
    -------
    dni : Series
        The modeled direct normal irradiance.

def dni(ghi, dhi, zenith, clearsky_dni=None, clearsky_tolerance=1.1,
        zenith_threshold_for_zero_dni=88.0,
        zenith_threshold_for_clearsky_limit=80.0):
    """
    Determine DNI from GHI and DHI.

    When calculating the DNI from GHI and DHI the calculated DNI may be
    unreasonably high or negative for zenith angles close to 90 degrees
    (sunrise/sunset transitions). This function identifies unreasonable DNI
    values and sets them to NaN. If the clearsky DNI is given unreasonably high
    values are cut off.

    Parameters
    ----------
    ghi : Series
        Global horizontal irradiance.

    dhi : Series
        Diffuse horizontal irradiance.

    zenith : Series
        True (not refraction-corrected) zenith angles in decimal
        degrees. Angles must be >=0 and <=180.

    clearsky_dni : None or Series, default None
        Clearsky direct normal irradiance.

    clearsky_tolerance : float, default 1.1
        If 'clearsky_dni' is given this parameter can be used to allow a
        tolerance by how much the calculated DNI value can be greater than
        the clearsky value before it is identified as an unreasonable value.

    zenith_threshold_for_zero_dni : float, default 88.0
        Non-zero DNI values for zenith angles greater than or equal to
        'zenith_threshold_for_zero_dni' will be set to NaN.

    zenith_threshold_for_clearsky_limit : float, default 80.0
        DNI values for zenith angles greater than or equal to
        'zenith_threshold_for_clearsky_limit' and smaller the
        'zenith_threshold_for_zero_dni' that are greater than the clearsky DNI
        (times allowed tolerance) will be corrected. Only applies if
        'clearsky_dni' is not None.

    Returns
    -------
    dni : Series
        The modeled direct normal irradiance.
    """

    # calculate DNI
    dni = (ghi - dhi) / tools.cosd(zenith)

    # cutoff negative values
    dni[dni < 0] = float('nan')

    # set non-zero DNI values for zenith angles >=
    # zenith_threshold_for_zero_dni to NaN
    dni[(zenith >= zenith_threshold_for_zero_dni) & (dni != 0)] = float('nan')

    # correct DNI values for zenith angles greater or equal to the
    # zenith_threshold_for_clearsky_limit and smaller than the
    # upper_cutoff_zenith that are greater than the clearsky DNI (times
    # clearsky_tolerance)
    if clearsky_dni is not None:
        max_dni = clearsky_dni * clearsky_tolerance
        dni[(zenith >= zenith_threshold_for_clearsky_limit) &
            (zenith < zenith_threshold_for_zero_dni) &
            (dni > max_dni)] = max_dni
    return dni