def test_deprecated_07():
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.extraradiation(300)
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.grounddiffuse(40, 900)
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.total_irrad(32, 180, 10, 180, 0, 0, 0, 1400, 1)
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.globalinplane(0, 1000, 100, 10)
Пример #2
0
def test_deprecated_07():
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.extraradiation(300)
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.grounddiffuse(40, 900)
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.total_irrad(32, 180, 10, 180, 0, 0, 0, 1400, 1)
    with pytest.warns(pvlibDeprecationWarning):
        irradiance.globalinplane(0, 1000, 100, 10)
Пример #3
0
    def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs):
        """
        Estimates irradiance from cloud cover in the following steps:

        1. Determine transmittance using a function of cloud cover e.g.
           :py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear`
        2. Calculate GHI, DNI, DHI using the
           :py:func:`pvlib.irradiance.liujordan` model

        Parameters
        ----------
        cloud_cover : Series

        Returns
        -------
        irradiance : DataFrame
            Columns include ghi, dni, dhi
        """
        # in principle, get_solarposition could use the forecast
        # pressure, temp, etc., but the cloud cover forecast is not
        # accurate enough to justify using these minor corrections
        solar_position = self.location.get_solarposition(cloud_cover.index)
        dni_extra = extraradiation(cloud_cover.index)
        airmass = self.location.get_airmass(cloud_cover.index)

        transmittance = self.cloud_cover_to_transmittance_linear(
            cloud_cover, **kwargs)

        irrads = liujordan(solar_position['apparent_zenith'],
                           transmittance,
                           airmass['airmass_absolute'],
                           dni_extra=dni_extra)
        irrads = irrads.fillna(0)

        return irrads
Пример #4
0
    def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs):
        """
        Estimates irradiance from cloud cover in the following steps:

        1. Determine transmittance using a function of cloud cover e.g.
           :py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear`
        2. Calculate GHI, DNI, DHI using the
           :py:func:`pvlib.irradiance.liujordan` model

        Parameters
        ----------
        cloud_cover : Series

        Returns
        -------
        irradiance : DataFrame
            Columns include ghi, dni, dhi
        """
        # in principle, get_solarposition could use the forecast
        # pressure, temp, etc., but the cloud cover forecast is not
        # accurate enough to justify using these minor corrections
        solar_position = self.location.get_solarposition(cloud_cover.index)
        dni_extra = extraradiation(cloud_cover.index)
        airmass = self.location.get_airmass(cloud_cover.index)

        transmittance = self.cloud_cover_to_transmittance_linear(cloud_cover,
                                                                 **kwargs)

        irrads = liujordan(solar_position['apparent_zenith'],
                           transmittance, airmass['airmass_absolute'],
                           dni_extra=dni_extra)
        irrads = irrads.fillna(0)

        return irrads
Пример #5
0
def test_extraradiation_nrel_numba():
    result = irradiance.extraradiation(times,
                                       method='nrel',
                                       how='numba',
                                       numthreads=8)
    assert_allclose(result,
                    [1322.332316, 1322.296282, 1322.261205, 1322.227091])
Пример #6
0
    def get_poa(self,
                dates,
                ghi,
                dhi,
                dni,
                sun_zenith,
                sun_azimuth,
                dni_extra=None,
                airmass=None,
                model="haydavies"):

        if self.method == "pvlib":

            sun_zenith = deg(sun_zenith)
            sun_azimuth = deg(sun_azimuth)

            if dni_extra is None:
                dni_extra = irradiance.extraradiation(DatetimeIndex(dates))
            if airmass is None:
                airmass = atmosphere.relativeairmass(sun_zenith)

            poa = irradiance.total_irrad(self.tilt,
                                         self.azimuth,
                                         sun_zenith,
                                         sun_azimuth,
                                         dni,
                                         ghi,
                                         dhi,
                                         dni_extra=dni_extra,
                                         airmass=airmass,
                                         model=model,
                                         albedo=self.albedo)

            self.poa = poa['poa_global']
Пример #7
0
    def get_clearsky(self, times, model='ineichen', **kwargs):
        """
        Calculate the clear sky estimates of GHI, DNI, and/or DHI
        at this location.

        Parameters
        ----------
        times : DatetimeIndex

        model : str
            The clear sky model to use. Must be one of
            'ineichen', 'haurwitz', 'simplified_solis'.

        kwargs passed to the relevant functions. Climatological values
        are assumed in many cases. See code for details.

        Returns
        -------
        clearsky : DataFrame
            Column names are: ``ghi, dni, dhi``.
        """

        if model == 'ineichen':
            cs = clearsky.ineichen(times, latitude=self.latitude,
                                   longitude=self.longitude,
                                   altitude=self.altitude,
                                   **kwargs)
        elif model == 'haurwitz':
            solpos = self.get_solarposition(times, **kwargs)
            cs = clearsky.haurwitz(solpos['apparent_zenith'])
        elif model == 'simplified_solis':

            # these try/excepts define default values that are only
            # evaluated if necessary. ineichen does some of this internally
            try:
                dni_extra = kwargs.pop('dni_extra')
            except KeyError:
                dni_extra = irradiance.extraradiation(times.dayofyear)

            try:
                pressure = kwargs.pop('pressure')
            except KeyError:
                pressure = atmosphere.alt2pres(self.altitude)

            try:
                apparent_elevation = kwargs.pop('apparent_elevation')
            except KeyError:
                solpos = self.get_solarposition(
                    times, pressure=pressure, **kwargs)
                apparent_elevation = solpos['apparent_elevation']

            cs = clearsky.simplified_solis(
                apparent_elevation, pressure=pressure, dni_extra=dni_extra,
                **kwargs)
        else:
            raise ValueError('{} is not a valid clear sky model'
                             .format(model))

        return cs
Пример #8
0
def get_perfect_voltage_for_a_day(start, freq):
    """This method is used to build a pandas serie with voltage values.
    This serie has DateTime index and contains a value for every "freq"
    seconds during 24 hours starting from "start" date.
    There are several assumptions:
    1. Location is Munich
    2. A battery is pointing to the south, amount of blocks is 20
    3. Sandia Module database is used
    4. pvlib library is heavily used

    :param start: datetime. First timestamp in result series
    :param freq: str. How often voltage should be sampled
    :return: voltage : Series
    """
    surface_tilt = _munich_location.latitude
    surface_azimuth = 180  # pointing south
    date_range = pd.date_range(start=start,
                               end=start + dt.timedelta(
                                   hours=23, minutes=59, seconds=59),
                               freq=freq, tz=_munich_location.tz)

    clearsky_estimations = _munich_location.get_clearsky(date_range)
    dni_extra = irradiance.extraradiation(date_range)
    solar_position = solarposition.get_solarposition(
        date_range, _munich_location.latitude, _munich_location.longitude)
    airmass = atmosphere.relativeairmass(solar_position['apparent_zenith'])
    pressure = atmosphere.alt2pres(_munich_location.altitude)
    am_abs = atmosphere.absoluteairmass(airmass, pressure)

    total_irrad = irradiance.total_irrad(surface_tilt,
                                         surface_azimuth,
                                         solar_position['apparent_zenith'],
                                         solar_position['azimuth'],
                                         clearsky_estimations['dni'],
                                         clearsky_estimations['ghi'],
                                         clearsky_estimations['dhi'],
                                         dni_extra=dni_extra,
                                         model='haydavies')

    temps = pvsystem.sapm_celltemp(total_irrad['poa_global'], 0, 15)
    aoi = irradiance.aoi(surface_tilt, surface_azimuth,
                         solar_position['apparent_zenith'],
                         solar_position['azimuth'])
    # add 0.0001 to avoid np.log(0) and warnings about that
    effective_irradiance = pvsystem.sapm_effective_irradiance(
        total_irrad['poa_direct'], total_irrad['poa_diffuse'], am_abs,
        aoi, _sandia_module) + 0.0001
    sapm = pvsystem.sapm(effective_irradiance, temps['temp_cell'],
                         _sandia_module)

    return sapm['p_mp'] * _module_count
Пример #9
0
def test_extraradiation_ephem_doyarray():
    irradiance.extraradiation(times.dayofyear, method='pyephem')
from pvlib import irradiance
from pvlib import atmosphere

# setup times and location to be tested.
times = pd.date_range(start=datetime.datetime(2014,6,24), 
                      end=datetime.datetime(2014,6,26), freq='1Min')

tus = Location(32.2, -111, 'US/Arizona', 700)

times_localized = times.tz_localize(tus.tz)

ephem_data = solarposition.get_solarposition(times, tus, method='pyephem')

irrad_data = clearsky.ineichen(times, tus, solarposition_method='pyephem')

dni_et = irradiance.extraradiation(times.dayofyear)

ghi = irrad_data['GHI']


# the test functions. these are almost all functional tests.
# need to add physical tests.

def test_extraradiation():
    assert_almost_equals(1382, irradiance.extraradiation(300), -1)
    
def test_extraradiation_dtindex():
    irradiance.extraradiation(times)
    
