Source code for pecos.pv

"""
The pv module contains custom methods for PV applications.
"""
import pandas as pd
from pecos.metrics import time_integral
import logging

logger = logging.getLogger(__name__)

[docs]def insolation(G, tfilter=None, per_day=True): """ Compute insolation defined as: :math:`H=\int{Gdt}` where :math:`G` is irradiance and :math:`dt` is the time step between observations. The time integral is computed using the trapezoidal rule. Results are given in [irradiance units]*seconds. Parameters ----------- G : pd.DataFrame Irradiance time series tfilter : pd.Series (optional) Time filter containing boolean values for each time index per_day : boolean (optional) Flag indicating if the results should be computed per day, default = True Returns ------- H : pd.DataFrame Insolation """ if type(G) is pd.core.series.Series: G = G.to_frame('Irradiance') H = time_integral(G, tfilter=tfilter, per_day=per_day) return H
[docs]def energy(P, tfilter=None, per_day=True): """ Convert energy defined as: :math:`E=\int{Pdt}` where :math:`P` is power and :math:`dt` is the time step between observations. The time integral is computed using the trapezoidal rule. Results are given in [power units]*seconds. Parameters ----------- P : pd.DataFrame Power time series tfilter : pd.Series (optional) Time filter containing boolean values for each time index per_day : boolean (optional) Flag indicating if the results should be computed per day, default = True Returns ------- E : pd.DataFrame Energy """ if type(P) is pd.core.series.Series: P = P.to_frame('Power') E = time_integral(P, tfilter=tfilter, per_day=per_day) return E
[docs]def performance_ratio(E, H_poa, P_ref, G_ref=1000): """ Compute performance ratio defined as: :math:`PR=\dfrac{Y_{f}}{Yr} = \dfrac{\dfrac{E}{P_{ref}}}{\dfrac{H_{poa}}{G_{ref}}}` where :math:`Y_f` is the observed energy (AC or DC) produced by the PV system (kWh) divided by the DC power rating at STC conditions. :math:`Y_r` is the plane-of-array insolation (kWh/m2) divided by the reference irradiance (1000 W/m2). Parameters ----------- E : pd.DataFrame with a single column or pd.Series Energy (AC or DC) H_poa : pd.DataFrame with a single column or pd.Series Plane of array insolation P_ref : float DC power rating at STC conditions G_ref : float (optional) Reference irradiance, default = 1000 Returns ------- PR : pd.DataFrame Performance ratio """ logger.info("Compute Performance Ratio") if type(E) is pd.core.frame.DataFrame: E = pd.Series(E.values[:,0], index=E.index) if type(H_poa) is pd.core.frame.DataFrame: H_poa = pd.Series(H_poa.values[:,0], index=H_poa.index) Yf = E/P_ref Yr = H_poa/G_ref PR = Yf/Yr PR = PR.to_frame('Performance Ratio') return PR
[docs]def normalized_current(I, G_poa, I_sco, G_ref=1000): """ Compute normalized current defined as: :math:`NI = \dfrac{\dfrac{I}{I_{sco}}}{\dfrac{G_{poa}}{G_{ref}}}` where :math:`I` is current, :math:`I_{sco}` is the short circuit current at STC conditions, :math:`G_{poa}` is the plane-of-array irradiance, and :math:`G_{ref}` is the reference irradiance. Parameters ----------- I : pd.DataFrame with a single column or pd.Series Current G_poa : pd.DataFrame with a single column or pd.Series Plane of array irradiance I_sco : float Short circuit current at STC conditions G_ref : float (optional) Reference irradiance, default = 1000 Returns ------- NI : pd.DataFrame Normalized current """ logger.info("Compute Normalized Current") if type(I) is pd.core.frame.DataFrame: I = pd.Series(I.values.values[:,0], index=I.index) if type(G_poa) is pd.core.frame.DataFrame: G_poa = pd.Series(G_poa.values[:,0], index=G_poa.index) N = I/I_sco D = G_poa/G_ref NI = N/D NI = NI.to_frame('Normalized Current') return NI
[docs]def normalized_efficiency(P, G_poa, P_ref, G_ref=1000): """ Compute normalized efficiency defined as: :math:`NE = \dfrac{\dfrac{P}{P_{ref}}}{\dfrac{G_{poa}}{G_{ref}}}` where :math:`P` is the observed power (AC or DC), :math:`P_{ref}` is the DC power rating at STC conditions, :math:`G_{poa}` is the plane-of-array irradiance, and :math:`G_{ref}` is the reference irradiance. Parameters ----------- P : pd.DataFrame with a single column or pd.Series Power (AC or DC) G_poa : pd.DataFrame with a single column or pd.Series Plane of array irradiance P_ref : float DC power rating at STC conditions G_ref : float (optional) Reference irradiance, default = 1000 Returns ------- NE : pd.DataFrame Normalized efficiency """ logger.info("Compute Normalized Efficiency") if type(P) is pd.core.frame.DataFrame: P = pd.Series(P.values.values[:,0], index=P.index) if type(G_poa) is pd.core.frame.DataFrame: G_poa = pd.Series(G_poa.values[:,0], index=G_poa.index) Yf = P/P_ref Yr = G_poa/G_ref NE = Yf/Yr NE = NE.to_frame('Normalized Efficiency') return NE
[docs]def performance_index(E, E_predicted): """ Compute performance index defined as: :math:`PI=\dfrac{E}{\hat{E}}` where :math:`E` is the observed energy from a PV system and :math:`\hat{E}` is the predicted energy over the same time frame. :math:`\hat{E}` can be computed using by first predicting power using ``pecos.pv.basic_pvlib_performance_model`` or methods in ``pvlib.pvsystem`` and then convert power to energy using ``pecos.pv.enery``. Unlike with the performance ratio, the performance index should be very close to 1 for a well functioning PV system and should not vary by season due to temperature variations. Parameters ----------- E : pd.DataFrame with a single column or pd.Series Observed energy E_predicted : pd.DataFrame with a single column or pd.Series Predicted energy Returns --------- PI : pd.DataFrame Performance index """ logger.info("Compute Performance Index") if type(E) is pd.core.frame.DataFrame: E = pd.Series(E.values[:,0], index=E.index) if type(E_predicted) is pd.core.frame.DataFrame: E_predicted = pd.Series(E_predicted.values[:,0], index=E_predicted.index) PI = E/E_predicted PI = PI.to_frame('Performance Index') return PI
[docs]def energy_yield(E, P_ref): """ Compute energy yield is defined as: :math:`EY=\dfrac{E}{P_{ref}}` where :math:`E` is the observed energy from a PV system and :math:`P_{ref}` is the DC power rating of the system at STC conditions. Parameters ----------- E : pd.DataFrame with a single column or pd.Series Observed energy P_ref : float DC power rating at STC conditions Returns --------- EY : pd.DataFrame Energy yield """ logger.info("Compute Energy Yield") if type(E) is pd.core.frame.DataFrame: E = pd.Series(E.values[:,0], index=E.index) EY = E/P_ref EY = EY.to_frame('Energy Yield') return EY
[docs]def clearness_index(H_dn, H_ea): """ Compute clearness index defined as: :math:`Kt=\dfrac{H_{dn}}{H_{ea}}` where :math:`H_{dn}` is the direct-normal insolation (kWh/m2) :math:`H_{ea}` is the extraterrestrial insolation (kWh/m2) over the same time frame. Extraterrestrial irradiation can be computed using ``pvlib.irradiance.extraradiation``. Irradiation can be converted to insolation using ``pecos.pv.insolation``. Parameters ----------- H_dn : pd.DataFrame with a single column or pd.Series Direct normal insolation H_ea : pd.DataFrame with a single column or pd.Series Extraterrestrial insolation Returns ------- Kt : pd.DataFrame Clearness index """ logger.info("Compute Clearness Index") if type(H_dn) is pd.core.frame.DataFrame: H_dn = pd.Series(H_dn.values[:,0], index=H_dn.index) if type(H_ea) is pd.core.frame.DataFrame: H_ea = pd.Series(H_ea.values[:,0], index=H_ea.index) Kt = H_dn/H_ea Kt = Kt.to_frame('Clearness Index') return Kt
[docs]def basic_pvlib_performance_model(parameters, latitude, longitude, wind_speed, air_temp, poa_global, poa_diffuse=None, model='SAPM'): """ Compute a very basic pv performance model using the SAPM or single diode model from pvlib. Input includes observed wind speed, air temperature, and POA irradiance. Default model options, defined in pvlib, are used to compute the performance model. Use pvlib directly to customize the model. Parameters ----------- parameters : dict Model parameters, see ``pvlib.pvsystem`` module for more details latitude : float Latitude longitude : float Longitude wind speed : pd.DataFrame with a single column or pd.Series Wind speed time series air_temp : pd.DataFrame with a single column or pd.Series Air temperature time series poa_global : pd.DataFrame with a single column or pd.Series Global POA irradiance time series poa_diffuse : pd.DataFrame with a single column or pd.Series (optional) Diffuse POA irradiance time series, default = 0 model : string (optional) 'SAPM' or 'singlediode', default = 'SAPM' Returns --------- model : pd.DataFrame Predicted Isc, Imp, Voc, Vmp """ import pvlib if type(wind_speed) is pd.core.frame.DataFrame: wind_speed = pd.Series(wind_speed.values[:,0], index=wind_speed.index) if type(air_temp) is pd.core.frame.DataFrame: air_temp = pd.Series(air_temp.values[:,0], index=air_temp.index) if type(poa_global) is pd.core.frame.DataFrame: poa_global = pd.Series(poa_global.values[:,0], index=poa_global.index) if type(poa_diffuse) is pd.core.frame.DataFrame: poa_diffuse = pd.Series(poa_diffuse.values[:,0], index=poa_diffuse.index) if poa_diffuse is None: poa_diffuse = pd.Series(data=0, index=poa_global.index) index = poa_global.index # Copute sun position solarposition = pvlib.solarposition.ephemeris(index, latitude, longitude) # Compute cell temperature celltemp = pvlib.pvsystem.sapm_celltemp(poa_global, wind_speed, air_temp) # Compute alsolute airmass airmass_relative = pvlib.atmosphere.relativeairmass(solarposition['zenith']) airmass_absolute = pvlib.atmosphere.absoluteairmass(airmass_relative) # Compute aoi aoi = pvlib.irradiance.aoi(latitude, 180, solarposition['zenith'], solarposition['azimuth']) if model == 'SAPM': model = pvlib.pvsystem.sapm(parameters, poa_global, poa_diffuse, celltemp['temp_cell'], airmass_absolute, aoi) elif model == 'singlediode': (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) = pvlib.pvsystem.calcparams_desoto(poa_global, celltemp['temp_cell'], parameters['Aisc'], parameters, 0, 0) model = pvlib.pvsystem.singlediode(parameters, photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) return model