def test_airmass(model, expected, zeniths): out = atmosphere.get_relative_airmass(zeniths, model) expected = np.array(expected) assert_allclose(out, expected, equal_nan=True, atol=0.001) # test series in/out. index does not matter # hits the isinstance() block in get_relative_airmass times = pd.date_range(start='20180101', periods=len(zeniths), freq='1s') zeniths = pd.Series(zeniths, index=times) expected = pd.Series(expected, index=times) out = atmosphere.get_relative_airmass(zeniths, model) assert_series_equal(out, expected, check_less_precise=True)
def simulate_sun_positions_by_station(station_index, solar_position_method, days, lead_times, latitudes, longitudes): """ This is the worker function of calculating sun positions at a specified station index. This function should be called from the parallel version of this function, `simulate_sun_positions`. Direct use of this function is discouraged. Please use the parallel version of this fucntion, `simulate_sun_positions`. :param station_index: A station index for simulation :param days: See `simulate_sun_positions` :param lead_times: See `simulate_sun_positions` :param latitudes: See `simulate_sun_positions` :param longitudes: See `simulate_sun_positions` :param solar_position_method: See `simulate_sun_positions` :return: A list with DNI, air mass, zenith, apparent zenith, and azimuth. """ # Initialization num_lead_times, num_days = len(lead_times), len(days) dni_extra = np.zeros((num_lead_times, num_days)) air_mass = np.zeros((num_lead_times, num_days)) zenith = np.zeros((num_lead_times, num_days)) apparent_zenith = np.zeros((num_lead_times, num_days)) azimuth = np.zeros((num_lead_times, num_days)) # Determine the current location current_location = location.Location(latitude=latitudes[station_index], longitude=longitudes[station_index]) for day_index in range(num_days): for lead_time_index in range(num_lead_times): # Determine the current time current_posix = days[day_index] + lead_times[lead_time_index] current_time = pd.Timestamp(current_posix, tz="UTC", unit='s') # Calculate sun position solar_position = current_location.get_solarposition( current_time, method=solar_position_method, numthreads=1) # Calculate extraterrestrial DNI dni_extra[lead_time_index, day_index] = irradiance.get_extra_radiation(current_time) # Calculate air mass air_mass[lead_time_index, day_index] = atmosphere.get_relative_airmass( solar_position["apparent_zenith"]) # Store other keys zenith[lead_time_index, day_index] = solar_position["zenith"] apparent_zenith[lead_time_index, day_index] = solar_position["apparent_zenith"] azimuth[lead_time_index, day_index] = solar_position["azimuth"] return [dni_extra, air_mass, zenith, apparent_zenith, azimuth]
def _air_mass(zenith, pressure): """ Returns the absolute air mass. """ # Methods from PvLib rel_am = get_relative_airmass(zenith=zenith) abs_am = get_absolute_airmass(rel_am, pressure=pressure) return abs_am
def test_airmass_default_post(self): am_data = {'zenith_data': AM_DATA['zenith_data']} r = self.client.post('/api/v1/pvlib/airmass/', am_data) self.assertEqual(r.status_code, 200) s = pd.Series(r.json()) t = pd.DatetimeIndex(s.index) zdata = pd.read_json(ZDATA).T times = pd.DatetimeIndex(zdata.index) am = atmosphere.get_relative_airmass(zdata['apparent_zenith']) assert np.allclose(times.values.astype(int), t.values.astype(int)) assert np.allclose(am, s)
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, default None Only used if solar_position is not provided. solar_position : None or DataFrame, default None DataFrame with with columns 'apparent_zenith', 'zenith'. model : str, default 'kastenyoung1989' Relative airmass model. See :py:func:`pvlib.atmosphere.get_relative_airmass` for a list of available models. Returns ------- airmass : DataFrame Columns are 'airmass_relative', 'airmass_absolute' See also -------- pvlib.atmosphere.get_relative_airmass """ 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(f'{model} is not a valid airmass model') airmass_relative = atmosphere.get_relative_airmass(zenith, model) pressure = atmosphere.alt2pres(self.altitude) airmass_absolute = atmosphere.get_absolute_airmass( airmass_relative, pressure) airmass = pd.DataFrame(index=solar_position.index) airmass['airmass_relative'] = airmass_relative airmass['airmass_absolute'] = airmass_absolute return airmass
def airmass_resource(request): if request.method == 'GET': params = AirmassForm(request.GET) else: params = AirmassForm(request.POST) if params.is_valid(): zenith_data = params.cleaned_data['zenith_data'] zenith_file = params.cleaned_data['zenith_file'] filetype = params.cleaned_data['filetype'] model = params.cleaned_data['model'] else: return JsonResponse(params.errors, status=400) if filetype is None: filetype = 'json' if zenith_data is None and zenith_file is None: return JsonResponse(params.errors, status=400) if zenith_data: zenith_data = json.loads(zenith_data) if len(zenith_data) == 0: return JsonResponse( {"zenith_data": ["Invalid data in zenith data"]}, status=400) times = pd.DatetimeIndex(zenith_data.keys()) # keys not necessary columns = {} for row in zenith_data.values(): if not columns: columns = {k: [float(v)] for k, v in row.items()} else: for k, v in row.items(): columns[k].append(float(v)) zenith_data = pd.DataFrame(columns, index=times) if zenith_file and zenith_data is None: if filetype == 'json': zenith_data = pd.read_json(zenith_file) elif filetype == 'csv': zenith_data = pd.read_csv(zenith_file) elif filetype == 'xlsx': zenith_data = pd.read_excel(zenith_file) else: return JsonResponse(params.errors, status=400) if not model: model = 'kastenyoung1989' apparent_or_true = APPARENT_OR_TRUE.get(model) if not apparent_or_true: return JsonResponse(params.errors, status=400) am = atmosphere.get_relative_airmass(zenith_data[apparent_or_true], model) am.fillna(-9999.9, inplace=True) am.index = times.strftime('%Y-%m-%dT%H:%M:%S%z') data = am.to_dict() return JsonResponse(data)
def __init__(self, lat, lon, elevation, date): # Must be longer than a day to make sure we see a continuous cycle index = pd.date_range(start=date, freq='1s', periods=24 * 60 * 60 * 2) # Generate solar position df self.solar_df = solarposition.get_solarposition(index, lat, lon) # generate sky df solpos = solarposition.get_solarposition(index, lat, lon) apparent_zenith = solpos['apparent_zenith'] airmass = atmosphere.get_relative_airmass(apparent_zenith) pressure = pvlib.atmosphere.alt2pres(elevation) airmass = pvlib.atmosphere.get_absolute_airmass(airmass, pressure) linke_turbidity = pvlib.clearsky.lookup_linke_turbidity( index, lat, lon) dni_extra = pvlib.irradiance.get_extra_radiation(index) self.sky_df = clearsky.ineichen(apparent_zenith, airmass, linke_turbidity, elevation, dni_extra)
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, default None Only used if solar_position is not provided. solar_position : None or DataFrame, default None DataFrame with with columns 'apparent_zenith', 'zenith'. model : str, default 'kastenyoung1989' 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.get_relative_airmass(zenith, model) pressure = atmosphere.alt2pres(self.altitude) airmass_absolute = atmosphere.get_absolute_airmass(airmass_relative, pressure) airmass = pd.DataFrame(index=solar_position.index) airmass['airmass_relative'] = airmass_relative airmass['airmass_absolute'] = airmass_absolute return airmass
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_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:meth:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) return irradiance.get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def __init__(self, panel=None, forecast_length=7, forecast_model=None): self.forecast_length = forecast_length if panel == None: self.panel = Panel() else: self.panel = panel if forecast_model == None: self.fm = GFS() else: self.fm = forecast_model self.start = pd.Timestamp(datetime.date.today(), tz=self.panel.tz) # today's date self.end = self.start + pd.Timedelta( days=forecast_length) # days from today print( "getting processed data with lat: %s, lng: %s, start:%s, end:%s" % (self.panel.latitude, self.panel.longitude, self.start, self.end)) # get forecast data forecast_data = self.fm.get_processed_data(self.panel.latitude, self.panel.longitude, self.start, self.end) ghi = forecast_data['ghi'] # get solar position time = forecast_data.index a_point = self.fm.location solpos = a_point.get_solarposition(time) # get PV(photovoltaic device) modules sandia_modules = pvsystem.retrieve_sam('SandiaMod') sandia_module = sandia_modules.Canadian_Solar_CS5P_220M___2009_ dni_extra = irradiance.get_extra_radiation( self.fm.time) # extra terrestrial radiation airmass = atmosphere.get_relative_airmass(solpos['apparent_zenith']) # POA: Plane Of Array: an image sensing device consisting of an array # (typically rectangular) of light-sensing pixels at the focal plane of a lens. # https://en.wikipedia.org/wiki/Staring_array # Diffuse sky radiation is solar radiation reaching the Earth's surface after # having been scattered from the direct solar beam by molecules or particulates # in the atmosphere. # https://en.wikipedia.org/wiki/Diffuse_sky_radiation poa_sky_diffuse = irradiance.haydavies(self.panel.surface_tilt, self.panel.surface_azimuth, forecast_data['dhi'], forecast_data['dni'], dni_extra, solpos['apparent_zenith'], solpos['azimuth']) # Diffuse reflection is the reflection of light or other waves or particles # from a surface such that a ray incident on the surface is scattered at many # angles rather than at just one angle as in the case of specular reflection. poa_ground_diffuse = irradiance.get_ground_diffuse( self.panel.surface_tilt, ghi, albedo=self.panel.albedo) # AOI: Angle Of Incidence aoi = irradiance.aoi(self.panel.surface_tilt, self.panel.surface_azimuth, solpos['apparent_zenith'], solpos['azimuth']) # irradiance is the radiant flux (power) received by a surface per unit area # https://en.wikipedia.org/wiki/Irradiance poa_irrad = irradiance.poa_components(aoi, forecast_data['dni'], poa_sky_diffuse, poa_ground_diffuse) temperature = forecast_data['temp_air'] wnd_spd = forecast_data['wind_speed'] # pvtemps: pv temperature pvtemps = pvsystem.sapm_celltemp(poa_irrad['poa_global'], wnd_spd, temperature) # irradiance actually used by PV effective_irradiance = pvsystem.sapm_effective_irradiance( poa_irrad.poa_direct, poa_irrad.poa_diffuse, airmass, aoi, sandia_module) # SAPM: Sandia PV Array Performance Model # https://pvpmc.sandia.gov/modeling-steps/2-dc-module-iv/point-value-models/sandia-pv-array-performance-model/ self.sapm_out = pvsystem.sapm(effective_irradiance, pvtemps['temp_cell'], sandia_module) sapm_inverters = pvsystem.retrieve_sam('sandiainverter') sapm_inverter = sapm_inverters[ 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] self.ac_power = pvsystem.snlinverter(self.sapm_out.v_mp, self.sapm_out.p_mp, sapm_inverter)
def get_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:meth:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.get_total_irradiance`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) # SingleAxisTracker only supports a single Array, but we need the # validate/iterate machinery so that single length tuple input/output # is handled the same as PVSystem.get_irradiance. GH 1159 dni = self._validate_per_array(dni, system_wide=True) ghi = self._validate_per_array(ghi, system_wide=True) dhi = self._validate_per_array(dhi, system_wide=True) return tuple( irradiance.get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.arrays[0].albedo, **kwargs) for array, dni, ghi, dhi in zip(self.arrays, dni, ghi, dhi))
def 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') 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 get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :py:func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a Fixed panel. Parameters ---------- solar_zenith : float or Series. Solar zenith angle. solar_azimuth : float or Series. Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : None, float or Series, default None Extraterrestrial direct normal irradiance airmass : None, float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) return irradiance.get_total_irradiance(self.surface_tilt, self.surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def test_airmass(model): out = atmosphere.get_relative_airmass(ephem_data['zenith'], model) assert isinstance(out, pd.Series) out = atmosphere.get_relative_airmass(ephem_data['zenith'].values, model) assert isinstance(out, np.ndarray)
ax = solar_df.loc[solar_df.index, ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1) ax.axhline(0, color='darkgray') # add 0 deg line for sunrise/sunset ax.axhline(180, color='darkgray') # add 180 deg line for azimuth at solar noon ax.set_ylim(-60, 200) # zoom in, but cuts off full azimuth range ax.set_xlabel('Time (UTC)') ax.set_ylabel('(degrees)') solpos = solarposition.get_solarposition(index, lat, lon) apparent_zenith = solpos['apparent_zenith'] airmass = atmosphere.get_relative_airmass(apparent_zenith) pressure = pvlib.atmosphere.alt2pres(alt) airmass = pvlib.atmosphere.get_absolute_airmass(airmass, pressure) linke_turbidity = pvlib.clearsky.lookup_linke_turbidity(index, lat, lon) dni_extra = pvlib.irradiance.get_extra_radiation(index) sky_df = clearsky.ineichen(apparent_zenith, airmass, linke_turbidity, alt, dni_extra) #trim sky df sky_df = sky_df.drop(sky_df[sky_df.index < left].index) sky_df = sky_df.drop(sky_df[sky_df.index > right].index) print(sky_df) plt.figure() ax = sky_df.plot()
def test_airmass_scalar(): assert not np.isnan(atmosphere.get_relative_airmass(10))
# Retrieve data forecast_data = fm.get_processed_data(latitude, longitude, start, end) ghi = forecast_data['ghi'] sandia_modules = pvsystem.retrieve_sam('SandiaMod') sandia_module = sandia_modules.Canadian_Solar_CS5P_220M___2009_ # retrieve time and location parameters time = forecast_data.index a_point = fm.location solpos = a_point.get_solarposition(time) dni_extra = irradiance.get_extra_radiation(fm.time) airmass = atmosphere.get_relative_airmass(solpos['apparent_zenith']) poa_sky_diffuse = irradiance.haydavies(surface_tilt, surface_azimuth, forecast_data['dhi'], forecast_data['dni'], dni_extra, solpos['apparent_zenith'], solpos['azimuth']) poa_ground_diffuse = irradiance.get_ground_diffuse(surface_tilt, ghi, albedo=albedo) aoi = irradiance.aoi(surface_tilt, surface_azimuth, solpos['apparent_zenith'], solpos['azimuth']) poa_irrad = irradiance.poa_components(aoi, forecast_data['dni'], poa_sky_diffuse, poa_ground_diffuse) temperature = forecast_data['temp_air'] wnd_spd = forecast_data['wind_speed'] pvtemps = pvsystem.sapm_celltemp(poa_irrad['poa_global'], wnd_spd, temperature) effective_irradiance = pvsystem.sapm_effective_irradiance(poa_irrad.poa_direct,
def test_bird(): """Test Bird/Hulstrom Clearsky Model""" times = pd.date_range(start='1/1/2015 0:00', end='12/31/2015 23:00', freq='H') tz = -7 # test timezone gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz)) times = times.tz_localize(gmt_tz) # set timezone # match test data from BIRD_08_16_2012.xls latitude = 40. longitude = -105. press_mB = 840. o3_cm = 0.3 h2o_cm = 1.5 aod_500nm = 0.1 aod_380nm = 0.15 b_a = 0.85 alb = 0.2 eot = solarposition.equation_of_time_spencer71(times.dayofyear) hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15. declination = solarposition.declination_spencer71(times.dayofyear) zenith = solarposition.solar_zenith_analytical( np.deg2rad(latitude), np.deg2rad(hour_angle), declination ) zenith = np.rad2deg(zenith) airmass = atmosphere.get_relative_airmass(zenith, model='kasten1966') etr = irradiance.get_extra_radiation(times) # test Bird with time series data field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi') irrads = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100., etr, b_a, alb ) Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names) clearsky_path = os.path.dirname(os.path.abspath(__file__)) pvlib_path = os.path.dirname(clearsky_path) data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012.csv') testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata.index = times[1:48] assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48])) assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4) assert np.allclose(testdata['Hour Angle'], hour_angle[1:48]) assert np.allclose(testdata['Zenith Ang'], zenith[1:48]) dawn = zenith < 88. dusk = testdata['Zenith Ang'] < 88. am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0) assert np.allclose( testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3 ) direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3 ) direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3 ) global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3 ) diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3 ) # test keyword parameters irrads2 = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr ) Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names) clearsky_path = os.path.dirname(os.path.abspath(__file__)) pvlib_path = os.path.dirname(clearsky_path) data_path = os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012_patm.csv') testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata2.index = times[1:48] direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3 ) direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3 ) global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3 ) diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3 ) # test scalars just at noon # XXX: calculations start at 12am so noon is at index = 12 irrads3 = clearsky.bird( zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12] ) Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names) # XXX: testdata starts at 1am so noon is at index = 11 np.allclose( [Eb3, Ebh3, Gh3, Dh3], testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11], rtol=1e-3) return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)
def test_bird(): """Test Bird/Hulstrom Clearsky Model""" times = pd.date_range(start='1/1/2015 0:00', end='12/31/2015 23:00', freq='H') tz = -7 # test timezone gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz)) times = times.tz_localize(gmt_tz) # set timezone # match test data from BIRD_08_16_2012.xls latitude = 40. longitude = -105. press_mB = 840. o3_cm = 0.3 h2o_cm = 1.5 aod_500nm = 0.1 aod_380nm = 0.15 b_a = 0.85 alb = 0.2 eot = solarposition.equation_of_time_spencer71(times.dayofyear) hour_angle = solarposition.hour_angle(times, longitude, eot) - 0.5 * 15. declination = solarposition.declination_spencer71(times.dayofyear) zenith = solarposition.solar_zenith_analytical( np.deg2rad(latitude), np.deg2rad(hour_angle), declination ) zenith = np.rad2deg(zenith) airmass = atmosphere.get_relative_airmass(zenith, model='kasten1966') etr = irradiance.get_extra_radiation(times) # test Bird with time series data field_names = ('dni', 'direct_horizontal', 'ghi', 'dhi') irrads = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, o3_cm, press_mB * 100., etr, b_a, alb ) Eb, Ebh, Gh, Dh = (irrads[_] for _ in field_names) data_path = DATA_DIR / 'BIRD_08_16_2012.csv' testdata = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata.index = times[1:48] assert np.allclose(testdata['DEC'], np.rad2deg(declination[1:48])) assert np.allclose(testdata['EQT'], eot[1:48], rtol=1e-4) assert np.allclose(testdata['Hour Angle'], hour_angle[1:48]) assert np.allclose(testdata['Zenith Ang'], zenith[1:48]) dawn = zenith < 88. dusk = testdata['Zenith Ang'] < 88. am = pd.Series(np.where(dawn, airmass, 0.), index=times).fillna(0.0) assert np.allclose( testdata['Air Mass'].where(dusk, 0.), am[1:48], rtol=1e-3 ) direct_beam = pd.Series(np.where(dawn, Eb, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Beam'].where(dusk, 0.), direct_beam[1:48], rtol=1e-3 ) direct_horz = pd.Series(np.where(dawn, Ebh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Direct Hz'].where(dusk, 0.), direct_horz[1:48], rtol=1e-3 ) global_horz = pd.Series(np.where(dawn, Gh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Global Hz'].where(dusk, 0.), global_horz[1:48], rtol=1e-3 ) diffuse_horz = pd.Series(np.where(dawn, Dh, 0.), index=times).fillna(0.) assert np.allclose( testdata['Dif Hz'].where(dusk, 0.), diffuse_horz[1:48], rtol=1e-3 ) # test keyword parameters irrads2 = clearsky.bird( zenith, airmass, aod_380nm, aod_500nm, h2o_cm, dni_extra=etr ) Eb2, Ebh2, Gh2, Dh2 = (irrads2[_] for _ in field_names) data_path = DATA_DIR / 'BIRD_08_16_2012_patm.csv' testdata2 = pd.read_csv(data_path, usecols=range(1, 26), header=1).dropna() testdata2.index = times[1:48] direct_beam2 = pd.Series(np.where(dawn, Eb2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Beam'].where(dusk, 0.), direct_beam2[1:48], rtol=1e-3 ) direct_horz2 = pd.Series(np.where(dawn, Ebh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Direct Hz'].where(dusk, 0.), direct_horz2[1:48], rtol=1e-3 ) global_horz2 = pd.Series(np.where(dawn, Gh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Global Hz'].where(dusk, 0.), global_horz2[1:48], rtol=1e-3 ) diffuse_horz2 = pd.Series(np.where(dawn, Dh2, 0.), index=times).fillna(0.) assert np.allclose( testdata2['Dif Hz'].where(dusk, 0.), diffuse_horz2[1:48], rtol=1e-3 ) # test scalars just at noon # XXX: calculations start at 12am so noon is at index = 12 irrads3 = clearsky.bird( zenith[12], airmass[12], aod_380nm, aod_500nm, h2o_cm, dni_extra=etr[12] ) Eb3, Ebh3, Gh3, Dh3 = (irrads3[_] for _ in field_names) # XXX: testdata starts at 1am so noon is at index = 11 np.allclose( [Eb3, Ebh3, Gh3, Dh3], testdata2[['Direct Beam', 'Direct Hz', 'Global Hz', 'Dif Hz']].iloc[11], rtol=1e-3) return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=times)
def test_airmass_invalid(): with pytest.raises(ValueError): atmosphere.get_relative_airmass(ephem_data['zenith'], 'invalid')
def test_get_absolute_airmass(): relative_am = atmosphere.get_relative_airmass(ephem_data['zenith'], 'simple') atmosphere.get_absolute_airmass(relative_am) atmosphere.get_absolute_airmass(relative_am, pressure=100000)
tilt = 37 azimuth = 180 pressure = 101300 # sea level, roughly water_vapor_content = 0.5 # cm tau500 = 0.1 ozone = 0.31 # atm-cm albedo = 0.2 times = pd.date_range('1984-03-20 06:17', freq='h', periods=6, tz='Etc/GMT+7') solpos = solarposition.get_solarposition(times, lat, lon) aoi = irradiance.aoi(tilt, azimuth, solpos.apparent_zenith, solpos.azimuth) # The technical report uses the 'kasten1966' airmass model, but later # versions of SPECTRL2 use 'kastenyoung1989'. Here we use 'kasten1966' # for consistency with the technical report. relative_airmass = atmosphere.get_relative_airmass(solpos.apparent_zenith, model='kasten1966') # %% # With all the necessary inputs in hand we can model spectral irradiance using # :py:func:`pvlib.spectrum.spectrl2`. Note that because we are calculating # the spectra for more than one set of conditions, we will get back 2-D # arrays (one dimension for wavelength, one for time). spectra = spectrum.spectrl2( apparent_zenith=solpos.apparent_zenith, aoi=aoi, surface_tilt=tilt, ground_albedo=albedo, surface_pressure=pressure, relative_airmass=relative_airmass, precipitable_water=water_vapor_content,
def test_airmass_invalid(): with pytest.raises(ValueError): atmosphere.get_relative_airmass(0, 'invalid')
def get_airmass(solpos): # Calculate airmass. Lots of model options here, see the ``atmosphere`` module tutorial for more details. airmass = atmosphere.get_relative_airmass(solpos['apparent_zenith']) return airmass
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 calculate_overcast_spectrl2(): """ This example will loop over a range of cloud covers and latitudes (at longitude=0) for a specific date and calculate the spectral irradiance with and without accounting for clouds. Clouds are accounted for by applying the cloud opacity factor defined in [1]. Several steps are required: 1. Calculate the atmospheric and solar conditions for the location and time 2. Calculate the spectral irradiance using `pvlib.spectrum.spectrl2` for clear sky conditions 3. Calculate the dni, dhi, and ghi for cloudy conditions using `pvlib.irradiance.campbell_norman` 4. Determine total in-plane irradiance and its beam, sky diffuse and ground reflected components for cloudy conditions - `pvlib.irradiance.get_total_irradiance` 5. Calculate the dni, dhi, and ghi for clear sky conditions using `pvlib.irradiance.campbell_norman` 6. Determine total in-plane irradiance and its beam, sky diffuse and ground reflected components for clear sky conditions - `pvlib.irradiance.get_total_irradiance` 7. Calculate the cloud opacity factor [1] and scale the spectral results from step 4 - func cloud_opacity_factor 8. Plot the results - func plot_spectral_irradiance """ month = 2 hour_of_day = 12 altitude = 0.0 longitude = 0.0 latitudes = [10, 40] # cloud cover in fraction units cloud_covers = [0.2, 0.5] water_vapor_content = 0.5 tau500 = 0.1 ground_albedo = 0.06 ozone = 0.3 surface_tilt = 0.0 ctime, pv_system = setup_pv_system(month, hour_of_day) for cloud_cover in cloud_covers: for latitude in latitudes: sol = get_solarposition(ctime, latitude, longitude) az = sol['apparent_zenith'].to_numpy() airmass_relative = get_relative_airmass(az, model='kastenyoung1989') pressure = pvlib.atmosphere.alt2pres(altitude) az = sol['apparent_zenith'].to_numpy() azimuth = sol['azimuth'].to_numpy() surface_azimuth = pv_system['surface_azimuth'] transmittance = (1.0 - cloud_cover) * 0.75 calc_aoi = aoi(surface_tilt, surface_azimuth, az, azimuth) # day of year is an int64index array so access first item day_of_year = ctime.dayofyear[0] spectra = pvlib.spectrum.spectrl2( apparent_zenith=az, aoi=calc_aoi, surface_tilt=surface_tilt, ground_albedo=ground_albedo, surface_pressure=pressure, relative_airmass=airmass_relative, precipitable_water=water_vapor_content, ozone=ozone, aerosol_turbidity_500nm=tau500, dayofyear=day_of_year) irrads_clouds = campbell_norman(sol['zenith'].to_numpy(), transmittance) # Convert the irradiance to a plane with tilt zero # horizontal to the earth. This is done applying # tilt=0 to POA calculations using the output from # `campbell_norman`. The POA calculations include # calculating sky and ground diffuse light where # specific models can be selected (we use default). poa_irr_clouds = get_total_irradiance( surface_tilt=surface_tilt, surface_azimuth=pv_system['surface_azimuth'], dni=irrads_clouds['dni'], ghi=irrads_clouds['ghi'], dhi=irrads_clouds['dhi'], solar_zenith=sol['apparent_zenith'], solar_azimuth=sol['azimuth']) show_info(latitude, poa_irr_clouds) zen = sol['zenith'].to_numpy() irr_clearsky = campbell_norman(zen, transmittance=0.75) poa_irr_clearsky = get_total_irradiance( surface_tilt=surface_tilt, surface_azimuth=pv_system['surface_azimuth'], dni=irr_clearsky['dni'], ghi=irr_clearsky['ghi'], dhi=irr_clearsky['dhi'], solar_zenith=sol['apparent_zenith'], solar_azimuth=sol['azimuth']) show_info(latitude, poa_irr_clearsky) poa_dr = poa_irr_clouds['poa_direct'].values poa_diff = poa_irr_clouds['poa_diffuse'].values poa_global = poa_irr_clouds['poa_global'].values f_dir, f_diff = cloud_opacity_factor(poa_dr, poa_diff, poa_global, spectra) plot_spectral_irr(spectra, f_dir, f_diff, lat=latitude, doy=day_of_year, year=ctime.year[0], clouds=cloud_cover)
def perez_diffuse_luminance(timestamps, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, dhi): """Function used to calculate the luminance and the view factor terms from the Perez diffuse light transposition model, as implemented in the ``pvlib-python`` library. This function was custom made to allow the calculation of the circumsolar component on the back surface as well. Otherwise, the ``pvlib`` implementation would ignore it. Parameters ---------- timestamps : array-like simulation timestamps surface_tilt : array-like Surface tilt angles in decimal degrees. surface_tilt must be >=0 and <=180. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90) surface_azimuth : array-like The azimuth of the rotated panel, determined by projecting the vector normal to the panel's surface to the earth's surface [degrees]. solar_zenith : array-like solar zenith angles solar_azimuth : array-like solar azimuth angles dni : array-like values for direct normal irradiance dhi : array-like values for diffuse horizontal irradiance Returns ------- df_inputs : `pandas.DataFrame` Dataframe with the following columns: ['solar_zenith', 'solar_azimuth', 'surface_tilt', 'surface_azimuth', 'dhi', 'dni', 'vf_horizon', 'vf_circumsolar', 'vf_isotropic', 'luminance_horizon', 'luminance_circuqmsolar', 'luminance_isotropic', 'poa_isotropic', 'poa_circumsolar', 'poa_horizon', 'poa_total_diffuse'] """ # Create a dataframe to help filtering on all arrays df_inputs = pd.DataFrame( { 'surface_tilt': surface_tilt, 'surface_azimuth': surface_azimuth, 'solar_zenith': solar_zenith, 'solar_azimuth': solar_azimuth, 'dni': dni, 'dhi': dhi }, index=pd.DatetimeIndex(timestamps)) dni_et = irradiance.get_extra_radiation(df_inputs.index.dayofyear) am = atmosphere.get_relative_airmass(df_inputs.solar_zenith) # Need to treat the case when the sun is hitting the back surface of pvrow aoi_proj = irradiance.aoi_projection(df_inputs.surface_tilt, df_inputs.surface_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) sun_hitting_back_surface = ((aoi_proj < 0) & (df_inputs.solar_zenith <= 90)) df_inputs_back_surface = df_inputs.loc[sun_hitting_back_surface].copy() # Reverse the surface normal to switch to back-surface circumsolar calc df_inputs_back_surface.loc[:, 'surface_azimuth'] = ( df_inputs_back_surface.loc[:, 'surface_azimuth'] - 180.) df_inputs_back_surface.loc[:, 'surface_azimuth'] = np.mod( df_inputs_back_surface.loc[:, 'surface_azimuth'], 360.) df_inputs_back_surface.loc[:, 'surface_tilt'] = ( 180. - df_inputs_back_surface.surface_tilt) if df_inputs_back_surface.shape[0] > 0: # Use recursion to calculate circumsolar luminance for back surface df_inputs_back_surface = perez_diffuse_luminance( *breakup_df_inputs(df_inputs_back_surface)) # Calculate Perez diffuse components components = irradiance.perez(df_inputs.surface_tilt, df_inputs.surface_azimuth, df_inputs.dhi, df_inputs.dni, dni_et, df_inputs.solar_zenith, df_inputs.solar_azimuth, am, return_components=True) # Calculate Perez view factors: a = irradiance.aoi_projection(df_inputs.surface_tilt, df_inputs.surface_azimuth, df_inputs.solar_zenith, df_inputs.solar_azimuth) a = np.maximum(a, 0) b = cosd(df_inputs.solar_zenith) b = np.maximum(b, cosd(85)) vf_perez = pd.DataFrame( { 'vf_horizon': sind(df_inputs.surface_tilt), 'vf_circumsolar': a / b, 'vf_isotropic': (1. + cosd(df_inputs.surface_tilt)) / 2. }, index=df_inputs.index) # Calculate diffuse luminance luminance = pd.DataFrame(np.array([ components['horizon'] / vf_perez['vf_horizon'], components['circumsolar'] / vf_perez['vf_circumsolar'], components['isotropic'] / vf_perez['vf_isotropic'] ]).T, index=df_inputs.index, columns=[ 'luminance_horizon', 'luminance_circumsolar', 'luminance_isotropic' ]) luminance.loc[components['sky_diffuse'] == 0, :] = 0. # Format components column names components = components.rename( columns={ 'isotropic': 'poa_isotropic', 'circumsolar': 'poa_circumsolar', 'horizon': 'poa_horizon' }) df_inputs = pd.concat([df_inputs, components, vf_perez, luminance], axis=1, join='outer') df_inputs = df_inputs.rename(columns={'sky_diffuse': 'poa_total_diffuse'}) # Adjust the circumsolar luminance when it hits the back surface if df_inputs_back_surface.shape[0] > 0: df_inputs.loc[sun_hitting_back_surface, 'luminance_circumsolar'] = ( df_inputs_back_surface.loc[:, 'luminance_circumsolar']) return df_inputs