def test_extraradiation_doyarray():
    irradiance.extraradiation(times.dayofyear)
Пример #11
0
def test_extraradiation_asce():
    assert_almost_equals(1382, irradiance.extraradiation(300, method='asce'),
                         -1)
Пример #12
0
def test_extraradiation_ephem_dtindex():
    irradiance.extraradiation(times, method='pyephem')
Пример #13
0
from conftest import requires_ephem, requires_numba, needs_numpy_1_10

# setup times and location to be tested.
tus = Location(32.2, -111, 'US/Arizona', 700)

# must include night values
times = pd.date_range(start='20140624', freq='6H', periods=4, tz=tus.tz)

ephem_data = solarposition.get_solarposition(times,
                                             tus.latitude,
                                             tus.longitude,
                                             method='nrel_numpy')

irrad_data = tus.get_clearsky(times, model='ineichen', linke_turbidity=3)

dni_et = irradiance.extraradiation(times.dayofyear)

ghi = irrad_data['ghi']

# setup for et rad test. put it here for readability
timestamp = pd.Timestamp('20161026')
dt_index = pd.DatetimeIndex([timestamp])
doy = timestamp.dayofyear
dt_date = timestamp.date()
dt_datetime = datetime.datetime.combine(dt_date, datetime.time(0))
dt_np64 = np.datetime64(dt_datetime)
value = 1383.636203


@pytest.mark.parametrize('input, expected',
                         [(doy, value), (np.float64(doy), value),
Пример #14
0
def test_extraradiation_spencer():
    assert_allclose(
        1382, irradiance.extraradiation(300, method='spencer'), atol=10)
Пример #15
0
    def get_irradiance(self, dni, ghi, dhi,
                       dni_extra=None, airmass=None, model='haydavies',
                       **kwargs):
        """
        Uses the :func:`irradiance.total_irrad` function to calculate
        the plane of array irradiance components on a tilted surface
        defined by 
        ``self.surface_tilt``, ``self.surface_azimuth``, and
        ``self.albedo``.
        
        Parameters
        ----------
        solar_zenith : float or Series.
            Solar zenith angle.
        solar_azimuth : float or Series.
            Solar azimuth angle.
        dni : float or Series
            Direct Normal Irradiance
        ghi : float or Series
            Global horizontal irradiance
        dhi : float or Series
            Diffuse horizontal irradiance
        dni_extra : float or Series
            Extraterrestrial direct normal irradiance
        airmass : float or Series
            Airmass
        model : String
            Irradiance model.
        
        **kwargs
            Passed to :func:`irradiance.total_irrad`.
        
        Returns
        -------
        poa_irradiance : DataFrame
            Column names are: ``total, beam, sky, ground``.
        """

        surface_tilt = kwargs.pop('surface_tilt', self.surface_tilt)
        surface_azimuth = kwargs.pop('surface_azimuth', self.surface_azimuth)
        
        try:
            solar_zenith = kwargs['solar_zenith']
        except KeyError:
            solar_zenith = self.solar_zenith
        
        try:
            solar_azimuth = kwargs['solar_azimuth']
        except KeyError:
            solar_azimuth = self.solar_azimuth

        # not needed for all models, but this is easier
        if dni_extra is None:
            dni_extra = irradiance.extraradiation(solar_zenith.index)
            dni_extra = pd.Series(dni_extra, index=solar_zenith.index)

        if airmass is None:
            airmass = atmosphere.relativeairmass(solar_zenith)

        return irradiance.total_irrad(surface_tilt,
                                      surface_azimuth,
                                      solar_zenith,
                                      solar_azimuth,
                                      dni, ghi, dhi,
                                      dni_extra=dni_extra, airmass=airmass,
                                      model=model,
                                      albedo=self.albedo,
                                      **kwargs)
def test_extraradiation_ephem_dtindex():
    irradiance.extraradiation(times, method='pyephem')
Пример #17
0
def test_extraradiation_invalid():
    with pytest.raises(ValueError):
        irradiance.extraradiation(times.dayofyear, method='invalid')
Пример #18
0
def test_extraradiation_nrel_numba():
    result = irradiance.extraradiation(times, method='nrel', how='numba', numthreads=8)
    assert_allclose(result, [1322.332316, 1322.296282, 1322.261205, 1322.227091])
Пример #19
0
def test_extraradiation_nrel_doyarray():
    irradiance.extraradiation(times.dayofyear, method='nrel')
Пример #20
0
def test_extraradiation_nrel_scalar():
    assert_allclose(
        1382, irradiance.extraradiation(300, method='nrel').values[0],
        atol=10)
Пример #21
0
def test_extraradiation_nrel_dtindex():
    irradiance.extraradiation(times, method='nrel')
def test_extraradiation_dtindex():
    irradiance.extraradiation(times)
Пример #23
0
def test_extraradiation_invalid():
    with pytest.raises(ValueError):
        irradiance.extraradiation(300, method='invalid')
def test_extraradiation_asce():
    assert_almost_equals(1382, irradiance.extraradiation(300, method='asce'), -1)
Пример #25
0
def test_extraradiation():
    assert_allclose(1382, irradiance.extraradiation(300), atol=10)
def test_extraradiation_ephem_doyarray():
    irradiance.extraradiation(times.dayofyear, method='pyephem')
Пример #27
0
def test_extraradiation_dtindex():
    irradiance.extraradiation(times)
Пример #28
0
def ineichen(time,
             location,
             linke_turbidity=None,
             solarposition_method='pyephem',
             zenith_data=None,
             airmass_model='young1994',
             airmass_data=None,
             interp_turbidity=True):
    '''
    Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model

    Implements the Ineichen and Perez clear sky model for global horizontal
    irradiance (GHI), direct normal irradiance (DNI), and calculates
    the clear-sky diffuse horizontal (DHI) component as the difference
    between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear
    sky models found the Ineichen/Perez model to have excellent performance
    with a minimal input data set [3]. 
    
    Default values for montly Linke turbidity provided by SoDa [4, 5].

    Parameters
    -----------
    time : pandas.DatetimeIndex
    
    location : pvlib.Location
    
    linke_turbidity : None or float
        If None, uses ``LinkeTurbidities.mat`` lookup table.
    
    solarposition_method : string
        Sets the solar position algorithm. 
        See solarposition.get_solarposition()
    
    zenith_data : None or Series
        If None, ephemeris data will be calculated using ``solarposition_method``.
    
    airmass_model : string
        See pvlib.airmass.relativeairmass().
    
    airmass_data : None or Series
        If None, absolute air mass data will be calculated using 
        ``airmass_model`` and location.alitude.
    
    interp_turbidity : bool
        If ``True``, interpolates the monthly Linke turbidity values
        found in ``LinkeTurbidities.mat`` to daily values.

    Returns
    --------
    DataFrame with the following columns: ``ghi, dni, dhi``.

    Notes
    -----
    If you are using this function
    in a loop, it may be faster to load LinkeTurbidities.mat outside of
    the loop and feed it in as a keyword argument, rather than
    having the function open and process the file each time it is called.

    References
    ----------

    [1] P. Ineichen and R. Perez, "A New airmass independent formulation for
        the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002.

    [2] R. Perez et. al., "A New Operational Model for Satellite-Derived
        Irradiances: Description and Validation", Solar Energy, vol 73, pp.
        307-317, 2002.

    [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear
        Sky Models: Implementation and Analysis", Sandia National
        Laboratories, SAND2012-2389, 2012.

    [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained
        July 17, 2012).

    [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc.
        ISES Solar World Congress, June 2003. Goteborg, Sweden.
    '''
    # Initial implementation of this algorithm by Matthew Reno.
    # Ported to python by Rob Andrews
    # Added functionality by Will Holmgren (@wholmgren)

    I0 = irradiance.extraradiation(time.dayofyear)

    if zenith_data is None:
        ephem_data = solarposition.get_solarposition(
            time, location, method=solarposition_method)
        time = ephem_data.index  # fixes issue with time possibly not being tz-aware
        try:
            ApparentZenith = ephem_data['apparent_zenith']
        except KeyError:
            ApparentZenith = ephem_data['zenith']
            logger.warning('could not find apparent_zenith. using zenith')
    else:
        ApparentZenith = zenith_data
    #ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases

    if linke_turbidity is None:
        TL = lookup_linke_turbidity(time,
                                    location.latitude,
                                    location.longitude,
                                    interp_turbidity=interp_turbidity)
    else:
        TL = linke_turbidity

    # Get the absolute airmass assuming standard local pressure (per
    # alt2pres) using Kasten and Young's 1989 formula for airmass.

    if airmass_data is None:
        AMabsolute = atmosphere.absoluteairmass(
            airmass_relative=atmosphere.relativeairmass(
                ApparentZenith, airmass_model),
            pressure=atmosphere.alt2pres(location.altitude))
    else:
        AMabsolute = airmass_data

    fh1 = np.exp(-location.altitude / 8000.)
    fh2 = np.exp(-location.altitude / 1250.)
    cg1 = 5.09e-05 * location.altitude + 0.868
    cg2 = 3.92e-05 * location.altitude + 0.0387
    logger.debug('fh1=%s, fh2=%s, cg1=%s, cg2=%s', fh1, fh2, cg1, cg2)

    #  Dan's note on the TL correction: By my reading of the publication on
    #  pages 151-157, Ineichen and Perez introduce (among other things) three
    #  things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and
    #  appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear
    #  to use the new turbidity factor (item 2 above) in either the beam or GHI
    #  models. The phrasing of appendix A seems as if there are two separate
    #  corrections, the first correction is used to correct the beam/GHI models,
    #  and the second correction is used to correct the revised turibidity
    #  factor. In my estimation, there is no need to correct the turbidity
    #  factor used in the beam/GHI models.

    #  Create the corrected TL for TL < 2
    #  TLcorr = TL;
    #  TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5);

    #  This equation is found in Solar Energy 73, pg 311.
    #  Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002).
    #  It is slightly different than the equation given in Solar Energy 73, pg 156.
    #  We used the equation from pg 311 because of the existence of known typos
    #  in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)).

    cos_zenith = tools.cosd(ApparentZenith)

    clearsky_GHI = (cg1 * I0 * cos_zenith * np.exp(-cg2 * AMabsolute *
                                                   (fh1 + fh2 * (TL - 1))) *
                    np.exp(0.01 * AMabsolute**1.8))
    clearsky_GHI[clearsky_GHI < 0] = 0

    # BncI == "normal beam clear sky radiation"
    b = 0.664 + 0.163 / fh1
    BncI = b * I0 * np.exp(-0.09 * AMabsolute * (TL - 1))
    logger.debug('b=%s', b)

    # "empirical correction" SE 73, 157 & SE 73, 312.
    BncI_2 = (clearsky_GHI * (1 - (0.1 - 0.2 * np.exp(-TL)) /
                              (0.1 + 0.882 / fh1)) / cos_zenith)

    clearsky_DNI = np.minimum(BncI, BncI_2)

    clearsky_DHI = clearsky_GHI - clearsky_DNI * cos_zenith

    df_out = pd.DataFrame({
        'ghi': clearsky_GHI,
        'dni': clearsky_DNI,
        'dhi': clearsky_DHI
    })
    df_out.fillna(0, inplace=True)

    return df_out
Пример #29
0
def test_extraradiation_asce():
    assert_allclose(
        1382, irradiance.extraradiation(300, method='asce'), atol=10)
Пример #30
0
def test_bird():
    """Test Bird/Hulstrom Clearsky Model"""
    times = pd.DatetimeIndex(start='1/1/2015 0:00', end='12/31/2015 23:00',
                             freq='H')
    tz = -7  # test timezone
    gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz))
    times = times.tz_localize(gmt_tz)  # set timezone
    # match test data from BIRD_08_16_2012.xls
    latitude = 40.
    longitude = -105.
    press_mB = 840.
    o3_cm = 0.3
    h2o_cm = 1.5
    aod_500nm = 0.1
    aod_380nm = 0.15
    b_a = 0.85
    alb = 0.2
    eot = solarposition.equation_of_time_spencer71(times.dayofyear)
    hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15.
    declination = solarposition.declination_spencer71(times.dayofyear)
    zenith = solarposition.solar_zenith_analytical(
        np.deg2rad(latitude), np.deg2rad(hour_angle), declination
    )
    zenith = np.rad2deg(zenith)
    airmass = atmosphere.relativeairmass(zenith, model='kasten1966')
    etr = irradiance.extraradiation(times)
    # test Bird with time series data
    field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi')
    irrads = clearsky.bird(
        zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100.,
        etr, b_a, alb
    )
    Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names)
    clearsky_path = os.path.dirname(os.path.abspath(__file__))
    pvlib_path = os.path.dirname(clearsky_path)
    data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012.csv')
    testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna()
    testdata.index = times[1:48]
    assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48]))
    assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4)
    assert np.allclose(testdata['Hour Angle'], hour_angle[1:48])
    assert np.allclose(testdata['Zenith Ang'], zenith[1:48])
    dawn = zenith < 88.
    dusk = testdata['Zenith Ang'] < 88.
    am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0)
    assert np.allclose(
        testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3
    )
    direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3
    )
    direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3
    )
    global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3
    )
    diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3
    )
    # test keyword parameters
    irrads2 = clearsky.bird(
        zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr
    )
    Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names)
    clearsky_path = os.path.dirname(os.path.abspath(__file__))
    pvlib_path = os.path.dirname(clearsky_path)
    data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012_patm.csv')
    testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna()
    testdata2.index = times[1:48]
    direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3
    )
    direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3
    )
    global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3
    )
    diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3
    )
    # test scalars just at noon
    # XXX: calculations start at 12am so noon is at index = 12
    irrads3 = clearsky.bird(
        zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12]
    )
    Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names)
    # XXX: testdata starts at 1am so noon is at index = 11
    np.allclose(
        [Eb3, Ebh3, Gh3, Dh3],
        testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11],
        rtol=1e-3)
    return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)
