def test_lookup_linke_turbidity_nointerp_months(): times = pd.date_range(start='2014-04-10', end='2014-07-10', freq='1M', tz='America/Phoenix') expected = pd.Series(np.array([2.85, 2.95, 3.]), index=times) out = clearsky.lookup_linke_turbidity(times, 32.125, -110.875, 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='America/Phoenix') out = clearsky.lookup_linke_turbidity(times, 32.125, -110.875, interp_turbidity=False) assert_series_equal(expected, out)
def test_lookup_linke_turbidity_months(): times = pd.date_range(start='2014-04-01', end='2014-07-01', freq='1M', tz='America/Phoenix') expected = pd.Series(np.array([2.8943038, 2.97316456, 3.18025316]), index=times) out = clearsky.lookup_linke_turbidity(times, 32.2, -111) assert_series_equal(expected, out)
def linke_turbidity_resource(request): if request.method == 'GET': params = LinkeTurbidityForm(request.GET) else: params = LinkeTurbidityForm(request.POST) if params.is_valid(): lat = params.cleaned_data['tl_lat'] lon = params.cleaned_data['tl_lon'] start = params.cleaned_data['tl_start'] end = params.cleaned_data['tl_end'] tz = params.cleaned_data['tl_tz'] freq = params.cleaned_data['tl_freq'] else: return JsonResponse(params.errors, status=400) tz = tz or 0 # if tz is None then use zero freq = freq or 'H' # if freq if '' then use 'H' if start > end: return JsonResponse({'start': ['End time must be after start.']}, status=400) # drop the timezone start = start.replace(tzinfo=None) end = end.replace(tzinfo=None) tz = 'Etc/GMT{:+d}'.format(-tz) try: times = pd.DatetimeIndex(start=start, end=end, freq=freq, tz=tz) except ValueError as exc: return JsonResponse({'freq': [str(exc)]}, status=400) tl = clearsky.lookup_linke_turbidity(times, lat, lon) tl.index = times.strftime('%Y-%m-%dT%H:%M:%S%z') # [t.isoformat() for t in times.to_pydatetime()] data = tl.to_dict() return JsonResponse(data)
def test_lookup_linke_turbidity_months_leapyear(): times = pd.date_range(start='2016-04-01', end='2016-07-01', freq='1M', tz='America/Phoenix') expected = pd.Series( np.array([2.89918032787, 2.97540983607, 3.19672131148]), index=times ) out = clearsky.lookup_linke_turbidity(times, 32.125, -110.875) assert_series_equal(expected, out)
def test_lookup_linke_turbidity_months(): times = pd.date_range(start='2014-04-01', end='2014-07-01', freq='1M', tz=tus.tz) expected = pd.Series(np.array([2.8943038, 2.97316456, 3.18025316]), index=times) out = clearsky.lookup_linke_turbidity(times, tus.latitude, tus.longitude) assert_series_equal(expected, out)
def test_lookup_linke_turbidity_nointerp(): times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='12h', tz=tus.tz) # 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, interp_turbidity=False) assert_series_equal(expected, out)
def test_lookup_linke_turbidity_nointerp(): times = pd.date_range(start='2014-06-24', end='2014-06-25', 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, 32.125, -110.875, interp_turbidity=False) assert_series_equal(expected, out)
def test_lookup_linke_turbidity(): times = pd.date_range(start='2014-06-24', end='2014-06-25', freq='12h', tz=tus.tz) # 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) assert_series_equal(expected, out)
def test_lookup_linke_turbidity(): times = pd.date_range(start='2014-06-24', end='2014-06-25', 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, 32.2, -111) assert_series_equal(expected, out)
def test_lookup_linke_turbidity_leapyear(): times = pd.date_range(start='2016-06-24', end='2016-06-25', freq='12h', tz='America/Phoenix') # expect same value on 2016-06-24 0000 and 1200, and # diff value on 2016-06-25 expected = pd.Series( np.array([3.11803278689, 3.11803278689, 3.13114754098]), index=times ) out = clearsky.lookup_linke_turbidity(times, 32.125, -110.875) assert_series_equal(expected, out)
def test_tl_lookup(self): data = { 'tl_lat': 38.2, 'tl_lon': -122.1, 'tl_freq': 'T', 'tl_tz': -8, 'tl_start': '2018-01-01 07:00', 'tl_end': '2018-01-01 08:00' } r = self.client.get('/api/v1/pvlib/linke-turbidity/', data) self.assertEqual(r.status_code, 200) s = pd.Series(r.json()) t = pd.DatetimeIndex(s.index) times = pd.DatetimeIndex(start=data['tl_start'], end=data['tl_end'], freq=data['tl_freq'], tz='Etc/GMT{:+d}'.format(-data['tl_tz'])) tl = clearsky.lookup_linke_turbidity(times, data['tl_lat'], data['tl_lon']) assert np.allclose(times.values.astype(int), t.values.astype(int)) assert np.allclose(tl, s)
def generate_clearsky_sequence(time, location, sun_position=None, p_0=101325.0, model="ineichen"): """ Generate clear-sky irradiance sequence :param time: :param location: pvlib.location.Location object :param sun_position: :param p_0: sea level pressure :param model: {'ineichen', 'solis'} #TODO: implement solis clearsky model :return: """ if sun_position is None: sun_position = get_solar_position(time, location.latitude, location.longitude, location.altitude) # Altitude corrected (King et al. 1997; Rigollier et al. 2000) pressure = p_0 * np.exp(-0.0001184 * location.altitude) # Relative airmass am = get_relative_airmass(sun_position["zenith"]) # Absolute airmass am_a = get_absolute_airmass(am, pressure) # Linke turbidity linke_turbidity = lookup_linke_turbidity(time, location.latitude, location.longitude) if model == "ineichen": return ineichen(sun_position["apparent_zenith"], am_a, linke_turbidity)["ghi"] elif model == "solis": pass
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 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 source code for details! Returns ------- clearsky : DataFrame Column names are: ``ghi, dni, dhi``. """ if dni_extra is None: dni_extra = irradiance.extraradiation(times) try: pressure = kwargs.pop('pressure') except KeyError: pressure = atmosphere.alt2pres(self.altitude) if solar_position is None: solar_position = self.get_solarposition(times, pressure=pressure, **kwargs) apparent_zenith = solar_position['apparent_zenith'] apparent_elevation = solar_position['apparent_elevation'] if model == 'ineichen': try: linke_turbidity = kwargs.pop('linke_turbidity') except KeyError: interp_turbidity = kwargs.pop('interp_turbidity', True) linke_turbidity = clearsky.lookup_linke_turbidity( times, self.latitude, self.longitude, interp_turbidity=interp_turbidity) try: airmass_absolute = kwargs.pop('airmass_absolute') except KeyError: airmass_absolute = self.get_airmass( times, solar_position=solar_position)['airmass_absolute'] cs = clearsky.ineichen(apparent_zenith, airmass_absolute, linke_turbidity, altitude=self.altitude, dni_extra=dni_extra) elif model == 'haurwitz': cs = clearsky.haurwitz(apparent_zenith) elif model == 'simplified_solis': cs = clearsky.simplified_solis(apparent_elevation, pressure=pressure, dni_extra=dni_extra, **kwargs) else: raise ValueError( ('{} is not a valid clear sky model. Must be ' + 'one of ineichen, simplified_solis, haurwitz').format(model)) return cs
def solar_irradiation(latitude, longitude, Z, moment, surface_tilt, surface_azimuth, T=None, P=None, solar_constant=1366.1, atmos_refract=0.5667, albedo=0.25, linke_turbidity=None, extraradiation_method='spencer', airmass_model='kastenyoung1989', cache=None): r'''Calculates the amount of solar radiation and radiation reflected back the atmosphere which hits a surface at a specified tilt, and facing a specified azimuth. This functions is a wrapper for the incredibly comprehensive `pvlib library <https://github.com/pvlib/pvlib-python>`_, and requires it to be installed. Parameters ---------- latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Z : float, optional Elevation above sea level for the position, [m] moment : datetime, optionally with pytz info Time and date for the calculation, in UTC time OR in the time zone of the latitude/longitude specified BUT WITH A TZINFO ATTATCHED! Please be careful with this argument, time zones are confusing. [-] surface_tilt : float The angle above the horizontal of the object being hit by radiation, [degrees] surface_azimuth : float The angle the object is facing (positive, North eastwards 0° to 360°), [degrees] T : float, optional Temperature of atmosphere at ground level, [K] P : float, optional Pressure of atmosphere at ground level, [Pa] solar_constant : float, optional The amount of solar radiation which reaches earth's disk (at a standardized distance of 1 AU); this constant is independent of activity or conditions on earth, but will vary throughout the sun's lifetime and may increase or decrease slightly due to solar activity, [W/m^2] atmos_refract : float, optional Atmospheric refractivity at sunrise/sunset (0.5667 deg is an often used value; this varies substantially and has an impact of a few minutes on when sunrise and sunset is), [degrees] albedo : float, optional The average amount of reflection of the terrain surrounding the object at quite a distance; this impacts how much sunlight reflected off the ground, gets reflected back off clouds, [-] linke_turbidity : float, optional The amount of pollution/water in the sky versus a perfect clear sky; If not specified, this will be retrieved from a historical grid; typical values are 3 for cloudy, and 7 for severe pollution around a city, [-] extraradiation_method : str, optional The specified method to calculate the effect of earth's position on the amount of radiation which reaches earth according to the methods available in the `pvlib` library, [-] airmass_model : str, optional The specified method to calculate the amount of air the sunlight needs to travel through to reach the earth according to the methods available in the `pvlib` library, [-] cache : dict, optional Dictionary to to check for values to use to skip some calculations; `apparent_zenith`, `zenith`, `azimuth` supported, [-] Returns ------- poa_global : float The total irradiance in the plane of the surface, [W/m^2] poa_direct : float The total beam irradiance in the plane of the surface, [W/m^2] poa_diffuse : float The total diffuse irradiance in the plane of the surface, [W/m^2] poa_sky_diffuse : float The sky component of the diffuse irradiance, excluding the impact from the ground, [W/m^2] poa_ground_diffuse : float The ground-sky diffuse irradiance component, [W/m^2] Examples -------- >>> import pytz >>> solar_irradiation(Z=1100.0, latitude=51.0486, longitude=-114.07, linke_turbidity=3, ... moment=pytz.timezone('America/Edmonton').localize(datetime(2018, 4, 15, 13, 43, 5)), surface_tilt=41.0, ... surface_azimuth=180.0) (1065.7621896280, 945.2656564506, 120.49653317744, 95.31535344213, 25.181179735317) >>> cache = {'apparent_zenith': 41.099082295767545, 'zenith': 41.11285376417578, 'azimuth': 182.5631874250523} >>> solar_irradiation(Z=1100.0, latitude=51.0486, longitude=-114.07, ... moment=pytz.timezone('America/Edmonton').localize(datetime(2018, 4, 15, 13, 43, 5)), surface_tilt=41.0, ... linke_turbidity=3, T=300, P=1E5, ... surface_azimuth=180.0, cache=cache) (1042.567770367, 918.237754854, 124.3300155131, 99.622865737, 24.7071497753) At night, there is no solar radiation and this function returns zeros: >>> solar_irradiation(Z=1100.0, latitude=51.0486, longitude=-114.07, linke_turbidity=3, ... moment=pytz.timezone('America/Edmonton').localize(datetime(2018, 4, 15, 2, 43, 5)), surface_tilt=41.0, ... surface_azimuth=180.0) (0.0, -0.0, 0.0, 0.0, 0.0) Notes ----- The retrieval of `linke_turbidity` requires the pytables library (and Pandas); if it is not installed, specify a value of `linke_turbidity` to avoid the dependency. There is some redundancy of the calculated results, according to the following relations. The total irradiance is normally that desired for engineering calculations. poa_diffuse = poa_ground_diffuse + poa_sky_diffuse poa_global = poa_direct + poa_diffuse For a surface such as a pipe or vessel, an approach would be to split it into a number of rectangles and sum up the radiation absorbed by each. This calculation is fairly slow. References ---------- .. [1] Will Holmgren, Calama-Consulting, Tony Lorenzo, Uwe Krien, bmu, DaCoEx, mayudong, et al. Pvlib/Pvlib-Python: 0.5.1. Zenodo, 2017. https://doi.org/10.5281/zenodo.1016425. ''' # Atmospheric refraction at sunrise/sunset (0.5667 deg is an often used value) import calendar from fluids.optional import spa from fluids.optional.irradiance import (get_relative_airmass, get_absolute_airmass, ineichen, get_relative_airmass, get_absolute_airmass, get_total_irradiance) moment_timetuple = moment.timetuple() moment_arg_dni = (moment_timetuple.tm_yday if extraradiation_method == 'spencer' else moment) dni_extra = _get_extra_radiation_shim(moment_arg_dni, solar_constant=solar_constant, method=extraradiation_method, epoch_year=moment.year) if T is None or P is None: atmosphere = ATMOSPHERE_NRLMSISE00(Z=Z, latitude=latitude, longitude=longitude, day=moment_timetuple.tm_yday) if T is None: T = atmosphere.T if P is None: P = atmosphere.P if cache is not None and 'zenith' in cache: zenith = cache['zenith'] apparent_zenith = cache['apparent_zenith'] azimuth = cache['azimuth'] else: apparent_zenith, zenith, _, _, azimuth, _ = solar_position(moment=moment, latitude=latitude, longitude=longitude, Z=Z, T=T, P=P, atmos_refract=atmos_refract) if linke_turbidity is None: try: import pvlib except: raise ImportError(PVLIB_MISSING_MSG) from pvlib.clearsky import lookup_linke_turbidity import pandas as pd linke_turbidity = float(lookup_linke_turbidity( pd.DatetimeIndex([moment]), latitude, longitude).values) if airmass_model in apparent_zenith_airmass_models: used_zenith = apparent_zenith elif airmass_model in true_zenith_airmass_models: used_zenith = zenith else: raise ValueError('Unrecognized airmass model') relative_airmass = get_relative_airmass(used_zenith, model=airmass_model) airmass_absolute = get_absolute_airmass(relative_airmass, pressure=P) ans = ineichen(apparent_zenith=apparent_zenith, airmass_absolute=airmass_absolute, linke_turbidity=linke_turbidity, altitude=Z, dni_extra=solar_constant, perez_enhancement=True) ghi = ans['ghi'] dni = ans['dni'] dhi = ans['dhi'] # from pvlib.irradiance import get_total_irradiance ans = get_total_irradiance(surface_tilt=surface_tilt, surface_azimuth=surface_azimuth, solar_zenith=apparent_zenith, solar_azimuth=azimuth, dni=dni, ghi=ghi, dhi=dhi, dni_extra=dni_extra, airmass=airmass_absolute, albedo=albedo) poa_global = float(ans['poa_global']) poa_direct = float(ans['poa_direct']) poa_diffuse = float(ans['poa_diffuse']) poa_sky_diffuse = float(ans['poa_sky_diffuse']) poa_ground_diffuse = float(ans['poa_ground_diffuse']) return (poa_global, poa_direct, poa_diffuse, poa_sky_diffuse, poa_ground_diffuse)
def monthly_lt_nointerp(lat, lon, time=months): """monthly Linke turbidity factor without time interpolation""" return clearsky.lookup_linke_turbidity( time, lat, lon, interp_turbidity=False )
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 get_clearsky(self, times, model='ineichen', solar_position=None, dni_extra=None, **kwargs): """ Calculate the clear sky estimates of GHI, DNI, and/or DHI at this location. Parameters ---------- times: DatetimeIndex model: str, default 'ineichen' The clear sky model to use. Must be one of 'ineichen', 'haurwitz', 'simplified_solis'. solar_position : None or DataFrame, default None DataFrame with columns 'apparent_zenith', 'zenith', 'apparent_elevation'. dni_extra: None or numeric, default None If None, will be calculated from times. kwargs passed to the relevant functions. Climatological values are assumed in many cases. See source code for details! Returns ------- clearsky : DataFrame Column names are: ``ghi, dni, dhi``. """ if dni_extra is None: dni_extra = irradiance.get_extra_radiation(times) try: pressure = kwargs.pop('pressure') except KeyError: pressure = atmosphere.alt2pres(self.altitude) if solar_position is None: solar_position = self.get_solarposition(times, pressure=pressure, **kwargs) apparent_zenith = solar_position['apparent_zenith'] apparent_elevation = solar_position['apparent_elevation'] if model == 'ineichen': try: linke_turbidity = kwargs.pop('linke_turbidity') except KeyError: interp_turbidity = kwargs.pop('interp_turbidity', True) linke_turbidity = clearsky.lookup_linke_turbidity( times, self.latitude, self.longitude, interp_turbidity=interp_turbidity) try: airmass_absolute = kwargs.pop('airmass_absolute') except KeyError: airmass_absolute = self.get_airmass( times, solar_position=solar_position)['airmass_absolute'] cs = clearsky.ineichen(apparent_zenith, airmass_absolute, linke_turbidity, altitude=self.altitude, dni_extra=dni_extra, **kwargs) elif model == 'haurwitz': cs = clearsky.haurwitz(apparent_zenith) elif model == 'simplified_solis': cs = clearsky.simplified_solis( apparent_elevation, pressure=pressure, dni_extra=dni_extra, **kwargs) else: raise ValueError('{} is not a valid clear sky model. Must be ' 'one of ineichen, simplified_solis, haurwitz' .format(model)) return cs
def 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, default None If None, calculates clear sky data. Columns must be 'dni', 'ghi', 'dhi'. weather : None or DataFrame, default None If None, assumes air temperature is 20 C and wind speed is 0 m/s. Columns must be 'wind_speed', 'temp_air'. surface_tilt : None, float or Series, default None 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 : None, float or Series, default None 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, default None 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, default 'haydavies' Passed to system.get_irradiance. solar_position_method : str, default 'nrel_numpy' Passed to solarposition.get_solarposition. airmass_model : str, default 'kastenyoung1989' Passed to atmosphere.relativeairmass. altitude : None or float, default None If None, computed from pressure. Assumed to be 0 m if pressure is also None. pressure : None or float, default None 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, method=solar_position_method, **kwargs) # possible error with using apparent zenith with some models airmass = atmosphere.get_relative_airmass( solar_position['apparent_zenith'], model=airmass_model) airmass = atmosphere.get_absolute_airmass(airmass, pressure) dni_extra = pvlib.irradiance.get_extra_radiation(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.get_total_irradiance( 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 solar_irradiation(latitude, longitude, Z, moment, surface_tilt, surface_azimuth, T=None, P=None, solar_constant=1366.1, atmos_refract=0.5667, albedo=0.25, linke_turbidity=None, extraradiation_method='spencer', airmass_model='kastenyoung1989', cache=None): r'''Calculates the amount of solar radiation and radiation reflected back the atmosphere which hits a surface at a specified tilt, and facing a specified azimuth. This functions is a wrapper for the incredibly comprehensive `pvlib library <https://github.com/pvlib/pvlib-python>`_, and requires it to be installed. Parameters ---------- latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Z : float, optional Elevation above sea level for the position, [m] moment : datetime Time and date for the calculation, in local UTC time (not daylight savings time), [-] surface_tilt : float The angle above the horizontal of the object being hit by radiation, [degrees] surface_azimuth : float The angle the object is facing (positive North eastwards 0° to 360°), [degrees] T : float, optional Temperature of atmosphere at ground level, [K] P : float, optional Pressure of atmosphere at ground level, [Pa] solar_constant : float, optional The amount of solar radiation which reaches earth's disk (at a standardized distance of 1 AU); this constant is independent of activity or conditions on earth, but will vary throughout the sun's lifetime and may increase or decrease slightly due to solar activity, [W/m^2] atmos_refract : float, optional Atmospheric refractivity at sunrise/sunset (0.5667 deg is an often used value; this varies substantially and has an impact of a few minutes on when sunrise and sunset is), [degrees] albedo : float, optional The average amount of reflection of the terrain surrounding the object at quite a distance; this impacts how much sunlight reflected off the ground, gest reflected back off clouds, [-] linke_turbidity : float, optional The amount of pollution/water in the sky versus a perfect clear sky; If not specified, this will be retrieved from a historical grid; typical values are 3 for cloudy, and 7 for severe pollution around a city, [-] extraradiation_method : str, optional The specified method to calculate the effect of earth's position on the amount of radiation which reaches earth according to the methods available in the `pvlib` library, [-] airmass_model : str, optional The specified method to calculate the amount of air the sunlight needs to travel through to reach the earth according to the methods available in the `pvlib` library, [-] cache : dict, optional Dictionary to to check for values to use to skip some calculations; `apparent_zenith`, `zenith`, `azimuth` supported, [-] Returns ------- poa_global : float The total irradiance in the plane of the surface, [W/m^2] poa_direct : float The total beam irradiance in the plane of the surface, [W/m^2] poa_diffuse : float The total diffuse irradiance in the plane of the surface, [W/m^2] poa_sky_diffuse : float The sky component of the diffuse irradiance, excluding the impact from the ground, [W/m^2] poa_ground_diffuse : float The ground-sky diffuse irradiance component, [W/m^2] Examples -------- >>> solar_irradiation(Z=1100.0, latitude=51.0486, longitude=-114.07, ... moment=datetime(2018, 4, 15, 13, 43, 5), surface_tilt=41.0, ... surface_azimuth=180.0) (1065.7621896280812, 945.2656564506323, 120.49653317744884, 95.31535344213178, 25.181179735317063) >>> cache = {'apparent_zenith': 41.099082295767545, 'zenith': 41.11285376417578, 'azimuth': 182.5631874250523} >>> solar_irradiation(Z=1100.0, latitude=51.0486, longitude=-114.07, ... moment=datetime(2018, 4, 15, 13, 43, 5), surface_tilt=41.0, ... linke_turbidity=3, T=300, P=1E5, ... surface_azimuth=180.0, cache=cache) (1042.5677703677097, 918.2377548545295, 124.33001551318027, 99.6228657378363, 24.70714977534396) At night, there is no solar radiation and this function returns zeros: >>> solar_irradiation(Z=1100.0, latitude=51.0486, longitude=-114.07, ... moment=datetime(2018, 4, 15, 2, 43, 5), surface_tilt=41.0, ... surface_azimuth=180.0) (0.0, -0.0, 0.0, 0.0, 0.0) Notes ----- The retrieval of `linke_turbidity` requires the pytables library (and Pandas); if it is not installed, specify a value of `linke_turbidity` to avoid the dependency. There is some redundancy of the calculated results, according to the following relations. The total irradiance is normally that desired for engineering calculations. poa_diffuse = poa_ground_diffuse + poa_sky_diffuse poa_global = poa_direct + poa_diffuse FOr a surface such as a pipe or vessel, an approach would be to split it into a number of rectangles and sum up the radiation absorbed by each. This calculation is fairly slow. References ---------- .. [1] Will Holmgren, Calama-Consulting, Tony Lorenzo, Uwe Krien, bmu, DaCoEx, mayudong, et al. Pvlib/Pvlib-Python: 0.5.1. Zenodo, 2017. https://doi.org/10.5281/zenodo.1016425. ''' # Atmospheric refraction at sunrise/sunset (0.5667 deg is an often used value) from fluids.optional import spa from fluids.optional.irradiance import (get_relative_airmass, get_absolute_airmass, ineichen, get_relative_airmass, get_absolute_airmass, get_total_irradiance) # try: # import pvlib # except: # raise ImportError(PVLIB_MISSING_MSG) moment_timetuple = moment.timetuple() moment_arg_dni = (moment_timetuple.tm_yday if extraradiation_method == 'spencer' else moment) dni_extra = _get_extra_radiation_shim(moment_arg_dni, solar_constant=solar_constant, method=extraradiation_method, epoch_year=moment.year) if T is None or P is None: atmosphere = ATMOSPHERE_NRLMSISE00(Z=Z, latitude=latitude, longitude=longitude, day=moment_timetuple.tm_yday) if T is None: T = atmosphere.T if P is None: P = atmosphere.P if cache is not None and 'zenith' in cache: zenith = cache['zenith'] apparent_zenith = cache['apparent_zenith'] azimuth = cache['azimuth'] else: apparent_zenith, zenith, _, _, azimuth, _ = solar_position(moment=moment, latitude=latitude, longitude=longitude, Z=Z, T=T, P=P, atmos_refract=atmos_refract) if linke_turbidity is None: from pvlib.clearsky import lookup_linke_turbidity import pandas as pd linke_turbidity = float(lookup_linke_turbidity( pd.DatetimeIndex([moment]), latitude, longitude).values) if airmass_model in apparent_zenith_airmass_models: used_zenith = apparent_zenith elif airmass_model in true_zenith_airmass_models: used_zenith = zenith else: raise Exception('Unrecognized airmass model') relative_airmass = get_relative_airmass(used_zenith, model=airmass_model) airmass_absolute = get_absolute_airmass(relative_airmass, pressure=P) ans = ineichen(apparent_zenith=apparent_zenith, airmass_absolute=airmass_absolute, linke_turbidity=linke_turbidity, altitude=Z, dni_extra=solar_constant, perez_enhancement=True) ghi = ans['ghi'] dni = ans['dni'] dhi = ans['dhi'] # from pvlib.irradiance import get_total_irradiance ans = get_total_irradiance(surface_tilt=surface_tilt, surface_azimuth=surface_azimuth, solar_zenith=apparent_zenith, solar_azimuth=azimuth, dni=dni, ghi=ghi, dhi=dhi, dni_extra=dni_extra, airmass=airmass_absolute, albedo=albedo) poa_global = float(ans['poa_global']) poa_direct = float(ans['poa_direct']) poa_diffuse = float(ans['poa_diffuse']) poa_sky_diffuse = float(ans['poa_sky_diffuse']) poa_ground_diffuse = float(ans['poa_ground_diffuse']) return (poa_global, poa_direct, poa_diffuse, poa_sky_diffuse, poa_ground_diffuse)