Example #1
0
    def get_solarposition(self,
                          times,
                          pressure=None,
                          temperature=12,
                          **kwargs):
        """
        Uses the :py:func:`solarposition.get_solarposition` function
        to calculate the solar zenith, azimuth, etc. at this location.

        Parameters
        ----------
        times : DatetimeIndex
        pressure : None, float, or array-like
            If None, pressure will be calculated using
            :py:func:`atmosphere.alt2pres` and ``self.altitude``.
        temperature : None, float, or array-like

        kwargs passed to :py:func:`solarposition.get_solarposition`

        Returns
        -------
        solar_position : DataFrame
            Columns depend on the ``method`` kwarg, but always include
            ``zenith`` and ``azimuth``.
        """
        if pressure is None:
            pressure = atmosphere.alt2pres(self.altitude)

        return solarposition.get_solarposition(times,
                                               latitude=self.latitude,
                                               longitude=self.longitude,
                                               altitude=self.altitude,
                                               pressure=pressure,
                                               temperature=temperature,
                                               **kwargs)
Example #2
0
    def get_solarposition(self, times, pressure=None, temperature=12,
                          **kwargs):
        """
        Uses the :py:func:`solarposition.get_solarposition` function
        to calculate the solar zenith, azimuth, etc. at this location.

        Parameters
        ----------
        times : DatetimeIndex
        pressure : None, float, or array-like, default None
            If None, pressure will be calculated using
            :py:func:`atmosphere.alt2pres` and ``self.altitude``.
        temperature : None, float, or array-like, default 12

        kwargs passed to :py:func:`solarposition.get_solarposition`

        Returns
        -------
        solar_position : DataFrame
            Columns depend on the ``method`` kwarg, but always include
            ``zenith`` and ``azimuth``.
        """
        if pressure is None:
            pressure = atmosphere.alt2pres(self.altitude)

        return solarposition.get_solarposition(times, latitude=self.latitude,
                                               longitude=self.longitude,
                                               altitude=self.altitude,
                                               pressure=pressure,
                                               temperature=temperature,
                                               **kwargs)
Example #3
0
def get_solarposition(time,
                      latitude,
                      longitude,
                      altitude=None,
                      pressure=None,
                      temperature=10,
                      **kwargs):

    #calculate the altitude and pressure based on default vales
    if altitude is None and pressure is None:
        altitude = 0
        pressure = 101325
    elif altitude is None:
        altitude = atmosphere.pres2alt(pressure)
    elif pressure is None:
        pressure = atmosphere.alt2pres(altitude)

    #converting the time into DatetimeIndex
    time = pd.DatetimeIndex([
        time,
    ])

    #Normalizing the time
    t = time.tz_localize(tz='UTC')
    spa_python(t, latitude, longitude, altitude, pressure, temperature,
               **kwargs)
Example #4
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
Example #5
0
        def rho(L, theta):

            # From altitude, calculate pressure
            # Assume T = 288.15K and 0% humidity
            P = atmosphere.alt2pres(L * np.sin(theta))
            T = 288.15
            R = 287.058  # Specific gas constant of air
            return P / (R * T)
Example #6
0
    def get_airmass(self,
                    times=None,
                    solar_position=None,
                    model='kastenyoung1989'):
        """
        Calculate the relative and absolute airmass.

        Automatically chooses zenith or apparant zenith
        depending on the selected model.

        Parameters
        ----------
        times : None or DatetimeIndex, default None
            Only used if solar_position is not provided.
        solar_position : None or DataFrame, default None
            DataFrame with with columns 'apparent_zenith', 'zenith'.
        model : str, default 'kastenyoung1989'
            Relative airmass model. See
            :py:func:`pvlib.atmosphere.get_relative_airmass`
            for a list of available models.

        Returns
        -------
        airmass : DataFrame
            Columns are 'airmass_relative', 'airmass_absolute'

        See also
        --------
        pvlib.atmosphere.get_relative_airmass
        """

        if solar_position is None:
            solar_position = self.get_solarposition(times)

        if model in atmosphere.APPARENT_ZENITH_MODELS:
            zenith = solar_position['apparent_zenith']
        elif model in atmosphere.TRUE_ZENITH_MODELS:
            zenith = solar_position['zenith']
        else:
            raise ValueError(f'{model} is not a valid airmass model')

        airmass_relative = atmosphere.get_relative_airmass(zenith, model)

        pressure = atmosphere.alt2pres(self.altitude)
        airmass_absolute = atmosphere.get_absolute_airmass(
            airmass_relative, pressure)

        airmass = pd.DataFrame(index=solar_position.index)
        airmass['airmass_relative'] = airmass_relative
        airmass['airmass_absolute'] = airmass_absolute

        return airmass
