def get_poa(self, dates, ghi, dhi, dni, sun_zenith, sun_azimuth, dni_extra=None, airmass=None, model="haydavies"): if self.method == "pvlib": sun_zenith = deg(sun_zenith) sun_azimuth = deg(sun_azimuth) if dni_extra is None: dni_extra = irradiance.extraradiation(DatetimeIndex(dates)) if airmass is None: airmass = atmosphere.relativeairmass(sun_zenith) poa = irradiance.total_irrad(self.tilt, self.azimuth, sun_zenith, sun_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo) self.poa = poa['poa_global']
def test_perez(): AM = atmosphere.relativeairmass(ephem_data['apparent_zenith']) irradiance.perez(40, 180, irrad_data['DHI'], irrad_data['DNI'], dni_et, ephem_data['apparent_zenith'], ephem_data['apparent_azimuth'], AM)
def test_perez_components(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan out, df_components = irradiance.perez(40, 180, irrad_data['dhi'], dni, dni_et, ephem_data['apparent_zenith'], ephem_data['azimuth'], am, return_components=True) expected = pd.Series(np.array( [ 0. , 31.46046871, np.nan, 45.45539877]), index=times) expected_components = pd.DataFrame( np.array([[ 0. , 26.84138589, np.nan, 31.72696071], [ 0. , 0. , np.nan, 4.47966439], [ 0. , 4.62212181, np.nan, 9.25316454]]).T, columns=['isotropic', 'circumsolar', 'horizon'], index=times ) if pandas_0_22(): expected_for_sum = expected.copy() expected_for_sum.iloc[2] = 0 else: expected_for_sum = expected sum_components = df_components.sum(axis=1) assert_series_equal(out, expected, check_less_precise=2) assert_frame_equal(df_components, expected_components) assert_series_equal(sum_components, expected_for_sum, check_less_precise=2)
def test_perez_components(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan out, df_components = irradiance.perez(40, 180, irrad_data['dhi'], dni, dni_et, ephem_data['apparent_zenith'], ephem_data['azimuth'], am, return_components=True) expected = pd.Series(np.array([0., 31.46046871, np.nan, 45.45539877]), index=times) expected_components = pd.DataFrame( np.array([[0., 26.84138589, np.nan, 31.72696071], [0., 0., np.nan, 4.47966439], [0., 4.62212181, np.nan, 9.25316454]]).T, columns=['isotropic', 'circumsolar', 'horizon'], index=times) sum_components = df_components.sum(axis=1) assert_series_equal(out, expected, check_less_precise=2) assert_frame_equal(df_components, expected_components) assert_series_equal(sum_components, expected, check_less_precise=2)
def test_perez_arrays(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan out = irradiance.perez(40, 180, irrad_data['dhi'].values, dni.values, dni_et, ephem_data['apparent_zenith'].values, ephem_data['azimuth'].values, am.values) expected = np.array([0., 31.46046871, np.nan, 45.45539877]) assert_allclose(out, expected, atol=1e-2)
def test_perez_arrays(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan out = irradiance.perez(40, 180, irrad_data['dhi'].values, dni.values, dni_et, ephem_data['apparent_zenith'].values, ephem_data['azimuth'].values, am.values) expected = np.array( [ 0. , 31.46046871, np.nan, 45.45539877]) assert_allclose(out, expected, atol=1e-2)
def test_perez(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan out = irradiance.perez(40, 180, irrad_data['dhi'], dni, dni_et, ephem_data['apparent_zenith'], ephem_data['azimuth'], am) expected = pd.Series(np.array([0., 31.46046871, np.nan, 45.45539877]), index=times) assert_series_equal(out, expected, check_less_precise=2)
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['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']) 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 test_perez(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan out = irradiance.perez(40, 180, irrad_data['dhi'], dni, dni_et, ephem_data['apparent_zenith'], ephem_data['azimuth'], am) expected = pd.Series(np.array( [ 0. , 31.46046871, np.nan, 45.45539877]), index=times) assert_series_equal(out, expected, check_less_precise=2)
def test_total_irrad(): models = ['isotropic', 'klutcher', 'haydavies', 'reindl', 'king', 'perez'] AM = atmosphere.relativeairmass(ephem_data['apparent_zenith']) for model in models: total = irradiance.total_irrad( 32, 180, ephem_data['apparent_zenith'], ephem_data['azimuth'], dni=irrad_data['dni'], ghi=irrad_data['ghi'], dhi=irrad_data['dhi'], dni_extra=dni_et, airmass=AM, model=model, surface_type='urban')
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 DC_out(solpos, poa_irrad, aoi, pvtemps): ## First calculate airmass airmass = atmosphere.relativeairmass(solpos['apparent_zenith']) ## Get list of modules sandia_modules = pvsystem.retrieve_sam('SandiaMod') ## Choose model (e.g. CS5P_220M___2009) sandia_module = sandia_modules.Canadian_Solar_CS5P_220M___2009_ ## Run SAPM model effective_irradiance = pvsystem.sapm_effective_irradiance( poa_irrad.poa_direct, poa_irrad.poa_diffuse, airmass, aoi, sandia_module) p_dc = pvsystem.sapm(effective_irradiance, pvtemps['temp_cell'], sandia_module) return (p_dc)
def test_total_irrad(): models = ['isotropic', 'klutcher', 'haydavies', 'reindl', 'king', 'perez'] AM = atmosphere.relativeairmass(ephem_data['apparent_zenith']) for model in models: total = irradiance.total_irrad(32, 180, ephem_data['apparent_zenith'], ephem_data['azimuth'], dni=irrad_data['dni'], ghi=irrad_data['ghi'], dhi=irrad_data['dhi'], dni_extra=dni_et, airmass=AM, model=model, surface_type='urban')
def get_airmass(self, times=None, solar_position=None, model='kastenyoung1989'): """ Calculate the relative and absolute airmass. Automatically chooses zenith or apparant zenith depending on the selected model. Parameters ---------- times : None or DatetimeIndex Only used if solar_position is not provided. solar_position : None or DataFrame DataFrame with with columns 'apparent_zenith', 'zenith'. model : str Relative airmass model Returns ------- airmass : DataFrame Columns are 'airmass_relative', 'airmass_absolute' """ if solar_position is None: solar_position = self.get_solarposition(times) if model in atmosphere.APPARENT_ZENITH_MODELS: zenith = solar_position['apparent_zenith'] elif model in atmosphere.TRUE_ZENITH_MODELS: zenith = solar_position['zenith'] else: raise ValueError('{} is not a valid airmass model'.format(model)) airmass_relative = atmosphere.relativeairmass(zenith, model) pressure = atmosphere.alt2pres(self.altitude) airmass_absolute = atmosphere.absoluteairmass(airmass_relative, pressure) airmass = pd.DataFrame() airmass['airmass_relative'] = airmass_relative airmass['airmass_absolute'] = airmass_absolute return airmass
def test_total_irrad(): models = ['isotropic', 'klutcher', 'klucher', 'haydavies', 'reindl', 'king', 'perez'] AM = atmosphere.relativeairmass(ephem_data['apparent_zenith']) for model in models: total = irradiance.total_irrad( 32, 180, ephem_data['apparent_zenith'], ephem_data['azimuth'], dni=irrad_data['dni'], ghi=irrad_data['ghi'], dhi=irrad_data['dhi'], dni_extra=dni_et, airmass=AM, model=model, surface_type='urban') assert total.columns.tolist() == ['poa_global', 'poa_direct', 'poa_diffuse', 'poa_sky_diffuse', 'poa_ground_diffuse']
def test_ineichen_series(): tus = Location(32.2, -111, 'US/Arizona', 700) times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='3h') times_localized = times.tz_localize(tus.tz) ephem_data = solarposition.get_solarposition(times_localized, tus.latitude, tus.longitude) am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) am = atmosphere.absoluteairmass(am, atmosphere.alt2pres(tus.altitude)) expected = pd.DataFrame( np.array([[0., 0., 0.], [0., 0., 0.], [91.12492792, 321.16092181, 51.17628184], [716.46580533, 888.90147035, 99.5050056], [1053.42066043, 953.24925854, 116.32868969], [863.54692781, 922.06124712, 106.95536561], [271.06382274, 655.44925241, 73.05968071], [0., 0., 0.], [0., 0., 0.]]), columns=['ghi', 'dni', 'dhi'], index=times_localized) out = clearsky.ineichen(ephem_data['apparent_zenith'], am, 3) assert_frame_equal(expected, out)
def test_ineichen_series(): tus = Location(32.2, -111, 'US/Arizona', 700) times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='3h') times_localized = times.tz_localize(tus.tz) ephem_data = solarposition.get_solarposition(times_localized, tus.latitude, tus.longitude) am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) am = atmosphere.absoluteairmass(am, atmosphere.alt2pres(tus.altitude)) expected = pd.DataFrame(np. array([[ 0. , 0. , 0. ], [ 0. , 0. , 0. ], [ 91.12492792, 321.16092181, 51.17628184], [ 716.46580533, 888.90147035, 99.5050056 ], [ 1053.42066043, 953.24925854, 116.32868969], [ 863.54692781, 922.06124712, 106.95536561], [ 271.06382274, 655.44925241, 73.05968071], [ 0. , 0. , 0. ], [ 0. , 0. , 0. ]]), columns=['ghi', 'dni', 'dhi'], index=times_localized) out = clearsky.ineichen(ephem_data['apparent_zenith'], am, 3) assert_frame_equal(expected, out)
def test_airmass_invalid(): atmosphere.relativeairmass(ephem_data['zenith'], 'invalid')
def test_perez(): AM = atmosphere.relativeairmass(ephem_data['apparent_zenith']) irradiance.perez(40, 180, irrad_data['dhi'], irrad_data['dni'], dni_et, ephem_data['apparent_zenith'], ephem_data['apparent_azimuth'], AM)
def test_airmass_invalid(): with pytest.raises(ValueError): atmosphere.relativeairmass(ephem_data['zenith'], 'invalid')
def test_airmass(model): out = atmosphere.relativeairmass(ephem_data['zenith'], model) assert isinstance(out, pd.Series) out = atmosphere.relativeairmass(ephem_data['zenith'].values, model) assert isinstance(out, np.ndarray)
def get_irradiance(self, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.total_irrad` function to calculate the plane of array irradiance components on a tilted surface defined by ``self.surface_tilt``, ``self.surface_azimuth``, and ``self.albedo``. Parameters ---------- solar_zenith : float or Series. Solar zenith angle. solar_azimuth : float or Series. Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series Extraterrestrial direct normal irradiance airmass : float or Series Airmass model : String Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ surface_tilt = kwargs.pop('surface_tilt', self.surface_tilt) surface_azimuth = kwargs.pop('surface_azimuth', self.surface_azimuth) try: solar_zenith = kwargs['solar_zenith'] except KeyError: solar_zenith = self.solar_zenith try: solar_azimuth = kwargs['solar_azimuth'] except KeyError: solar_azimuth = self.solar_azimuth # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.extraradiation(solar_zenith.index) dni_extra = pd.Series(dni_extra, index=solar_zenith.index) if airmass is None: airmass = atmosphere.relativeairmass(solar_zenith) return irradiance.total_irrad(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def test_bird(): """Test Bird/Hulstrom Clearsky Model""" times = pd.DatetimeIndex(start='1/1/2015 0:00', end='12/31/2015 23:00', freq='H') tz = -7 # test timezone gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz)) times = times.tz_localize(gmt_tz) # set timezone # match test data from BIRD_08_16_2012.xls latitude = 40. longitude = -105. press_mB = 840. o3_cm = 0.3 h2o_cm = 1.5 aod_500nm = 0.1 aod_380nm = 0.15 b_a = 0.85 alb = 0.2 eot = solarposition.equation_of_time_spencer71(times.dayofyear) hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15. declination = solarposition.declination_spencer71(times.dayofyear) zenith = solarposition.solar_zenith_analytical( np.deg2rad(latitude), np.deg2rad(hour_angle), declination ) zenith = np.rad2deg(zenith) airmass = atmosphere.relativeairmass(zenith, model='kasten1966') etr = irradiance.extraradiation(times) # test Bird with time series data field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi') irrads = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100., etr, b_a, alb ) Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names) clearsky_path = os.path.dirname(os.path.abspath(__file__)) pvlib_path = os.path.dirname(clearsky_path) data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012.csv') testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata.index = times[1:48] assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48])) assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4) assert np.allclose(testdata['Hour Angle'], hour_angle[1:48]) assert np.allclose(testdata['Zenith Ang'], zenith[1:48]) dawn = zenith < 88. dusk = testdata['Zenith Ang'] < 88. am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0) assert np.allclose( testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3 ) direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3 ) direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3 ) global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3 ) diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3 ) # test keyword parameters irrads2 = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr ) Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names) clearsky_path = os.path.dirname(os.path.abspath(__file__)) pvlib_path = os.path.dirname(clearsky_path) data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012_patm.csv') testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata2.index = times[1:48] direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3 ) direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3 ) global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3 ) diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3 ) # test scalars just at noon # XXX: calculations start at 12am so noon is at index = 12 irrads3 = clearsky.bird( zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12] ) Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names) # XXX: testdata starts at 1am so noon is at index = 11 np.allclose( [Eb3, Ebh3, Gh3, Dh3], testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11], rtol=1e-3) return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)
def test_absoluteairmass(): relative_am = atmosphere.relativeairmass(ephem_data["zenith"], "simple") atmosphere.absoluteairmass(relative_am) atmosphere.absoluteairmass(relative_am, pressure=100000)
def test_perez(): am = atmosphere.relativeairmass(ephem_data['apparent_zenith']) out = irradiance.perez(40, 180, irrad_data['dhi'], irrad_data['dni'], dni_et, ephem_data['apparent_zenith'], ephem_data['azimuth'], am) assert not out.isnull().any()
def perez_diffuse_luminance(df_inputs): """ Function used to calculate the luminance and the view factor terms from the Perez diffuse light transposition model, as implemented in the ``pvlib-python`` library. :param df_inputs: class:`pandas.DataFrame` with following columns: ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi', 'dni']. Units are: ['deg', 'deg', 'deg', 'deg', 'W/m2', 'W/m2'] :return: class:`pandas.DataFrame` with the following columns: ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi', 'dni', 'vf_horizon', 'vf_circumsolar', 'vf_isotropic', 'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic', 'poa_isotropic', 'poa_circumsolar', 'poa_horizon', 'poa_total_diffuse'] """ dni_et = irradiance.extraradiation(df_inputs.index.dayofyear) am = atmosphere.relativeairmass(df_inputs.solar_zenith) # Need to treat the case when the sun is hitting the back surface of pvrow aoi_proj = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) sun_hitting_back_surface = ((aoi_proj < 0) & (df_inputs.solar_zenith <= 90)) df_inputs_back_surface = df_inputs.loc[sun_hitting_back_surface] # Reverse the surface normal to switch to back-surface circumsolar calc df_inputs_back_surface.loc[:, 'array_azimuth'] -= 180. df_inputs_back_surface.loc[:, 'array_azimuth'] = np.mod( df_inputs_back_surface.loc[:, 'array_azimuth'], 360. ) df_inputs_back_surface.loc[:, 'array_tilt'] = ( 180. - df_inputs_back_surface.array_tilt) if df_inputs_back_surface.shape[0] > 0: # Use recursion to calculate circumsolar luminance for back surface df_inputs_back_surface = perez_diffuse_luminance( df_inputs_back_surface) # Calculate Perez diffuse components diffuse_poa, components = irradiance.perez(df_inputs.array_tilt, df_inputs.array_azimuth, df_inputs.dhi, df_inputs.dni, dni_et, df_inputs.solar_zenith, df_inputs.solar_azimuth, am, return_components=True) # Calculate Perez view factors: a = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) a = np.maximum(a, 0) b = cosd(df_inputs.solar_zenith) b = np.maximum(b, cosd(85)) vf_perez = pd.DataFrame( np.array([ sind(df_inputs.array_tilt), a / b, (1. + cosd(df_inputs.array_tilt)) / 2. ]).T, index=df_inputs.index, columns=['vf_horizon', 'vf_circumsolar', 'vf_isotropic'] ) # Calculate diffuse luminance luminance = pd.DataFrame( np.array([ components['horizon'] / vf_perez['vf_horizon'], components['circumsolar'] / vf_perez['vf_circumsolar'], components['isotropic'] / vf_perez['vf_isotropic'] ]).T, index=df_inputs.index, columns=['luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic'] ) luminance.loc[diffuse_poa == 0, :] = 0. # Format components column names components = components.rename(columns={'isotropic': 'poa_isotropic', 'circumsolar': 'poa_circumsolar', 'horizon': 'poa_horizon'}) df_inputs = pd.concat([df_inputs, components, vf_perez, luminance, diffuse_poa], axis=1, join='outer') df_inputs = df_inputs.rename(columns={0: 'poa_total_diffuse'}) # Adjust the circumsolar luminance when it hits the back surface if df_inputs_back_surface.shape[0] > 0: df_inputs.loc[sun_hitting_back_surface, 'luminance_circumsolar'] = ( df_inputs_back_surface.loc[:, 'luminance_circumsolar'] ) return df_inputs
def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): atmosphere.relativeairmass(2) with pytest.warns(pvlibDeprecationWarning): atmosphere.absoluteairmass(2)
def run_airmass(model, zenith): atmosphere.relativeairmass(zenith, model)
def get_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.total_irrad` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:method:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.extraradiation(solar_zenith.index) if airmass is None: airmass = atmosphere.relativeairmass(solar_zenith) return irradiance.total_irrad(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def ineichen(time, latitude, longitude, altitude=0, linke_turbidity=None, solarposition_method='nrel_numpy', zenith_data=None, airmass_model='young1994', airmass_data=None, interp_turbidity=True): ''' Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model Implements the Ineichen and Perez clear sky model for global horizontal irradiance (GHI), direct normal irradiance (DNI), and calculates the clear-sky diffuse horizontal (DHI) component as the difference between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear sky models found the Ineichen/Perez model to have excellent performance with a minimal input data set [3]. Default values for montly Linke turbidity provided by SoDa [4, 5]. Parameters ----------- time : pandas.DatetimeIndex latitude : float longitude : float altitude : float linke_turbidity : None or float If None, uses ``LinkeTurbidities.mat`` lookup table. solarposition_method : string Sets the solar position algorithm. See solarposition.get_solarposition() zenith_data : None or Series If None, ephemeris data will be calculated using ``solarposition_method``. airmass_model : string See pvlib.airmass.relativeairmass(). airmass_data : None or Series If None, absolute air mass data will be calculated using ``airmass_model`` and location.alitude. interp_turbidity : bool If ``True``, interpolates the monthly Linke turbidity values found in ``LinkeTurbidities.mat`` to daily values. Returns -------- DataFrame with the following columns: ``ghi, dni, dhi``. Notes ----- If you are using this function in a loop, it may be faster to load LinkeTurbidities.mat outside of the loop and feed it in as a keyword argument, rather than having the function open and process the file each time it is called. References ---------- [1] P. Ineichen and R. Perez, "A New airmass independent formulation for the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002. [2] R. Perez et. al., "A New Operational Model for Satellite-Derived Irradiances: Description and Validation", Solar Energy, vol 73, pp. 307-317, 2002. [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear Sky Models: Implementation and Analysis", Sandia National Laboratories, SAND2012-2389, 2012. [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained July 17, 2012). [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc. ISES Solar World Congress, June 2003. Goteborg, Sweden. ''' # Initial implementation of this algorithm by Matthew Reno. # Ported to python by Rob Andrews # Added functionality by Will Holmgren (@wholmgren) I0 = irradiance.extraradiation(time.dayofyear) if zenith_data is None: ephem_data = solarposition.get_solarposition(time, latitude=latitude, longitude=longitude, altitude=altitude, method=solarposition_method) time = ephem_data.index # fixes issue with time possibly not being tz-aware try: ApparentZenith = ephem_data['apparent_zenith'] except KeyError: ApparentZenith = ephem_data['zenith'] logger.warning('could not find apparent_zenith. using zenith') else: ApparentZenith = zenith_data #ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases if linke_turbidity is None: TL = lookup_linke_turbidity(time, latitude, longitude, interp_turbidity=interp_turbidity) else: TL = linke_turbidity # Get the absolute airmass assuming standard local pressure (per # alt2pres) using Kasten and Young's 1989 formula for airmass. if airmass_data is None: AMabsolute = atmosphere.absoluteairmass(airmass_relative=atmosphere.relativeairmass(ApparentZenith, airmass_model), pressure=atmosphere.alt2pres(altitude)) else: AMabsolute = airmass_data fh1 = np.exp(-altitude/8000.) fh2 = np.exp(-altitude/1250.) cg1 = 5.09e-05 * altitude + 0.868 cg2 = 3.92e-05 * altitude + 0.0387 logger.debug('fh1=%s, fh2=%s, cg1=%s, cg2=%s', fh1, fh2, cg1, cg2) # Dan's note on the TL correction: By my reading of the publication on # pages 151-157, Ineichen and Perez introduce (among other things) three # things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and # appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear # to use the new turbidity factor (item 2 above) in either the beam or GHI # models. The phrasing of appendix A seems as if there are two separate # corrections, the first correction is used to correct the beam/GHI models, # and the second correction is used to correct the revised turibidity # factor. In my estimation, there is no need to correct the turbidity # factor used in the beam/GHI models. # Create the corrected TL for TL < 2 # TLcorr = TL; # TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5); # This equation is found in Solar Energy 73, pg 311. # Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002). # It is slightly different than the equation given in Solar Energy 73, pg 156. # We used the equation from pg 311 because of the existence of known typos # in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)). cos_zenith = tools.cosd(ApparentZenith) clearsky_GHI = ( cg1 * I0 * cos_zenith * np.exp(-cg2*AMabsolute*(fh1 + fh2*(TL - 1))) * np.exp(0.01*AMabsolute**1.8) ) clearsky_GHI[clearsky_GHI < 0] = 0 # BncI == "normal beam clear sky radiation" b = 0.664 + 0.163/fh1 BncI = b * I0 * np.exp( -0.09 * AMabsolute * (TL - 1) ) logger.debug('b=%s', b) # "empirical correction" SE 73, 157 & SE 73, 312. BncI_2 = ( clearsky_GHI * ( 1 - (0.1 - 0.2*np.exp(-TL))/(0.1 + 0.882/fh1) ) / cos_zenith ) clearsky_DNI = np.minimum(BncI, BncI_2) clearsky_DHI = clearsky_GHI - clearsky_DNI*cos_zenith df_out = pd.DataFrame({'ghi':clearsky_GHI, 'dni':clearsky_DNI, 'dhi':clearsky_DHI}) df_out.fillna(0, inplace=True) return df_out
def ineichen(time, location, linke_turbidity=None, solarposition_method='pyephem', zenith_data=None, airmass_model='young1994', airmass_data=None, interp_turbidity=True): ''' Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model Implements the Ineichen and Perez clear sky model for global horizontal irradiance (GHI), direct normal irradiance (DNI), and calculates the clear-sky diffuse horizontal (DHI) component as the difference between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear sky models found the Ineichen/Perez model to have excellent performance with a minimal input data set [3]. Default values for montly Linke turbidity provided by SoDa [4, 5]. Parameters ----------- time : pandas.DatetimeIndex location : pvlib.Location linke_turbidity : None or float If None, uses ``LinkeTurbidities.mat`` lookup table. solarposition_method : string Sets the solar position algorithm. See solarposition.get_solarposition() zenith_data : None or Series If None, ephemeris data will be calculated using ``solarposition_method``. airmass_model : string See pvlib.airmass.relativeairmass(). airmass_data : None or Series If None, absolute air mass data will be calculated using ``airmass_model`` and location.alitude. interp_turbidity : bool If ``True``, interpolates the monthly Linke turbidity values found in ``LinkeTurbidities.mat`` to daily values. Returns -------- DataFrame with the following columns: ``ghi, dni, dhi``. Notes ----- If you are using this function in a loop, it may be faster to load LinkeTurbidities.mat outside of the loop and feed it in as a keyword argument, rather than having the function open and process the file each time it is called. References ---------- [1] P. Ineichen and R. Perez, "A New airmass independent formulation for the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002. [2] R. Perez et. al., "A New Operational Model for Satellite-Derived Irradiances: Description and Validation", Solar Energy, vol 73, pp. 307-317, 2002. [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear Sky Models: Implementation and Analysis", Sandia National Laboratories, SAND2012-2389, 2012. [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained July 17, 2012). [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc. ISES Solar World Congress, June 2003. Goteborg, Sweden. ''' # Initial implementation of this algorithm by Matthew Reno. # Ported to python by Rob Andrews # Added functionality by Will Holmgren (@wholmgren) I0 = irradiance.extraradiation(time.dayofyear) if zenith_data is None: ephem_data = solarposition.get_solarposition( time, location, method=solarposition_method) time = ephem_data.index # fixes issue with time possibly not being tz-aware try: ApparentZenith = ephem_data['apparent_zenith'] except KeyError: ApparentZenith = ephem_data['zenith'] logger.warning('could not find apparent_zenith. using zenith') else: ApparentZenith = zenith_data #ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases if linke_turbidity is None: TL = lookup_linke_turbidity(time, location.latitude, location.longitude, interp_turbidity=interp_turbidity) else: TL = linke_turbidity # Get the absolute airmass assuming standard local pressure (per # alt2pres) using Kasten and Young's 1989 formula for airmass. if airmass_data is None: AMabsolute = atmosphere.absoluteairmass( airmass_relative=atmosphere.relativeairmass( ApparentZenith, airmass_model), pressure=atmosphere.alt2pres(location.altitude)) else: AMabsolute = airmass_data fh1 = np.exp(-location.altitude / 8000.) fh2 = np.exp(-location.altitude / 1250.) cg1 = 5.09e-05 * location.altitude + 0.868 cg2 = 3.92e-05 * location.altitude + 0.0387 logger.debug('fh1=%s, fh2=%s, cg1=%s, cg2=%s', fh1, fh2, cg1, cg2) # Dan's note on the TL correction: By my reading of the publication on # pages 151-157, Ineichen and Perez introduce (among other things) three # things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and # appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear # to use the new turbidity factor (item 2 above) in either the beam or GHI # models. The phrasing of appendix A seems as if there are two separate # corrections, the first correction is used to correct the beam/GHI models, # and the second correction is used to correct the revised turibidity # factor. In my estimation, there is no need to correct the turbidity # factor used in the beam/GHI models. # Create the corrected TL for TL < 2 # TLcorr = TL; # TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5); # This equation is found in Solar Energy 73, pg 311. # Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002). # It is slightly different than the equation given in Solar Energy 73, pg 156. # We used the equation from pg 311 because of the existence of known typos # in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)). cos_zenith = tools.cosd(ApparentZenith) clearsky_GHI = (cg1 * I0 * cos_zenith * np.exp(-cg2 * AMabsolute * (fh1 + fh2 * (TL - 1))) * np.exp(0.01 * AMabsolute**1.8)) clearsky_GHI[clearsky_GHI < 0] = 0 # BncI == "normal beam clear sky radiation" b = 0.664 + 0.163 / fh1 BncI = b * I0 * np.exp(-0.09 * AMabsolute * (TL - 1)) logger.debug('b=%s', b) # "empirical correction" SE 73, 157 & SE 73, 312. BncI_2 = (clearsky_GHI * (1 - (0.1 - 0.2 * np.exp(-TL)) / (0.1 + 0.882 / fh1)) / cos_zenith) clearsky_DNI = np.minimum(BncI, BncI_2) clearsky_DHI = clearsky_GHI - clearsky_DNI * cos_zenith df_out = pd.DataFrame({ 'ghi': clearsky_GHI, 'dni': clearsky_DNI, 'dhi': clearsky_DHI }) df_out.fillna(0, inplace=True) return df_out
def basic_chain(times, latitude, longitude, module_parameters, inverter_parameters, irradiance=None, weather=None, surface_tilt=None, surface_azimuth=None, orientation_strategy=None, transposition_model='haydavies', solar_position_method='nrel_numpy', airmass_model='kastenyoung1989', altitude=None, pressure=None, **kwargs): """ An experimental function that computes all of the modeling steps necessary for calculating power or energy for a PV system at a given location. Parameters ---------- times : DatetimeIndex Times at which to evaluate the model. latitude : float. Positive is north of the equator. Use decimal degrees notation. longitude : float. Positive is east of the prime meridian. Use decimal degrees notation. module_parameters : None, dict or Series Module parameters as defined by the SAPM. inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. irradiance : None or DataFrame If None, calculates clear sky data. Columns must be 'dni', 'ghi', 'dhi'. weather : None or DataFrame If None, assumes air temperature is 20 C and wind speed is 0 m/s. Columns must be 'wind_speed', 'temp_air'. surface_tilt : float or Series Surface tilt angles in decimal degrees. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90) surface_azimuth : float or Series Surface azimuth angles in decimal degrees. The azimuth convention is defined as degrees east of north (North=0, South=180, East=90, West=270). orientation_strategy : None or str The strategy for aligning the modules. If not None, sets the ``surface_azimuth`` and ``surface_tilt`` properties of the ``system``. Allowed strategies include 'flat', 'south_at_latitude_tilt'. Ignored for SingleAxisTracker systems. transposition_model : str Passed to system.get_irradiance. solar_position_method : str Passed to location.get_solarposition. airmass_model : str Passed to location.get_airmass. altitude : None or float If None, computed from pressure. Assumed to be 0 m if pressure is also None. pressure : None or float If None, computed from altitude. Assumed to be 101325 Pa if altitude is also None. **kwargs Arbitrary keyword arguments. See code for details. Returns ------- output : (dc, ac) Tuple of DC power (with SAPM parameters) (DataFrame) and AC power (Series). """ # use surface_tilt and surface_azimuth if provided, # otherwise set them using the orientation_strategy if surface_tilt is not None and surface_azimuth is not None: pass elif orientation_strategy is not None: surface_tilt, surface_azimuth = \ get_orientation(orientation_strategy, latitude=latitude) else: raise ValueError('orientation_strategy or surface_tilt and ' + 'surface_azimuth must be provided') times = times if altitude is None and pressure is None: altitude = 0. pressure = 101325. elif altitude is None: altitude = atmosphere.pres2alt(pressure) elif pressure is None: pressure = atmosphere.alt2pres(altitude) solar_position = solarposition.get_solarposition(times, latitude, longitude, altitude=altitude, pressure=pressure, **kwargs) # possible error with using apparent zenith with some models airmass = atmosphere.relativeairmass(solar_position['apparent_zenith'], model=airmass_model) airmass = atmosphere.absoluteairmass(airmass, pressure) dni_extra = pvlib.irradiance.extraradiation(solar_position.index) dni_extra = pd.Series(dni_extra, index=solar_position.index) aoi = pvlib.irradiance.aoi(surface_tilt, surface_azimuth, solar_position['apparent_zenith'], solar_position['azimuth']) if irradiance is None: linke_turbidity = clearsky.lookup_linke_turbidity( solar_position.index, latitude, longitude) irradiance = clearsky.ineichen(solar_position['apparent_zenith'], airmass, linke_turbidity, altitude=altitude, dni_extra=dni_extra) total_irrad = pvlib.irradiance.total_irrad( surface_tilt, surface_azimuth, solar_position['apparent_zenith'], solar_position['azimuth'], irradiance['dni'], irradiance['ghi'], irradiance['dhi'], model=transposition_model, dni_extra=dni_extra) if weather is None: weather = {'wind_speed': 0, 'temp_air': 20} temps = pvsystem.sapm_celltemp(total_irrad['poa_global'], weather['wind_speed'], weather['temp_air']) effective_irradiance = pvsystem.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi, module_parameters) dc = pvsystem.sapm(effective_irradiance, temps['temp_cell'], module_parameters) ac = pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter_parameters) return dc, ac
def test_airmass_scalar_nan(): assert np.isnan(atmosphere.relativeairmass(100))
def ineichen( time, location, linke_turbidity=None, solarposition_method="pyephem", zenith_data=None, airmass_model="young1994", airmass_data=None, interp_turbidity=True, ): """ Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model Implements the Ineichen and Perez clear sky model for global horizontal irradiance (GHI), direct normal irradiance (DNI), and calculates the clear-sky diffuse horizontal (DHI) component as the difference between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear sky models found the Ineichen/Perez model to have excellent performance with a minimal input data set [3]. Default values for montly Linke turbidity provided by SoDa [4, 5]. Parameters ----------- time : pandas.DatetimeIndex location : pvlib.Location linke_turbidity : None or float If None, uses ``LinkeTurbidities.mat`` lookup table. solarposition_method : string Sets the solar position algorithm. See solarposition.get_solarposition() zenith_data : None or pandas.Series If None, ephemeris data will be calculated using ``solarposition_method``. airmass_model : string See pvlib.airmass.relativeairmass(). airmass_data : None or pandas.Series If None, absolute air mass data will be calculated using ``airmass_model`` and location.alitude. interp_turbidity : bool If ``True``, interpolates the monthly Linke turbidity values found in ``LinkeTurbidities.mat`` to daily values. Returns -------- DataFrame with the following columns: ``GHI, DNI, DHI``. Notes ----- If you are using this function in a loop, it may be faster to load LinkeTurbidities.mat outside of the loop and feed it in as a variable, rather than having the function open the file each time it is called. References ---------- [1] P. Ineichen and R. Perez, "A New airmass independent formulation for the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002. [2] R. Perez et. al., "A New Operational Model for Satellite-Derived Irradiances: Description and Validation", Solar Energy, vol 73, pp. 307-317, 2002. [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear Sky Models: Implementation and Analysis", Sandia National Laboratories, SAND2012-2389, 2012. [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained July 17, 2012). [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc. ISES Solar World Congress, June 2003. Goteborg, Sweden. """ # Initial implementation of this algorithm by Matthew Reno. # Ported to python by Rob Andrews # Added functionality by Will Holmgren I0 = irradiance.extraradiation(time.dayofyear) if zenith_data is None: ephem_data = solarposition.get_solarposition(time, location, method=solarposition_method) time = ephem_data.index # fixes issue with time possibly not being tz-aware try: ApparentZenith = ephem_data["apparent_zenith"] except KeyError: ApparentZenith = ephem_data["zenith"] logger.warning("could not find apparent_zenith. using zenith") else: ApparentZenith = zenith_data # ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases if linke_turbidity is None: # The .mat file 'LinkeTurbidities.mat' contains a single 2160 x 4320 x 12 # matrix of type uint8 called 'LinkeTurbidity'. The rows represent global # latitudes from 90 to -90 degrees; the columns represent global longitudes # from -180 to 180; and the depth (third dimension) represents months of # the year from January (1) to December (12). To determine the Linke # turbidity for a position on the Earth's surface for a given month do the # following: LT = LinkeTurbidity(LatitudeIndex, LongitudeIndex, month). # Note that the numbers within the matrix are 20 * Linke Turbidity, # so divide the number from the file by 20 to get the # turbidity. try: import scipy.io except ImportError: raise ImportError( "The Linke turbidity lookup table requires scipy. " + "You can still use clearsky.ineichen if you " + "supply your own turbidities." ) # consider putting this code at module level this_path = os.path.dirname(os.path.abspath(__file__)) logger.debug("this_path={}".format(this_path)) mat = scipy.io.loadmat(os.path.join(this_path, "data", "LinkeTurbidities.mat")) linke_turbidity = mat["LinkeTurbidity"] LatitudeIndex = np.round_(_linearly_scale(location.latitude, 90, -90, 1, 2160)) LongitudeIndex = np.round_(_linearly_scale(location.longitude, -180, 180, 1, 4320)) g = linke_turbidity[LatitudeIndex][LongitudeIndex] if interp_turbidity: logger.info("interpolating turbidity to the day") g2 = np.concatenate([[g[-1]], g, [g[0]]]) # wrap ends around days = np.linspace(-15, 380, num=14) # map day of year onto month (approximate) LT = pd.Series(np.interp(time.dayofyear, days, g2), index=time) else: logger.info("using monthly turbidity") ApplyMonth = lambda x: g[x[0] - 1] LT = pd.DataFrame(time.month, index=time) LT = LT.apply(ApplyMonth, axis=1) TL = LT / 20.0 logger.info("using TL=\n{}".format(TL)) else: TL = linke_turbidity # Get the absolute airmass assuming standard local pressure (per # alt2pres) using Kasten and Young's 1989 formula for airmass. if airmass_data is None: AMabsolute = atmosphere.absoluteairmass( AMrelative=atmosphere.relativeairmass(ApparentZenith, airmass_model), pressure=atmosphere.alt2pres(location.altitude), ) else: AMabsolute = airmass_data fh1 = np.exp(-location.altitude / 8000.0) fh2 = np.exp(-location.altitude / 1250.0) cg1 = 5.09e-05 * location.altitude + 0.868 cg2 = 3.92e-05 * location.altitude + 0.0387 logger.debug("fh1={}, fh2={}, cg1={}, cg2={}".format(fh1, fh2, cg1, cg2)) # Dan's note on the TL correction: By my reading of the publication on # pages 151-157, Ineichen and Perez introduce (among other things) three # things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and # appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear # to use the new turbidity factor (item 2 above) in either the beam or GHI # models. The phrasing of appendix A seems as if there are two separate # corrections, the first correction is used to correct the beam/GHI models, # and the second correction is used to correct the revised turibidity # factor. In my estimation, there is no need to correct the turbidity # factor used in the beam/GHI models. # Create the corrected TL for TL < 2 # TLcorr = TL; # TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5); # This equation is found in Solar Energy 73, pg 311. # Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002). # It is slightly different than the equation given in Solar Energy 73, pg 156. # We used the equation from pg 311 because of the existence of known typos # in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)). cos_zenith = tools.cosd(ApparentZenith) clearsky_GHI = ( cg1 * I0 * cos_zenith * np.exp(-cg2 * AMabsolute * (fh1 + fh2 * (TL - 1))) * np.exp(0.01 * AMabsolute ** 1.8) ) clearsky_GHI[clearsky_GHI < 0] = 0 # BncI == "normal beam clear sky radiation" b = 0.664 + 0.163 / fh1 BncI = b * I0 * np.exp(-0.09 * AMabsolute * (TL - 1)) logger.debug("b={}".format(b)) # "empirical correction" SE 73, 157 & SE 73, 312. BncI_2 = clearsky_GHI * (1 - (0.1 - 0.2 * np.exp(-TL)) / (0.1 + 0.882 / fh1)) / cos_zenith # return BncI, BncI_2 clearsky_DNI = np.minimum(BncI, BncI_2) # Will H: use np.minimum explicitly clearsky_DHI = clearsky_GHI - clearsky_DNI * cos_zenith df_out = pd.DataFrame({"GHI": clearsky_GHI, "DNI": clearsky_DNI, "DHI": clearsky_DHI}) df_out.fillna(0, inplace=True) # df_out['BncI'] = BncI # df_out['BncI_2'] = BncI return df_out
def test_absoluteairmass(): relative_am = atmosphere.relativeairmass(ephem_data['zenith'], 'simple') atmosphere.absoluteairmass(relative_am) atmosphere.absoluteairmass(relative_am, pressure=100000)
def ineichen(time, location, linke_turbidity=None, solarposition_method='pyephem', zenith_data=None, airmass_model='young1994', airmass_data=None, interp_turbidity=True): ''' Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model Implements the Ineichen and Perez clear sky model for global horizontal irradiance (GHI), direct normal irradiance (DNI), and calculates the clear-sky diffuse horizontal (DHI) component as the difference between GHI and DNI*cos(zenith) as presented in [1, 2]. A report on clear sky models found the Ineichen/Perez model to have excellent performance with a minimal input data set [3]. Default values for montly Linke turbidity provided by SoDa [4, 5]. Parameters ----------- time : pandas.DatetimeIndex location : pvlib.Location linke_turbidity : None or float If None, uses ``LinkeTurbidities.mat`` lookup table. solarposition_method : string Sets the solar position algorithm. See solarposition.get_solarposition() zenith_data : None or pandas.Series If None, ephemeris data will be calculated using ``solarposition_method``. airmass_model : string See pvlib.airmass.relativeairmass(). airmass_data : None or pandas.Series If None, absolute air mass data will be calculated using ``airmass_model`` and location.alitude. interp_turbidity : bool If ``True``, interpolates the monthly Linke turbidity values found in ``LinkeTurbidities.mat`` to daily values. Returns -------- DataFrame with the following columns: ``GHI, DNI, DHI``. Notes ----- If you are using this function in a loop, it may be faster to load LinkeTurbidities.mat outside of the loop and feed it in as a variable, rather than having the function open the file each time it is called. References ---------- [1] P. Ineichen and R. Perez, "A New airmass independent formulation for the Linke turbidity coefficient", Solar Energy, vol 73, pp. 151-157, 2002. [2] R. Perez et. al., "A New Operational Model for Satellite-Derived Irradiances: Description and Validation", Solar Energy, vol 73, pp. 307-317, 2002. [3] M. Reno, C. Hansen, and J. Stein, "Global Horizontal Irradiance Clear Sky Models: Implementation and Analysis", Sandia National Laboratories, SAND2012-2389, 2012. [4] http://www.soda-is.com/eng/services/climat_free_eng.php#c5 (obtained July 17, 2012). [5] J. Remund, et. al., "Worldwide Linke Turbidity Information", Proc. ISES Solar World Congress, June 2003. Goteborg, Sweden. ''' # Initial implementation of this algorithm by Matthew Reno. # Ported to python by Rob Andrews # Added functionality by Will Holmgren I0 = irradiance.extraradiation(time.dayofyear) if zenith_data is None: ephem_data = solarposition.get_solarposition( time, location, method=solarposition_method) time = ephem_data.index # fixes issue with time possibly not being tz-aware try: ApparentZenith = ephem_data['apparent_zenith'] except KeyError: ApparentZenith = ephem_data['zenith'] logger.warning('could not find apparent_zenith. using zenith') else: ApparentZenith = zenith_data #ApparentZenith[ApparentZenith >= 90] = 90 # can cause problems in edge cases if linke_turbidity is None: # The .mat file 'LinkeTurbidities.mat' contains a single 2160 x 4320 x 12 # matrix of type uint8 called 'LinkeTurbidity'. The rows represent global # latitudes from 90 to -90 degrees; the columns represent global longitudes # from -180 to 180; and the depth (third dimension) represents months of # the year from January (1) to December (12). To determine the Linke # turbidity for a position on the Earth's surface for a given month do the # following: LT = LinkeTurbidity(LatitudeIndex, LongitudeIndex, month). # Note that the numbers within the matrix are 20 * Linke Turbidity, # so divide the number from the file by 20 to get the # turbidity. try: import scipy.io except ImportError: raise ImportError( 'The Linke turbidity lookup table requires scipy. ' + 'You can still use clearsky.ineichen if you ' + 'supply your own turbidities.') # consider putting this code at module level this_path = os.path.dirname(os.path.abspath(__file__)) logger.debug('this_path={}'.format(this_path)) mat = scipy.io.loadmat( os.path.join(this_path, 'data', 'LinkeTurbidities.mat')) linke_turbidity = mat['LinkeTurbidity'] LatitudeIndex = np.round_( _linearly_scale(location.latitude, 90, -90, 1, 2160)) LongitudeIndex = np.round_( _linearly_scale(location.longitude, -180, 180, 1, 4320)) g = linke_turbidity[LatitudeIndex][LongitudeIndex] if interp_turbidity: logger.info('interpolating turbidity to the day') g2 = np.concatenate([[g[-1]], g, [g[0]]]) # wrap ends around days = np.linspace( -15, 380, num=14) # map day of year onto month (approximate) LT = pd.Series(np.interp(time.dayofyear, days, g2), index=time) else: logger.info('using monthly turbidity') ApplyMonth = lambda x: g[x[0] - 1] LT = pd.DataFrame(time.month, index=time) LT = LT.apply(ApplyMonth, axis=1) TL = LT / 20. logger.info('using TL=\n{}'.format(TL)) else: TL = linke_turbidity # Get the absolute airmass assuming standard local pressure (per # alt2pres) using Kasten and Young's 1989 formula for airmass. if airmass_data is None: AMabsolute = atmosphere.absoluteairmass( AMrelative=atmosphere.relativeairmass(ApparentZenith, airmass_model), pressure=atmosphere.alt2pres(location.altitude)) else: AMabsolute = airmass_data fh1 = np.exp(-location.altitude / 8000.) fh2 = np.exp(-location.altitude / 1250.) cg1 = 5.09e-05 * location.altitude + 0.868 cg2 = 3.92e-05 * location.altitude + 0.0387 logger.debug('fh1={}, fh2={}, cg1={}, cg2={}'.format(fh1, fh2, cg1, cg2)) # Dan's note on the TL correction: By my reading of the publication on # pages 151-157, Ineichen and Perez introduce (among other things) three # things. 1) Beam model in eqn. 8, 2) new turbidity factor in eqn 9 and # appendix A, and 3) Global horizontal model in eqn. 11. They do NOT appear # to use the new turbidity factor (item 2 above) in either the beam or GHI # models. The phrasing of appendix A seems as if there are two separate # corrections, the first correction is used to correct the beam/GHI models, # and the second correction is used to correct the revised turibidity # factor. In my estimation, there is no need to correct the turbidity # factor used in the beam/GHI models. # Create the corrected TL for TL < 2 # TLcorr = TL; # TLcorr(TL < 2) = TLcorr(TL < 2) - 0.25 .* (2-TLcorr(TL < 2)) .^ (0.5); # This equation is found in Solar Energy 73, pg 311. # Full ref: Perez et. al., Vol. 73, pp. 307-317 (2002). # It is slightly different than the equation given in Solar Energy 73, pg 156. # We used the equation from pg 311 because of the existence of known typos # in the pg 156 publication (notably the fh2-(TL-1) should be fh2 * (TL-1)). cos_zenith = tools.cosd(ApparentZenith) clearsky_GHI = cg1 * I0 * cos_zenith * np.exp( -cg2 * AMabsolute * (fh1 + fh2 * (TL - 1))) * np.exp(0.01 * AMabsolute**1.8) clearsky_GHI[clearsky_GHI < 0] = 0 # BncI == "normal beam clear sky radiation" b = 0.664 + 0.163 / fh1 BncI = b * I0 * np.exp(-0.09 * AMabsolute * (TL - 1)) logger.debug('b={}'.format(b)) # "empirical correction" SE 73, 157 & SE 73, 312. BncI_2 = clearsky_GHI * (1 - (0.1 - 0.2 * np.exp(-TL)) / (0.1 + 0.882 / fh1)) / cos_zenith #return BncI, BncI_2 clearsky_DNI = np.minimum(BncI, BncI_2) # Will H: use np.minimum explicitly clearsky_DHI = clearsky_GHI - clearsky_DNI * cos_zenith df_out = pd.DataFrame({ 'GHI': clearsky_GHI, 'DNI': clearsky_DNI, 'DHI': clearsky_DHI }) df_out.fillna(0, inplace=True) #df_out['BncI'] = BncI #df_out['BncI_2'] = BncI return df_out
def basic_chain(times, latitude, longitude, module_parameters, inverter_parameters, irradiance=None, weather=None, surface_tilt=None, surface_azimuth=None, orientation_strategy=None, transposition_model='haydavies', solar_position_method='nrel_numpy', airmass_model='kastenyoung1989', altitude=None, pressure=None, **kwargs): """ An experimental function that computes all of the modeling steps necessary for calculating power or energy for a PV system at a given location. Parameters ---------- times : DatetimeIndex Times at which to evaluate the model. latitude : float. Positive is north of the equator. Use decimal degrees notation. longitude : float. Positive is east of the prime meridian. Use decimal degrees notation. module_parameters : None, dict or Series Module parameters as defined by the SAPM. inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. irradiance : None or DataFrame If None, calculates clear sky data. Columns must be 'dni', 'ghi', 'dhi'. weather : None or DataFrame If None, assumes air temperature is 20 C and wind speed is 0 m/s. Columns must be 'wind_speed', 'temp_air'. surface_tilt : float or Series Surface tilt angles in decimal degrees. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90) surface_azimuth : float or Series Surface azimuth angles in decimal degrees. The azimuth convention is defined as degrees east of north (North=0, South=180, East=90, West=270). orientation_strategy : None or str The strategy for aligning the modules. If not None, sets the ``surface_azimuth`` and ``surface_tilt`` properties of the ``system``. transposition_model : str Passed to system.get_irradiance. solar_position_method : str Passed to location.get_solarposition. airmass_model : str Passed to location.get_airmass. altitude : None or float If None, computed from pressure. Assumed to be 0 m if pressure is also None. pressure : None or float If None, computed from altitude. Assumed to be 101325 Pa if altitude is also None. **kwargs Arbitrary keyword arguments. See code for details. Returns ------- output : (dc, ac) Tuple of DC power (with SAPM parameters) (DataFrame) and AC power (Series). """ # use surface_tilt and surface_azimuth if provided, # otherwise set them using the orientation_strategy if surface_tilt is not None and surface_azimuth is not None: pass elif orientation_strategy is not None: surface_tilt, surface_azimuth = \ get_orientation(orientation_strategy, latitude=latitude) else: raise ValueError('orientation_strategy or surface_tilt and ' + 'surface_azimuth must be provided') times = times if altitude is None and pressure is None: altitude = 0. pressure = 101325. elif altitude is None: altitude = atmosphere.pres2alt(pressure) elif pressure is None: pressure = atmosphere.alt2pres(altitude) solar_position = solarposition.get_solarposition(times, latitude, longitude, altitude=altitude, pressure=pressure, **kwargs) # possible error with using apparent zenith with some models airmass = atmosphere.relativeairmass(solar_position['apparent_zenith'], model=airmass_model) airmass = atmosphere.absoluteairmass(airmass, pressure) dni_extra = pvlib.irradiance.extraradiation(solar_position.index) dni_extra = pd.Series(dni_extra, index=solar_position.index) aoi = pvlib.irradiance.aoi(surface_tilt, surface_azimuth, solar_position['apparent_zenith'], solar_position['azimuth']) if irradiance is None: irradiance = clearsky.ineichen( solar_position.index, latitude, longitude, zenith_data=solar_position['apparent_zenith'], airmass_data=airmass, altitude=altitude) total_irrad = pvlib.irradiance.total_irrad( surface_tilt, surface_azimuth, solar_position['apparent_zenith'], solar_position['azimuth'], irradiance['dni'], irradiance['ghi'], irradiance['dhi'], model=transposition_model, dni_extra=dni_extra) if weather is None: weather = {'wind_speed': 0, 'temp_air': 20} temps = pvsystem.sapm_celltemp(total_irrad['poa_global'], weather['wind_speed'], weather['temp_air']) dc = pvsystem.sapm(module_parameters, total_irrad['poa_direct'], total_irrad['poa_diffuse'], temps['temp_cell'], airmass, aoi) ac = pvsystem.snlinverter(inverter_parameters, dc['v_mp'], dc['p_mp']) return dc, ac
def perez_diffuse_luminance(timestamps, array_tilt, array_azimuth, solar_zenith, solar_azimuth, dni, dhi): """ Function used to calculate the luminance and the view factor terms from the Perez diffuse light transposition model, as implemented in the ``pvlib-python`` library. This function was custom made to allow the calculation of the circumsolar component on the back surface as well. Otherwise, the ``pvlib`` implementation would ignore it. :param array-like timestamps: simulation timestamps :param array-like array_tilt: pv module tilt angles :param array-like array_azimuth: pv array azimuth angles :param array-like solar_zenith: solar zenith angles :param array-like solar_azimuth: solar azimuth angles :param array-like dni: values for direct normal irradiance :param array-like dhi: values for diffuse horizontal irradiance :return: ``df_inputs``, dataframe with the following columns: ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi', 'dni', 'vf_horizon', 'vf_circumsolar', 'vf_isotropic', 'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic', 'poa_isotropic', 'poa_circumsolar', 'poa_horizon', 'poa_total_diffuse'] :rtype: class:`pandas.DataFrame` """ # Create a dataframe to help filtering on all arrays df_inputs = pd.DataFrame( { 'array_tilt': array_tilt, 'array_azimuth': array_azimuth, 'solar_zenith': solar_zenith, 'solar_azimuth': solar_azimuth, 'dni': dni, 'dhi': dhi }, index=pd.DatetimeIndex(timestamps)) dni_et = irradiance.extraradiation(df_inputs.index.dayofyear) am = atmosphere.relativeairmass(df_inputs.solar_zenith) # Need to treat the case when the sun is hitting the back surface of pvrow aoi_proj = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) sun_hitting_back_surface = ((aoi_proj < 0) & (df_inputs.solar_zenith <= 90)) df_inputs_back_surface = df_inputs.loc[sun_hitting_back_surface] # Reverse the surface normal to switch to back-surface circumsolar calc df_inputs_back_surface.loc[:, 'array_azimuth'] -= 180. df_inputs_back_surface.loc[:, 'array_azimuth'] = np.mod( df_inputs_back_surface.loc[:, 'array_azimuth'], 360.) df_inputs_back_surface.loc[:, 'array_tilt'] = ( 180. - df_inputs_back_surface.array_tilt) if df_inputs_back_surface.shape[0] > 0: # Use recursion to calculate circumsolar luminance for back surface df_inputs_back_surface = perez_diffuse_luminance( *breakup_df_inputs(df_inputs_back_surface)) # Calculate Perez diffuse components diffuse_poa, components = irradiance.perez(df_inputs.array_tilt, df_inputs.array_azimuth, df_inputs.dhi, df_inputs.dni, dni_et, df_inputs.solar_zenith, df_inputs.solar_azimuth, am, return_components=True) # Calculate Perez view factors: a = aoi_projection(df_inputs.array_tilt, df_inputs.array_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) a = np.maximum(a, 0) b = cosd(df_inputs.solar_zenith) b = np.maximum(b, cosd(85)) vf_perez = pd.DataFrame( np.array([ sind(df_inputs.array_tilt), a / b, (1. + cosd(df_inputs.array_tilt)) / 2. ]).T, index=df_inputs.index, columns=['vf_horizon', 'vf_circumsolar', 'vf_isotropic']) # Calculate diffuse luminance luminance = pd.DataFrame(np.array([ components['horizon'] / vf_perez['vf_horizon'], components['circumsolar'] / vf_perez['vf_circumsolar'], components['isotropic'] / vf_perez['vf_isotropic'] ]).T, index=df_inputs.index, columns=[ 'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic' ]) luminance.loc[diffuse_poa == 0, :] = 0. # Format components column names components = components.rename( columns={ 'isotropic': 'poa_isotropic', 'circumsolar': 'poa_circumsolar', 'horizon': 'poa_horizon' }) df_inputs = pd.concat( [df_inputs, components, vf_perez, luminance, diffuse_poa], axis=1, join='outer') df_inputs = df_inputs.rename(columns={0: 'poa_total_diffuse'}) # Adjust the circumsolar luminance when it hits the back surface if df_inputs_back_surface.shape[0] > 0: df_inputs.loc[sun_hitting_back_surface, 'luminance_circumsolar'] = ( df_inputs_back_surface.loc[:, 'luminance_circumsolar']) return df_inputs
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_airmass_invalid(): atmosphere.relativeairmass(ephem_data["zenith"], "invalid")