Пример #31
0
def test_extraradiation(input, expected, method):
    out = irradiance.extraradiation(input)
    assert_allclose(out, expected, atol=1)
Пример #32
0
def test_extraradiation():
    assert_almost_equals(1382, irradiance.extraradiation(300), -1)
Пример #33
0
def test_extraradiation_nrel_numba():
    irradiance.extraradiation(times, method='nrel', how='numba', numthreads=8)
Пример #34
0
def test_extraradiation_doyarray():
    irradiance.extraradiation(times.dayofyear)
Пример #35
0
def test_extraradiation_epoch_year():
    out = irradiance.extraradiation(doy, method='nrel', epoch_year=2012)
    assert_allclose(out, 1382.4926804890767, atol=0.1)
Пример #36
0
def test_extraradiation_spencer():
    assert_almost_equals(1382, irradiance.extraradiation(300,
                                                         method='spencer'), -1)
Пример #37
0
def test_extraradiation_invalid():
    with pytest.raises(ValueError):
        irradiance.extraradiation(300, method='invalid')
Пример #38
0
def test_extraradiation_ephem_scalar():
    assert_almost_equals(
        1382,
        irradiance.extraradiation(300, method='pyephem').values[0], -1)
Пример #39
0
def perez_diffuse_luminance(timestamps, array_tilt, array_azimuth,
                            solar_zenith, solar_azimuth, dni, dhi):
    """
    Function used to calculate the luminance and the view factor terms from the
    Perez diffuse light transposition model, as implemented in the
    ``pvlib-python`` library.
    This function was custom made to allow the calculation of the circumsolar
    component on the back surface as well. Otherwise, the ``pvlib``
    implementation would ignore it.

    :param array-like timestamps: simulation timestamps
    :param array-like array_tilt: pv module tilt angles
    :param array-like array_azimuth: pv array azimuth angles
    :param array-like solar_zenith: solar zenith angles
    :param array-like solar_azimuth: solar azimuth angles
    :param array-like dni: values for direct normal irradiance
    :param array-like dhi: values for diffuse horizontal irradiance
    :return: ``df_inputs``, dataframe with the following columns:
        ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi',
        'dni', 'vf_horizon', 'vf_circumsolar', 'vf_isotropic',
        'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic',
        'poa_isotropic', 'poa_circumsolar', 'poa_horizon', 'poa_total_diffuse']
    :rtype: class:`pandas.DataFrame`
    """
    # Create a dataframe to help filtering on all arrays
    df_inputs = pd.DataFrame(
        {
            'array_tilt': array_tilt,
            'array_azimuth': array_azimuth,
            'solar_zenith': solar_zenith,
            'solar_azimuth': solar_azimuth,
            'dni': dni,
            'dhi': dhi
        },
        index=pd.DatetimeIndex(timestamps))

    dni_et = irradiance.extraradiation(df_inputs.index.dayofyear)
    am = atmosphere.relativeairmass(df_inputs.solar_zenith)

    # Need to treat the case when the sun is hitting the back surface of pvrow
    aoi_proj = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth,
                              df_inputs.solar_zenith, df_inputs.solar_azimuth)
    sun_hitting_back_surface = ((aoi_proj < 0) &
                                (df_inputs.solar_zenith <= 90))
    df_inputs_back_surface = df_inputs.loc[sun_hitting_back_surface]
    # Reverse the surface normal to switch to back-surface circumsolar calc
    df_inputs_back_surface.loc[:, 'array_azimuth'] -= 180.
    df_inputs_back_surface.loc[:, 'array_azimuth'] = np.mod(
        df_inputs_back_surface.loc[:, 'array_azimuth'], 360.)
    df_inputs_back_surface.loc[:, 'array_tilt'] = (
        180. - df_inputs_back_surface.array_tilt)

    if df_inputs_back_surface.shape[0] > 0:
        # Use recursion to calculate circumsolar luminance for back surface
        df_inputs_back_surface = perez_diffuse_luminance(
            *breakup_df_inputs(df_inputs_back_surface))

    # Calculate Perez diffuse components
    diffuse_poa, components = irradiance.perez(df_inputs.array_tilt,
                                               df_inputs.array_azimuth,
                                               df_inputs.dhi,
                                               df_inputs.dni,
                                               dni_et,
                                               df_inputs.solar_zenith,
                                               df_inputs.solar_azimuth,
                                               am,
                                               return_components=True)

    # Calculate Perez view factors:
    a = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth,
                       df_inputs.solar_zenith, df_inputs.solar_azimuth)
    a = np.maximum(a, 0)
    b = cosd(df_inputs.solar_zenith)
    b = np.maximum(b, cosd(85))

    vf_perez = pd.DataFrame(
        np.array([
            sind(df_inputs.array_tilt), a / b,
            (1. + cosd(df_inputs.array_tilt)) / 2.
        ]).T,
        index=df_inputs.index,
        columns=['vf_horizon', 'vf_circumsolar', 'vf_isotropic'])

    # Calculate diffuse luminance
    luminance = pd.DataFrame(np.array([
        components['horizon'] / vf_perez['vf_horizon'],
        components['circumsolar'] / vf_perez['vf_circumsolar'],
        components['isotropic'] / vf_perez['vf_isotropic']
    ]).T,
                             index=df_inputs.index,
                             columns=[
                                 'luminance_horizon', 'luminance_circumsolar',
                                 'luminance_isotropic'
                             ])
    luminance.loc[diffuse_poa == 0, :] = 0.

    # Format components column names
    components = components.rename(
        columns={
            'isotropic': 'poa_isotropic',
            'circumsolar': 'poa_circumsolar',
            'horizon': 'poa_horizon'
        })

    df_inputs = pd.concat(
        [df_inputs, components, vf_perez, luminance, diffuse_poa],
        axis=1,
        join='outer')
    df_inputs = df_inputs.rename(columns={0: 'poa_total_diffuse'})

    # Adjust the circumsolar luminance when it hits the back surface
    if df_inputs_back_surface.shape[0] > 0:
        df_inputs.loc[sun_hitting_back_surface, 'luminance_circumsolar'] = (
            df_inputs_back_surface.loc[:, 'luminance_circumsolar'])
    return df_inputs