Example #7
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
Example #8
0
    def get_airmass(self,
                    times=None,
                    solar_position=None,
                    model='kastenyoung1989'):
        """
        Calculate the relative and absolute airmass.

        Automatically chooses zenith or apparant zenith
        depending on the selected model.

        Parameters
        ----------
        times : None or DatetimeIndex
            Only used if solar_position is not provided.
        solar_position : None or DataFrame
            DataFrame with with columns 'apparent_zenith', 'zenith'.
        model : str
            Relative airmass model

        Returns
        -------
        airmass : DataFrame
            Columns are 'airmass_relative', 'airmass_absolute'
        """

        if solar_position is None:
            solar_position = self.get_solarposition(times)

        if model in atmosphere.APPARENT_ZENITH_MODELS:
            zenith = solar_position['apparent_zenith']
        elif model in atmosphere.TRUE_ZENITH_MODELS:
            zenith = solar_position['zenith']
        else:
            raise ValueError('{} is not a valid airmass model'.format(model))

        airmass_relative = atmosphere.relativeairmass(zenith, model)

        pressure = atmosphere.alt2pres(self.altitude)
        airmass_absolute = atmosphere.absoluteairmass(airmass_relative,
                                                      pressure)

        airmass = pd.DataFrame()
        airmass['airmass_relative'] = airmass_relative
        airmass['airmass_absolute'] = airmass_absolute

        return airmass
Example #9
0
    def get_airmass(self, times=None, solar_position=None,
                    model='kastenyoung1989'):
        """
        Calculate the relative and absolute airmass.
        
        Automatically chooses zenith or apparant zenith
        depending on the selected model.

        Parameters
        ----------
        times : None or DatetimeIndex
            Only used if solar_position is not provided.
        solar_position : None or DataFrame
            DataFrame with with columns 'apparent_zenith', 'zenith'.
        model : str
            Relative airmass model
        
        Returns
        -------
        airmass : DataFrame
            Columns are 'airmass_relative', 'airmass_absolute'
        """

        if solar_position is None:
            solar_position = self.get_solarposition(times)

        if model in atmosphere.APPARENT_ZENITH_MODELS:
            zenith = solar_position['apparent_zenith']
        elif model in atmosphere.TRUE_ZENITH_MODELS:
            zenith = solar_position['zenith']
        else:
            raise ValueError('{} is not a valid airmass model'.format(model))

        airmass_relative = atmosphere.relativeairmass(zenith, model)

        pressure = atmosphere.alt2pres(self.altitude)
        airmass_absolute = atmosphere.absoluteairmass(airmass_relative,
                                                      pressure)

        airmass = pd.DataFrame()
        airmass['airmass_relative'] = airmass_relative
        airmass['airmass_absolute'] = airmass_absolute

        return airmass
            
Example #10
0
def test_ineichen_series():
    tus = Location(32.2, -111, 'US/Arizona', 700)
    times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='3h')
    times_localized = times.tz_localize(tus.tz)
    ephem_data = solarposition.get_solarposition(times_localized, tus.latitude,
                                                 tus.longitude)
    am = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
    am = atmosphere.absoluteairmass(am, atmosphere.alt2pres(tus.altitude))
    expected = pd.DataFrame(
        np.array([[0., 0., 0.], [0., 0., 0.],
                  [91.12492792, 321.16092181, 51.17628184],
                  [716.46580533, 888.90147035, 99.5050056],
                  [1053.42066043, 953.24925854, 116.32868969],
                  [863.54692781, 922.06124712, 106.95536561],
                  [271.06382274, 655.44925241, 73.05968071], [0., 0., 0.],
                  [0., 0., 0.]]),
        columns=['ghi', 'dni', 'dhi'],
        index=times_localized)

    out = clearsky.ineichen(ephem_data['apparent_zenith'], am, 3)
    assert_frame_equal(expected, out)
