Beispiel #1
0
    def get_aoi(self, surface_tilt, surface_azimuth, solar_zenith,
                solar_azimuth):
        """Get the angle of incidence on the system.

        For a given set of solar zenith and azimuth angles, the
        surface tilt and azimuth parameters are typically determined
        by :py:method:`~SingleAxisTracker.singleaxis`. The
        :py:method:`~SingleAxisTracker.singleaxis` method also returns
        the angle of incidence, so this method is only needed
        if using a different tracking algorithm.

        Parameters
        ----------
        surface_tilt : numeric
            Panel tilt from horizontal.
        surface_azimuth : numeric
            Panel azimuth from north
        solar_zenith : float or Series.
            Solar zenith angle.
        solar_azimuth : float or Series.
            Solar azimuth angle.

        Returns
        -------
        aoi : Series
            The angle of incidence in degrees from normal.
        """

        aoi = irradiance.aoi(surface_tilt, surface_azimuth,
                             solar_zenith, solar_azimuth)
        return aoi
Beispiel #2
0
    def get_aoi(self, surface_tilt, surface_azimuth, solar_zenith,
                solar_azimuth):
        """Get the angle of incidence on the system.

        For a given set of solar zenith and azimuth angles, the
        surface tilt and azimuth parameters are typically determined
        by :py:method:`~SingleAxisTracker.singleaxis`. The
        :py:method:`~SingleAxisTracker.singleaxis` method also returns
        the angle of incidence, so this method is only needed
        if using a different tracking algorithm.

        Parameters
        ----------
        surface_tilt : numeric
            Panel tilt from horizontal.
        surface_azimuth : numeric
            Panel azimuth from north
        solar_zenith : float or Series.
            Solar zenith angle.
        solar_azimuth : float or Series.
            Solar azimuth angle.

        Returns
        -------
        aoi : Series
            The angle of incidence in degrees from normal.
        """

        aoi = irradiance.aoi(surface_tilt, surface_azimuth, solar_zenith,
                             solar_azimuth)
        return aoi
def _shaded_fraction(solar_zenith, solar_azimuth, surface_tilt,
                     surface_azimuth, gcr):
    """
    Calculate fraction (from the bottom) of row slant height that is shaded
    from direct irradiance by the row in front toward the sun.

    See [1], Eq. 14 and also [2], Eq. 32.

    .. math::
        F_x = \\max \\left( 0, \\min \\left(\\frac{\\text{GCR} \\cos \\theta
        + \\left( \\text{GCR} \\sin \\theta - \\tan \\beta_{c} \\right)
        \\tan Z - 1}
        {\\text{GCR} \\left( \\cos \\theta + \\sin \\theta \\tan Z \\right)},
        1 \\right) \\right)

    Parameters
    ----------
    solar_zenith : numeric
        Apparent (refraction-corrected) solar zenith. [degrees]
    solar_azimuth : numeric
        Solar azimuth. [degrees]
    surface_tilt : numeric
        Row tilt from horizontal, e.g. surface facing up = 0, surface facing
        horizon = 90. [degrees]
    surface_azimuth : numeric
        Azimuth angle of the row surface. North=0, East=90, South=180,
        West=270. [degrees]
    gcr : numeric
        Ground coverage ratio, which is the ratio of row slant length to row
        spacing (pitch). [unitless]

    Returns
    -------
    f_x : numeric
        Fraction of row slant height from the bottom that is shaded from
        direct irradiance.

    References
    ----------
    .. [1] Mikofski, M., Darawali, R., Hamer, M., Neubert, A., and Newmiller,
       J. "Bifacial Performance Modeling in Large Arrays". 2019 IEEE 46th
       Photovoltaic Specialists Conference (PVSC), 2019, pp. 1282-1287.
       :doi:`10.1109/PVSC40753.2019.8980572`.
    .. [2] Kevin Anderson and Mark Mikofski, "Slope-Aware Backtracking for
       Single-Axis Trackers", Technical Report NREL/TP-5K00-76626, July 2020.
       https://www.nrel.gov/docs/fy20osti/76626.pdf
    """
    tan_phi = utils._solar_projection_tangent(solar_zenith, solar_azimuth,
                                              surface_azimuth)
    # length of shadow behind a row as a fraction of pitch
    x = gcr * (sind(surface_tilt) * tan_phi + cosd(surface_tilt))
    f_x = 1 - 1. / x
    # set f_x to be 1 when sun is behind the array
    ao = aoi(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth)
    f_x = np.where(ao < 90, f_x, 1.)
    # when x < 1, the shadow is not long enough to fall on the row surface
    f_x = np.where(x > 1., f_x, 0.)
    return f_x
Beispiel #4
0
def test_aoi_and_aoi_projection(surface_tilt, surface_azimuth, solar_zenith,
                                solar_azimuth, aoi_expected,
                                aoi_proj_expected):
    aoi = irradiance.aoi(surface_tilt, surface_azimuth, solar_zenith,
                         solar_azimuth)
    assert_allclose(aoi, aoi_expected, atol=1e-6)

    aoi_projection = irradiance.aoi_projection(surface_tilt, surface_azimuth,
                                               solar_zenith, solar_azimuth)
    assert_allclose(aoi_projection, aoi_proj_expected, atol=1e-6)
Beispiel #5
0
def test_aoi_and_aoi_projection(surface_tilt, surface_azimuth, solar_zenith,
                                solar_azimuth, aoi_expected,
                                aoi_proj_expected):
    aoi = irradiance.aoi(surface_tilt, surface_azimuth, solar_zenith,
                         solar_azimuth)
    assert_allclose(aoi, aoi_expected, atol=1e-6)

    aoi_projection = irradiance.aoi_projection(
        surface_tilt, surface_azimuth, solar_zenith, solar_azimuth)
    assert_allclose(aoi_projection, aoi_proj_expected, atol=1e-6)