Пример #40
0
    def get_clearsky(self, times, model='ineichen', solar_position=None,
                     dni_extra=None, **kwargs):
        """
        Calculate the clear sky estimates of GHI, DNI, and/or DHI
        at this location.

        Parameters
        ----------
        times: DatetimeIndex
        model: str, default 'ineichen'
            The clear sky model to use. Must be one of
            'ineichen', 'haurwitz', 'simplified_solis'.
        solar_position : None or DataFrame, default None
            DataFrame with columns 'apparent_zenith', 'zenith',
            'apparent_elevation'.
        dni_extra: None or numeric, default None
            If None, will be calculated from times.

        kwargs passed to the relevant functions. Climatological values
        are assumed in many cases. See source code for details!

        Returns
        -------
        clearsky : DataFrame
            Column names are: ``ghi, dni, dhi``.
        """
        if dni_extra is None:
            dni_extra = irradiance.extraradiation(times)

        try:
            pressure = kwargs.pop('pressure')
        except KeyError:
            pressure = atmosphere.alt2pres(self.altitude)

        if solar_position is None:
            solar_position = self.get_solarposition(times, pressure=pressure,
                                                    **kwargs)

        apparent_zenith = solar_position['apparent_zenith']
        apparent_elevation = solar_position['apparent_elevation']

        if model == 'ineichen':
            try:
                linke_turbidity = kwargs.pop('linke_turbidity')
            except KeyError:
                interp_turbidity = kwargs.pop('interp_turbidity', True)
                linke_turbidity = clearsky.lookup_linke_turbidity(
                    times, self.latitude, self.longitude,
                    interp_turbidity=interp_turbidity)

            try:
                airmass_absolute = kwargs.pop('airmass_absolute')
            except KeyError:
                airmass_absolute = self.get_airmass(
                    times, solar_position=solar_position)['airmass_absolute']

            cs = clearsky.ineichen(apparent_zenith, airmass_absolute,
                                   linke_turbidity, altitude=self.altitude,
                                   dni_extra=dni_extra)
        elif model == 'haurwitz':
            cs = clearsky.haurwitz(apparent_zenith)
        elif model == 'simplified_solis':
            cs = clearsky.simplified_solis(
                apparent_elevation, pressure=pressure, dni_extra=dni_extra,
                **kwargs)
        else:
            raise ValueError('{} is not a valid clear sky model. Must be '
                             'one of ineichen, simplified_solis, haurwitz'
                             .format(model))

        return cs
Пример #41
0
def ineichen(time, latitude, longitude, altitude=0, linke_turbidity=None,
             solarposition_method='nrel_numpy', zenith_data=None,
             airmass_model='young1994', airmass_data=None,
             interp_turbidity=True):
    '''
    Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model

    Implements the Ineichen and Perez clear sky model for global horizontal
    irradiance (GHI), direct normal irradiance (DNI), and calculates
    the clear-sky diffuse horizontal (DHI) component as the difference
    between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear
    sky models found the Ineichen/Perez model to have excellent performance
    with a minimal input data set [3].

    Default values for montly Linke turbidity provided by SoDa [4, 5].

    Parameters
    -----------
    time : pandas.DatetimeIndex

    latitude : float

    longitude : float

    altitude : float

    linke_turbidity : None or float
        If None, uses ``LinkeTurbidities.mat`` lookup table.

    solarposition_method : string
        Sets the solar position algorithm.
        See solarposition.get_solarposition()

    zenith_data : None or Series
        If None, ephemeris data will be calculated using ``solarposition_method``.

    airmass_model : string
        See pvlib.airmass.relativeairmass().

    airmass_data : None or Series
        If None, absolute air mass data will be calculated using
        ``airmass_model`` and location.alitude.

    interp_turbidity : bool
        If ``True``, interpolates the monthly Linke turbidity values
        found in ``LinkeTurbidities.mat`` to daily values.

    Returns
    --------
    DataFrame with the following columns: ``ghi, dni, dhi``.

    Notes
    -----
    If you are using this function
    in a loop, it may be faster to load LinkeTurbidities.mat outside of
    the loop and feed it in as a keyword argument, rather than
    having the function open and process the file each time it is called.

    References
    ----------

    [1] P. Ineichen and R. Perez, "A New airmass independent formulation for
        the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002.

    [2] R. Perez et. al., "A New Operational Model for Satellite-Derived
        Irradiances: Description and Validation", Solar Energy, vol 73, pp.
        307-317, 2002.

    [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear
        Sky Models: Implementation and Analysis", Sandia National
        Laboratories, SAND2012-2389, 2012.

    [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained
        July 17, 2012).

    [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc.
        ISES Solar World Congress, June 2003. Goteborg, Sweden.
    '''
    # Initial implementation of this algorithm by Matthew Reno.
    # Ported to python by Rob Andrews
    # Added functionality by Will Holmgren (@wholmgren)

    I0 = irradiance.extraradiation(time.dayofyear)

    if zenith_data is None:
        ephem_data = solarposition.get_solarposition(time,
                                                     latitude=latitude,
                                                     longitude=longitude,
                                                     altitude=altitude,
                                                     method=solarposition_method)
        time = ephem_data.index # fixes issue with time possibly not being tz-aware
        try:
            ApparentZenith = ephem_data['apparent_zenith']
        except KeyError:
            ApparentZenith = ephem_data['zenith']
            logger.warning('could not find apparent_zenith. using zenith')
    else:
        ApparentZenith = zenith_data
    #ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases


    if linke_turbidity is None:
        TL = lookup_linke_turbidity(time, latitude, longitude,
                                    interp_turbidity=interp_turbidity)
    else:
        TL = linke_turbidity

    # Get the absolute airmass assuming standard local pressure (per
    # alt2pres) using Kasten and Young's 1989 formula for airmass.

    if airmass_data is None:
        AMabsolute = atmosphere.absoluteairmass(airmass_relative=atmosphere.relativeairmass(ApparentZenith, airmass_model),
                                                pressure=atmosphere.alt2pres(altitude))
    else:
        AMabsolute = airmass_data

    fh1 = np.exp(-altitude/8000.)
    fh2 = np.exp(-altitude/1250.)
    cg1 = 5.09e-05 * altitude + 0.868
    cg2 = 3.92e-05 * altitude + 0.0387
    logger.debug('fh1=%s, fh2=%s, cg1=%s, cg2=%s', fh1, fh2, cg1, cg2)

    #  Dan's note on the TL correction: By my reading of the publication on
    #  pages 151-157, Ineichen and Perez introduce (among other things) three
    #  things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and
    #  appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear
    #  to use the new turbidity factor (item 2 above) in either the beam or GHI
    #  models. The phrasing of appendix A seems as if there are two separate
    #  corrections, the first correction is used to correct the beam/GHI models,
    #  and the second correction is used to correct the revised turibidity
    #  factor. In my estimation, there is no need to correct the turbidity
    #  factor used in the beam/GHI models.

    #  Create the corrected TL for TL < 2
    #  TLcorr = TL;
    #  TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5);

    #  This equation is found in Solar Energy 73, pg 311.
    #  Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002).
    #  It is slightly different than the equation given in Solar Energy 73, pg 156.
    #  We used the equation from pg 311 because of the existence of known typos
    #  in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)).

    cos_zenith = tools.cosd(ApparentZenith)

    clearsky_GHI = ( cg1 * I0 * cos_zenith *
                     np.exp(-cg2*AMabsolute*(fh1 + fh2*(TL - 1))) *
                     np.exp(0.01*AMabsolute**1.8) )
    clearsky_GHI[clearsky_GHI < 0] = 0

    # BncI == "normal beam clear sky radiation"
    b = 0.664 + 0.163/fh1
    BncI = b * I0 * np.exp( -0.09 * AMabsolute * (TL - 1) )
    logger.debug('b=%s', b)

    # "empirical correction" SE 73, 157 & SE 73, 312.
    BncI_2 = ( clearsky_GHI *
               ( 1 - (0.1 - 0.2*np.exp(-TL))/(0.1 + 0.882/fh1) ) /
               cos_zenith )

    clearsky_DNI = np.minimum(BncI, BncI_2)

    clearsky_DHI = clearsky_GHI - clearsky_DNI*cos_zenith

    df_out = pd.DataFrame({'ghi':clearsky_GHI, 'dni':clearsky_DNI,
                           'dhi':clearsky_DHI})
    df_out.fillna(0, inplace=True)

    return df_out
