def time_get_total_irradiance(self): irradiance.get_total_irradiance(self.tilt, self.azimuth, self.solar_position.apparent_zenith, self.solar_position.azimuth, self.clearsky_irradiance.dni, self.clearsky_irradiance.ghi, self.clearsky_irradiance.dhi)
def test_get_total_irradiance_missing_dni_extra(): msg = 'dni_extra is required' with pytest.raises(ValueError, match=msg): irradiance.get_total_irradiance(32, 180, 10, 180, dni=1000, ghi=1100, dhi=100, model='haydavies')
def system_power(request, system_location, naive_times): tilt = request.param[0] azimuth = request.param[1] local_time = naive_times.tz_localize(system_location.tz) clearsky = system_location.get_clearsky(local_time, model='simplified_solis') solar_position = system_location.get_solarposition(local_time) poa = irradiance.get_total_irradiance(tilt, azimuth, solar_position['zenith'], solar_position['azimuth'], **clearsky) temp_cell = pvlib.temperature.sapm_cell( poa['poa_global'], 25, 0, **pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'] ['open_rack_glass_glass']) pdc = pvsystem.pvwatts_dc(poa['poa_global'], temp_cell, 100, -0.002) pac = pvsystem.inverter.pvwatts(pdc, 120) return { 'location': system_location, 'tilt': tilt, 'azimuth': azimuth, 'clearsky': clearsky, 'solar_position': solar_position, 'ac': pac }
def get_irradiance(self, date, tilt, surface_azimuth): # Creates one day's worth of x min intervals times = pd.date_range(date, freq='1min', periods=60 * 24, tz=self.site.tz) # Generate clearsky data using the Ineichen model, which is the default # The get_clearsky method returns a dataframe with values for GHI, DNI, # and DHI clearsky = self.site.get_clearsky(times) # Get solar azimuth and zenith to pass to the transposition function solar_position = self.site.get_solarposition(times=times) # Use the get_total_irradiance function to transpose the GHI to POA POA_irradiance = irradiance.get_total_irradiance( surface_tilt=tilt, surface_azimuth=surface_azimuth, dni=clearsky['dni'], ghi=clearsky['ghi'], dhi=clearsky['dhi'], solar_zenith=solar_position['apparent_zenith'], solar_azimuth=solar_position['azimuth']) # Return DataFrame with only GHI and POA return pd.DataFrame({ 'GHI': clearsky['ghi'], 'POA': POA_irradiance['poa_global'] })
def _calculate_poa(tmy, PV): """ Input: tmy irradiance data Output: PV.poa -- plane of array Remember, PV GIS (C) defines the folowing: G(h): Global irradiance on the horizontal plane (W/m2) === GHI Gb(n): Beam/direct irradiance on a plane always normal to sun rays (W/m2) === DNI Gd(h): Diffuse irradiance on the horizontal plane (W/m2) === DHI """ # define site location for getting solar positions tmy.site = location.Location(tmy.lat, tmy.lon, tmy.tz) # Get solar azimuth and zenith to pass to the transposition function solar_position = tmy.site.get_solarposition(times=tmy.index) # Use get_total_irradiance to transpose, based on solar position POA_irradiance = irradiance.get_total_irradiance( surface_tilt=PV.tilt, surface_azimuth=PV.azimuth, dni=tmy["Gb(n)"], ghi=tmy["G(h)"], dhi=tmy["Gd(h)"], solar_zenith=solar_position["apparent_zenith"], solar_azimuth=solar_position["azimuth"], ) # Return DataFrame PV.poa = POA_irradiance["poa_global"] return
def get_irradiance(site_location, tilt, surface_azimuth, times): clearsky = site_location.get_clearsky(times) solar_position = site_location.get_solarposition(times) POA_irradiance = irradiance.get_total_irradiance( surface_tilt=tilt, surface_azimuth=surface_azimuth, dni=clearsky['dni'], ghi=clearsky['ghi'], dhi=clearsky['dhi'], solar_zenith=solar_position['apparent_zenith'], solar_azimuth=solar_position['azimuth']) return pd.DataFrame(POA_irradiance['poa_global'])
def calculate_poa(tmy, solar_position, surface_tilt, surface_azimuth): # Use the get_total_irradiance function to transpose the irradiance # components to POA irradiance poa = irradiance.get_total_irradiance( surface_tilt=surface_tilt, surface_azimuth=surface_azimuth, dni=tmy['DNI'], ghi=tmy['GHI'], dhi=tmy['DHI'], solar_zenith=solar_position['apparent_zenith'], solar_azimuth=solar_position['azimuth'], model='isotropic') return poa['poa_global'] # just return the total in-plane irradiance
def test_get_total_irradiance_missing_airmass(): total = irradiance.get_total_irradiance(32, 180, 10, 180, dni=1000, ghi=1100, dhi=100, dni_extra=1400, model='perez') assert list(total.keys()) == [ 'poa_global', 'poa_direct', 'poa_diffuse', 'poa_sky_diffuse', 'poa_ground_diffuse' ]
def test_get_total_irradiance_scalars(model): total = irradiance.get_total_irradiance( 32, 180, 10, 180, dni=1000, ghi=1100, dhi=100, dni_extra=1400, airmass=1, model=model, surface_type='urban') assert list(total.keys()) == ['poa_global', 'poa_direct', 'poa_diffuse', 'poa_sky_diffuse', 'poa_ground_diffuse'] # test that none of the values are nan assert np.isnan(np.array(list(total.values()))).sum() == 0
def test_get_total_irradiance(irrad_data, ephem_data, dni_et, relative_airmass): models = ['isotropic', 'klucher', 'haydavies', 'reindl', 'king', 'perez'] for model in models: total = irradiance.get_total_irradiance( 32, 180, ephem_data['apparent_zenith'], ephem_data['azimuth'], dni=irrad_data['dni'], ghi=irrad_data['ghi'], dhi=irrad_data['dhi'], dni_extra=dni_et, airmass=relative_airmass, model=model, surface_type='urban') assert total.columns.tolist() == ['poa_global', 'poa_direct', 'poa_diffuse', 'poa_sky_diffuse', 'poa_ground_diffuse']
def test_stuck_tracker_profile(solarposition, clearsky): """Test POA irradiance at a awkward orientation (high tilt and oriented West).""" poa = irradiance.get_total_irradiance( surface_tilt=45, surface_azimuth=270, **clearsky, solar_zenith=solarposition['apparent_zenith'], solar_azimuth=solarposition['azimuth']) assert not orientation.tracking_nrel( poa['poa_global'], solarposition['zenith'] < 87, ).any() # by restricting the data to the middle of the day (lower zenith # angles) we should classify the system as fixed assert orientation.fixed_nrel(poa['poa_global'], solarposition['zenith'] < 70).all()
def test_orientation_fit_pvwatts_missing_data(naive_times): tilt = 30 azimuth = 100 system_location = location.Location(35, -106) local_time = naive_times.tz_localize('MST') clearsky = system_location.get_clearsky(local_time, model='simplified_solis') clearsky.loc['3/1/2020':'3/15/2020'] = np.nan solar_position = system_location.get_solarposition(clearsky.index) solar_position.loc['3/1/2020':'3/15/2020'] = np.nan poa = irradiance.get_total_irradiance(tilt, azimuth, solar_position['zenith'], solar_position['azimuth'], **clearsky) temp_cell = pvlib.temperature.sapm_cell( poa['poa_global'], 25, 0, **pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'] ['open_rack_glass_glass']) pdc = pvsystem.pvwatts_dc(poa['poa_global'], temp_cell, 100, -0.002) pac = pvsystem.inverter.pvwatts(pdc, 120) solar_position.dropna(inplace=True) with pytest.raises(ValueError, match=".* must not contain undefined values"): system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], **clearsky) pac.dropna(inplace=True) with pytest.raises(ValueError, match=".* must not contain undefined values"): system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], **clearsky) clearsky.dropna(inplace=True) tilt_out, azimuth_out, rsquared = system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], **clearsky) assert rsquared > 0.9 assert tilt_out == pytest.approx(tilt, abs=10) assert azimuth_out == pytest.approx(azimuth, abs=10)
def test_orientation_with_gaps(clearsky_year, solarposition_year): poa = irradiance.get_total_irradiance( surface_tilt=15, surface_azimuth=180, **clearsky_year, solar_zenith=solarposition_year['apparent_zenith'], solar_azimuth=solarposition_year['azimuth']) poa.loc['2020-07-19':'2020-07-23'] = np.nan azimuth, tilt = system.infer_orientation_daily_peak( poa['poa_global'].dropna(), tilts=[15], azimuths=[180], solar_zenith=solarposition_year['apparent_zenith'], solar_azimuth=solarposition_year['azimuth'], sunny=solarposition_year['apparent_zenith'] < 87, **clearsky_year) assert azimuth == 180 assert tilt == 15
def test_simple_poa_orientation(clearsky_year, solarposition_year, fine_clearsky, fine_solarposition): poa = irradiance.get_total_irradiance( surface_tilt=15, surface_azimuth=180, **clearsky_year, solar_zenith=solarposition_year['apparent_zenith'], solar_azimuth=solarposition_year['azimuth']) azimuth, tilt = system.infer_orientation_daily_peak( poa['poa_global'], tilts=[15, 30], azimuths=[110, 180, 220], solar_zenith=fine_solarposition['apparent_zenith'], solar_azimuth=fine_solarposition['azimuth'], sunny=solarposition_year['apparent_zenith'] < 87, **fine_clearsky) assert azimuth == 180 assert tilt == 15
def power_pvwatts(request, clearsky_july, solarposition_july): pdc0 = 100 pdc0_inverter = 110 tilt = 30 azimuth = 180 pdc0_marker = request.node.get_closest_marker("pdc0_inverter") if pdc0_marker is not None: pdc0_inverter = pdc0_marker.args[0] poa = irradiance.get_total_irradiance( tilt, azimuth, solarposition_july['zenith'], solarposition_july['azimuth'], **clearsky_july ) cell_temp = temperature.sapm_cell( poa['poa_global'], 25, 0, **TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass'] ) dc = pvsystem.pvwatts_dc(poa['poa_global'], cell_temp, pdc0, -0.004) return inverter.pvwatts(dc, pdc0_inverter)
def test_azimuth_different_index(clearsky_year, solarposition_year, fine_clearsky, fine_solarposition): """Can use solar position and clearsky with finer time-resolution to get an accurate estimate of tilt and azimuth.""" poa = irradiance.get_total_irradiance( surface_tilt=40, surface_azimuth=120, **clearsky_year, solar_zenith=solarposition_year['apparent_zenith'], solar_azimuth=solarposition_year['azimuth']) azimuth, tilt = system.infer_orientation_daily_peak( poa['poa_global'], sunny=solarposition_year['apparent_zenith'] < 87, tilts=[40], azimuths=[100, 120, 150], solar_azimuth=fine_solarposition['azimuth'], solar_zenith=fine_solarposition['apparent_zenith'], **fine_clearsky, ) assert azimuth == 120
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 get_irradiance(self, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=None, airmass=None, model='haydavies', **kwargs): """ Uses the :func:`irradiance.get_total_irradiance` function to calculate the plane of array irradiance components on a tilted surface defined by the input data and ``self.albedo``. For a given set of solar zenith and azimuth angles, the surface tilt and azimuth parameters are typically determined by :py:meth:`~SingleAxisTracker.singleaxis`. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : float or Series Direct Normal Irradiance ghi : float or Series Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance dni_extra : float or Series, default None Extraterrestrial direct normal irradiance airmass : float or Series, default None Airmass model : String, default 'haydavies' Irradiance model. **kwargs Passed to :func:`irradiance.total_irrad`. Returns ------- poa_irradiance : DataFrame Column names are: ``total, beam, sky, ground``. """ # not needed for all models, but this is easier if dni_extra is None: dni_extra = irradiance.get_extra_radiation(solar_zenith.index) if airmass is None: airmass = atmosphere.get_relative_airmass(solar_zenith) return irradiance.get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, albedo=self.albedo, **kwargs)
def get_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 fit(self, timestamps, DNI, DHI, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, rho_front, rho_back, albedo, GHI=None): """Use vectorization to calculate values used for the isotropic irradiance model. Parameters ---------- timestamps : array-like List of timestamps of the simulation. DNI : array-like Direct normal irradiance values [W/m2] DHI : array-like Diffuse horizontal irradiance values [W/m2] solar_zenith : array-like Solar zenith angles [deg] solar_azimuth : array-like Solar azimuth angles [deg] surface_tilt : array-like Surface tilt angles, from 0 to 180 [deg] surface_azimuth : array-like Surface azimuth angles [deg] rho_front : float Reflectivity of the front side of the PV rows rho_back : float Reflectivity of the back side of the PV rows albedo : array-like Albedo values (or ground reflectivity) GHI : array-like, optional Global horizontal irradiance [W/m2], if not provided, will be calculated from DNI and DHI (Default = None) """ # Make sure getting array-like values if np.isscalar(DNI): timestamps = [timestamps] DNI = np.array([DNI]) DHI = np.array([DHI]) solar_zenith = np.array([solar_zenith]) solar_azimuth = np.array([solar_azimuth]) surface_tilt = np.array([surface_tilt]) surface_azimuth = np.array([surface_azimuth]) if GHI is not None: GHI = np.array([GHI]) # Length of arrays n = len(DNI) # Make sure that albedo is a vector if np.isscalar(albedo): albedo = albedo * np.ones(n) # Save and calculate total POA values from Perez model if GHI is None: # Calculate GHI if not specified GHI = DNI * cosd(solar_zenith) + DHI self.GHI = GHI self.DHI = DHI perez_front_pvrow = get_total_irradiance( surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, DNI, GHI, DHI, albedo=albedo) # Save diffuse light self.isotropic_luminance = DHI self.rho_front = rho_front self.rho_back = rho_back self.albedo = albedo # DNI seen by ground illuminated surfaces self.direct['ground'] = DNI * cosd(solar_zenith) # Calculate AOI on front pvrow using pvlib implementation aoi_front_pvrow = aoi_function( surface_tilt, surface_azimuth, solar_zenith, solar_azimuth) aoi_back_pvrow = 180. - aoi_front_pvrow # DNI seen by pvrow illuminated surfaces front_is_illum = aoi_front_pvrow <= 90 self.direct['front_pvrow'] = np.where( front_is_illum, DNI * cosd(aoi_front_pvrow), 0.) self.direct['back_pvrow'] = np.where( ~front_is_illum, DNI * cosd(aoi_back_pvrow), 0.) self.total_perez['front_pvrow'] = perez_front_pvrow['poa_global']
def fit(self, timestamps, DNI, DHI, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, albedo, ghi=None): """Use vectorization to calculate values used for the hybrid Perez irradiance model. Parameters ---------- timestamps : array-like List of timestamps of the simulation. DNI : array-like Direct normal irradiance values [W/m2] DHI : array-like Diffuse horizontal irradiance values [W/m2] solar_zenith : array-like Solar zenith angles [deg] solar_azimuth : array-like Solar azimuth angles [deg] surface_tilt : array-like Surface tilt angles, from 0 to 180 [deg] surface_azimuth : array-like Surface azimuth angles [deg] albedo : array-like Albedo values (or ground reflectivity) ghi : array-like, optional Global horizontal irradiance [W/m2], if not provided, will be calculated from DNI and DHI (Default = None) """ # Make sure getting array-like values if np.isscalar(DNI): timestamps = [timestamps] DNI = np.array([DNI]) DHI = np.array([DHI]) solar_zenith = np.array([solar_zenith]) solar_azimuth = np.array([solar_azimuth]) surface_tilt = np.array([surface_tilt]) surface_azimuth = np.array([surface_azimuth]) ghi = None if ghi is None else np.array([ghi]) # Length of arrays n = len(DNI) # Make sure that albedo is a vector albedo = albedo * np.ones(n) if np.isscalar(albedo) else albedo # Calculate terms from Perez model luminance_isotropic, luminance_circumsolar, poa_horizon, \ poa_circumsolar_front, poa_circumsolar_back, \ aoi_front_pvrow, aoi_back_pvrow = \ self._calculate_luminance_poa_components( timestamps, DNI, DHI, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth) # Save and calculate total POA values from Perez model ghi = DNI * cosd(solar_zenith) + DHI if ghi is None else ghi self.GHI = ghi self.DHI = DHI self.n_steps = n perez_front_pvrow = get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, DNI, ghi, DHI, albedo=albedo) total_perez_front_pvrow = perez_front_pvrow['poa_global'] # Save isotropic luminance self.isotropic_luminance = luminance_isotropic self.albedo = albedo # Ground surfaces self.direct['ground_illum'] = DNI * cosd(solar_zenith) self.direct['ground_shaded'] = ( # Direct light through PV modules spacing self.direct['ground_illum'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.direct['ground_illum'] * (1. - self.module_spacing_ratio) * self.module_transparency) self.circumsolar['ground_illum'] = luminance_circumsolar self.circumsolar['ground_shaded'] = ( # Circumsolar light through PV modules spacing self.circumsolar['ground_illum'] * self.module_spacing_ratio # Circumsolar light through PV modules, by transparency + self.circumsolar['ground_illum'] * (1. - self.module_spacing_ratio) * self.module_transparency) self.horizon['ground'] = np.zeros(n) # PV row surfaces front_is_illum = aoi_front_pvrow <= 90 # direct self.direct['front_illum_pvrow'] = np.where( front_is_illum, DNI * cosd(aoi_front_pvrow), 0.) self.direct['front_shaded_pvrow'] = ( # Direct light through PV modules spacing self.direct['front_illum_pvrow'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.direct['front_illum_pvrow'] * (1. - self.module_spacing_ratio) * self.module_transparency) self.direct['back_illum_pvrow'] = np.where(~front_is_illum, DNI * cosd(aoi_back_pvrow), 0.) self.direct['back_shaded_pvrow'] = ( # Direct light through PV modules spacing self.direct['back_illum_pvrow'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.direct['back_illum_pvrow'] * (1. - self.module_spacing_ratio) * self.module_transparency) # circumsolar self.circumsolar['front_illum_pvrow'] = np.where( front_is_illum, poa_circumsolar_front, 0.) self.circumsolar['front_shaded_pvrow'] = ( # Direct light through PV modules spacing self.circumsolar['front_illum_pvrow'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.circumsolar['front_illum_pvrow'] * (1. - self.module_spacing_ratio) * self.module_transparency) self.circumsolar['back_illum_pvrow'] = np.where( ~front_is_illum, poa_circumsolar_back, 0.) self.circumsolar['back_shaded_pvrow'] = ( # Direct light through PV modules spacing self.circumsolar['back_illum_pvrow'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.circumsolar['back_illum_pvrow'] * (1. - self.module_spacing_ratio) * self.module_transparency) # horizon self.horizon['front_pvrow'] = poa_horizon self.horizon['back_pvrow'] = poa_horizon # perez self.total_perez['front_illum_pvrow'] = total_perez_front_pvrow self.total_perez['front_shaded_pvrow'] = ( total_perez_front_pvrow - self.direct['front_illum_pvrow'] - self.circumsolar['front_illum_pvrow'] + self.direct['front_shaded_pvrow'] + self.circumsolar['front_shaded_pvrow']) self.total_perez['ground_shaded'] = ( DHI - self.circumsolar['ground_illum'] + self.circumsolar['ground_shaded'] + self.direct['ground_shaded']) self.total_perez['ground_illum'] = ghi self.total_perez['sky'] = luminance_isotropic
def fit(self, timestamps, DNI, DHI, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, albedo, ghi=None): """Use vectorization to calculate values used for the isotropic irradiance model. Parameters ---------- timestamps : array-like List of timestamps of the simulation. DNI : array-like Direct normal irradiance values [W/m2] DHI : array-like Diffuse horizontal irradiance values [W/m2] solar_zenith : array-like Solar zenith angles [deg] solar_azimuth : array-like Solar azimuth angles [deg] surface_tilt : array-like Surface tilt angles, from 0 to 180 [deg] surface_azimuth : array-like Surface azimuth angles [deg] albedo : array-like Albedo values (or ground reflectivity) ghi : array-like, optional Global horizontal irradiance [W/m2], if not provided, will be calculated from DNI and DHI (Default = None) """ # Make sure getting array-like values if np.isscalar(DNI): timestamps = [timestamps] DNI = np.array([DNI]) DHI = np.array([DHI]) solar_zenith = np.array([solar_zenith]) solar_azimuth = np.array([solar_azimuth]) surface_tilt = np.array([surface_tilt]) surface_azimuth = np.array([surface_azimuth]) ghi = None if ghi is None else np.array([ghi]) # Length of arrays n = len(DNI) # Make sure that albedo is a vector albedo = albedo * np.ones(n) if np.isscalar(albedo) else albedo # Save and calculate total POA values from Perez model ghi = DNI * cosd(solar_zenith) + DHI if ghi is None else ghi self.GHI = ghi self.DHI = DHI self.n_steps = n perez_front_pvrow = get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, DNI, ghi, DHI, albedo=albedo) # Save diffuse light self.isotropic_luminance = DHI self.albedo = albedo # DNI seen by ground illuminated surfaces self.direct['ground_illum'] = DNI * cosd(solar_zenith) self.direct['ground_shaded'] = ( # Direct light through PV modules spacing self.direct['ground_illum'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.direct['ground_illum'] * (1. - self.module_spacing_ratio) * self.module_transparency) # Calculate AOI on front pvrow using pvlib implementation aoi_front_pvrow = aoi_function(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth) aoi_back_pvrow = 180. - aoi_front_pvrow # DNI seen by pvrow illuminated surfaces front_is_illum = aoi_front_pvrow <= 90 # direct self.direct['front_illum_pvrow'] = np.where( front_is_illum, DNI * cosd(aoi_front_pvrow), 0.) self.direct['front_shaded_pvrow'] = ( # Direct light through PV modules spacing self.direct['front_illum_pvrow'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.direct['front_illum_pvrow'] * (1. - self.module_spacing_ratio) * self.module_transparency) self.direct['back_illum_pvrow'] = np.where(~front_is_illum, DNI * cosd(aoi_back_pvrow), 0.) self.direct['back_shaded_pvrow'] = ( # Direct light through PV modules spacing self.direct['back_illum_pvrow'] * self.module_spacing_ratio # Direct light through PV modules, by transparency + self.direct['back_illum_pvrow'] * (1. - self.module_spacing_ratio) * self.module_transparency) # perez self.total_perez['front_illum_pvrow'] = perez_front_pvrow['poa_global'] self.total_perez['front_shaded_pvrow'] = ( self.total_perez['front_illum_pvrow'] - self.direct['front_illum_pvrow']) self.total_perez['ground_shaded'] = (self.DHI + self.direct['ground_shaded'])
def test_orientation_fit_pvwatts_temp_wind_as_series(naive_times): tilt = 30 azimuth = 100 system_location = location.Location(35, -106) local_time = naive_times.tz_localize('MST') clearsky = system_location.get_clearsky(local_time, model='simplified_solis') solar_position = system_location.get_solarposition(clearsky.index) poa = irradiance.get_total_irradiance(tilt, azimuth, solar_position['zenith'], solar_position['azimuth'], **clearsky) temp_cell = pvlib.temperature.sapm_cell( poa['poa_global'], 25, 1, **pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'] ['open_rack_glass_glass']) temperature = pd.Series(25, index=clearsky.index) wind_speed = pd.Series(1, index=clearsky.index) temperature_missing = temperature.copy() temperature_missing.loc['4/5/2020':'4/10/2020'] = np.nan wind_speed_missing = wind_speed.copy() wind_speed_missing.loc['5/5/2020':'5/15/2020'] = np.nan pdc = pvsystem.pvwatts_dc(poa['poa_global'], temp_cell, 100, -0.002) pac = pvsystem.inverter.pvwatts(pdc, 120) with pytest.raises(ValueError, match=".* must not contain undefined values"): system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], temperature=temperature_missing, wind_speed=wind_speed_missing, **clearsky) with pytest.raises(ValueError, match="temperature must not contain undefined values"): system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], temperature=temperature_missing, wind_speed=wind_speed, **clearsky) with pytest.raises(ValueError, match="wind_speed must not contain undefined values"): system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], temperature=temperature, wind_speed=wind_speed_missing, **clearsky) # ValueError if indices don't match with pytest.raises(ValueError): system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], temperature=temperature_missing.dropna(), wind_speed=wind_speed_missing.dropna(), **clearsky) tilt_out, azimuth_out, rsquared = system.infer_orientation_fit_pvwatts( pac, solar_zenith=solar_position['zenith'], solar_azimuth=solar_position['azimuth'], **clearsky) assert rsquared > 0.9 assert tilt_out == pytest.approx(tilt, abs=10) assert azimuth_out == pytest.approx(azimuth, abs=10)
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 find_clearsky_poa(df, lat, lon, irradiance_poa_key='irradiance_poa_o_###', mounting='fixed', tilt=0, azimuth=180, altitude=0): loc = Location(lat, lon, altitude=altitude) CS = loc.get_clearsky(df.index) df['csghi'] = CS.ghi df['csdhi'] = CS.dhi df['csdni'] = CS.dni if mounting.lower() == "fixed": sun = get_solarposition(df.index, lat, lon) fixedpoa = get_total_irradiance(tilt, azimuth, sun.zenith, sun.azimuth, CS.dni, CS.ghi, CS.dhi) df['cspoa'] = fixedpoa.poa_global if mounting.lower() == "tracking": sun = get_solarposition(df.index, lat, lon) # default to axis_tilt=0 and axis_azimuth=180 tracker_data = singleaxis(sun.apparent_zenith, sun.azimuth, axis_tilt=tilt, axis_azimuth=azimuth, max_angle=50, backtrack=True, gcr=0.35) track = get_total_irradiance(tracker_data['surface_tilt'], tracker_data['surface_azimuth'], sun.zenith, sun.azimuth, CS.dni, CS.ghi, CS.dhi) df['cspoa'] = track.poa_global # the following code is assuming clear sky poa has been generated per pvlib, aligned in the same # datetime index, and daylight savings or any time shifts were previously corrected # the inputs below were tuned for POA at a 15 minute frequency # note that detect_clearsky has a scaling factor but I still got slightly different results when I scaled measured poa first df['poa'] = df[irradiance_poa_key] / df[irradiance_poa_key].quantile( 0.98) * df.cspoa.quantile(0.98) # inputs for detect_clearsky measured = df.poa.copy() clear = df.cspoa.copy() dur = 60 lower_line_length = -41.416 upper_line_length = 77.789 var_diff = .00745 mean_diff = 80 max_diff = 90 slope_dev = 3 is_clear_results = detect_clearsky(measured.values, clear.values, df.index, dur, mean_diff, max_diff, lower_line_length, upper_line_length, var_diff, slope_dev, return_components=True) clearSeries = pd.Series(index=df.index, data=is_clear_results[0]) clearSeries = clearSeries.reindex(index=df.index, method='ffill', limit=3) return clearSeries