Beispiel #6
0
def test_globalinplane():
    aoi = irradiance.aoi(40, 180, ephem_data['apparent_zenith'],
                         ephem_data['azimuth'])
    airmass = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
    gr_sand = irradiance.grounddiffuse(40, ghi, surface_type='sand')
    diff_perez = irradiance.perez(
        40, 180, irrad_data['dhi'], irrad_data['dni'], dni_et,
        ephem_data['apparent_zenith'], ephem_data['azimuth'], airmass)
    irradiance.globalinplane(
        aoi=aoi, dni=irrad_data['dni'], poa_sky_diffuse=diff_perez,
        poa_ground_diffuse=gr_sand)
def test_globalinplane():
    aoi = irradiance.aoi(40, 180, ephem_data['apparent_zenith'],
                         ephem_data['apparent_azimuth'])
    airmass = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
    gr_sand = irradiance.grounddiffuse(40, ghi, surface_type='sand')
    diff_perez = irradiance.perez(
        40, 180, irrad_data['dhi'], irrad_data['dni'], dni_et,
        ephem_data['apparent_zenith'], ephem_data['apparent_azimuth'], airmass)
    irradiance.globalinplane(
        aoi=aoi, dni=irrad_data['dni'], poa_sky_diffuse=diff_perez,
        poa_ground_diffuse=gr_sand)
def test_globalinplane():
    AOI = irradiance.aoi(40, 180, ephem_data['apparent_zenith'],
                         ephem_data['apparent_azimuth'])
    AM = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
    gr_sand = irradiance.grounddiffuse(40, ghi, surface_type='sand')
    diff_perez = irradiance.perez(
        40, 180, irrad_data['DHI'], irrad_data['DNI'], dni_et,
        ephem_data['apparent_zenith'], ephem_data['apparent_azimuth'], AM)
    irradiance.globalinplane(
        AOI=AOI, DNI=irrad_data['DNI'], In_Plane_SkyDiffuse=diff_perez,
        GR=gr_sand)
Beispiel #9
0
 def setup(self):
     self.times = pd.date_range(start='20180601', freq='1min',
                                periods=14400)
     self.days = pd.date_range(start='20180601', freq='d', periods=30)
     self.location = location.Location(40, -80)
     self.solar_position = self.location.get_solarposition(self.times)
     self.clearsky_irradiance = self.location.get_clearsky(self.times)
     self.tilt = 20
     self.azimuth = 180
     self.aoi = irradiance.aoi(self.tilt, self.azimuth,
                               self.solar_position.apparent_zenith,
                               self.solar_position.azimuth)
Beispiel #10
0
def test_globalinplane():
    AOI = irradiance.aoi(40, 180, ephem_data['apparent_zenith'],
                         ephem_data['apparent_azimuth'])
    AM = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
    gr_sand = irradiance.grounddiffuse(40, ghi, surface_type='sand')
    diff_perez = irradiance.perez(40, 180, irrad_data['DHI'],
                                  irrad_data['DNI'], dni_et,
                                  ephem_data['apparent_zenith'],
                                  ephem_data['apparent_azimuth'], AM)
    irradiance.globalinplane(AOI=AOI,
                             DNI=irrad_data['DNI'],
                             In_Plane_SkyDiffuse=diff_perez,
                             GR=gr_sand)
Beispiel #11
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
Beispiel #12
0
    def tilt_irr(self,
                 surface_tilt=None,
                 surface_azimuth=180,
                 include_solar_pos=False) -> pd.DataFrame:
        """
        Calculate the irradiances(DNI) and angle of incidence (aoi) on a tilted surface

        :param surface_tilt: The surface tilt angle (in degree).
        :param surface_azimuth: The azimuth angle of the surface. Default is 180 degrees.
        :param include_solar_pos: whether to include solar position in the output dataframe.
        :return: a dataframe with calculated solar incidence.
        """

        if surface_tilt is None:
            surface_tilt = self.latitude

        ngo = Location(latitude=self.latitude,
                       longitude=self.longitude,
                       altitude=0,
                       tz='Japan')
        solar_pos = ngo.get_solarposition(
            pd.DatetimeIndex(self.hour_df['avg_time']))

        dni_arr = self.get_DNI()

        irrad_df = total_irrad(surface_tilt=surface_tilt,
                               surface_azimuth=surface_azimuth,
                               apparent_zenith=solar_pos['apparent_zenith'],
                               azimuth=solar_pos['azimuth'],
                               dni=dni_arr,
                               ghi=self.hour_df['GHI'],
                               dhi=self.hour_df['dHI'])

        irrad_df['aoi'] = aoi(surface_tilt, surface_azimuth,
                              solar_pos['zenith'], solar_pos['azimuth'])

        irrad_df['DNI'] = dni_arr

        n_df = pd.concat([self.hour_df, irrad_df], axis=1)

        if include_solar_pos:
            n_df = pd.concat([n_df, solar_pos], axis=1)

        return n_df
Beispiel #13
0
    def get_aoi(self, solar_zenith, solar_azimuth):
        """
        Get the angle of incidence on the Static CPV System.

        Parameters
        ----------
        solar_zenith : float or Series.
            Solar zenith angle.
            
        solar_azimuth : float or Series.
            Solar azimuth angle.

        Returns
        -------
        aoi : Series
            The angle of incidence
        """

        aoi = irradiance.aoi(self.surface_tilt, self.surface_azimuth,
                             solar_zenith, solar_azimuth)
        return aoi