def test_extraradiation():
    assert_almost_equals(1382, irradiance.extraradiation(300), -1)
Пример #43
0
    def get_clearsky(self, times, model='ineichen', **kwargs):
        """
        Calculate the clear sky estimates of GHI, DNI, and/or DHI
        at this location.

        Parameters
        ----------
        times : DatetimeIndex

        model : str
            The clear sky model to use. Must be one of
            'ineichen', 'haurwitz', 'simplified_solis'.

        kwargs passed to the relevant functions. Climatological values
        are assumed in many cases. See code for details.

        Returns
        -------
        clearsky : DataFrame
            Column names are: ``ghi, dni, dhi``.
        """

        if model == 'ineichen':
            cs = clearsky.ineichen(times,
                                   latitude=self.latitude,
                                   longitude=self.longitude,
                                   altitude=self.altitude,
                                   **kwargs)
        elif model == 'haurwitz':
            solpos = self.get_solarposition(times, **kwargs)
            cs = clearsky.haurwitz(solpos['apparent_zenith'])
        elif model == 'simplified_solis':

            # these try/excepts define default values that are only
            # evaluated if necessary. ineichen does some of this internally
            try:
                dni_extra = kwargs.pop('dni_extra')
            except KeyError:
                dni_extra = irradiance.extraradiation(times.dayofyear)

            try:
                pressure = kwargs.pop('pressure')
            except KeyError:
                pressure = atmosphere.alt2pres(self.altitude)

            try:
                apparent_elevation = kwargs.pop('apparent_elevation')
            except KeyError:
                solpos = self.get_solarposition(times,
                                                pressure=pressure,
                                                **kwargs)
                apparent_elevation = solpos['apparent_elevation']

            cs = clearsky.simplified_solis(apparent_elevation,
                                           pressure=pressure,
                                           dni_extra=dni_extra,
                                           **kwargs)
        else:
            raise ValueError('{} is not a valid clear sky model'.format(model))

        return cs
def test_extraradiation_doyarray():
    irradiance.extraradiation(times.dayofyear)
Пример #45
0
def ineichen(
    time,
    location,
    linke_turbidity=None,
    solarposition_method="pyephem",
    zenith_data=None,
    airmass_model="young1994",
    airmass_data=None,
    interp_turbidity=True,
):
    """
    Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model

    Implements the Ineichen and Perez clear sky model for global horizontal
    irradiance (GHI), direct normal irradiance (DNI), and calculates
    the clear-sky diffuse horizontal (DHI) component as the difference
    between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear
    sky models found the Ineichen/Perez model to have excellent performance
    with a minimal input data set [3]. 
    
    Default values for montly Linke turbidity provided by SoDa [4, 5].

    Parameters
    -----------
    time : pandas.DatetimeIndex
    
    location : pvlib.Location
    
    linke_turbidity : None or float
        If None, uses ``LinkeTurbidities.mat`` lookup table.
    
    solarposition_method : string
        Sets the solar position algorithm. 
        See solarposition.get_solarposition()
    
    zenith_data : None or pandas.Series
        If None, ephemeris data will be calculated using ``solarposition_method``.
    
    airmass_model : string
        See pvlib.airmass.relativeairmass().
    
    airmass_data : None or pandas.Series
        If None, absolute air mass data will be calculated using 
        ``airmass_model`` and location.alitude.
    
    interp_turbidity : bool
        If ``True``, interpolates the monthly Linke turbidity values
        found in ``LinkeTurbidities.mat`` to daily values.

    Returns
    --------
    DataFrame with the following columns: ``GHI, DNI, DHI``.

    Notes
    -----
    If you are using this function
    in a loop, it may be faster to load LinkeTurbidities.mat outside of
    the loop and feed it in as a variable, rather than
    having the function open the file each time it is called.

    References
    ----------

    [1] P. Ineichen and R. Perez, "A New airmass independent formulation for
        the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002.

    [2] R. Perez et. al., "A New Operational Model for Satellite-Derived
        Irradiances: Description and Validation", Solar Energy, vol 73, pp.
        307-317, 2002.

    [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear
        Sky Models: Implementation and Analysis", Sandia National
        Laboratories, SAND2012-2389, 2012.

    [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained
        July 17, 2012).

    [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc.
        ISES Solar World Congress, June 2003. Goteborg, Sweden.
    """
    # Initial implementation of this algorithm by Matthew Reno.
    # Ported to python by Rob Andrews
    # Added functionality by Will Holmgren

    I0 = irradiance.extraradiation(time.dayofyear)

    if zenith_data is None:
        ephem_data = solarposition.get_solarposition(time, location, method=solarposition_method)
        time = ephem_data.index  # fixes issue with time possibly not being tz-aware
        try:
            ApparentZenith = ephem_data["apparent_zenith"]
        except KeyError:
            ApparentZenith = ephem_data["zenith"]
            logger.warning("could not find apparent_zenith. using zenith")
    else:
        ApparentZenith = zenith_data
    # ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases

    if linke_turbidity is None:
        # The .mat file 'LinkeTurbidities.mat' contains a single 2160 x 4320 x 12
        # matrix of type uint8 called 'LinkeTurbidity'. The rows represent global
        # latitudes from 90 to -90 degrees; the columns represent global longitudes
        # from -180 to 180; and the depth (third dimension) represents months of
        # the year from January (1) to December (12). To determine the Linke
        # turbidity for a position on the Earth's surface for a given month do the
        # following: LT = LinkeTurbidity(LatitudeIndex, LongitudeIndex, month).
        # Note that the numbers within the matrix are 20 * Linke Turbidity,
        # so divide the number from the file by 20 to get the
        # turbidity.

        try:
            import scipy.io
        except ImportError:
            raise ImportError(
                "The Linke turbidity lookup table requires scipy. "
                + "You can still use clearsky.ineichen if you "
                + "supply your own turbidities."
            )

        # consider putting this code at module level
        this_path = os.path.dirname(os.path.abspath(__file__))
        logger.debug("this_path={}".format(this_path))

        mat = scipy.io.loadmat(os.path.join(this_path, "data", "LinkeTurbidities.mat"))
        linke_turbidity = mat["LinkeTurbidity"]
        LatitudeIndex = np.round_(_linearly_scale(location.latitude, 90, -90, 1, 2160))
        LongitudeIndex = np.round_(_linearly_scale(location.longitude, -180, 180, 1, 4320))
        g = linke_turbidity[LatitudeIndex][LongitudeIndex]
        if interp_turbidity:
            logger.info("interpolating turbidity to the day")
            g2 = np.concatenate([[g[-1]], g, [g[0]]])  # wrap ends around
            days = np.linspace(-15, 380, num=14)  # map day of year onto month (approximate)
            LT = pd.Series(np.interp(time.dayofyear, days, g2), index=time)
        else:
            logger.info("using monthly turbidity")
            ApplyMonth = lambda x: g[x[0] - 1]
            LT = pd.DataFrame(time.month, index=time)
            LT = LT.apply(ApplyMonth, axis=1)
        TL = LT / 20.0
        logger.info("using TL=\n{}".format(TL))
    else:
        TL = linke_turbidity

    # Get the absolute airmass assuming standard local pressure (per
    # alt2pres) using Kasten and Young's 1989 formula for airmass.

    if airmass_data is None:
        AMabsolute = atmosphere.absoluteairmass(
            AMrelative=atmosphere.relativeairmass(ApparentZenith, airmass_model),
            pressure=atmosphere.alt2pres(location.altitude),
        )
    else:
        AMabsolute = airmass_data

    fh1 = np.exp(-location.altitude / 8000.0)
    fh2 = np.exp(-location.altitude / 1250.0)
    cg1 = 5.09e-05 * location.altitude + 0.868
    cg2 = 3.92e-05 * location.altitude + 0.0387
    logger.debug("fh1={}, fh2={}, cg1={}, cg2={}".format(fh1, fh2, cg1, cg2))

    #  Dan's note on the TL correction: By my reading of the publication on
    #  pages 151-157, Ineichen and Perez introduce (among other things) three
    #  things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and
    #  appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear
    #  to use the new turbidity factor (item 2 above) in either the beam or GHI
    #  models. The phrasing of appendix A seems as if there are two separate
    #  corrections, the first correction is used to correct the beam/GHI models,
    #  and the second correction is used to correct the revised turibidity
    #  factor. In my estimation, there is no need to correct the turbidity
    #  factor used in the beam/GHI models.

    #  Create the corrected TL for TL < 2
    #  TLcorr = TL;
    #  TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5);

    #  This equation is found in Solar Energy 73, pg 311.
    #  Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002).
    #  It is slightly different than the equation given in Solar Energy 73, pg 156.
    #  We used the equation from pg 311 because of the existence of known typos
    #  in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)).

    cos_zenith = tools.cosd(ApparentZenith)

    clearsky_GHI = (
        cg1 * I0 * cos_zenith * np.exp(-cg2 * AMabsolute * (fh1 + fh2 * (TL - 1))) * np.exp(0.01 * AMabsolute ** 1.8)
    )
    clearsky_GHI[clearsky_GHI < 0] = 0

    # BncI == "normal beam clear sky radiation"
    b = 0.664 + 0.163 / fh1
    BncI = b * I0 * np.exp(-0.09 * AMabsolute * (TL - 1))
    logger.debug("b={}".format(b))

    # "empirical correction" SE 73, 157 & SE 73, 312.
    BncI_2 = clearsky_GHI * (1 - (0.1 - 0.2 * np.exp(-TL)) / (0.1 + 0.882 / fh1)) / cos_zenith
    # return BncI, BncI_2
    clearsky_DNI = np.minimum(BncI, BncI_2)  # Will H: use np.minimum explicitly

    clearsky_DHI = clearsky_GHI - clearsky_DNI * cos_zenith

    df_out = pd.DataFrame({"GHI": clearsky_GHI, "DNI": clearsky_DNI, "DHI": clearsky_DHI})
    df_out.fillna(0, inplace=True)
    # df_out['BncI'] = BncI
    # df_out['BncI_2'] = BncI

    return df_out