Example #11
0
def test_ineichen_series():
    tus = Location(32.2, -111, 'US/Arizona', 700)
    times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='3h')
    times_localized = times.tz_localize(tus.tz)
    ephem_data = solarposition.get_solarposition(times_localized, tus.latitude,
                                                 tus.longitude)
    am = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
    am = atmosphere.absoluteairmass(am, atmosphere.alt2pres(tus.altitude))
    expected = pd.DataFrame(np.
        array([[    0.        ,     0.        ,     0.        ],
               [    0.        ,     0.        ,     0.        ],
               [   91.12492792,   321.16092181,    51.17628184],
               [  716.46580533,   888.90147035,    99.5050056 ],
               [ 1053.42066043,   953.24925854,   116.32868969],
               [  863.54692781,   922.06124712,   106.95536561],
               [  271.06382274,   655.44925241,    73.05968071],
               [    0.        ,     0.        ,     0.        ],
               [    0.        ,     0.        ,     0.        ]]),
                            columns=['ghi', 'dni', 'dhi'],
                            index=times_localized)

    out = clearsky.ineichen(ephem_data['apparent_zenith'], am, 3)
    assert_frame_equal(expected, out)
Example #12
0
    def get_solarposition(self,
                          times,
                          pressure=None,
                          temperature=12,
                          **kwargs):
        """
        Uses the :py:func:`pvlib.solarposition.get_solarposition` function
        to calculate the solar zenith, azimuth, etc. at this location.

        Parameters
        ----------
        times : pandas.DatetimeIndex
            Must be localized or UTC will be assumed.
        pressure : None, float, or array-like, default None
            If None, pressure will be calculated using
            :py:func:`pvlib.atmosphere.alt2pres` and ``self.altitude``.
        temperature : None, float, or array-like, default 12

        kwargs
            passed to :py:func:`pvlib.solarposition.get_solarposition`

        Returns
        -------
        solar_position : DataFrame
            Columns depend on the ``method`` kwarg, but always include
            ``zenith`` and ``azimuth``. The angles are in degrees.
        """
        if pressure is None:
            pressure = atmosphere.alt2pres(self.altitude)

        return solarposition.get_solarposition(times,
                                               latitude=self.latitude,
                                               longitude=self.longitude,
                                               altitude=self.altitude,
                                               pressure=pressure,
                                               temperature=temperature,
                                               **kwargs)