Beispiel #14
0
def _get_poa(data, solar_position):
    """
    Return the radiation adjusted to 
    Plane of Array (SkyDiffuse, GroundDiffuse, Total).
    """

    extra_radiation = get_extra_radiation(data.data.index)

    # Best orientation for pv system
    surface_tilt = _surface_tilt(data.latitude)
    surface_azimuth = _surface_azimuth(data.latitude)

    # Sky Diffuse radiation (From PvLib)
    poa_diffuse = haydavies(surface_tilt=surface_tilt,
                            surface_azimuth=surface_azimuth,
                            dhi=data.data['Gd(h)'],
                            dni=data.data['Gb(n)'],
                            dni_extra=extra_radiation,
                            solar_zenith=solar_position.apparent_zenith,
                            solar_azimuth=solar_position.azimuth)

    # Ground Diffuse radiation (From PvLib)
    poa_ground = get_ground_diffuse(data.latitude,
                                    data.data['G(h)'],
                                    surface_type='urban')

    # Angle of incidence at best orientation
    _aoi = aoi(surface_tilt=surface_tilt,
               surface_azimuth=surface_azimuth,
               solar_zenith=solar_position.apparent_zenith,
               solar_azimuth=solar_position.apparent_zenith)

    # Total radiation at plane of the pv array.
    poa_total = poa_components(aoi=_aoi,
                               dni=data.data['Gb(n)'],
                               poa_sky_diffuse=poa_diffuse,
                               poa_ground_diffuse=poa_ground)

    return poa_total, _aoi
Beispiel #15
0
def test_poa_components(irrad_data, ephem_data, dni_et, relative_airmass):
    aoi = irradiance.aoi(40, 180, ephem_data['apparent_zenith'],
                         ephem_data['azimuth'])
    gr_sand = irradiance.get_ground_diffuse(40,
                                            irrad_data['ghi'],
                                            surface_type='sand')
    diff_perez = irradiance.perez(40, 180, irrad_data['dhi'],
                                  irrad_data['dni'], dni_et,
                                  ephem_data['apparent_zenith'],
                                  ephem_data['azimuth'], relative_airmass)
    out = irradiance.poa_components(aoi, irrad_data['dni'], diff_perez,
                                    gr_sand)
    expected = pd.DataFrame(np.array(
        [[0., -0., 0., 0., 0.],
         [35.19456561, 0., 35.19456561, 31.4635077, 3.73105791],
         [956.18253696, 798.31939281, 157.86314414, 109.08433162, 48.77881252],
         [90.99624896, 33.50143401, 57.49481495, 45.45978964, 12.03502531]]),
                            columns=[
                                'poa_global', 'poa_direct', 'poa_diffuse',
                                'poa_sky_diffuse', 'poa_ground_diffuse'
                            ],
                            index=irrad_data.index)
    assert_frame_equal(out, expected)
Beispiel #16
0
def test_poa_components(irrad_data, ephem_data, dni_et, relative_airmass):
    aoi = irradiance.aoi(40, 180, ephem_data['apparent_zenith'],
                         ephem_data['azimuth'])
    gr_sand = irradiance.get_ground_diffuse(40, irrad_data['ghi'],
                                            surface_type='sand')
    diff_perez = irradiance.perez(
        40, 180, irrad_data['dhi'], irrad_data['dni'], dni_et,
        ephem_data['apparent_zenith'], ephem_data['azimuth'], relative_airmass)
    out = irradiance.poa_components(
        aoi, irrad_data['dni'], diff_perez, gr_sand)
    expected = pd.DataFrame(np.array(
        [[  0.        ,  -0.        ,   0.        ,   0.        ,
            0.        ],
         [ 35.19456561,   0.        ,  35.19456561,  31.4635077 ,
            3.73105791],
         [956.18253696, 798.31939281, 157.86314414, 109.08433162,
           48.77881252],
         [ 90.99624896,  33.50143401,  57.49481495,  45.45978964,
           12.03502531]]),
        columns=['poa_global', 'poa_direct', 'poa_diffuse', 'poa_sky_diffuse',
                 'poa_ground_diffuse'],
        index=irrad_data.index)
    assert_frame_equal(out, expected)
from nose.tools import assert_equals, assert_almost_equals
from pandas.util.testing import assert_series_equal, assert_frame_equal

from pvlib import tmy
from pvlib import pvsystem
from pvlib import clearsky
from pvlib import irradiance
from pvlib import atmosphere
from pvlib import solarposition
from pvlib.location import Location

tus = Location(32.2, -111, "US/Arizona", 700, "Tucson")
times = pd.date_range(start=datetime.datetime(2014, 1, 1), end=datetime.datetime(2014, 1, 2), freq="1Min")
ephem_data = solarposition.get_solarposition(times, tus, method="pyephem")
irrad_data = clearsky.ineichen(times, tus, linke_turbidity=3, solarposition_method="pyephem")
aoi = irradiance.aoi(0, 0, ephem_data["apparent_zenith"], ephem_data["apparent_azimuth"])
am = atmosphere.relativeairmass(ephem_data.apparent_zenith)

meta = {"latitude": 37.8, "longitude": -122.3, "altitude": 10, "Name": "Oakland", "State": "CA", "TZ": -8}

pvlib_abspath = os.path.dirname(os.path.abspath(inspect.getfile(tmy)))

tmy3_testfile = os.path.join(pvlib_abspath, "data", "703165TY.csv")
tmy2_testfile = os.path.join(pvlib_abspath, "data", "12839.tm2")

tmy3_data, tmy3_metadata = tmy.readtmy3(tmy3_testfile)
tmy2_data, tmy2_metadata = tmy.readtmy2(tmy2_testfile)