def test_extraradiation_spencer():
    assert_almost_equals(1382, irradiance.extraradiation(300, method='spencer'), -1)
Пример #47
0
def ineichen(time,
             location,
             linke_turbidity=None,
             solarposition_method='pyephem',
             zenith_data=None,
             airmass_model='young1994',
             airmass_data=None,
             interp_turbidity=True):
    '''
    Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model

    Implements the Ineichen and Perez clear sky model for global horizontal
    irradiance (GHI), direct normal irradiance (DNI), and calculates
    the clear-sky diffuse horizontal (DHI) component as the difference
    between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear
    sky models found the Ineichen/Perez model to have excellent performance
    with a minimal input data set [3]. 
    
    Default values for montly Linke turbidity provided by SoDa [4, 5].

    Parameters
    -----------
    time : pandas.DatetimeIndex
    
    location : pvlib.Location
    
    linke_turbidity : None or float
        If None, uses ``LinkeTurbidities.mat`` lookup table.
    
    solarposition_method : string
        Sets the solar position algorithm. 
        See solarposition.get_solarposition()
    
    zenith_data : None or pandas.Series
        If None, ephemeris data will be calculated using ``solarposition_method``.
    
    airmass_model : string
        See pvlib.airmass.relativeairmass().
    
    airmass_data : None or pandas.Series
        If None, absolute air mass data will be calculated using 
        ``airmass_model`` and location.alitude.
    
    interp_turbidity : bool
        If ``True``, interpolates the monthly Linke turbidity values
        found in ``LinkeTurbidities.mat`` to daily values.

    Returns
    --------
    DataFrame with the following columns: ``GHI, DNI, DHI``.

    Notes
    -----
    If you are using this function
    in a loop, it may be faster to load LinkeTurbidities.mat outside of
    the loop and feed it in as a variable, rather than
    having the function open the file each time it is called.

    References
    ----------

    [1] P. Ineichen and R. Perez, "A New airmass independent formulation for
        the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002.

    [2] R. Perez et. al., "A New Operational Model for Satellite-Derived
        Irradiances: Description and Validation", Solar Energy, vol 73, pp.
        307-317, 2002.

    [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear
        Sky Models: Implementation and Analysis", Sandia National
        Laboratories, SAND2012-2389, 2012.

    [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained
        July 17, 2012).

    [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc.
        ISES Solar World Congress, June 2003. Goteborg, Sweden.
    '''
    # Initial implementation of this algorithm by Matthew Reno.
    # Ported to python by Rob Andrews
    # Added functionality by Will Holmgren

    I0 = irradiance.extraradiation(time.dayofyear)

    if zenith_data is None:
        ephem_data = solarposition.get_solarposition(
            time, location, method=solarposition_method)
        time = ephem_data.index  # fixes issue with time possibly not being tz-aware
        try:
            ApparentZenith = ephem_data['apparent_zenith']
        except KeyError:
            ApparentZenith = ephem_data['zenith']
            logger.warning('could not find apparent_zenith. using zenith')
    else:
        ApparentZenith = zenith_data
    #ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases

    if linke_turbidity is None:
        # The .mat file 'LinkeTurbidities.mat' contains a single 2160 x 4320 x 12
        # matrix of type uint8 called 'LinkeTurbidity'. The rows represent global
        # latitudes from 90 to -90 degrees; the columns represent global longitudes
        # from -180 to 180; and the depth (third dimension) represents months of
        # the year from January (1) to December (12). To determine the Linke
        # turbidity for a position on the Earth's surface for a given month do the
        # following: LT = LinkeTurbidity(LatitudeIndex, LongitudeIndex, month).
        # Note that the numbers within the matrix are 20 * Linke Turbidity,
        # so divide the number from the file by 20 to get the
        # turbidity.

        try:
            import scipy.io
        except ImportError:
            raise ImportError(
                'The Linke turbidity lookup table requires scipy. ' +
                'You can still use clearsky.ineichen if you ' +
                'supply your own turbidities.')

        # consider putting this code at module level
        this_path = os.path.dirname(os.path.abspath(__file__))
        logger.debug('this_path={}'.format(this_path))

        mat = scipy.io.loadmat(
            os.path.join(this_path, 'data', 'LinkeTurbidities.mat'))
        linke_turbidity = mat['LinkeTurbidity']
        LatitudeIndex = np.round_(
            _linearly_scale(location.latitude, 90, -90, 1, 2160))
        LongitudeIndex = np.round_(
            _linearly_scale(location.longitude, -180, 180, 1, 4320))
        g = linke_turbidity[LatitudeIndex][LongitudeIndex]
        if interp_turbidity:
            logger.info('interpolating turbidity to the day')
            g2 = np.concatenate([[g[-1]], g, [g[0]]])  # wrap ends around
            days = np.linspace(
                -15, 380, num=14)  # map day of year onto month (approximate)
            LT = pd.Series(np.interp(time.dayofyear, days, g2), index=time)
        else:
            logger.info('using monthly turbidity')
            ApplyMonth = lambda x: g[x[0] - 1]
            LT = pd.DataFrame(time.month, index=time)
            LT = LT.apply(ApplyMonth, axis=1)
        TL = LT / 20.
        logger.info('using TL=\n{}'.format(TL))
    else:
        TL = linke_turbidity

    # Get the absolute airmass assuming standard local pressure (per
    # alt2pres) using Kasten and Young's 1989 formula for airmass.

    if airmass_data is None:
        AMabsolute = atmosphere.absoluteairmass(
            AMrelative=atmosphere.relativeairmass(ApparentZenith,
                                                  airmass_model),
            pressure=atmosphere.alt2pres(location.altitude))
    else:
        AMabsolute = airmass_data

    fh1 = np.exp(-location.altitude / 8000.)
    fh2 = np.exp(-location.altitude / 1250.)
    cg1 = 5.09e-05 * location.altitude + 0.868
    cg2 = 3.92e-05 * location.altitude + 0.0387
    logger.debug('fh1={}, fh2={}, cg1={}, cg2={}'.format(fh1, fh2, cg1, cg2))

    #  Dan's note on the TL correction: By my reading of the publication on
    #  pages 151-157, Ineichen and Perez introduce (among other things) three
    #  things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and
    #  appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear
    #  to use the new turbidity factor (item 2 above) in either the beam or GHI
    #  models. The phrasing of appendix A seems as if there are two separate
    #  corrections, the first correction is used to correct the beam/GHI models,
    #  and the second correction is used to correct the revised turibidity
    #  factor. In my estimation, there is no need to correct the turbidity
    #  factor used in the beam/GHI models.

    #  Create the corrected TL for TL < 2
    #  TLcorr = TL;
    #  TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5);

    #  This equation is found in Solar Energy 73, pg 311.
    #  Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002).
    #  It is slightly different than the equation given in Solar Energy 73, pg 156.
    #  We used the equation from pg 311 because of the existence of known typos
    #  in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)).

    cos_zenith = tools.cosd(ApparentZenith)

    clearsky_GHI = cg1 * I0 * cos_zenith * np.exp(
        -cg2 * AMabsolute * (fh1 + fh2 *
                             (TL - 1))) * np.exp(0.01 * AMabsolute**1.8)
    clearsky_GHI[clearsky_GHI < 0] = 0

    # BncI == "normal beam clear sky radiation"
    b = 0.664 + 0.163 / fh1
    BncI = b * I0 * np.exp(-0.09 * AMabsolute * (TL - 1))
    logger.debug('b={}'.format(b))

    # "empirical correction" SE 73, 157 & SE 73, 312.
    BncI_2 = clearsky_GHI * (1 - (0.1 - 0.2 * np.exp(-TL)) /
                             (0.1 + 0.882 / fh1)) / cos_zenith
    #return BncI, BncI_2
    clearsky_DNI = np.minimum(BncI,
                              BncI_2)  # Will H: use np.minimum explicitly

    clearsky_DHI = clearsky_GHI - clearsky_DNI * cos_zenith

    df_out = pd.DataFrame({
        'GHI': clearsky_GHI,
        'DNI': clearsky_DNI,
        'DHI': clearsky_DHI
    })
    df_out.fillna(0, inplace=True)
    #df_out['BncI'] = BncI
    #df_out['BncI_2'] = BncI

    return df_out
