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
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)
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)
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)
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)
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)
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
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
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
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
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)
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 = {
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)
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)
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,
def time_aoi(self): irradiance.aoi(self.tilt, self.azimuth, self.solar_position.apparent_zenith, self.solar_position.azimuth)
# 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()
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))