Example #13
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
Example #14
0
def basic_chain(times,
                latitude,
                longitude,
                module_parameters,
                inverter_parameters,
                irradiance=None,
                weather=None,
                surface_tilt=None,
                surface_azimuth=None,
                orientation_strategy=None,
                transposition_model='haydavies',
                solar_position_method='nrel_numpy',
                airmass_model='kastenyoung1989',
                altitude=None,
                pressure=None,
                **kwargs):
    """
    An experimental function that computes all of the modeling steps
    necessary for calculating power or energy for a PV system at a given
    location.

    Parameters
    ----------
    times : DatetimeIndex
        Times at which to evaluate the model.

    latitude : float.
        Positive is north of the equator.
        Use decimal degrees notation.

    longitude : float.
        Positive is east of the prime meridian.
        Use decimal degrees notation.

    module_parameters : None, dict or Series
        Module parameters as defined by the SAPM.

    inverter_parameters : None, dict or Series
        Inverter parameters as defined by the CEC.

    irradiance : None or DataFrame, default None
        If None, calculates clear sky data.
        Columns must be 'dni', 'ghi', 'dhi'.

    weather : None or DataFrame, default None
        If None, assumes air temperature is 20 C and
        wind speed is 0 m/s.
        Columns must be 'wind_speed', 'temp_air'.

    surface_tilt : None, float or Series, default None
        Surface tilt angles in decimal degrees.
        The tilt angle is defined as degrees from horizontal
        (e.g. surface facing up = 0, surface facing horizon = 90)

    surface_azimuth : None, float or Series, default None
        Surface azimuth angles in decimal degrees.
        The azimuth convention is defined
        as degrees east of north
        (North=0, South=180, East=90, West=270).

    orientation_strategy : None or str, default None
        The strategy for aligning the modules.
        If not None, sets the ``surface_azimuth`` and ``surface_tilt``
        properties of the ``system``. Allowed strategies include 'flat',
        'south_at_latitude_tilt'. Ignored for SingleAxisTracker systems.

    transposition_model : str, default 'haydavies'
        Passed to system.get_irradiance.

    solar_position_method : str, default 'nrel_numpy'
        Passed to solarposition.get_solarposition.

    airmass_model : str, default 'kastenyoung1989'
        Passed to atmosphere.relativeairmass.

    altitude : None or float, default None
        If None, computed from pressure. Assumed to be 0 m
        if pressure is also None.

    pressure : None or float, default None
        If None, computed from altitude. Assumed to be 101325 Pa
        if altitude is also None.

    **kwargs
        Arbitrary keyword arguments.
        See code for details.

    Returns
    -------
    output : (dc, ac)
        Tuple of DC power (with SAPM parameters) (DataFrame) and AC
        power (Series).
    """

    # use surface_tilt and surface_azimuth if provided,
    # otherwise set them using the orientation_strategy
    if surface_tilt is not None and surface_azimuth is not None:
        pass
    elif orientation_strategy is not None:
        surface_tilt, surface_azimuth = \
            get_orientation(orientation_strategy, latitude=latitude)
    else:
        raise ValueError('orientation_strategy or surface_tilt and '
                         'surface_azimuth must be provided')

    times = times

    if altitude is None and pressure is None:
        altitude = 0.
        pressure = 101325.
    elif altitude is None:
        altitude = atmosphere.pres2alt(pressure)
    elif pressure is None:
        pressure = atmosphere.alt2pres(altitude)

    solar_position = solarposition.get_solarposition(
        times,
        latitude,
        longitude,
        altitude=altitude,
        pressure=pressure,
        method=solar_position_method,
        **kwargs)

    # possible error with using apparent zenith with some models
    airmass = atmosphere.get_relative_airmass(
        solar_position['apparent_zenith'], model=airmass_model)
    airmass = atmosphere.get_absolute_airmass(airmass, pressure)
    dni_extra = pvlib.irradiance.get_extra_radiation(solar_position.index)

    aoi = pvlib.irradiance.aoi(surface_tilt, surface_azimuth,
                               solar_position['apparent_zenith'],
                               solar_position['azimuth'])

    if irradiance is None:
        linke_turbidity = clearsky.lookup_linke_turbidity(
            solar_position.index, latitude, longitude)
        irradiance = clearsky.ineichen(solar_position['apparent_zenith'],
                                       airmass,
                                       linke_turbidity,
                                       altitude=altitude,
                                       dni_extra=dni_extra)

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

    if weather is None:
        weather = {'wind_speed': 0, 'temp_air': 20}

    temps = pvsystem.sapm_celltemp(total_irrad['poa_global'],
                                   weather['wind_speed'], weather['temp_air'])

    effective_irradiance = pvsystem.sapm_effective_irradiance(
        total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi,
        module_parameters)

    dc = pvsystem.sapm(effective_irradiance, temps['temp_cell'],
                       module_parameters)

    ac = pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter_parameters)

    return dc, ac
Example #15
0
def test_alt2pres():
    out = atmosphere.alt2pres(np.array([-100, 0, 1000, 8000]))
    expected = np.array([102532.073, 101324.999, 89874.750, 35600.496])
    assert_allclose(out, expected, atol=0.001)