def test_extraradiation_ephem_scalar():
    assert_almost_equals(1382, irradiance.extraradiation(300, method='pyephem').values[0], -1)
Пример #49
0
    def get_irradiance(self, surface_tilt, surface_azimuth,
                       solar_zenith, solar_azimuth, dni, ghi, dhi,
                       dni_extra=None, airmass=None, model='haydavies',
                       **kwargs):
        """
        Uses the :func:`irradiance.total_irrad` function to calculate
        the plane of array irradiance components on a tilted surface
        defined by the input data and ``self.albedo``.

        For a given set of solar zenith and azimuth angles, the
        surface tilt and azimuth parameters are typically determined
        by :py:method:`~SingleAxisTracker.singleaxis`.

        Parameters
        ----------
        surface_tilt : numeric
            Panel tilt from horizontal.
        surface_azimuth : numeric
            Panel azimuth from north
        solar_zenith : numeric
            Solar zenith angle.
        solar_azimuth : numeric
            Solar azimuth angle.
        dni : float or Series
            Direct Normal Irradiance
        ghi : float or Series
            Global horizontal irradiance
        dhi : float or Series
            Diffuse horizontal irradiance
        dni_extra : float or Series, default None
            Extraterrestrial direct normal irradiance
        airmass : float or Series, default None
            Airmass
        model : String, default 'haydavies'
            Irradiance model.

        **kwargs
            Passed to :func:`irradiance.total_irrad`.

        Returns
        -------
        poa_irradiance : DataFrame
            Column names are: ``total, beam, sky, ground``.
        """

        # not needed for all models, but this is easier
        if dni_extra is None:
            dni_extra = irradiance.extraradiation(solar_zenith.index)

        if airmass is None:
            airmass = atmosphere.relativeairmass(solar_zenith)

        return irradiance.total_irrad(surface_tilt,
                                      surface_azimuth,
                                      solar_zenith,
                                      solar_azimuth,
                                      dni, ghi, dhi,
                                      dni_extra=dni_extra, airmass=airmass,
                                      model=model,
                                      albedo=self.albedo,
                                      **kwargs)
Пример #50
0
    def get_clearsky(self,
                     times,
                     model='ineichen',
                     solar_position=None,
                     dni_extra=None,
                     **kwargs):
        """
        Calculate the clear sky estimates of GHI, DNI, and/or DHI
        at this location.

        Parameters
        ----------
        times: DatetimeIndex
        model: str
            The clear sky model to use. Must be one of
            'ineichen', 'haurwitz', 'simplified_solis'.
        solar_position : None or DataFrame
            DataFrame with with columns 'apparent_zenith', 'zenith',
            'apparent_elevation'.
        dni_extra: None or numeric
            If None, will be calculated from times.

        kwargs passed to the relevant functions. Climatological values
        are assumed in many cases. See source code for details!

        Returns
        -------
        clearsky : DataFrame
            Column names are: ``ghi, dni, dhi``.
        """
        if dni_extra is None:
            dni_extra = irradiance.extraradiation(times)

        try:
            pressure = kwargs.pop('pressure')
        except KeyError:
            pressure = atmosphere.alt2pres(self.altitude)

        if solar_position is None:
            solar_position = self.get_solarposition(times,
                                                    pressure=pressure,
                                                    **kwargs)

        apparent_zenith = solar_position['apparent_zenith']
        apparent_elevation = solar_position['apparent_elevation']

        if model == 'ineichen':
            try:
                linke_turbidity = kwargs.pop('linke_turbidity')
            except KeyError:
                interp_turbidity = kwargs.pop('interp_turbidity', True)
                linke_turbidity = clearsky.lookup_linke_turbidity(
                    times,
                    self.latitude,
                    self.longitude,
                    interp_turbidity=interp_turbidity)

            try:
                airmass_absolute = kwargs.pop('airmass_absolute')
            except KeyError:
                airmass_absolute = self.get_airmass(
                    times, solar_position=solar_position)['airmass_absolute']

            cs = clearsky.ineichen(apparent_zenith,
                                   airmass_absolute,
                                   linke_turbidity,
                                   altitude=self.altitude,
                                   dni_extra=dni_extra)
        elif model == 'haurwitz':
            cs = clearsky.haurwitz(apparent_zenith)
        elif model == 'simplified_solis':
            cs = clearsky.simplified_solis(apparent_elevation,
                                           pressure=pressure,
                                           dni_extra=dni_extra,
                                           **kwargs)
        else:
            raise ValueError(
                ('{} is not a valid clear sky model. Must be ' +
                 'one of ineichen, simplified_solis, haurwitz').format(model))

        return cs
Пример #51
0
def calc_dnix(model):
    dni_extra = irradiance.extraradiation(model.time)
    return (dni_extra)
Пример #52
0
def test_extraradiation(input, expected, method):
    out = irradiance.extraradiation(input)
    assert_allclose(out, expected, atol=1)
Пример #53
0
    def get_irradiance(self,
                       surface_tilt,
                       surface_azimuth,
                       solar_zenith,
                       solar_azimuth,
                       dni,
                       ghi,
                       dhi,
                       dni_extra=None,
                       airmass=None,
                       model='haydavies',
                       **kwargs):
        """
        Uses the :func:`irradiance.total_irrad` function to calculate
        the plane of array irradiance components on a tilted surface
        defined by the input data and ``self.albedo``.

        For a given set of solar zenith and azimuth angles, the
        surface tilt and azimuth parameters are typically determined
        by :py:method:`~SingleAxisTracker.singleaxis`.

        Parameters
        ----------
        surface_tilt : numeric
            Panel tilt from horizontal.
        surface_azimuth : numeric
            Panel azimuth from north
        solar_zenith : numeric
            Solar zenith angle.
        solar_azimuth : numeric
            Solar azimuth angle.
        dni : float or Series
            Direct Normal Irradiance
        ghi : float or Series
            Global horizontal irradiance
        dhi : float or Series
            Diffuse horizontal irradiance
        dni_extra : float or Series, default None
            Extraterrestrial direct normal irradiance
        airmass : float or Series, default None
            Airmass
        model : String, default 'haydavies'
            Irradiance model.

        **kwargs
            Passed to :func:`irradiance.total_irrad`.

        Returns
        -------
        poa_irradiance : DataFrame
            Column names are: ``total, beam, sky, ground``.
        """

        # not needed for all models, but this is easier
        if dni_extra is None:
            dni_extra = irradiance.extraradiation(solar_zenith.index)

        if airmass is None:
            airmass = atmosphere.relativeairmass(solar_zenith)

        return irradiance.total_irrad(surface_tilt,
                                      surface_azimuth,
                                      solar_zenith,
                                      solar_azimuth,
                                      dni,
                                      ghi,
                                      dhi,
                                      dni_extra=dni_extra,
                                      airmass=airmass,
                                      model=model,
                                      albedo=self.albedo,
                                      **kwargs)
