def test_get_extra_radiation_nrel_numba(times): with warnings.catch_warnings(): # don't warn on method reload or num threads warnings.simplefilter("ignore") result = irradiance.get_extra_radiation(times, method='nrel', how='numba', numthreads=4) # and reset to no-numba state irradiance.get_extra_radiation(times, method='nrel') assert_allclose(result, [1322.332316, 1322.296282, 1322.261205, 1322.227091])
def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs): """ Estimates irradiance from cloud cover in the following steps: 1. Determine transmittance using a function of cloud cover e.g. :py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear` 2. Calculate GHI, DNI, DHI using the :py:func:`pvlib.irradiance.liujordan` model Parameters ---------- cloud_cover : Series Returns ------- irradiance : DataFrame Columns include ghi, dni, dhi """ # in principle, get_solarposition could use the forecast # pressure, temp, etc., but the cloud cover forecast is not # accurate enough to justify using these minor corrections solar_position = self.location.get_solarposition(cloud_cover.index) dni_extra = get_extra_radiation(cloud_cover.index) airmass = self.location.get_airmass(cloud_cover.index) transmittance = self.cloud_cover_to_transmittance_linear( cloud_cover, **kwargs) irrads = liujordan(solar_position['apparent_zenith'], transmittance, airmass['airmass_absolute'], dni_extra=dni_extra) irrads = irrads.fillna(0) return irrads
def test_get_extra_radiation_nrel_numba(times): result = irradiance.get_extra_radiation(times, method='nrel', how='numba', numthreads=8) assert_allclose(result, [1322.332316, 1322.296282, 1322.261205, 1322.227091])
def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs): """ Estimates irradiance from cloud cover in the following steps: 1. Determine transmittance using a function of cloud cover e.g. :py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear` 2. Calculate GHI, DNI, DHI using the :py:func:`pvlib.irradiance.liujordan` model Parameters ---------- cloud_cover : Series Returns ------- irradiance : DataFrame Columns include ghi, dni, dhi """ # in principle, get_solarposition could use the forecast # pressure, temp, etc., but the cloud cover forecast is not # accurate enough to justify using these minor corrections solar_position = self.location.get_solarposition(cloud_cover.index) dni_extra = get_extra_radiation(cloud_cover.index) airmass = self.location.get_airmass(cloud_cover.index) transmittance = self.cloud_cover_to_transmittance_linear(cloud_cover, **kwargs) irrads = liujordan(solar_position['apparent_zenith'], transmittance, airmass['airmass_absolute'], dni_extra=dni_extra) irrads = irrads.fillna(0) return irrads
def simulate_sun_positions_by_station(station_index, solar_position_method, days, lead_times, latitudes, longitudes): """ This is the worker function of calculating sun positions at a specified station index. This function should be called from the parallel version of this function, `simulate_sun_positions`. Direct use of this function is discouraged. Please use the parallel version of this fucntion, `simulate_sun_positions`. :param station_index: A station index for simulation :param days: See `simulate_sun_positions` :param lead_times: See `simulate_sun_positions` :param latitudes: See `simulate_sun_positions` :param longitudes: See `simulate_sun_positions` :param solar_position_method: See `simulate_sun_positions` :return: A list with DNI, air mass, zenith, apparent zenith, and azimuth. """ # Initialization num_lead_times, num_days = len(lead_times), len(days) dni_extra = np.zeros((num_lead_times, num_days)) air_mass = np.zeros((num_lead_times, num_days)) zenith = np.zeros((num_lead_times, num_days)) apparent_zenith = np.zeros((num_lead_times, num_days)) azimuth = np.zeros((num_lead_times, num_days)) # Determine the current location current_location = location.Location(latitude=latitudes[station_index], longitude=longitudes[station_index]) 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') # Calculate sun position solar_position = current_location.get_solarposition( current_time, method=solar_position_method, numthreads=1) # Calculate extraterrestrial DNI dni_extra[lead_time_index, day_index] = irradiance.get_extra_radiation(current_time) # Calculate air mass air_mass[lead_time_index, day_index] = atmosphere.get_relative_airmass( solar_position["apparent_zenith"]) # Store other keys zenith[lead_time_index, day_index] = solar_position["zenith"] apparent_zenith[lead_time_index, day_index] = solar_position["apparent_zenith"] azimuth[lead_time_index, day_index] = solar_position["azimuth"] return [dni_extra, air_mass, zenith, apparent_zenith, azimuth]
def _solpos_dni_extra(observation, values): solar_position = pvmodel.calculate_solar_position( observation.site.latitude, observation.site.longitude, observation.site.elevation, values.index) dni_extra = get_extra_radiation(values.index) timestamp_flag = _validate_timestamp(observation, values) night_flag = validator.check_irradiance_day_night(solar_position['zenith'], _return_mask=True) return solar_position, dni_extra, timestamp_flag, night_flag
def _get_extra_radiation_shim(datetime_or_doy, solar_constant=1366.1, method='spencer', epoch_year=2014, **kwargs): if method == 'spencer': if not isinstance(datetime_or_doy, (float, int)): dayofyear = datetime_or_doy.timetuple().tm_yday else: dayofyear = datetime_or_doy B = (2.*pi/365.)*(dayofyear - 1) RoverR0sqrd = (1.00011 + 0.034221*cos(B) + 0.00128*sin(B) + 0.000719*cos(2.0*B) + 7.7e-05*sin(2.0*B)) Ea = solar_constant * RoverR0sqrd return Ea from pvlib.irradiance import get_extra_radiation return get_extra_radiation(datetime_or_doy=datetime_or_doy, solar_constant=solar_constant, method=method, epoch_year=epoch_year, **kwargs)
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 get_from_pvgis(lat, lon, tz): """Return the pvgis data""" # PVGIS information info = get_pvgis_tmy(lat, lon) # Altitude alt = info[2]['location']['elevation'] # Get th principal information df = info[0] # Convert UTC to time zone df.index = df.index.tz_convert(tz) df.index.name = f'Time {tz}' # Get the solar position solpos = get_solarposition(time=df.index, latitude=lat, longitude=lon, altitude=alt, pressure=df.SP, temperature=df.T2m) # Create the schema data = df data = data.drop(df.columns, axis=1) data['HourOfDay'] = df.index.hour data['Zenith'] = solpos['zenith'] data['Temperature'] = df.T2m data['Humidity'] = df.RH data['WindSpeed'] = df.WS10m data['WindDirection'] = df.WD10m data['PrecipitableWater'] = gueymard94_pw(df.T2m, df.RH) data['Pressure'] = df.SP data['ExtraRadiation'] = get_extra_radiation(df.index) data['GHI'] = df['G(h)'] return data
def sun_extraradiation(dates=None, daydate=_day, solar_constant=1366.1, method='spencer', timezone=_timezone): """ Extraterrestrial radiation (W.m2) at the top of the earth atmosphere Args: dates: a pandas.DatetimeIndex specifying the dates at which output is required.If None, daydate is used and one position per hour is generated daydate: (str) yyyy-mm-dd (not used if dates is not None). solar_constant: (float) method: one method provided by pvlib timezone: a string identifying the timezone to be associated to dates if dates is not already localised. """ if dates is None: dates = pandas.date_range(daydate, periods=24, freq='H') if dates.tz is None: times = dates.tz_localize(timezone) else: times = dates return get_extra_radiation(times, solar_constant=solar_constant, method=method)
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 Extra parameters 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
def get_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:meth:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) return irradiance.get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def test_bird(): """Test Bird/Hulstrom Clearsky Model""" times = pd.date_range(start='1/1/2015 0:00', end='12/31/2015 23:00', freq='H') tz = -7 # test timezone gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz)) times = times.tz_localize(gmt_tz) # set timezone # match test data from BIRD_08_16_2012.xls latitude = 40. longitude = -105. press_mB = 840. o3_cm = 0.3 h2o_cm = 1.5 aod_500nm = 0.1 aod_380nm = 0.15 b_a = 0.85 alb = 0.2 eot = solarposition.equation_of_time_spencer71(times.dayofyear) hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15. declination = solarposition.declination_spencer71(times.dayofyear) zenith = solarposition.solar_zenith_analytical( np.deg2rad(latitude), np.deg2rad(hour_angle), declination ) zenith = np.rad2deg(zenith) airmass = atmosphere.get_relative_airmass(zenith, model='kasten1966') etr = irradiance.get_extra_radiation(times) # test Bird with time series data field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi') irrads = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100., etr, b_a, alb ) Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names) clearsky_path = os.path.dirname(os.path.abspath(__file__)) pvlib_path = os.path.dirname(clearsky_path) data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012.csv') testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata.index = times[1:48] assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48])) assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4) assert np.allclose(testdata['Hour Angle'], hour_angle[1:48]) assert np.allclose(testdata['Zenith Ang'], zenith[1:48]) dawn = zenith < 88. dusk = testdata['Zenith Ang'] < 88. am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0) assert np.allclose( testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3 ) direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3 ) direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3 ) global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3 ) diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3 ) # test keyword parameters irrads2 = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr ) Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names) clearsky_path = os.path.dirname(os.path.abspath(__file__)) pvlib_path = os.path.dirname(clearsky_path) data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012_patm.csv') testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata2.index = times[1:48] direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3 ) direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3 ) global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3 ) diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3 ) # test scalars just at noon # XXX: calculations start at 12am so noon is at index = 12 irrads3 = clearsky.bird( zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12] ) Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names) # XXX: testdata starts at 1am so noon is at index = 11 np.allclose( [Eb3, Ebh3, Gh3, Dh3], testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11], rtol=1e-3) return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)
def _toa_radiation(): toa = get_extra_radiation(elevation.index, SOLAR_CONSTANT) * np.sin( elevation.values * np.pi / 180) toa[toa < 0] = 0 return toa
def test_get_extra_radiation(input, expected, method): out = irradiance.get_extra_radiation(input) assert_allclose(out, expected, atol=1)
def test_get_extra_radiation_epoch_year(): out = irradiance.get_extra_radiation(doy, method='nrel', epoch_year=2012) assert_allclose(out, 1382.4926804890767, atol=0.1)
def test_get_extra_radiation(input, expected, method): out = irradiance.get_extra_radiation(input) assert_allclose(out, expected, atol=1)
def test_get_extra_radiation(testval, expected, method): out = irradiance.get_extra_radiation(testval, method=method) assert_allclose(out, expected, atol=10)
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 perez_diffuse_luminance(timestamps, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, dhi): """Function used to calculate the luminance and the view factor terms from the Perez diffuse light transposition model, as implemented in the ``pvlib-python`` library. This function was custom made to allow the calculation of the circumsolar component on the back surface as well. Otherwise, the ``pvlib`` implementation would ignore it. Parameters ---------- timestamps : array-like simulation timestamps surface_tilt : array-like Surface tilt angles in decimal degrees. surface_tilt must be >=0 and <=180. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90) surface_azimuth : array-like The azimuth of the rotated panel, determined by projecting the vector normal to the panel's surface to the earth's surface [degrees]. solar_zenith : array-like solar zenith angles solar_azimuth : array-like solar azimuth angles dni : array-like values for direct normal irradiance dhi : array-like values for diffuse horizontal irradiance Returns ------- df_inputs : `pandas.DataFrame` Dataframe with the following columns: ['solar_zenith', 'solar_azimuth', 'surface_tilt', 'surface_azimuth', 'dhi', 'dni', 'vf_horizon', 'vf_circumsolar', 'vf_isotropic', 'luminance_horizon', 'luminance_circuqmsolar', 'luminance_isotropic', 'poa_isotropic', 'poa_circumsolar', 'poa_horizon', 'poa_total_diffuse'] """ # Create a dataframe to help filtering on all arrays df_inputs = pd.DataFrame( { 'surface_tilt': surface_tilt, 'surface_azimuth': surface_azimuth, 'solar_zenith': solar_zenith, 'solar_azimuth': solar_azimuth, 'dni': dni, 'dhi': dhi }, index=pd.DatetimeIndex(timestamps)) dni_et = irradiance.get_extra_radiation(df_inputs.index.dayofyear) am = atmosphere.get_relative_airmass(df_inputs.solar_zenith) # Need to treat the case when the sun is hitting the back surface of pvrow aoi_proj = irradiance.aoi_projection(df_inputs.surface_tilt, df_inputs.surface_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) sun_hitting_back_surface = ((aoi_proj < 0) & (df_inputs.solar_zenith <= 90)) df_inputs_back_surface = df_inputs.loc[sun_hitting_back_surface].copy() # Reverse the surface normal to switch to back-surface circumsolar calc df_inputs_back_surface.loc[:, 'surface_azimuth'] = ( df_inputs_back_surface.loc[:, 'surface_azimuth'] - 180.) df_inputs_back_surface.loc[:, 'surface_azimuth'] = np.mod( df_inputs_back_surface.loc[:, 'surface_azimuth'], 360.) df_inputs_back_surface.loc[:, 'surface_tilt'] = ( 180. - df_inputs_back_surface.surface_tilt) if df_inputs_back_surface.shape[0] > 0: # Use recursion to calculate circumsolar luminance for back surface df_inputs_back_surface = perez_diffuse_luminance( *breakup_df_inputs(df_inputs_back_surface)) # Calculate Perez diffuse components components = irradiance.perez(df_inputs.surface_tilt, df_inputs.surface_azimuth, df_inputs.dhi, df_inputs.dni, dni_et, df_inputs.solar_zenith, df_inputs.solar_azimuth, am, return_components=True) # Calculate Perez view factors: a = irradiance.aoi_projection(df_inputs.surface_tilt, df_inputs.surface_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) a = np.maximum(a, 0) b = cosd(df_inputs.solar_zenith) b = np.maximum(b, cosd(85)) vf_perez = pd.DataFrame( { 'vf_horizon': sind(df_inputs.surface_tilt), 'vf_circumsolar': a / b, 'vf_isotropic': (1. + cosd(df_inputs.surface_tilt)) / 2. }, index=df_inputs.index) # Calculate diffuse luminance luminance = pd.DataFrame(np.array([ components['horizon'] / vf_perez['vf_horizon'], components['circumsolar'] / vf_perez['vf_circumsolar'], components['isotropic'] / vf_perez['vf_isotropic'] ]).T, index=df_inputs.index, columns=[ 'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic' ]) luminance.loc[components['sky_diffuse'] == 0, :] = 0. # Format components column names components = components.rename( columns={ 'isotropic': 'poa_isotropic', 'circumsolar': 'poa_circumsolar', 'horizon': 'poa_horizon' }) df_inputs = pd.concat([df_inputs, components, vf_perez, luminance], axis=1, join='outer') df_inputs = df_inputs.rename(columns={'sky_diffuse': 'poa_total_diffuse'}) # Adjust the circumsolar luminance when it hits the back surface if df_inputs_back_surface.shape[0] > 0: df_inputs.loc[sun_hitting_back_surface, 'luminance_circumsolar'] = ( df_inputs_back_surface.loc[:, 'luminance_circumsolar']) return df_inputs
def get_dni_extra(fm): # Calculate extra terrestrial radiation. This is needed for many plane of array diffuse irradiance models. dni_extra = irradiance.get_extra_radiation(fm.time) return dni_extra
def time_get_extra_radiation(self): irradiance.get_extra_radiation(self.days)
# Retrieve data forecast_data = fm.get_processed_data(latitude, longitude, start, end) ghi = forecast_data['ghi'] sandia_modules = pvsystem.retrieve_sam('SandiaMod') sandia_module = sandia_modules.Canadian_Solar_CS5P_220M___2009_ # 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)
def get_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:meth:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) return irradiance.get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
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
def test_get_extra_radiation_epoch_year(): out = irradiance.get_extra_radiation(doy, method='nrel', epoch_year=2012) assert_allclose(out, 1382.4926804890767, atol=0.1)
def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :py:func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a Fixed panel. Parameters ---------- solar_zenith : float or Series. Solar zenith angle. solar_azimuth : float or Series. Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : None, float or Series, default None Extraterrestrial direct normal irradiance airmass : None, float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) return irradiance.get_total_irradiance(self.surface_tilt, self.surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def test_get_extra_radiation_invalid(): with pytest.raises(ValueError): irradiance.get_extra_radiation(300, method='invalid')
def _solpos_dni_extra(observation, values): solar_position, night_flag = _solpos_night(observation, values) dni_extra = get_extra_radiation(values.index) timestamp_flag = _validate_timestamp(observation, values) return solar_position, dni_extra, timestamp_flag, night_flag
def test_get_extra_radiation_nrel_numba(times): result = irradiance.get_extra_radiation(times, method='nrel', how='numba', numthreads=8) assert_allclose(result, [1322.332316, 1322.296282, 1322.261205, 1322.227091])
def get_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:meth:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.get_total_irradiance`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) # SingleAxisTracker only supports a single Array, but we need the # validate/iterate machinery so that single length tuple input/output # is handled the same as PVSystem.get_irradiance. GH 1159 dni = self._validate_per_array(dni, system_wide=True) ghi = self._validate_per_array(ghi, system_wide=True) dhi = self._validate_per_array(dhi, system_wide=True) return tuple( irradiance.get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.arrays[0].albedo, **kwargs) for array, dni, ghi, dhi in zip(self.arrays, dni, ghi, dhi))
def test_get_extra_radiation_invalid(): with pytest.raises(ValueError): irradiance.get_extra_radiation(300, method='invalid')
def test_bird(): """Test Bird/Hulstrom Clearsky Model""" times = pd.date_range(start='1/1/2015 0:00', end='12/31/2015 23:00', freq='H') tz = -7 # test timezone gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz)) times = times.tz_localize(gmt_tz) # set timezone # match test data from BIRD_08_16_2012.xls latitude = 40. longitude = -105. press_mB = 840. o3_cm = 0.3 h2o_cm = 1.5 aod_500nm = 0.1 aod_380nm = 0.15 b_a = 0.85 alb = 0.2 eot = solarposition.equation_of_time_spencer71(times.dayofyear) hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15. declination = solarposition.declination_spencer71(times.dayofyear) zenith = solarposition.solar_zenith_analytical( np.deg2rad(latitude), np.deg2rad(hour_angle), declination ) zenith = np.rad2deg(zenith) airmass = atmosphere.get_relative_airmass(zenith, model='kasten1966') etr = irradiance.get_extra_radiation(times) # test Bird with time series data field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi') irrads = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100., etr, b_a, alb ) Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names) data_path = DATA_DIR / 'BIRD_08_16_2012.csv' testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata.index = times[1:48] assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48])) assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4) assert np.allclose(testdata['Hour Angle'], hour_angle[1:48]) assert np.allclose(testdata['Zenith Ang'], zenith[1:48]) dawn = zenith < 88. dusk = testdata['Zenith Ang'] < 88. am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0) assert np.allclose( testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3 ) direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3 ) direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3 ) global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3 ) diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3 ) # test keyword parameters irrads2 = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr ) Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names) data_path = DATA_DIR / 'BIRD_08_16_2012_patm.csv' testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata2.index = times[1:48] direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3 ) direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3 ) global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3 ) diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3 ) # test scalars just at noon # XXX: calculations start at 12am so noon is at index = 12 irrads3 = clearsky.bird( zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12] ) Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names) # XXX: testdata starts at 1am so noon is at index = 11 np.allclose( [Eb3, Ebh3, Gh3, Dh3], testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11], rtol=1e-3) return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)