Example #16
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
Example #17
0
def basic_chain(times, latitude, longitude,
                module_parameters, inverter_parameters,
                irradiance=None, weather=None,
                surface_tilt=None, surface_azimuth=None,
                orientation_strategy=None,
                transposition_model='haydavies',
                solar_position_method='nrel_numpy',
                airmass_model='kastenyoung1989',
                altitude=None, pressure=None,
                **kwargs):
    """
    An experimental function that computes all of the modeling steps
    necessary for calculating power or energy for a PV system at a given
    location.

    Parameters
    ----------
    times : DatetimeIndex
        Times at which to evaluate the model.

    latitude : float.
        Positive is north of the equator.
        Use decimal degrees notation.

    longitude : float.
        Positive is east of the prime meridian.
        Use decimal degrees notation.

    module_parameters : None, dict or Series
        Module parameters as defined by the SAPM.

    inverter_parameters : None, dict or Series
        Inverter parameters as defined by the CEC.

    irradiance : None or DataFrame
        If None, calculates clear sky data.
        Columns must be 'dni', 'ghi', 'dhi'.

    weather : None or DataFrame
        If None, assumes air temperature is 20 C and
        wind speed is 0 m/s.
        Columns must be 'wind_speed', 'temp_air'.

    surface_tilt : float or Series
        Surface tilt angles in decimal degrees.
        The tilt angle is defined as degrees from horizontal
        (e.g. surface facing up = 0, surface facing horizon = 90)

    surface_azimuth : float or Series
        Surface azimuth angles in decimal degrees.
        The azimuth convention is defined
        as degrees east of north
        (North=0, South=180, East=90, West=270).

    orientation_strategy : None or str
        The strategy for aligning the modules.
        If not None, sets the ``surface_azimuth`` and ``surface_tilt``
        properties of the ``system``.

    transposition_model : str
        Passed to system.get_irradiance.

    solar_position_method : str
        Passed to location.get_solarposition.

    airmass_model : str
        Passed to location.get_airmass.

    altitude : None or float
        If None, computed from pressure. Assumed to be 0 m
        if pressure is also None.

    pressure : None or float
        If None, computed from altitude. Assumed to be 101325 Pa
        if altitude is also None.

    **kwargs
        Arbitrary keyword arguments.
        See code for details.

    Returns
    -------
    output : (dc, ac)
        Tuple of DC power (with SAPM parameters) (DataFrame) and AC
        power (Series).
    """

    # use surface_tilt and surface_azimuth if provided,
    # otherwise set them using the orientation_strategy
    if surface_tilt is not None and surface_azimuth is not None:
        pass
    elif orientation_strategy is not None:
        surface_tilt, surface_azimuth = \
            get_orientation(orientation_strategy, latitude=latitude)
    else:
        raise ValueError('orientation_strategy or surface_tilt and ' +
                         'surface_azimuth must be provided')

    times = times

    if altitude is None and pressure is None:
        altitude = 0.
        pressure = 101325.
    elif altitude is None:
        altitude = atmosphere.pres2alt(pressure)
    elif pressure is None:
        pressure = atmosphere.alt2pres(altitude)

    solar_position = solarposition.get_solarposition(times, latitude,
                                                     longitude,
                                                     altitude=altitude,
                                                     pressure=pressure,
                                                     **kwargs)

    # possible error with using apparent zenith with some models
    airmass = atmosphere.relativeairmass(solar_position['apparent_zenith'],
                                         model=airmass_model)
    airmass = atmosphere.absoluteairmass(airmass, pressure)
    dni_extra = pvlib.irradiance.extraradiation(solar_position.index)
    dni_extra = pd.Series(dni_extra, index=solar_position.index)

    aoi = pvlib.irradiance.aoi(surface_tilt, surface_azimuth,
                               solar_position['apparent_zenith'],
                               solar_position['azimuth'])

    if irradiance is None:
        irradiance = clearsky.ineichen(
            solar_position.index,
            latitude,
            longitude,
            zenith_data=solar_position['apparent_zenith'],
            airmass_data=airmass,
            altitude=altitude)

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

    if weather is None:
        weather = {'wind_speed': 0, 'temp_air': 20}

    temps = pvsystem.sapm_celltemp(total_irrad['poa_global'],
                                   weather['wind_speed'],
                                   weather['temp_air'])

    dc = pvsystem.sapm(module_parameters, total_irrad['poa_direct'],
                       total_irrad['poa_diffuse'],
                       temps['temp_cell'],
                       airmass,
                       aoi)

    ac = pvsystem.snlinverter(inverter_parameters, dc['v_mp'], dc['p_mp'])

    return dc, ac
Example #18
0
def test_alt2pres():
    out = atmosphere.alt2pres(np.array([-100, 0, 1000, 8000]))
    expected = np.array([102532.073, 101324.999,  89874.750,  35600.496])
    assert_allclose(out, expected, atol=0.001)