Пример #54
0
def test_extraradiation_epoch_year():
    out = irradiance.extraradiation(doy, method='nrel', epoch_year=2012)
    assert_allclose(out, 1382.4926804890767, atol=0.1)
Пример #55
0
    def get_irradiance(self,
                       dni,
                       ghi,
                       dhi,
                       dni_extra=None,
                       airmass=None,
                       model='haydavies',
                       **kwargs):
        """
        Uses the :func:`irradiance.total_irrad` function to calculate
        the plane of array irradiance components on a tilted surface
        defined by ``self.surface_tilt``, ``self.surface_azimuth``, and
        ``self.albedo``.

        Parameters
        ----------
        solar_zenith : float or Series.
            Solar zenith angle.
        solar_azimuth : float or Series.
            Solar azimuth angle.
        dni : float or Series
            Direct Normal Irradiance
        ghi : float or Series
            Global horizontal irradiance
        dhi : float or Series
            Diffuse horizontal irradiance
        dni_extra : float or Series
            Extraterrestrial direct normal irradiance
        airmass : float or Series
            Airmass
        model : String
            Irradiance model.

        **kwargs
            Passed to :func:`irradiance.total_irrad`.

        Returns
        -------
        poa_irradiance : DataFrame
            Column names are: ``total, beam, sky, ground``.
        """

        surface_tilt = kwargs.pop('surface_tilt', self.surface_tilt)
        surface_azimuth = kwargs.pop('surface_azimuth', self.surface_azimuth)

        try:
            solar_zenith = kwargs['solar_zenith']
        except KeyError:
            solar_zenith = self.solar_zenith

        try:
            solar_azimuth = kwargs['solar_azimuth']
        except KeyError:
            solar_azimuth = self.solar_azimuth

        # not needed for all models, but this is easier
        if dni_extra is None:
            dni_extra = irradiance.extraradiation(solar_zenith.index)
            dni_extra = pd.Series(dni_extra, index=solar_zenith.index)

        if airmass is None:
            airmass = atmosphere.relativeairmass(solar_zenith)

        return irradiance.total_irrad(surface_tilt,
                                      surface_azimuth,
                                      solar_zenith,
                                      solar_azimuth,
                                      dni,
                                      ghi,
                                      dhi,
                                      dni_extra=dni_extra,
                                      airmass=airmass,
                                      model=model,
                                      albedo=self.albedo,
                                      **kwargs)
Пример #56
0
def perez_diffuse_luminance(df_inputs):
    """
    Function used to calculate the luminance and the view factor terms from the
    Perez diffuse light transposition model, as implemented in the
    ``pvlib-python`` library.

    :param df_inputs: class:`pandas.DataFrame` with following columns:
        ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi',
        'dni']. Units are: ['deg', 'deg', 'deg', 'deg', 'W/m2', 'W/m2']
    :return: class:`pandas.DataFrame` with the following columns:
        ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi',
        'dni', 'vf_horizon', 'vf_circumsolar', 'vf_isotropic',
        'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic',
        'poa_isotropic', 'poa_circumsolar', 'poa_horizon', 'poa_total_diffuse']
    """

    dni_et = irradiance.extraradiation(df_inputs.index.dayofyear)
    am = atmosphere.relativeairmass(df_inputs.solar_zenith)

    # Need to treat the case when the sun is hitting the back surface of pvrow
    aoi_proj = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth,
                              df_inputs.solar_zenith, df_inputs.solar_azimuth)
    sun_hitting_back_surface = ((aoi_proj < 0) &
                                (df_inputs.solar_zenith <= 90))
    df_inputs_back_surface = df_inputs.loc[sun_hitting_back_surface]
    # Reverse the surface normal to switch to back-surface circumsolar calc
    df_inputs_back_surface.loc[:, 'array_azimuth'] -= 180.
    df_inputs_back_surface.loc[:, 'array_azimuth'] = np.mod(
        df_inputs_back_surface.loc[:, 'array_azimuth'], 360.
    )
    df_inputs_back_surface.loc[:, 'array_tilt'] = (
        180. - df_inputs_back_surface.array_tilt)

    if df_inputs_back_surface.shape[0] > 0:
        # Use recursion to calculate circumsolar luminance for back surface
        df_inputs_back_surface = perez_diffuse_luminance(
            df_inputs_back_surface)

    # Calculate Perez diffuse components
    diffuse_poa, components = irradiance.perez(df_inputs.array_tilt,
                                               df_inputs.array_azimuth,
                                               df_inputs.dhi, df_inputs.dni,
                                               dni_et,
                                               df_inputs.solar_zenith,
                                               df_inputs.solar_azimuth,
                                               am,
                                               return_components=True)

    # Calculate Perez view factors:
    a = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth,
                       df_inputs.solar_zenith, df_inputs.solar_azimuth)
    a = np.maximum(a, 0)
    b = cosd(df_inputs.solar_zenith)
    b = np.maximum(b, cosd(85))

    vf_perez = pd.DataFrame(
        np.array([
            sind(df_inputs.array_tilt),
            a / b,
            (1. + cosd(df_inputs.array_tilt)) / 2.
        ]).T,
        index=df_inputs.index,
        columns=['vf_horizon', 'vf_circumsolar', 'vf_isotropic']
    )

    # Calculate diffuse luminance
    luminance = pd.DataFrame(
        np.array([
            components['horizon'] / vf_perez['vf_horizon'],
            components['circumsolar'] / vf_perez['vf_circumsolar'],
            components['isotropic'] / vf_perez['vf_isotropic']
        ]).T,
        index=df_inputs.index,
        columns=['luminance_horizon', 'luminance_circumsolar',
                 'luminance_isotropic']
    )
    luminance.loc[diffuse_poa == 0, :] = 0.

    # Format components column names
    components = components.rename(columns={'isotropic': 'poa_isotropic',
                                            'circumsolar': 'poa_circumsolar',
                                            'horizon': 'poa_horizon'})

    df_inputs = pd.concat([df_inputs, components, vf_perez, luminance,
                           diffuse_poa],
                          axis=1, join='outer')
    df_inputs = df_inputs.rename(columns={0: 'poa_total_diffuse'})

    # Adjust the circumsolar luminance when it hits the back surface
    if df_inputs_back_surface.shape[0] > 0:
        df_inputs.loc[sun_hitting_back_surface, 'luminance_circumsolar'] = (
            df_inputs_back_surface.loc[:, 'luminance_circumsolar']
        )
    return df_inputs
Пример #57
0
def test_bird():
    """Test Bird/Hulstrom Clearsky Model"""
    times = pd.DatetimeIndex(start='1/1/2015 0:00', end='12/31/2015 23:00',
                             freq='H')
    tz = -7  # test timezone
    gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz))
    times = times.tz_localize(gmt_tz)  # set timezone
    # match test data from BIRD_08_16_2012.xls
    latitude = 40.
    longitude = -105.
    press_mB = 840.
    o3_cm = 0.3
    h2o_cm = 1.5
    aod_500nm = 0.1
    aod_380nm = 0.15
    b_a = 0.85
    alb = 0.2
    eot = solarposition.equation_of_time_spencer71(times.dayofyear)
    hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15.
    declination = solarposition.declination_spencer71(times.dayofyear)
    zenith = solarposition.solar_zenith_analytical(
        np.deg2rad(latitude), np.deg2rad(hour_angle), declination
    )
    zenith = np.rad2deg(zenith)
    airmass = atmosphere.relativeairmass(zenith, model='kasten1966')
    etr = irradiance.extraradiation(times)
    # test Bird with time series data
    field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi')
    irrads = clearsky.bird(
        zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100.,
        etr, b_a, alb
    )
    Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names)
    clearsky_path = os.path.dirname(os.path.abspath(__file__))
    pvlib_path = os.path.dirname(clearsky_path)
    data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012.csv')
    testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna()
    testdata.index = times[1:48]
    assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48]))
    assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4)
    assert np.allclose(testdata['Hour Angle'], hour_angle[1:48])
    assert np.allclose(testdata['Zenith Ang'], zenith[1:48])
    dawn = zenith < 88.
    dusk = testdata['Zenith Ang'] < 88.
    am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0)
    assert np.allclose(
        testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3
    )
    direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3
    )
    direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3
    )
    global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3
    )
    diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3
    )
    # test keyword parameters
    irrads2 = clearsky.bird(
        zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr
    )
    Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names)
    clearsky_path = os.path.dirname(os.path.abspath(__file__))
    pvlib_path = os.path.dirname(clearsky_path)
    data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012_patm.csv')
    testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna()
    testdata2.index = times[1:48]
    direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3
    )
    direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3
    )
    global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3
    )
    diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.)
    assert np.allclose(
        testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3
    )
    # test scalars just at noon
    # XXX: calculations start at 12am so noon is at index = 12
    irrads3 = clearsky.bird(
        zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12]
    )
    Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names)
    # XXX: testdata starts at 1am so noon is at index = 11
    np.allclose(
        [Eb3, Ebh3, Gh3, Dh3],
        testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11],
        rtol=1e-3)
    return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)