def test_systemdef_tmy3():
    expected = {
Beispiel #18
0
from pvlib import tmy
from pvlib import pvsystem
from pvlib import clearsky
from pvlib import irradiance
from pvlib import atmosphere
from pvlib import solarposition
from pvlib.location import Location

tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson')
times = pd.date_range(start=datetime.datetime(2014,1,1),
                      end=datetime.datetime(2014,1,2), freq='1Min')
ephem_data = solarposition.get_solarposition(times, tus, method='pyephem')
irrad_data = clearsky.ineichen(times, tus, linke_turbidity=3,
                               solarposition_method='pyephem')
aoi = irradiance.aoi(0, 0, ephem_data['apparent_zenith'],
                     ephem_data['apparent_azimuth'])
am = atmosphere.relativeairmass(ephem_data.apparent_zenith)

meta = {'latitude': 37.8,
        'longitude': -122.3,
        'altitude': 10,
        'Name': 'Oakland',
        'State': 'CA',
        'TZ': -8}

pvlib_abspath = os.path.dirname(os.path.abspath(inspect.getfile(tmy)))

tmy3_testfile = os.path.join(pvlib_abspath, 'data', '703165TY.csv')
tmy2_testfile = os.path.join(pvlib_abspath, 'data', '12839.tm2')

tmy3_data, tmy3_metadata = tmy.readtmy3(tmy3_testfile)
def calculate_overcast_spectrl2():
    """
    This example will loop over a range of cloud covers and latitudes
    (at longitude=0) for a specific date and calculate the spectral
    irradiance with and without accounting for clouds. Clouds are accounted for
    by applying the cloud opacity factor defined in [1]. Several steps
    are required:
    1. Calculate the atmospheric and solar conditions for the
    location and time
    2. Calculate the spectral irradiance using `pvlib.spectrum.spectrl2`
    for clear sky conditions
    3. Calculate the dni, dhi, and ghi for cloudy conditions using
    `pvlib.irradiance.campbell_norman`
    4. Determine total in-plane irradiance and its beam,
    sky diffuse and ground
    reflected components for cloudy conditions -
    `pvlib.irradiance.get_total_irradiance`
    5. Calculate the dni, dhi, and ghi for clear sky conditions
    using `pvlib.irradiance.campbell_norman`
    6. Determine total in-plane irradiance and its beam,
    sky diffuse and ground
    reflected components for clear sky conditions -
    `pvlib.irradiance.get_total_irradiance`
    7. Calculate the cloud opacity factor [1] and scale the
    spectral results from step 4 - func cloud_opacity_factor
    8. Plot the results  - func plot_spectral_irradiance
    """
    month = 2
    hour_of_day = 12
    altitude = 0.0
    longitude = 0.0
    latitudes = [10, 40]

    # cloud cover in fraction units
    cloud_covers = [0.2, 0.5]
    water_vapor_content = 0.5
    tau500 = 0.1
    ground_albedo = 0.06
    ozone = 0.3
    surface_tilt = 0.0

    ctime, pv_system = setup_pv_system(month, hour_of_day)

    for cloud_cover in cloud_covers:
        for latitude in latitudes:
            sol = get_solarposition(ctime, latitude, longitude)
            az = sol['apparent_zenith'].to_numpy()
            airmass_relative = get_relative_airmass(az,
                                                    model='kastenyoung1989')
            pressure = pvlib.atmosphere.alt2pres(altitude)
            az = sol['apparent_zenith'].to_numpy()
            azimuth = sol['azimuth'].to_numpy()
            surface_azimuth = pv_system['surface_azimuth']

            transmittance = (1.0 - cloud_cover) * 0.75
            calc_aoi = aoi(surface_tilt, surface_azimuth, az, azimuth)

            # day of year is an int64index array so access first item
            day_of_year = ctime.dayofyear[0]

            spectra = pvlib.spectrum.spectrl2(
                apparent_zenith=az,
                aoi=calc_aoi,
                surface_tilt=surface_tilt,
                ground_albedo=ground_albedo,
                surface_pressure=pressure,
                relative_airmass=airmass_relative,
                precipitable_water=water_vapor_content,
                ozone=ozone,
                aerosol_turbidity_500nm=tau500,
                dayofyear=day_of_year)

            irrads_clouds = campbell_norman(sol['zenith'].to_numpy(),
                                            transmittance)

            # Convert the irradiance to a plane with tilt zero
            # horizontal to the earth. This is done applying
            #  tilt=0 to POA calculations using the output from
            # `campbell_norman`. The POA calculations include
            # calculating sky and ground diffuse light where
            # specific models can be selected (we use default).
            poa_irr_clouds = get_total_irradiance(
                surface_tilt=surface_tilt,
                surface_azimuth=pv_system['surface_azimuth'],
                dni=irrads_clouds['dni'],
                ghi=irrads_clouds['ghi'],
                dhi=irrads_clouds['dhi'],
                solar_zenith=sol['apparent_zenith'],
                solar_azimuth=sol['azimuth'])

            show_info(latitude, poa_irr_clouds)
            zen = sol['zenith'].to_numpy()
            irr_clearsky = campbell_norman(zen, transmittance=0.75)

            poa_irr_clearsky = get_total_irradiance(
                surface_tilt=surface_tilt,
                surface_azimuth=pv_system['surface_azimuth'],
                dni=irr_clearsky['dni'],
                ghi=irr_clearsky['ghi'],
                dhi=irr_clearsky['dhi'],
                solar_zenith=sol['apparent_zenith'],
                solar_azimuth=sol['azimuth'])

            show_info(latitude, poa_irr_clearsky)
            poa_dr = poa_irr_clouds['poa_direct'].values
            poa_diff = poa_irr_clouds['poa_diffuse'].values
            poa_global = poa_irr_clouds['poa_global'].values
            f_dir, f_diff = cloud_opacity_factor(poa_dr, poa_diff, poa_global,
                                                 spectra)

            plot_spectral_irr(spectra,
                              f_dir,
                              f_diff,
                              lat=latitude,
                              doy=day_of_year,
                              year=ctime.year[0],
                              clouds=cloud_cover)
Beispiel #20
0
    def __init__(self, panel=None, forecast_length=7, forecast_model=None):
        self.forecast_length = forecast_length
        if panel == None:
            self.panel = Panel()
        else:
            self.panel = panel

        if forecast_model == None:
            self.fm = GFS()
        else:
            self.fm = forecast_model

        self.start = pd.Timestamp(datetime.date.today(),
                                  tz=self.panel.tz)  # today's date
        self.end = self.start + pd.Timedelta(
            days=forecast_length)  # days from today

        print(
            "getting processed data with lat: %s, lng: %s, start:%s, end:%s" %
            (self.panel.latitude, self.panel.longitude, self.start, self.end))
        # get forecast data
        forecast_data = self.fm.get_processed_data(self.panel.latitude,
                                                   self.panel.longitude,
                                                   self.start, self.end)
        ghi = forecast_data['ghi']

        # get solar position
        time = forecast_data.index
        a_point = self.fm.location
        solpos = a_point.get_solarposition(time)

        # get PV(photovoltaic device) modules
        sandia_modules = pvsystem.retrieve_sam('SandiaMod')
        sandia_module = sandia_modules.Canadian_Solar_CS5P_220M___2009_

        dni_extra = irradiance.get_extra_radiation(
            self.fm.time)  # extra terrestrial radiation
        airmass = atmosphere.get_relative_airmass(solpos['apparent_zenith'])
        # POA: Plane Of Array: an image sensing device consisting of an array
        # (typically rectangular) of light-sensing pixels at the focal plane of a lens.
        # https://en.wikipedia.org/wiki/Staring_array

        # Diffuse sky radiation is solar radiation reaching the Earth's surface after
        # having been scattered from the direct solar beam by molecules or particulates
        # in the atmosphere.
        # https://en.wikipedia.org/wiki/Diffuse_sky_radiation
        poa_sky_diffuse = irradiance.haydavies(self.panel.surface_tilt,
                                               self.panel.surface_azimuth,
                                               forecast_data['dhi'],
                                               forecast_data['dni'], dni_extra,
                                               solpos['apparent_zenith'],
                                               solpos['azimuth'])

        # Diffuse reflection is the reflection of light or other waves or particles
        # from a surface such that a ray incident on the surface is scattered at many
        # angles rather than at just one angle as in the case of specular reflection.
        poa_ground_diffuse = irradiance.get_ground_diffuse(
            self.panel.surface_tilt, ghi, albedo=self.panel.albedo)

        # AOI: Angle Of Incidence
        aoi = irradiance.aoi(self.panel.surface_tilt,
                             self.panel.surface_azimuth,
                             solpos['apparent_zenith'], solpos['azimuth'])

        #  irradiance is the radiant flux (power) received by a surface per unit area
        # https://en.wikipedia.org/wiki/Irradiance
        poa_irrad = irradiance.poa_components(aoi, forecast_data['dni'],
                                              poa_sky_diffuse,
                                              poa_ground_diffuse)

        temperature = forecast_data['temp_air']
        wnd_spd = forecast_data['wind_speed']

        # pvtemps: pv temperature
        pvtemps = pvsystem.sapm_celltemp(poa_irrad['poa_global'], wnd_spd,
                                         temperature)

        # irradiance actually used by PV
        effective_irradiance = pvsystem.sapm_effective_irradiance(
            poa_irrad.poa_direct, poa_irrad.poa_diffuse, airmass, aoi,
            sandia_module)

        # SAPM: Sandia PV Array Performance Model
        # https://pvpmc.sandia.gov/modeling-steps/2-dc-module-iv/point-value-models/sandia-pv-array-performance-model/

        self.sapm_out = pvsystem.sapm(effective_irradiance,
                                      pvtemps['temp_cell'], sandia_module)

        sapm_inverters = pvsystem.retrieve_sam('sandiainverter')
        sapm_inverter = sapm_inverters[
            'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_']
        self.ac_power = pvsystem.snlinverter(self.sapm_out.v_mp,
                                             self.sapm_out.p_mp, sapm_inverter)
Beispiel #21
0
def get_angle_of_incidence(surface_tilt, surface_azimuth, solpos):
    # Calculate AOI
    aoi = irradiance.aoi(surface_tilt, surface_azimuth, solpos['apparent_zenith'], solpos['azimuth'])
    return aoi
import matplotlib.pyplot as plt

# assumptions from the technical report:
lat = 37
lon = -100
tilt = 37
azimuth = 180
pressure = 101300  # sea level, roughly
water_vapor_content = 0.5  # cm
tau500 = 0.1
ozone = 0.31  # atm-cm
albedo = 0.2

times = pd.date_range('1984-03-20 06:17', freq='h', periods=6, tz='Etc/GMT+7')
solpos = solarposition.get_solarposition(times, lat, lon)
aoi = irradiance.aoi(tilt, azimuth, solpos.apparent_zenith, solpos.azimuth)

# The technical report uses the 'kasten1966' airmass model, but later
# versions of SPECTRL2 use 'kastenyoung1989'.  Here we use 'kasten1966'
# for consistency with the technical report.
relative_airmass = atmosphere.get_relative_airmass(solpos.apparent_zenith,
                                                   model='kasten1966')

# %%
# With all the necessary inputs in hand we can model spectral irradiance using
# :py:func:`pvlib.spectrum.spectrl2`.  Note that because we are calculating
# the spectra for more than one set of conditions, we will get back 2-D
# arrays (one dimension for wavelength, one for time).

spectra = spectrum.spectrl2(
    apparent_zenith=solpos.apparent_zenith,
Beispiel #23
0
 def time_aoi(self):
     irradiance.aoi(self.tilt, self.azimuth,
                    self.solar_position.apparent_zenith,
                    self.solar_position.azimuth)
Beispiel #24
0


# retrieve time and location parameters
time = forecast_data.index
a_point = fm.location

solpos = a_point.get_solarposition(time)
dni_extra = irradiance.get_extra_radiation(fm.time)
airmass = atmosphere.get_relative_airmass(solpos['apparent_zenith'])
poa_sky_diffuse = irradiance.haydavies(surface_tilt, surface_azimuth,
                           forecast_data['dhi'], forecast_data['dni'], dni_extra,
                           solpos['apparent_zenith'], solpos['azimuth'])

poa_ground_diffuse = irradiance.get_ground_diffuse(surface_tilt, ghi, albedo=albedo)
aoi = irradiance.aoi(surface_tilt, surface_azimuth, solpos['apparent_zenith'], solpos['azimuth'])

poa_irrad = irradiance.poa_components(aoi, forecast_data['dni'], poa_sky_diffuse,
                                        poa_ground_diffuse)

temperature = forecast_data['temp_air']
wnd_spd = forecast_data['wind_speed']
pvtemps = pvsystem.sapm_celltemp(poa_irrad['poa_global'], wnd_spd, temperature)

effective_irradiance = pvsystem.sapm_effective_irradiance(poa_irrad.poa_direct,
                            poa_irrad.poa_diffuse, airmass, aoi, sandia_module)
sapm_out = pvsystem.sapm(effective_irradiance, pvtemps['temp_cell'], sandia_module)
# print(sapm_out.head())
print(sapm_out['p_mp'])
plot = "pop"
# sapm_out[['p_mp']].plot()
Beispiel #25
0
def singleaxis(apparent_zenith, apparent_azimuth,
               axis_tilt=0, axis_azimuth=0, max_angle=90,
               backtrack=True, gcr=2.0/7.0, cross_axis_tilt=0):
    """
    Determine the rotation angle of a single-axis tracker when given particular
    solar zenith and azimuth angles.

    See [1]_ for details about the equations. Backtracking may be specified,
    and if so, a ground coverage ratio is required.

    Rotation angle is determined in a right-handed coordinate system. The
    tracker `axis_azimuth` defines the positive y-axis, the positive x-axis is
    90 degrees clockwise from the y-axis and parallel to the Earth's surface,
    and the positive z-axis is normal to both x & y-axes and oriented skyward.
    Rotation angle `tracker_theta` is a right-handed rotation around the y-axis
    in the x, y, z coordinate system and indicates tracker position relative to
    horizontal. For example, if tracker `axis_azimuth` is 180 (oriented south)
    and `axis_tilt` is zero, then a `tracker_theta` of zero is horizontal, a
    `tracker_theta` of 30 degrees is a rotation of 30 degrees towards the west,
    and a `tracker_theta` of -90 degrees is a rotation to the vertical plane
    facing east.

    Parameters
    ----------
    apparent_zenith : float, 1d array, or Series
        Solar apparent zenith angles in decimal degrees.

    apparent_azimuth : float, 1d array, or Series
        Solar apparent azimuth angles in decimal degrees.

    axis_tilt : float, default 0
        The tilt of the axis of rotation (i.e, the y-axis defined by
        axis_azimuth) with respect to horizontal, in decimal degrees.

    axis_azimuth : float, default 0
        A value denoting the compass direction along which the axis of
        rotation lies. Measured in decimal degrees east of north.

    max_angle : float, default 90
        A value denoting the maximum rotation angle, in decimal degrees,
        of the one-axis tracker from its horizontal position (horizontal
        if axis_tilt = 0). A max_angle of 90 degrees allows the tracker
        to rotate to a vertical position to point the panel towards a
        horizon. max_angle of 180 degrees allows for full rotation.

    backtrack : bool, default True
        Controls whether the tracker has the capability to "backtrack"
        to avoid row-to-row shading. False denotes no backtrack
        capability. True denotes backtrack capability.

    gcr : float, default 2.0/7.0
        A value denoting the ground coverage ratio of a tracker system
        which utilizes backtracking; i.e. the ratio between the PV array
        surface area to total ground area. A tracker system with modules
        2 meters wide, centered on the tracking axis, with 6 meters
        between the tracking axes has a gcr of 2/6=0.333. If gcr is not
        provided, a gcr of 2/7 is default. gcr must be <=1.

    cross_axis_tilt : float, default 0.0
        The angle, relative to horizontal, of the line formed by the
        intersection between the slope containing the tracker axes and a plane
        perpendicular to the tracker axes. Cross-axis tilt should be specified
        using a right-handed convention. For example, trackers with axis
        azimuth of 180 degrees (heading south) will have a negative cross-axis
        tilt if the tracker axes plane slopes down to the east and positive
        cross-axis tilt if the tracker axes plane slopes up to the east. Use
        :func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate
        `cross_axis_tilt`. [degrees]

    Returns
    -------
    dict or DataFrame with the following columns:
        * `tracker_theta`: The rotation angle of the tracker is a right-handed
          rotation defined by `axis_azimuth`.
          tracker_theta = 0 is horizontal. [degrees]
        * `aoi`: The angle-of-incidence of direct irradiance onto the
          rotated panel surface. [degrees]
        * `surface_tilt`: The angle between the panel surface and the earth
          surface, accounting for panel rotation. [degrees]
        * `surface_azimuth`: The azimuth of the rotated panel, determined by
          projecting the vector normal to the panel's surface to the earth's
          surface. [degrees]

    See also
    --------
    pvlib.tracking.calc_axis_tilt
    pvlib.tracking.calc_cross_axis_tilt
    pvlib.tracking.calc_surface_orientation

    References
    ----------
    .. [1] Kevin Anderson and Mark Mikofski, "Slope-Aware Backtracking for
       Single-Axis Trackers", Technical Report NREL/TP-5K00-76626, July 2020.
       https://www.nrel.gov/docs/fy20osti/76626.pdf
    """

    # MATLAB to Python conversion by
    # Will Holmgren (@wholmgren), U. Arizona. March, 2015.

    if isinstance(apparent_zenith, pd.Series):
        index = apparent_zenith.index
    else:
        index = None

    # convert scalars to arrays
    apparent_azimuth = np.atleast_1d(apparent_azimuth)
    apparent_zenith = np.atleast_1d(apparent_zenith)

    if apparent_azimuth.ndim > 1 or apparent_zenith.ndim > 1:
        raise ValueError('Input dimensions must not exceed 1')

    # Calculate sun position x, y, z using coordinate system as in [1], Eq 1.

    # NOTE: solar elevation = 90 - solar zenith, then use trig identities:
    # sin(90-x) = cos(x) & cos(90-x) = sin(x)
    sin_zenith = sind(apparent_zenith)
    x = sin_zenith * sind(apparent_azimuth)
    y = sin_zenith * cosd(apparent_azimuth)
    z = cosd(apparent_zenith)

    # Assume the tracker reference frame is right-handed. Positive y-axis is
    # oriented along tracking axis; from north, the y-axis is rotated clockwise
    # by the axis azimuth and tilted from horizontal by the axis tilt. The
    # positive x-axis is 90 deg clockwise from the y-axis and parallel to
    # horizontal (e.g., if the y-axis is south, the x-axis is west); the
    # positive z-axis is normal to the x and y axes, pointed upward.

    # Calculate sun position (xp, yp, zp) in tracker coordinate system using
    # [1] Eq 4.

    cos_axis_azimuth = cosd(axis_azimuth)
    sin_axis_azimuth = sind(axis_azimuth)
    cos_axis_tilt = cosd(axis_tilt)
    sin_axis_tilt = sind(axis_tilt)
    xp = x*cos_axis_azimuth - y*sin_axis_azimuth
    # not necessary to calculate y'
    # yp = (x*cos_axis_tilt*sin_axis_azimuth
    #       + y*cos_axis_tilt*cos_axis_azimuth
    #       - z*sin_axis_tilt)
    zp = (x*sin_axis_tilt*sin_axis_azimuth
          + y*sin_axis_tilt*cos_axis_azimuth
          + z*cos_axis_tilt)

    # The ideal tracking angle wid is the rotation to place the sun position
    # vector (xp, yp, zp) in the (y, z) plane, which is normal to the panel and
    # contains the axis of rotation.  wid = 0 indicates that the panel is
    # horizontal. Here, our convention is that a clockwise rotation is
    # positive, to view rotation angles in the same frame of reference as
    # azimuth. For example, for a system with tracking axis oriented south, a
    # rotation toward the east is negative, and a rotation to the west is
    # positive. This is a right-handed rotation around the tracker y-axis.

    # Calculate angle from x-y plane to projection of sun vector onto x-z plane
    # using [1] Eq. 5.

    wid = np.degrees(np.arctan2(xp, zp))

    # filter for sun above panel horizon
    zen_gt_90 = apparent_zenith > 90
    wid[zen_gt_90] = np.nan

    # Account for backtracking
    if backtrack:
        # distance between rows in terms of rack lengths relative to cross-axis
        # tilt
        axes_distance = 1/(gcr * cosd(cross_axis_tilt))

        # NOTE: account for rare angles below array, see GH 824
        temp = np.abs(axes_distance * cosd(wid - cross_axis_tilt))

        # backtrack angle using [1], Eq. 14
        with np.errstate(invalid='ignore'):
            wc = np.degrees(-np.sign(wid)*np.arccos(temp))

        # NOTE: in the middle of the day, arccos(temp) is out of range because
        # there's no row-to-row shade to avoid, & backtracking is unnecessary
        # [1], Eqs. 15-16
        with np.errstate(invalid='ignore'):
            tracker_theta = wid + np.where(temp < 1, wc, 0)
    else:
        tracker_theta = wid

    # NOTE: max_angle defined relative to zero-point rotation, not the
    # system-plane normal
    tracker_theta = np.clip(tracker_theta, -max_angle, max_angle)

    # Calculate auxiliary angles
    surface = calc_surface_orientation(tracker_theta, axis_tilt, axis_azimuth)
    surface_tilt = surface['surface_tilt']
    surface_azimuth = surface['surface_azimuth']
    aoi = irradiance.aoi(surface_tilt, surface_azimuth,
                         apparent_zenith, apparent_azimuth)

    # Bundle DataFrame for return values and filter for sun below horizon.
    out = {'tracker_theta': tracker_theta, 'aoi': aoi,
           'surface_azimuth': surface_azimuth, 'surface_tilt': surface_tilt}
    if index is not None:
        out = pd.DataFrame(out, index=index)
        out[zen_gt_90] = np.nan
    else:
        out = {k: np.where(zen_gt_90, np.nan, v) for k, v in out.items()}

    return out
def simulate_power_by_station(station_index, surface_tilt, surface_azimuth,
                              pv_module, tcell_model_parameters, ghi, tamb,
                              wspd, albedo, days, lead_times, air_mass,
                              dni_extra, zenith, apparent_zenith, azimuth):
    """
    This is the worker function for simulating power at a specified location. This function should be used inside
    of `simulate_power` and direct usage is discouraged.

    :param station_index: A station index
    :param ghi: See `simulate_power`
    :param tamb: See `simulate_power`
    :param wspd: See `simulate_power`
    :param albedo: See `simulate_power`
    :param days: See `simulate_power`
    :param lead_times: See `simulate_power`
    :param air_mass: See `simulate_power`
    :param dni_extra: See `simulate_power`
    :param zenith: See `simulate_power`
    :param apparent_zenith: See `simulate_power`
    :param azimuth: See `simulate_power`
    :param surface_tilt: See `simulate_power`
    :param surface_azimuth: See `simulate_power`
    :param pv_module: A PV module name
    :param tcell_model_parameters: A cell module name
    :return: A list with power, cell temperature, and the effective irradiance
    """

    # Sanity check
    assert 0 <= station_index < ghi.shape[3], 'Invalid station index'

    # Determine the dimensions
    num_analogs = ghi.shape[0]
    num_lead_times = ghi.shape[1]
    num_days = ghi.shape[2]

    # Initialization
    p_mp = np.zeros((num_analogs, num_lead_times, num_days))
    tcell = np.zeros((num_analogs, num_lead_times, num_days))
    effective_irradiance = np.zeros((num_analogs, num_lead_times, num_days))
    pv_module = pvsystem.retrieve_sam("SandiaMod")[pv_module]
    tcell_model_parameters = temperature.TEMPERATURE_MODEL_PARAMETERS["sapm"][
        tcell_model_parameters]

    for day_index in range(num_days):
        for lead_time_index in range(num_lead_times):

            # Determine the current time
            current_posix = days[day_index] + lead_times[lead_time_index]
            current_time = pd.Timestamp(current_posix, tz="UTC", unit='s')

            for analog_index in range(num_analogs):
                ghi_ = ghi[analog_index, lead_time_index, day_index,
                           station_index]

                if ghi_ == 0:
                    continue

                albedo_ = albedo[analog_index, lead_time_index, day_index,
                                 station_index]
                wspd_ = wspd[analog_index, lead_time_index, day_index,
                             station_index]
                tamb_ = tamb[analog_index, lead_time_index, day_index,
                             station_index]
                air_mass_ = air_mass[lead_time_index, day_index, station_index]
                dni_extra_ = dni_extra[lead_time_index, day_index,
                                       station_index]
                zenith_ = zenith[lead_time_index, day_index, station_index]
                apparent_zenith_ = apparent_zenith[lead_time_index, day_index,
                                                   station_index]
                azimuth_ = azimuth[lead_time_index, day_index, station_index]

                ##########################################################################################
                #                                                                                        #
                #                     Core procedures of simulating power at one location                #
                #                                                                                        #
                ##########################################################################################

                # Decompose DNI from GHI
                dni_dict = irradiance.disc(ghi_, zenith_, current_time)

                # Calculate POA sky diffuse
                poa_sky_diffuse = irradiance.haydavies(
                    surface_tilt, surface_azimuth, ghi_, dni_dict["dni"],
                    dni_extra_, apparent_zenith_, azimuth_)

                # Calculate POA ground diffuse
                poa_ground_diffuse = irradiance.get_ground_diffuse(
                    surface_tilt, ghi_, albedo_)

                # Calculate angle of incidence
                aoi = irradiance.aoi(surface_tilt, surface_azimuth,
                                     apparent_zenith_, azimuth_)

                # Calculate POA total
                poa_irradiance = irradiance.poa_components(
                    aoi, dni_dict["dni"], poa_sky_diffuse, poa_ground_diffuse)

                # Calculate cell temperature
                tcell[analog_index, lead_time_index,
                      day_index] = pvsystem.temperature.sapm_cell(
                          poa_irradiance['poa_global'], tamb_, wspd_,
                          tcell_model_parameters['a'],
                          tcell_model_parameters['b'],
                          tcell_model_parameters["deltaT"])

                # Calculate effective irradiance
                effective_irradiance[
                    analog_index, lead_time_index,
                    day_index] = pvsystem.sapm_effective_irradiance(
                        poa_irradiance['poa_direct'],
                        poa_irradiance['poa_diffuse'], air_mass_, aoi,
                        pv_module)

                # Calculate power
                sapm_out = pvsystem.sapm(
                    effective_irradiance[analog_index, lead_time_index,
                                         day_index],
                    tcell[analog_index, lead_time_index, day_index], pv_module)

                # Save output to numpy
                p_mp[analog_index, lead_time_index,
                     day_index] = sapm_out["p_mp"]

    return [p_mp, tcell, effective_irradiance]
def AOI(surf_tilt, surf_azm, solpos):
    aoi = irradiance.aoi(surf_tilt, surf_azm, solpos['apparent_zenith'],
                         solpos['azimuth'])
    return (aoi)
                                             latitude=latitude,
                                             longitude=longitude,
                                             method='nrel_numpy')

# Compute the diffuse irradiance on the panel, reflected from the ground:
S_d_reflect = irradiance.grounddiffuse(surface_tilt,
                                       I_hor,
                                       albedo=0.25,
                                       surface_type=None)

# Compute the diffuse irradiance on the panel, from the sky:
S_d_sky = irradiance.klucher(surface_tilt, surface_azimuth, I_d_hor, I_hor,
                             ephem_data['zenith'], ephem_data['azimuth'])

# Compute the angles between the panel and the sun:
aoi = irradiance.aoi(surface_tilt, surface_azimuth, ephem_data['zenith'],
                     ephem_data['azimuth'])

# Compute the global irradiance on the panel:
S = irradiance.globalinplane(aoi, DNI, S_d_sky, S_d_reflect)

# Second case: with tracking (axis is supposed to be north-south):

S_track = tracking.singleaxis(ephem_data['apparent_zenith'],
                              ephem_data['azimuth'],
                              axis_tilt=0,
                              axis_azimuth=0,
                              max_angle=360,
                              backtrack=True)

S['Direct with tracking'] = DNI * np.cos(np.radians(S_track.aoi))