Example #19
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
Example #20
0
def get_solarposition(time,
                      latitude,
                      longitude,
                      altitude=None,
                      pressure=None,
                      method='nrel_numpy',
                      temperature=12,
                      **kwargs):
    """
    A convenience wrapper for the solar position calculators.

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

    latitude : float

    longitude : float

    altitude : None or float, default None
        If None, computed from pressure. Assumed to be 0 m
        if pressure is also None.

    pressure : None or float, default None
        If None, computed from altitude. Assumed to be 101325 Pa
        if altitude is also None.

    method : string, default 'nrel_numpy'
        'nrel_numpy' uses an implementation of the NREL SPA algorithm
        described in [1] (default, recommended): :py:func:`spa_python`

        'nrel_numba' uses an implementation of the NREL SPA algorithm
        described in [1], but also compiles the code first:
        :py:func:`spa_python`

        'pyephem' uses the PyEphem package: :py:func:`pyephem`

        'ephemeris' uses the pvlib ephemeris code: :py:func:`ephemeris`

        'nrel_c' uses the NREL SPA C code [3]: :py:func:`spa_c`

    temperature : float, default 12
        Degrees C.

    Other keywords are passed to the underlying solar position function.

    References
    ----------
    [1] I. Reda and A. Andreas, Solar position algorithm for solar radiation
    applications. Solar Energy, vol. 76, no. 5, pp. 577-589, 2004.

    [2] I. Reda and A. Andreas, Corrigendum to Solar position algorithm for
    solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007.

    [3] NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/
    """

    if altitude is None and pressure is None:
        altitude = 0.
        pressure = 101325.
    elif altitude is None:
        altitude = atmosphere.pres2alt(pressure)
    elif pressure is None:
        pressure = atmosphere.alt2pres(altitude)

    method = method.lower()
    if isinstance(time, dt.datetime):
        time = pd.DatetimeIndex([
            time,
        ])

    if method == 'nrel_c':
        ephem_df = spa_c(time, latitude, longitude, pressure, temperature,
                         **kwargs)
    elif method == 'nrel_numba':
        ephem_df = spa_python(time,
                              latitude,
                              longitude,
                              altitude,
                              pressure,
                              temperature,
                              how='numba',
                              **kwargs)
    elif method == 'nrel_numpy':
        ephem_df = spa_python(time,
                              latitude,
                              longitude,
                              altitude,
                              pressure,
                              temperature,
                              how='numpy',
                              **kwargs)
    elif method == 'pyephem':
        ephem_df = pyephem(time,
                           latitude,
                           longitude,
                           altitude=altitude,
                           pressure=pressure,
                           temperature=temperature,
                           **kwargs)
    elif method == 'ephemeris':
        ephem_df = ephemeris(time, latitude, longitude, pressure, temperature,
                             **kwargs)
    else:
        raise ValueError('Invalid solar position method')

    return ephem_df
Example #21
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
Example #22
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.get_extra_radiation(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, **kwargs)
        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
Example #23
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
Example #24
0
def get_solarposition(time, latitude, longitude,
                      altitude=None, pressure=None,
                      method='nrel_numpy',
                      temperature=12, **kwargs):
    """
    A convenience wrapper for the solar position calculators.

    Parameters
    ----------
    time : pandas.DatetimeIndex
    latitude : float
    longitude : float
    altitude : None or float
        If None, computed from pressure. Assumed to be 0 m
        if pressure is also None.
    pressure : None or float
        If None, computed from altitude. Assumed to be 101325 Pa
        if altitude is also None.
    method : string
        'pyephem' uses the PyEphem package: :func:`pyephem`

        'nrel_c' uses the NREL SPA C code [3]: :func:`spa_c`

        'nrel_numpy' uses an implementation of the NREL SPA algorithm
        described in [1] (default): :func:`spa_python`

        'nrel_numba' uses an implementation of the NREL SPA algorithm
        described in [1], but also compiles the code first: :func:`spa_python`

        'ephemeris' uses the pvlib ephemeris code: :func:`ephemeris`
    temperature : float
        Degrees C.

    Other keywords are passed to the underlying solar position function.

    References
    ----------
    [1] I. Reda and A. Andreas, Solar position algorithm for solar radiation
    applications. Solar Energy, vol. 76, no. 5, pp. 577-589, 2004.

    [2] I. Reda and A. Andreas, Corrigendum to Solar position algorithm for
    solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007.

    [3] NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/
    """

    if altitude is None and pressure is None:
        altitude = 0.
        pressure = 101325.
    elif altitude is None:
        altitude = atmosphere.pres2alt(pressure)
    elif pressure is None:
        pressure = atmosphere.alt2pres(altitude)

    method = method.lower()
    if isinstance(time, dt.datetime):
        time = pd.DatetimeIndex([time, ])

    if method == 'nrel_c':
        ephem_df = spa_c(time, latitude, longitude, pressure, temperature,
                         **kwargs)
    elif method == 'nrel_numba':
        ephem_df = spa_python(time, latitude, longitude, altitude,
                              pressure, temperature,
                              how='numba', **kwargs)
    elif method == 'nrel_numpy':
        ephem_df = spa_python(time, latitude, longitude, altitude,
                              pressure, temperature,
                              how='numpy', **kwargs)
    elif method == 'pyephem':
        ephem_df = pyephem(time, latitude, longitude, pressure, temperature,
                           **kwargs)
    elif method == 'ephemeris':
        ephem_df = ephemeris(time, latitude, longitude, pressure, temperature,
                             **kwargs)
    else:
        raise ValueError('Invalid solar position method')

    return ephem_df
Example #25
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