diff --git a/docs/sphinx/source/package_overview.rst b/docs/sphinx/source/package_overview.rst index 2a8ce6b091..e187887804 100644 --- a/docs/sphinx/source/package_overview.rst +++ b/docs/sphinx/source/package_overview.rst @@ -56,7 +56,7 @@ configuration at a handful of sites listed below. # get the module and inverter specifications from SAM sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod') - sapm_inverters = pvlib.pvsystem.retrieve_sam('sandiainverter') + sapm_inverters = pvlib.pvsystem.retrieve_sam('cecinverter') module = sandia_modules['Canadian_Solar_CS5P_220M___2009_'] inverter = sapm_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] @@ -80,17 +80,19 @@ to accomplish our system modeling goal: 'surface_azimuth': 180} energies = {} - # localize datetime indices (pvlib>=0.3.0) + for latitude, longitude, name, altitude, timezone in coordinates: times = naive_times.tz_localize(timezone) system['surface_tilt'] = latitude - cs = pvlib.clearsky.ineichen(times, latitude, longitude, altitude=altitude) solpos = pvlib.solarposition.get_solarposition(times, latitude, longitude) dni_extra = pvlib.irradiance.extraradiation(times) dni_extra = pd.Series(dni_extra, index=times) airmass = pvlib.atmosphere.relativeairmass(solpos['apparent_zenith']) pressure = pvlib.atmosphere.alt2pres(altitude) am_abs = pvlib.atmosphere.absoluteairmass(airmass, pressure) + tl = pvlib.clearsky.lookup_linke_turbidity(times, latitude, longitude) + cs = pvlib.clearsky.ineichen(solpos['apparent_zenith', am_abs, tl, + dni_extra=dni_extra, altitude=altitude) aoi = pvlib.irradiance.aoi(system['surface_tilt'], system['surface_azimuth'], solpos['apparent_zenith'], solpos['azimuth']) total_irrad = pvlib.irradiance.total_irrad(system['surface_tilt'], diff --git a/docs/sphinx/source/whatsnew/v0.4.0.txt b/docs/sphinx/source/whatsnew/v0.4.0.txt index 618290693d..719c314016 100644 --- a/docs/sphinx/source/whatsnew/v0.4.0.txt +++ b/docs/sphinx/source/whatsnew/v0.4.0.txt @@ -13,7 +13,7 @@ API Changes * Remove unneeded module argument from singlediode function. (:issue:`200`) * In ``pvlib.irradiance.perez``, renamed argument ``modelt`` to ``model``. - (:issue:`196`) + (:issue:`196`) Enhancements diff --git a/pvlib/clearsky.py b/pvlib/clearsky.py index e227b6ee92..85e1104dab 100644 --- a/pvlib/clearsky.py +++ b/pvlib/clearsky.py @@ -5,9 +5,6 @@ from __future__ import division -import logging -logger = logging.getLogger('pvlib') - import os from collections import OrderedDict @@ -15,74 +12,56 @@ import pandas as pd from pvlib import tools -from pvlib import irradiance -from pvlib import atmosphere -from pvlib import solarposition -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): +def ineichen(apparent_zenith, airmass_absolute, linke_turbidity, + altitude=0, dni_extra=1364.): ''' - Determine clear sky GHI, DNI, and DHI from Ineichen/Perez model + 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]. + 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]. + Default values for monthly 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. + apparent_zenith: numeric + Refraction corrected solar zenith angle in degrees. - solarposition_method : string - Sets the solar position algorithm. - See solarposition.get_solarposition() + airmass_absolute: numeric + Pressure corrected airmass. - zenith_data : None or Series - If None, ephemeris data will be calculated using ``solarposition_method``. + linke_turbidity: numeric + Linke Turbidity. - airmass_model : string - See pvlib.airmass.relativeairmass(). + altitude: numeric + Altitude above sea level in meters. - 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. + dni_extra: numeric + Extraterrestrial irradiance. The units of ``dni_extra`` + determine the units of the output. Returns - -------- - DataFrame with the following columns: ``ghi, dni, dhi``. + ------- + clearsky : DataFrame (if Series input) or OrderedDict of arrays + DataFrame/OrderedDict contains the columns/keys + ``'dhi', 'dni', 'ghi'``. - 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. + See also + -------- + lookup_linke_turbidity + pvlib.location.Location.get_clearsky 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. + 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. @@ -98,97 +77,76 @@ def ineichen(time, latitude, longitude, altitude=0, linke_turbidity=None, [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 + # 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)). + + # The NaN handling is a little subtle. The AM input is likely to + # have NaNs that we'll want to map to 0s in the output. However, we + # want NaNs in other inputs to propagate through to the output. This + # is accomplished by judicious use and placement of np.maximum, + # np.minimum, and np.fmax + + # use max so that nighttime values will result in 0s instead of + # negatives. propagates nans. + cos_zenith = np.maximum(tools.cosd(apparent_zenith), 0) + + tl = linke_turbidity 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" + + ghi = (np.exp(-cg2*airmass_absolute*(fh1 + fh2*(tl - 1))) * + np.exp(0.01*airmass_absolute**1.8)) + # use fmax to map airmass nans to 0s. multiply and divide by tl to + # reinsert tl nans + ghi = cg1 * dni_extra * cos_zenith * tl / tl * np.fmax(ghi, 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) + bnci = b * np.exp(-0.09 * airmass_absolute * (tl - 1)) + bnci = dni_extra * np.fmax(bnci, 0) # "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 ) + bnci_2 = ((1 - (0.1 - 0.2*np.exp(-tl))/(0.1 + 0.882/fh1)) / + cos_zenith) + bnci_2 = ghi * np.fmin(np.fmax(bnci_2, 0), 1e20) - clearsky_DNI = np.minimum(BncI, BncI_2) + dni = np.minimum(bnci, bnci_2) - clearsky_DHI = clearsky_GHI - clearsky_DNI*cos_zenith + dhi = ghi - dni*cos_zenith + + irrads = OrderedDict() + irrads['ghi'] = ghi + irrads['dni'] = dni + irrads['dhi'] = dhi - df_out = pd.DataFrame({'ghi':clearsky_GHI, 'dni':clearsky_DNI, - 'dhi':clearsky_DHI}) - df_out.fillna(0, inplace=True) + if isinstance(dni, pd.Series): + irrads = pd.DataFrame.from_dict(irrads) - return df_out + return irrads def lookup_linke_turbidity(time, latitude, longitude, filepath=None, @@ -242,14 +200,17 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None, mat = scipy.io.loadmat(filepath) linke_turbidity_table = mat['LinkeTurbidity'] - latitude_index = np.around(_linearly_scale(latitude, 90, -90, 1, 2160)).astype(np.int64) - longitude_index = np.around(_linearly_scale(longitude, -180, 180, 1, 4320)).astype(np.int64) + latitude_index = ( + np.around(_linearly_scale(latitude, 90, -90, 1, 2160)) + .astype(np.int64)) + longitude_index = ( + np.around(_linearly_scale(longitude, -180, 180, 1, 4320)) + .astype(np.int64)) g = linke_turbidity_table[latitude_index][longitude_index] if interp_turbidity: - logger.info('interpolating turbidity to the day') - # Cata covers 1 year. + # Data covers 1 year. # Assume that data corresponds to the value at # the middle of each month. # This means that we need to add previous Dec and next Jan @@ -262,10 +223,9 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None, linke_turbidity = pd.Series(np.interp(time.dayofyear, days, g2), index=time) else: - logger.info('using monthly turbidity') - apply_month = lambda x: g[x[0]-1] linke_turbidity = pd.DataFrame(time.month, index=time) - linke_turbidity = linke_turbidity.apply(apply_month, axis=1) + # apply monthly data + linke_turbidity = linke_turbidity.apply(lambda x: g[x[0]-1], axis=1) linke_turbidity /= 20. @@ -312,11 +272,11 @@ def haurwitz(apparent_zenith): cos_zenith = tools.cosd(apparent_zenith) - clearsky_GHI = 1098.0 * cos_zenith * np.exp(-0.059/cos_zenith) + clearsky_ghi = 1098.0 * cos_zenith * np.exp(-0.059/cos_zenith) - clearsky_GHI[clearsky_GHI < 0] = 0 + clearsky_ghi[clearsky_ghi < 0] = 0 - df_out = pd.DataFrame({'ghi':clearsky_GHI}) + df_out = pd.DataFrame({'ghi': clearsky_ghi}) return df_out @@ -326,8 +286,8 @@ def _linearly_scale(inputmatrix, inputmin, inputmax, outputmin, outputmax): inputrange = inputmax - inputmin outputrange = outputmax - outputmin - OutputMatrix = (inputmatrix-inputmin) * outputrange/inputrange + outputmin - return OutputMatrix + outputmatrix = (inputmatrix-inputmin) * outputrange/inputrange + outputmin + return outputmatrix def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1., @@ -360,16 +320,15 @@ def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1., or 101325 and 41000 Pascals. dni_extra: numeric - Extraterrestrial irradiance. + Extraterrestrial irradiance. The units of ``dni_extra`` + determine the units of the output. Returns - -------- + ------- clearsky : DataFrame (if Series input) or OrderedDict of arrays DataFrame/OrderedDict contains the columns/keys ``'dhi', 'dni', 'ghi'``. - The units of ``dni_extra`` determine the units of the output. - References ---------- .. [1] P. Ineichen, "A broadband simplified version of the diff --git a/pvlib/location.py b/pvlib/location.py index 9a69edc2ee..8dbe2f7588 100644 --- a/pvlib/location.py +++ b/pvlib/location.py @@ -167,62 +167,74 @@ def get_solarposition(self, times, pressure=None, temperature=12, **kwargs) - def get_clearsky(self, times, model='ineichen', **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 + times: DatetimeIndex + model: str The clear sky model to use. Must be one of 'ineichen', 'haurwitz', 'simplified_solis'. + solar_position : None or DataFrame + DataFrame with with columns 'apparent_zenith', 'zenith', + 'apparent_elevation'. + dni_extra: None or numeric + If None, will be calculated from times. kwargs passed to the relevant functions. Climatological values - are assumed in many cases. See code for details. + are assumed in many cases. See source code for details! Returns ------- clearsky : DataFrame Column names are: ``ghi, dni, dhi``. """ + if dni_extra is None: + dni_extra = irradiance.extraradiation(times.dayofyear) - if model == 'ineichen': - cs = clearsky.ineichen(times, latitude=self.latitude, - longitude=self.longitude, - altitude=self.altitude, - **kwargs) - elif model == 'haurwitz': - solpos = self.get_solarposition(times, **kwargs) - cs = clearsky.haurwitz(solpos['apparent_zenith']) - elif model == 'simplified_solis': + try: + pressure = kwargs.pop('pressure') + except KeyError: + pressure = atmosphere.alt2pres(self.altitude) - # these try/excepts define default values that are only - # evaluated if necessary. ineichen does some of this internally - try: - dni_extra = kwargs.pop('dni_extra') - except KeyError: - dni_extra = irradiance.extraradiation(times.dayofyear) + 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: - pressure = kwargs.pop('pressure') + linke_turbidity = kwargs.pop('linke_turbidity') except KeyError: - pressure = atmosphere.alt2pres(self.altitude) + interp_turbidity = kwargs.pop('interp_turbidity', True) + linke_turbidity = clearsky.lookup_linke_turbidity( + times, self.latitude, self.longitude, + interp_turbidity=interp_turbidity) try: - apparent_elevation = kwargs.pop('apparent_elevation') + airmass_absolute = kwargs.pop('airmass_absolute') except KeyError: - solpos = self.get_solarposition( - times, pressure=pressure, **kwargs) - apparent_elevation = solpos['apparent_elevation'] + airmass_absolute = self.get_airmass( + times, solar_position=solar_position)['airmass_absolute'] + cs = clearsky.ineichen(apparent_zenith, airmass_absolute, + linke_turbidity, altitude=self.altitude, + dni_extra=dni_extra) + elif model == 'haurwitz': + cs = clearsky.haurwitz(apparent_zenith) + elif model == 'simplified_solis': cs = clearsky.simplified_solis( apparent_elevation, pressure=pressure, dni_extra=dni_extra, **kwargs) else: - raise ValueError('{} is not a valid clear sky model' + raise ValueError(('{} is not a valid clear sky model. Must be ' + + 'one of ineichen, simplified_solis, haurwitz') .format(model)) return cs diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 5ca40c53a0..4c1836cc11 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -141,13 +141,15 @@ def basic_chain(times, latitude, longitude, solar_position['azimuth']) if irradiance is None: + linke_turbidity = clearsky.lookup_linke_turbidity( + solar_position.index, latitude, longitude) irradiance = clearsky.ineichen( - solar_position.index, - latitude, - longitude, - zenith_data=solar_position['apparent_zenith'], - airmass_data=airmass, - altitude=altitude) + solar_position['apparent_zenith'], + airmass, + linke_turbidity, + altitude=altitude, + dni_extra=dni_extra + ) total_irrad = pvlib.irradiance.total_irrad( surface_tilt, diff --git a/pvlib/test/test_clearsky.py b/pvlib/test/test_clearsky.py index 284a37e1bc..7529c94684 100644 --- a/pvlib/test/test_clearsky.py +++ b/pvlib/test/test_clearsky.py @@ -10,99 +10,169 @@ from pvlib.location import Location from pvlib import clearsky from pvlib import solarposition +from pvlib import atmosphere from conftest import requires_scipy -# setup times and location to be tested. -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) - -@requires_scipy -def test_ineichen_required(): - # the clearsky function should call lookup_linke_turbidity by default - expected = pd.DataFrame( - np.array([[ 0. , 0. , 0. ], - [ 0. , 0. , 0. ], - [ 51.47811191, 265.33462162, 84.48262202], - [ 105.008507 , 832.29100407, 682.67761951], - [ 121.97988054, 901.31821834, 1008.02102657], - [ 112.57957512, 867.76297247, 824.61702926], - [ 76.69672675, 588.8462898 , 254.5808329 ], - [ 0. , 0. , 0. ], - [ 0. , 0. , 0. ]]), - columns=['dhi', 'dni', 'ghi'], +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(times_localized, tus.latitude, tus.longitude) + + out = clearsky.ineichen(ephem_data['apparent_zenith'], am, 3) assert_frame_equal(expected, out) -def test_ineichen_supply_linke(): - expected = pd.DataFrame(np.array( - [[ 0. , 0. , 0. ], - [ 0. , 0. , 0. ], - [ 40.16490879, 321.71856556, 80.12815294], - [ 95.14336873, 876.49252839, 703.47605855], - [ 118.4587024 , 939.81646535, 1042.34480815], - [ 105.36645492, 909.11265773, 851.32459694], - [ 61.91187639, 647.35889938, 257.42691896], - [ 0. , 0. , 0. ], - [ 0. , 0. , 0. ]]), - columns=['dhi', 'dni', 'ghi'], - index=times_localized) - out = clearsky.ineichen(times_localized, tus.latitude, tus.longitude, - altitude=tus.altitude, - linke_turbidity=3) - assert_frame_equal(expected, out) +def test_ineichen_scalar_input(): + expected = OrderedDict() + expected['ghi'] = 1048.592893113678 + expected['dni'] = 942.2081860378344 + expected['dhi'] = 120.6989665520498 + + out = clearsky.ineichen(10., 1., 3.) + for k, v in expected.items(): + assert_allclose(expected[k], out[k]) -def test_ineichen_solpos(): - clearsky.ineichen(times_localized, tus.latitude, tus.longitude, - linke_turbidity=3, - solarposition_method='ephemeris') +def test_ineichen_nans(): + length = 4 + + apparent_zenith = np.full(length, 10.) + apparent_zenith[0] = np.nan + + linke_turbidity = np.full(length, 3.) + linke_turbidity[1] = np.nan + + dni_extra = np.full(length, 1370.) + dni_extra[2] = np.nan + + airmass_absolute = np.full(length, 1.) + + expected = OrderedDict() + expected['ghi'] = np.full(length, np.nan) + expected['dni'] = np.full(length, np.nan) + expected['dhi'] = np.full(length, np.nan) + + expected['ghi'][length-1] = 1053.205472 + expected['dni'][length-1] = 946.352797 + expected['dhi'][length-1] = 121.2299 + + out = clearsky.ineichen(apparent_zenith, airmass_absolute, + linke_turbidity, dni_extra=dni_extra) + + for k, v in expected.items(): + assert_allclose(expected[k], out[k]) -def test_ineichen_airmass(): +def test_ineichen_arrays(): + expected = OrderedDict() + + expected['ghi'] = (np. + array([[[ 1106.78342709, 1064.7691287 , 1024.34972343], + [ 847.84529406, 815.66047425, 784.69741345], + [ 192.19092519, 184.89521884, 177.87646277]], + + [[ 959.12310134, 775.2374976 , 626.60692548], + [ 734.73092205, 593.86637713, 480.00875328], + [ 166.54997871, 134.61857872, 108.80915072]], + + [[ 1026.15144142, 696.85030591, 473.22483724], + [ 786.0776095 , 533.81830453, 362.51125692], + [ 178.18932781, 121.00678573, 82.17463061]]])) + + expected['dni'] = (np. + array([[[ 1024.58284359, 942.20818604, 861.11344424], + [ 1024.58284359, 942.20818604, 861.11344424], + [ 1024.58284359, 942.20818604, 861.11344424]], + + [[ 687.61305142, 419.14891162, 255.50098235], + [ 687.61305142, 419.14891162, 255.50098235], + [ 687.61305142, 419.14891162, 255.50098235]], + + [[ 458.62196014, 186.46177428, 75.80970012], + [ 458.62196014, 186.46177428, 75.80970012], + [ 458.62196014, 186.46177428, 75.80970012]]])) + + expected['dhi'] = (np. + array([[[ 82.20058349, 122.56094266, 163.23627919], + [ 62.96930021, 93.88712907, 125.04624459], + [ 14.27398153, 21.28248435, 28.34568241]], + + [[ 271.51004993, 356.08858598, 371.10594313], + [ 207.988765 , 272.77968255, 284.28364554], + [ 47.14722539, 61.83413404, 64.44187075]], + + [[ 567.52948128, 510.38853163, 397.41513712], + [ 434.75280544, 390.98029849, 304.4376574 ], + [ 98.5504602 , 88.62803842, 69.01041434]]])) + + apparent_zenith = np.linspace(0, 80, 3) + airmass_absolute = np.linspace(1, 10, 3) + linke_turbidity = np.linspace(2, 4, 3) + + apparent_zenith, airmass_absolute, linke_turbidity = \ + np.meshgrid(apparent_zenith, airmass_absolute, linke_turbidity) + + out = clearsky.ineichen(apparent_zenith, airmass_absolute, linke_turbidity) + + for k, v in expected.items(): + assert_allclose(expected[k], out[k]) + + +def test_ineichen_dni_extra(): expected = pd.DataFrame( - np.array([[ 0. , 0. , 0. ], - [ 0. , 0. , 0. ], - [ 53.90422388, 257.01655613, 85.87406435], - [ 101.34055688, 842.92925705, 686.39337307], - [ 117.7573735 , 909.70367947, 1012.04184961], - [ 108.6233401 , 877.30589626, 828.49118038], - [ 75.23108133, 602.06895546, 257.10961202], - [ 0. , 0. , 0. ], - [ 0. , 0. , 0. ]]), - columns=['dhi', 'dni', 'ghi'], - index=times_localized) - out = clearsky.ineichen(times_localized, tus.latitude, tus.longitude, - linke_turbidity=3, - airmass_model='simple') + np.array([[ 1053.20547182, 946.35279683, 121.22990042]]), + columns=['ghi', 'dni', 'dhi']) + + out = clearsky.ineichen(10, 1, 3, dni_extra=pd.Series(1370)) + assert_frame_equal(expected, out) + + +def test_ineichen_altitude(): + expected = pd.DataFrame( + np.array([[ 1145.64245696, 994.95377835, 165.80426215]]), + columns=['ghi', 'dni', 'dhi']) + + out = clearsky.ineichen(10, 1, 3, altitude=pd.Series(2000)) assert_frame_equal(expected, out) @requires_scipy def test_lookup_linke_turbidity(): times = pd.date_range(start='2014-06-24', end='2014-06-25', - freq='12h', tz=tus.tz) + freq='12h', tz='America/Phoenix') # expect same value on 2014-06-24 0000 and 1200, and # diff value on 2014-06-25 expected = pd.Series(np.array([3.10126582, 3.10126582, 3.11443038]), index=times) - out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude) + out = clearsky.lookup_linke_turbidity(times, 32.2, -111) assert_series_equal(expected, out) @requires_scipy def test_lookup_linke_turbidity_nointerp(): times = pd.date_range(start='2014-06-24', end='2014-06-25', - freq='12h', tz=tus.tz) + freq='12h', tz='America/Phoenix') # expect same value for all days expected = pd.Series(np.array([3., 3., 3.]), index=times) - out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude, + out = clearsky.lookup_linke_turbidity(times, 32.2, -111, interp_turbidity=False) assert_series_equal(expected, out) @@ -110,31 +180,35 @@ def test_lookup_linke_turbidity_nointerp(): @requires_scipy def test_lookup_linke_turbidity_months(): times = pd.date_range(start='2014-04-01', end='2014-07-01', - freq='1M', tz=tus.tz) + freq='1M', tz='America/Phoenix') expected = pd.Series(np.array([2.8943038, 2.97316456, 3.18025316]), index=times) - out = clearsky.lookup_linke_turbidity(times, tus.latitude, - tus.longitude) + out = clearsky.lookup_linke_turbidity(times, 32.2, -111) assert_series_equal(expected, out) @requires_scipy def test_lookup_linke_turbidity_nointerp_months(): times = pd.date_range(start='2014-04-10', end='2014-07-10', - freq='1M', tz=tus.tz) + freq='1M', tz='America/Phoenix') expected = pd.Series(np.array([2.85, 2.95, 3.]), index=times) - out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude, + out = clearsky.lookup_linke_turbidity(times, 32.2, -111, interp_turbidity=False) assert_series_equal(expected, out) # changing the dates shouldn't matter if interp=False times = pd.date_range(start='2014-04-05', end='2014-07-05', - freq='1M', tz=tus.tz) - out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude, + freq='1M', tz='America/Phoenix') + out = clearsky.lookup_linke_turbidity(times, 32.2, -111, interp_turbidity=False) assert_series_equal(expected, out) def test_haurwitz(): + 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) expected = pd.DataFrame(np.array([[0.], [0.], [82.85934048], @@ -150,6 +224,11 @@ def test_haurwitz(): def test_simplified_solis_series_elevation(): + 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) expected = pd.DataFrame( np.array([[ 0. , 0. , 0. ], [ 0. , 0. , 0. ], diff --git a/pvlib/test/test_irradiance.py b/pvlib/test/test_irradiance.py index ffeef869f2..23d5d7fe57 100644 --- a/pvlib/test/test_irradiance.py +++ b/pvlib/test/test_irradiance.py @@ -24,9 +24,7 @@ ephem_data = solarposition.get_solarposition( times, tus.latitude, tus.longitude, method='nrel_numpy') -irrad_data = clearsky.ineichen(times, tus.latitude, tus.longitude, - altitude=tus.altitude, linke_turbidity=3, - solarposition_method='nrel_numpy') +irrad_data = tus.get_clearsky(times, model='ineichen', linke_turbidity=3) dni_et = irradiance.extraradiation(times.dayofyear) @@ -177,8 +175,8 @@ def test_globalinplane(): def test_disc_keys(): - clearsky_data = clearsky.ineichen(times, tus.latitude, tus.longitude, - linke_turbidity=3) + clearsky_data = tus.get_clearsky(times, model='ineichen', + linke_turbidity=3) disc_data = irradiance.disc(clearsky_data['ghi'], ephem_data['zenith'], ephem_data.index) assert 'dni' in disc_data.columns @@ -197,8 +195,8 @@ def test_disc_value(): def test_dirint(): - clearsky_data = clearsky.ineichen(times, tus.latitude, tus.longitude, - linke_turbidity=3) + clearsky_data = tus.get_clearsky(times, model='ineichen', + linke_turbidity=3) pressure = 93193. dirint_data = irradiance.dirint(clearsky_data['ghi'], ephem_data['zenith'], ephem_data.index, pressure=pressure) diff --git a/pvlib/test/test_location.py b/pvlib/test/test_location.py index 867978394a..7be7543861 100644 --- a/pvlib/test/test_location.py +++ b/pvlib/test/test_location.py @@ -56,17 +56,37 @@ def test_get_clearsky(): end='20160101T1800-0700', freq='3H') clearsky = tus.get_clearsky(times) - expected = pd.DataFrame(data=np.array( - [[ 0. , 0. , 0. ], - [ 49.99257714, 762.92663984, 258.84368467], - [ 70.79757257, 957.14396999, 612.04545874], - [ 59.01570645, 879.06844381, 415.26616693], - [ 0. , 0. , 0. ]]), - columns=['dhi', 'dni', 'ghi'], + expected = pd.DataFrame(data=np. + array([[ 0. , 0. , 0. ], + [ 258.60422702, 761.57329257, 50.1235982 ], + [ 611.96347869, 956.95353414, 70.8232806 ], + [ 415.10904044, 878.52649603, 59.07820922], + [ 0. , 0. , 0. ]]), + columns=['ghi', 'dni', 'dhi'], index=times) assert_frame_equal(expected, clearsky) +def test_get_clearsky_ineichen_supply_linke(): + 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) + expected = pd.DataFrame(np. + array([[ 0. , 0. , 0. ], + [ 0. , 0. , 0. ], + [ 79.73090244, 316.16436502, 40.45759009], + [ 703.43653498, 876.41452667, 95.15798252], + [ 1042.37962396, 939.86391062, 118.44687715], + [ 851.32411813, 909.11186737, 105.36662462], + [ 257.18266827, 646.16644264, 62.02777094], + [ 0. , 0. , 0. ], + [ 0. , 0. , 0. ]]), + columns=['ghi', 'dni', 'dhi'], + index=times_localized) + out = tus.get_clearsky(times_localized, linke_turbidity=3) + assert_frame_equal(expected, out) + + def test_get_clearsky_haurwitz(): tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') times = pd.DatetimeIndex(start='20160101T0600-0700', @@ -107,9 +127,10 @@ def test_get_clearsky_simplified_solis_apparent_elevation(): times = pd.DatetimeIndex(start='20160101T0600-0700', end='20160101T1800-0700', freq='3H') - apparent_elevation = pd.Series(80, index=times) + solar_position = {'apparent_elevation': pd.Series(80, index=times), + 'apparent_zenith': pd.Series(10, index=times)} clearsky = tus.get_clearsky(times, model='simplified_solis', - apparent_elevation=apparent_elevation) + solar_position=solar_position) expected = pd.DataFrame(data=np. array([[ 131.3124497 , 1001.14754036, 1108.14147919], [ 131.3124497 , 1001.14754036, 1108.14147919], diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index fc0ef0d254..100e0c0274 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -29,12 +29,12 @@ latitude=latitude, longitude=longitude, method='nrel_numpy') -irrad_data = clearsky.ineichen(times, latitude=latitude, longitude=longitude, - linke_turbidity=3, - solarposition_method='nrel_numpy') +am = atmosphere.relativeairmass(ephem_data.apparent_zenith) +irrad_data = clearsky.ineichen(ephem_data['apparent_zenith'], am, + linke_turbidity=3) aoi = irradiance.aoi(0, 0, ephem_data['apparent_zenith'], ephem_data['azimuth']) -am = atmosphere.relativeairmass(ephem_data.apparent_zenith) + meta = {'latitude': 37.8, 'longitude': -122.3,