From befa65e1612e49b36ffe8c77f05be795b04e0a06 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Fri, 14 Oct 2022 13:31:47 +0200 Subject: [PATCH 01/23] Start with a copy of the code from adriesse/pvpltools-python. --- pvlib/pvefficiency.py | 593 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 593 insertions(+) create mode 100644 pvlib/pvefficiency.py diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py new file mode 100644 index 0000000000..401aa7f0d9 --- /dev/null +++ b/pvlib/pvefficiency.py @@ -0,0 +1,593 @@ +""" +This module contains implementations of several PV module efficiency models. + +These models have a common purpose, which is to predict the efficiency at +maximum power point as a function of the main operating conditions: +effective irradiance and module temperature. + +A function to fit any of these models to measurements is also provided. + +Copyright (c) 2019-2020 Anton Driesse, PV Performance Labs. +""" + +import inspect + +import numpy as np +import pandas as pd +from scipy.optimize import curve_fit + +from pvpltools.iec61853 import BilinearInterpolator + + +def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, + **kwargs): + """ + Determine the parameters of a module efficiency model by non-linear + least-squares fit. + + This is a convenience function that calls the scipy curve_fit function + with suitable parameters and defaults. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + model : function + A PV module efficiency function such as `adr`. It must take + irradiance and temperature as the first two arguments and the + model-specific parameters as the remaining arguments. + + p0 : array_like, optional + Initial guess for the parameters, which may speed up the fit process. + + kwargs : + Optional keyword arguments passed to `curve_fit`. + + Returns + ------- + popt : array + Optimal values for the parameters so that the sum of the squared + residuals of ``model(irradiance, temperature, *popt) - eta`` is minimized. + + pcov : 2-D array + The estimated covariance of popt. See `curve_fit` for details. + + Raises + ------ + (These errors and warnings are from `curve_fit`.) + + ValueError + if either `ydata` or `xdata` contain NaNs, or if incompatible options + are used. + + RuntimeError + if the least-squares minimization fails. + + OptimizeWarning + if covariance of the parameters can not be estimated. + + See also + -------- + pvpltools.module_efficiency.adr + scipy.optimize.curve_fit + + Author: Anton Driesse, PV Performance Labs + """ + + if p0 is None: + # determine number of parameters by inspecting the function + # and set initial parameters all to 1 + sig = inspect.signature(model) + p0 = np.zeros(len(sig.parameters) - 2) + + if not 'method' in kwargs: + kwargs['method'] = 'trf' + + def model_wrapper(xdata, *params): + return model(*xdata, *params) + + popt, pcov = curve_fit(model_wrapper, + xdata=[irradiance, temperature], + ydata=eta, + p0=p0, + **kwargs + ) + return popt, pcov + + +def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): + ''' + Calculate PV module efficiency using the ADR model. + + The efficiency varies with irradiance and operating temperature + and is determined by 5 model parameters as described in [1]_. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + k_a : float + Absolute scaling factor, which is equal to the efficiency at + reference conditions. This factor allows the model to be used + with relative or absolute efficiencies, and to accommodate data sets + which are not perfectly normalized but have a slight bias at + the reference conditions. + + k_d : negative float + “Dark irradiance” or diode coefficient which influences the voltage + increase with irradiance. + + tc_d : float + Temperature coefficient of the diode coefficient, which indirectly + influences voltage. Because it is the only temperature coefficient + in the model, its value will also reflect secondary temperature + dependencies that are present in the PV module. + + k_rs and k_rsh : float + Series and shunt resistance loss factors. Because of the normalization + they can be read as power loss fractions at reference conditions. + For example, if k_rs is 0.05, the internal loss assigned to the + series resistance has a magnitude equal to 5% of the module output. + + Returns + ------- + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + Notes + ----- + The efficiency values may be absolute or relative, and may be expressed + as percent or per unit. This is determined by the efficiency data + used to derive values for the 5 model parameters. The first model + parameter k_a is equal to the efficiency at STC and therefore + indicates the efficiency scale being used. k_a can also be changed + freely to adjust the scale, or to change the module class to a slightly + higher or lower efficiency. + + References + ---------- + .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + ''' + g = np.asanyarray(irradiance) + t = np.asanyarray(temperature) + + # normalize the irradiance + G_REF = 1000 + s = g / G_REF + + # obtain the difference from reference temperature + T_REF = 25 + dt = t - T_REF + t_abs = t + 273.15 + + # equation 29 in JPV + s_o = 10**(k_d + (tc_d * dt)) + s_o_ref = 10**(k_d) + + # equation 28 and 30 in JPV + # the constant k_v does not appear here because it cancels out + v = np.log(s / s_o + 1) + v /= np.log(1 / s_o_ref + 1) + + # equation 25 in JPV + eta = k_a * ((1 + k_rs + k_rsh) * v - k_rs * s - k_rsh * v**2) + + return eta + + +def heydenreich(irradiance, temperature, a, b, c, gamma_pmp): + """ + Calculate PV module efficiency using the Heydenreich model. + + The efficiency varies with irradiance and operating temperature + and is determined by three parameters for irradiance dependency and + one for temperature dependency as described in [1]_. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + a, b, c : float + Three model parameters usually determined by regression. + + gamma_pmp : float + The temperature coefficient of power, which may be taken + from the module datasheet or also determined by regression. + + Returns + ------- + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + See also + -------- + fit_efficiency_model + adr + + Notes + ----- + A comprehensive comparison of efficiency models is found in [2]_ and [3]_. + + References + ---------- + .. [1] W. Heydenreich, et al., "Describing the world with three parameters: + a new approach to PV module power modelling," in 23rd European PV + Solar Energy Conference and Exhibition (EU PVSEC), 2008, pp. 2786-2789. + + .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + """ + from numpy import log, exp, square + + g = np.asanyarray(irradiance) + t = np.asanyarray(temperature) + + dt = t - 25 + + eta = ( + # power loss in R series + a * g + + # power gain from voltage * current + b * log(g + 1) + + # power loss in R shunt (constant Rsh) + c * (square(log(g + exp(1))) / (g + 1) - 1) + ) + + eta *= 1 + gamma_pmp * dt + + return eta + + +def motherpv(irradiance, temperature, a, b, c, d, gamma_ref, aa, bb): + """ + Calculate PV module efficiency using the MotherPV model. + + The efficiency varies with irradiance and operating temperature + and is determined by 7 parameters as described in [1]_. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + a, b, c, d, aa, bb : float + Six model parameters usually determined by regression. + + gamma_pmp : float + The temperature coefficient of power, which may be taken + from the module datasheet or also determined by regression. + + Returns + ------- + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + See also + -------- + fit_efficiency_model + adr + + Notes + ----- + A comprehensive comparison of efficiency models is found in [2]_ and [3]_. + + References + ---------- + .. [1] A. G. de Montgareuil, et al., "A new tool for the MotherPV method: + modelling of the irradiance coefficient of photovoltaic modules," + in 24th European Photovoltaic Solar Energy Conference (EU PVSEC), + 2009, pp. 21-25. + + .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + """ + from numpy import log + + g = np.asanyarray(irradiance) + t = np.asanyarray(temperature) + + s = g / 1000 + dt = t - 25 + + eta = ( 1 + a * (s - 1) + b * log(s) + + c * (s - 1)**2 + d * log(s)**2 + ) + gamma = gamma_ref * ( 1 + aa * (s - 1) + bb * log(s)) + + eta *= 1 + gamma * dt + + return eta + + +def pvgis(irradiance, temperature, k1, k2, k3, k4, k5, k6): + """ + Calculate PV module efficiency using the PVGIS model. + + The efficiency varies with irradiance and operating temperature + and is determined by 6 parameters as described in [1]_. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + k1, k2, k3, k4, k5, k6 : float + Six model parameters usually determined by regression. + + Returns + ------- + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + See also + -------- + fit_efficiency_model + adr + + Notes + ----- + A comprehensive comparison of efficiency models is found in [2]_ and [3]_. + + References + ---------- + .. [1] T. Huld, et al., "A power-rating model for crystalline silicon + PV modules," Solar Energy Materials and Solar Cells, vol. 95, + pp. 3359-3369, 2011. + + .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + """ + from numpy import log + + g = np.asanyarray(irradiance) + t = np.asanyarray(temperature) + + g = g / 1000 + dt = t - 25 + + eta = ( 1 + + k1 * log(g) + + k2 * log(g)**2 + + dt * (k3 + + k4 * log(g) + + k5 * log(g)**2 + ) + + k6 * dt**2 + ) + + return eta + + +def mpm6(irradiance, temperature, c1, c2, c3, c4, c6=0.0): + """ + Calculate PV module efficiency using the MPM6 model (without windspeed). + + The efficiency varies with irradiance and operating temperature + and is determined by 5 parameters as described in [1]_. A sixth + parameter captures the effect of windspeed but is not used in this + implementation. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + c1, c2, c3, c4, c6 : float + Five model parameters usually determined by regression. + + Returns + ------- + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + See also + -------- + mpm5 + fit_efficiency_model + adr + + Notes + ----- + The author of MPM6 recommends the fitting constraint c6 <= 0. + + A comprehensive comparison of efficiency models is found in [2]_ and [3]_. + + References + ---------- + .. [1] S. Ransome and J. Sutterlueti, "How to Choose the Best Empirical + Model for Optimum Energy Yield Predictions," in 44th IEEE Photovoltaic + Specialist Conference (PVSC), 2017, pp. 652-657. + + .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + """ + + g = np.asanyarray(irradiance) + t = np.asanyarray(temperature) + + g = g / 1000 + dt = t - 25 + + eta = ( + # "actual/nominal" + c1 + # loss due to temperature + + c2 * dt + # loss at low light / due to Voc + + c3 * np.log10(g) + # loss at high light / due to Rs + + c4 * g + # loss due to Rsh + + c6 / g + ) + return eta + + +def mpm5(irradiance, temperature, c1, c2, c3, c4): + """ + Call `mpm6` with one less parameter. See `mpm6` for more information. + """ + return mpm6(irradiance, temperature, c1, c2, c3, c4, c6=0.0) + + +def fit_bilinear(irradiance, temperature, eta): + """ + Prepare a bilinear interpolant for module efficiency. + + This function allows the class `pvpltools.iec61853.BilinearInterpolator` + to be used in a way that is compatible with other efficiency models + in this module. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + Returns + ------- + interpolator : object + A callable `BilinearInterpolator` object + + See also + -------- + pvpltools.module_efficiency.bilinear + pvpltools.iec61853.BilinearInterpolator + + Notes + ----- + Unlike the other efficiency models, bilinear interpolation only works + with a regular grid of measurements. Missing values at low irradiance + high temperature and vice versa are filled using the method described + in [1]_ and [2]_. + + References + ---------- + .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + """ + # (re)construct the matrix as a grid for the BilinearInterpolator + data = pd.DataFrame([irradiance, temperature, eta]).T + grid = data.pivot(*data.columns) + + # now create the interpolator object + interpolator = BilinearInterpolator(grid) + return interpolator + + +def bilinear(irradiance, temperature, interpolator): + """ + Calculate PV module efficiency using bilinear interpolation/extrapolation. + + This function allows the class `pvpltools.iec61853.BilinearInterpolator` + to be used in a way that is compatible with other efficiency models + in this module. + + Parameters + ---------- + irradiance : non-negative numeric, W/m² + The effective irradiance incident on the PV module. + + temperature : numeric, °C + The module operating temperature. + + interpolator : object + A callable `BilinearInterpolator` object + + Returns + ------- + eta : numeric + The efficiency of the module at the specified irradiance and + temperature. + + See also + -------- + module_efficiency.fit_bilinear + pvpltools.iec61853.BilinearInterpolator + + References + ---------- + .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements + to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + + .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic + Module Efficiency Model for Energy Prediction and Rating", + forthcoming. + + Author: Anton Driesse, PV Performance Labs + """ + return interpolator(irradiance, temperature) + From 411974c493e3636fa46d4aa290ebb3f6c4c8a064 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 22 Nov 2022 17:19:46 +0100 Subject: [PATCH 02/23] Reduce scope of this PR to the ADR model. --- pvlib/pvefficiency.py | 397 ------------------------------------------ 1 file changed, 397 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 401aa7f0d9..647fba8b8d 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -194,400 +194,3 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): return eta - -def heydenreich(irradiance, temperature, a, b, c, gamma_pmp): - """ - Calculate PV module efficiency using the Heydenreich model. - - The efficiency varies with irradiance and operating temperature - and is determined by three parameters for irradiance dependency and - one for temperature dependency as described in [1]_. - - Parameters - ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. - - temperature : numeric, °C - The module operating temperature. - - a, b, c : float - Three model parameters usually determined by regression. - - gamma_pmp : float - The temperature coefficient of power, which may be taken - from the module datasheet or also determined by regression. - - Returns - ------- - eta : numeric - The efficiency of the module at the specified irradiance and - temperature. - - See also - -------- - fit_efficiency_model - adr - - Notes - ----- - A comprehensive comparison of efficiency models is found in [2]_ and [3]_. - - References - ---------- - .. [1] W. Heydenreich, et al., "Describing the world with three parameters: - a new approach to PV module power modelling," in 23rd European PV - Solar Energy Conference and Exhibition (EU PVSEC), 2008, pp. 2786-2789. - - .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements - to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - - .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. - - Author: Anton Driesse, PV Performance Labs - """ - from numpy import log, exp, square - - g = np.asanyarray(irradiance) - t = np.asanyarray(temperature) - - dt = t - 25 - - eta = ( - # power loss in R series - a * g + - # power gain from voltage * current - b * log(g + 1) + - # power loss in R shunt (constant Rsh) - c * (square(log(g + exp(1))) / (g + 1) - 1) - ) - - eta *= 1 + gamma_pmp * dt - - return eta - - -def motherpv(irradiance, temperature, a, b, c, d, gamma_ref, aa, bb): - """ - Calculate PV module efficiency using the MotherPV model. - - The efficiency varies with irradiance and operating temperature - and is determined by 7 parameters as described in [1]_. - - Parameters - ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. - - temperature : numeric, °C - The module operating temperature. - - a, b, c, d, aa, bb : float - Six model parameters usually determined by regression. - - gamma_pmp : float - The temperature coefficient of power, which may be taken - from the module datasheet or also determined by regression. - - Returns - ------- - eta : numeric - The efficiency of the module at the specified irradiance and - temperature. - - See also - -------- - fit_efficiency_model - adr - - Notes - ----- - A comprehensive comparison of efficiency models is found in [2]_ and [3]_. - - References - ---------- - .. [1] A. G. de Montgareuil, et al., "A new tool for the MotherPV method: - modelling of the irradiance coefficient of photovoltaic modules," - in 24th European Photovoltaic Solar Energy Conference (EU PVSEC), - 2009, pp. 21-25. - - .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements - to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - - .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. - - Author: Anton Driesse, PV Performance Labs - """ - from numpy import log - - g = np.asanyarray(irradiance) - t = np.asanyarray(temperature) - - s = g / 1000 - dt = t - 25 - - eta = ( 1 + a * (s - 1) + b * log(s) - + c * (s - 1)**2 + d * log(s)**2 - ) - gamma = gamma_ref * ( 1 + aa * (s - 1) + bb * log(s)) - - eta *= 1 + gamma * dt - - return eta - - -def pvgis(irradiance, temperature, k1, k2, k3, k4, k5, k6): - """ - Calculate PV module efficiency using the PVGIS model. - - The efficiency varies with irradiance and operating temperature - and is determined by 6 parameters as described in [1]_. - - Parameters - ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. - - temperature : numeric, °C - The module operating temperature. - - k1, k2, k3, k4, k5, k6 : float - Six model parameters usually determined by regression. - - Returns - ------- - eta : numeric - The efficiency of the module at the specified irradiance and - temperature. - - See also - -------- - fit_efficiency_model - adr - - Notes - ----- - A comprehensive comparison of efficiency models is found in [2]_ and [3]_. - - References - ---------- - .. [1] T. Huld, et al., "A power-rating model for crystalline silicon - PV modules," Solar Energy Materials and Solar Cells, vol. 95, - pp. 3359-3369, 2011. - - .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements - to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - - .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. - - Author: Anton Driesse, PV Performance Labs - """ - from numpy import log - - g = np.asanyarray(irradiance) - t = np.asanyarray(temperature) - - g = g / 1000 - dt = t - 25 - - eta = ( 1 - + k1 * log(g) - + k2 * log(g)**2 - + dt * (k3 - + k4 * log(g) - + k5 * log(g)**2 - ) - + k6 * dt**2 - ) - - return eta - - -def mpm6(irradiance, temperature, c1, c2, c3, c4, c6=0.0): - """ - Calculate PV module efficiency using the MPM6 model (without windspeed). - - The efficiency varies with irradiance and operating temperature - and is determined by 5 parameters as described in [1]_. A sixth - parameter captures the effect of windspeed but is not used in this - implementation. - - Parameters - ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. - - temperature : numeric, °C - The module operating temperature. - - c1, c2, c3, c4, c6 : float - Five model parameters usually determined by regression. - - Returns - ------- - eta : numeric - The efficiency of the module at the specified irradiance and - temperature. - - See also - -------- - mpm5 - fit_efficiency_model - adr - - Notes - ----- - The author of MPM6 recommends the fitting constraint c6 <= 0. - - A comprehensive comparison of efficiency models is found in [2]_ and [3]_. - - References - ---------- - .. [1] S. Ransome and J. Sutterlueti, "How to Choose the Best Empirical - Model for Optimum Energy Yield Predictions," in 44th IEEE Photovoltaic - Specialist Conference (PVSC), 2017, pp. 652-657. - - .. [2] A. Driesse and J. S. Stein, "From IEC 61853 power measurements - to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - - .. [3] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. - - Author: Anton Driesse, PV Performance Labs - """ - - g = np.asanyarray(irradiance) - t = np.asanyarray(temperature) - - g = g / 1000 - dt = t - 25 - - eta = ( - # "actual/nominal" - c1 - # loss due to temperature - + c2 * dt - # loss at low light / due to Voc - + c3 * np.log10(g) - # loss at high light / due to Rs - + c4 * g - # loss due to Rsh - + c6 / g - ) - return eta - - -def mpm5(irradiance, temperature, c1, c2, c3, c4): - """ - Call `mpm6` with one less parameter. See `mpm6` for more information. - """ - return mpm6(irradiance, temperature, c1, c2, c3, c4, c6=0.0) - - -def fit_bilinear(irradiance, temperature, eta): - """ - Prepare a bilinear interpolant for module efficiency. - - This function allows the class `pvpltools.iec61853.BilinearInterpolator` - to be used in a way that is compatible with other efficiency models - in this module. - - Parameters - ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. - - temperature : numeric, °C - The module operating temperature. - - eta : numeric - The efficiency of the module at the specified irradiance and - temperature. - - Returns - ------- - interpolator : object - A callable `BilinearInterpolator` object - - See also - -------- - pvpltools.module_efficiency.bilinear - pvpltools.iec61853.BilinearInterpolator - - Notes - ----- - Unlike the other efficiency models, bilinear interpolation only works - with a regular grid of measurements. Missing values at low irradiance - high temperature and vice versa are filled using the method described - in [1]_ and [2]_. - - References - ---------- - .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements - to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - - .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. - - Author: Anton Driesse, PV Performance Labs - """ - # (re)construct the matrix as a grid for the BilinearInterpolator - data = pd.DataFrame([irradiance, temperature, eta]).T - grid = data.pivot(*data.columns) - - # now create the interpolator object - interpolator = BilinearInterpolator(grid) - return interpolator - - -def bilinear(irradiance, temperature, interpolator): - """ - Calculate PV module efficiency using bilinear interpolation/extrapolation. - - This function allows the class `pvpltools.iec61853.BilinearInterpolator` - to be used in a way that is compatible with other efficiency models - in this module. - - Parameters - ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. - - temperature : numeric, °C - The module operating temperature. - - interpolator : object - A callable `BilinearInterpolator` object - - Returns - ------- - eta : numeric - The efficiency of the module at the specified irradiance and - temperature. - - See also - -------- - module_efficiency.fit_bilinear - pvpltools.iec61853.BilinearInterpolator - - References - ---------- - .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements - to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - - .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. - - Author: Anton Driesse, PV Performance Labs - """ - return interpolator(irradiance, temperature) - From 527523b616cab96389f7044bd0143acafab7ec6f Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 22 Nov 2022 18:27:12 +0100 Subject: [PATCH 03/23] Handle array-like inputs on all arguments. --- pvlib/pvefficiency.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 647fba8b8d..12fe7017fd 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -15,8 +15,7 @@ import numpy as np import pandas as pd from scipy.optimize import curve_fit - -from pvpltools.iec61853 import BilinearInterpolator +from scipy.special import exp10 def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, @@ -168,21 +167,26 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): Author: Anton Driesse, PV Performance Labs ''' - g = np.asanyarray(irradiance) - t = np.asanyarray(temperature) + g = irradiance + t = temperature + + k_a = np.array(k_a) + k_d = np.array(k_d) + tc_d = np.array(tc_d) + k_rs = np.array(k_rs) + k_rsh = np.array(k_rsh) # normalize the irradiance - G_REF = 1000 + G_REF = np.array(1000.) s = g / G_REF # obtain the difference from reference temperature - T_REF = 25 + T_REF = np.array(25.) dt = t - T_REF - t_abs = t + 273.15 # equation 29 in JPV - s_o = 10**(k_d + (tc_d * dt)) - s_o_ref = 10**(k_d) + s_o = exp10(k_d + (dt * tc_d)) + s_o_ref = exp10(k_d) # equation 28 and 30 in JPV # the constant k_v does not appear here because it cancels out From ba8e14ecc6ae3cb223efbdac5f51b183e00f96ee Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 22 Nov 2022 22:47:32 +0100 Subject: [PATCH 04/23] Substantial doc string edits. --- pvlib/pvefficiency.py | 82 +++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 12fe7017fd..8747d67ad6 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -1,5 +1,5 @@ """ -This module contains implementations of several PV module efficiency models. +This module contains implementations of PV module efficiency models. These models have a common purpose, which is to predict the efficiency at maximum power point as a function of the main operating conditions: @@ -29,23 +29,24 @@ def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, Parameters ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. + irradiance : numeric, non-negative + Effective irradiance incident on the PV module. [W/m²] - temperature : numeric, °C - The module operating temperature. + temperature : numeric + PV module operating temperature. [°C] eta : numeric - The efficiency of the module at the specified irradiance and - temperature. + Efficiency of the PV module at the specified irradiance and + temperature(s). [unitless|%] model : function A PV module efficiency function such as `adr`. It must take irradiance and temperature as the first two arguments and the - model-specific parameters as the remaining arguments. + model-specific parameters as the remaining arguments. [n/a] p0 : array_like, optional Initial guess for the parameters, which may speed up the fit process. + Units depend on the model. kwargs : Optional keyword arguments passed to `curve_fit`. @@ -54,28 +55,15 @@ def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, ------- popt : array Optimal values for the parameters so that the sum of the squared - residuals of ``model(irradiance, temperature, *popt) - eta`` is minimized. + residuals ``model(irradiance, temperature, *popt) - eta`` is + minimized. pcov : 2-D array - The estimated covariance of popt. See `curve_fit` for details. - - Raises - ------ - (These errors and warnings are from `curve_fit`.) - - ValueError - if either `ydata` or `xdata` contain NaNs, or if incompatible options - are used. - - RuntimeError - if the least-squares minimization fails. - - OptimizeWarning - if covariance of the parameters can not be estimated. + Estimated covariance of popt. See `curve_fit` for details. See also -------- - pvpltools.module_efficiency.adr + pvlib.pvefficiency.adr scipy.optimize.curve_fit Author: Anton Driesse, PV Performance Labs @@ -111,34 +99,35 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): Parameters ---------- - irradiance : non-negative numeric, W/m² - The effective irradiance incident on the PV module. + irradiance : numeric, non-negative + The effective irradiance incident on the PV module. [W/m²] - temperature : numeric, °C - The module operating temperature. + temperature : numeric + The PV module operating temperature. [°C] - k_a : float + k_a : numeric Absolute scaling factor, which is equal to the efficiency at reference conditions. This factor allows the model to be used with relative or absolute efficiencies, and to accommodate data sets which are not perfectly normalized but have a slight bias at - the reference conditions. + the reference conditions. [unitless|%] - k_d : negative float + k_d : numeric, negative “Dark irradiance” or diode coefficient which influences the voltage - increase with irradiance. + increase with irradiance. [unitless] - tc_d : float + tc_d : numeric Temperature coefficient of the diode coefficient, which indirectly influences voltage. Because it is the only temperature coefficient in the model, its value will also reflect secondary temperature - dependencies that are present in the PV module. + dependencies that are present in the PV module. [unitless] - k_rs and k_rsh : float + k_rs and k_rsh : numeric Series and shunt resistance loss factors. Because of the normalization they can be read as power loss fractions at reference conditions. For example, if k_rs is 0.05, the internal loss assigned to the series resistance has a magnitude equal to 5% of the module output. + [unitless] Returns ------- @@ -156,14 +145,29 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): freely to adjust the scale, or to change the module class to a slightly higher or lower efficiency. + All arguments may be scalars or vectors. If multiple arguments + are vectors they must be the same length. + References ---------- .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic - Module Efficiency Model for Energy Prediction and Rating", - forthcoming. + .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic Module + Efficiency Model for Energy Prediction and Rating," in IEEE Journal + of Photovoltaics, vol. 11, no. 2, pp. 527-534, March 2021, + doi: 10.1109/JPHOTOV.2020.3045677. + + Examples + -------- + >>> adr([1000, 200], 25, + k_a=100, k_d=-6.0, tc_d=-0.02, k_rs=0.01, k_rsh=0.01) + array([100. , 89.13695871]) + + >>> adr([1000, 200], 25, + k_a=1.0, k_d=-6.0, tc_d=-0.02, k_rs=0.01, k_rsh=0.01) + array([1. , 0.89136959]) + Author: Anton Driesse, PV Performance Labs ''' From 3c9f05f86da843724456915bb372796868ba404c Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Wed, 23 Nov 2022 16:10:11 +0100 Subject: [PATCH 05/23] Rewrite fitting function, add tests and much more. --- pvlib/pvefficiency.py | 118 ++++++++++++++++--------------- pvlib/tests/test_pvefficiency.py | 44 ++++++++++++ 2 files changed, 105 insertions(+), 57 deletions(-) create mode 100644 pvlib/tests/test_pvefficiency.py diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 8747d67ad6..8cdfa0c59f 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -4,28 +4,19 @@ These models have a common purpose, which is to predict the efficiency at maximum power point as a function of the main operating conditions: effective irradiance and module temperature. - -A function to fit any of these models to measurements is also provided. - -Copyright (c) 2019-2020 Anton Driesse, PV Performance Labs. """ -import inspect - import numpy as np import pandas as pd from scipy.optimize import curve_fit from scipy.special import exp10 -def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, +def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, **kwargs): """ - Determine the parameters of a module efficiency model by non-linear - least-squares fit. - - This is a convenience function that calls the scipy curve_fit function - with suitable parameters and defaults. + Determine the parameters of the adr module efficiency model by non-linear + least-squares fit to lab or field measurements. Parameters ---------- @@ -37,16 +28,11 @@ def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, eta : numeric Efficiency of the PV module at the specified irradiance and - temperature(s). [unitless|%] + temperature(s). [unitless] or [%] - model : function - A PV module efficiency function such as `adr`. It must take - irradiance and temperature as the first two arguments and the - model-specific parameters as the remaining arguments. [n/a] - - p0 : array_like, optional - Initial guess for the parameters, which may speed up the fit process. - Units depend on the model. + dict_output : boolean, optional + When True, return the result as a dictionary; when False, return + the result as an numpy array. kwargs : Optional keyword arguments passed to `curve_fit`. @@ -54,9 +40,7 @@ def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, Returns ------- popt : array - Optimal values for the parameters so that the sum of the squared - residuals ``model(irradiance, temperature, *popt) - eta`` is - minimized. + Optimal values for the parameters. pcov : 2-D array Estimated covariance of popt. See `curve_fit` for details. @@ -64,30 +48,48 @@ def fit_efficiency_model(irradiance, temperature, eta, model, p0=None, See also -------- pvlib.pvefficiency.adr - scipy.optimize.curve_fit - - Author: Anton Driesse, PV Performance Labs - """ - - if p0 is None: - # determine number of parameters by inspecting the function - # and set initial parameters all to 1 - sig = inspect.signature(model) - p0 = np.zeros(len(sig.parameters) - 2) - if not 'method' in kwargs: - kwargs['method'] = 'trf' - def model_wrapper(xdata, *params): - return model(*xdata, *params) - - popt, pcov = curve_fit(model_wrapper, - xdata=[irradiance, temperature], - ydata=eta, - p0=p0, - **kwargs - ) - return popt, pcov + Adapted from https://github.com/adriesse/pvpltools-python + Copyright (c) 2022, Anton Driesse, PV Performance Labs + All rights reserved. + """ + irradiance = np.asarray(irradiance, dtype=float).reshape(-1) + temperature = np.asarray(temperature, dtype=float).reshape(-1) + eta = np.asarray(eta, dtype=float).reshape(-1) + + eta_max = np.max(eta) + + P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] + P_MAX = [+np.inf, 0, +0.1, 1, 1] + P_MIN = [0, -12, -0.1, 0, 0] + P0 = [eta_max, -6, 0.0, 0, 0] + P_SCALE = [eta_max, 100, 0.1, 1, 1] + + fit_options = dict( + p0 = P0, + bounds = [P_MIN, P_MAX], + method = 'trf', + x_scale = P_SCALE, + loss = 'soft_l1', + f_scale = eta_max * 0.05, + ) + + fit_options.update(kwargs) + + def adr_wrapper(xdata, *params): + return adr(*xdata, *params) + + result = curve_fit(adr_wrapper, + xdata=[irradiance, temperature], + ydata=eta, + **fit_options, + ) + popt = result[0] + if dict_output: + return dict(zip(P_NAMES, popt)) + else: + return popt def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): @@ -148,6 +150,10 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): All arguments may be scalars or vectors. If multiple arguments are vectors they must be the same length. + See also + -------- + pvlib.pvefficiency.fit_pvefficiency_adr + References ---------- .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements @@ -161,19 +167,18 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): Examples -------- >>> adr([1000, 200], 25, - k_a=100, k_d=-6.0, tc_d=-0.02, k_rs=0.01, k_rsh=0.01) - array([100. , 89.13695871]) + k_a=100, k_d=-6.0, tc_d=0.02, k_rs=0.05, k_rsh=0.10) + array([100. , 92.79729308]) >>> adr([1000, 200], 25, - k_a=1.0, k_d=-6.0, tc_d=-0.02, k_rs=0.01, k_rsh=0.01) - array([1. , 0.89136959]) + k_a=1.0, k_d=-6.0, tc_d=0.02, k_rs=0.05, k_rsh=0.10) + array([1. , 0.92797293]) - Author: Anton Driesse, PV Performance Labs + Adapted from https://github.com/adriesse/pvpltools-python + Copyright (c) 2022, Anton Driesse, PV Performance Labs + All rights reserved. ''' - g = irradiance - t = temperature - k_a = np.array(k_a) k_d = np.array(k_d) tc_d = np.array(tc_d) @@ -182,11 +187,11 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): # normalize the irradiance G_REF = np.array(1000.) - s = g / G_REF + s = irradiance / G_REF # obtain the difference from reference temperature T_REF = np.array(25.) - dt = t - T_REF + dt = temperature - T_REF # equation 29 in JPV s_o = exp10(k_d + (dt * tc_d)) @@ -201,4 +206,3 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): eta = k_a * ((1 + k_rs + k_rsh) * v - k_rs * s - k_rsh * v**2) return eta - diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py new file mode 100644 index 0000000000..87179abd78 --- /dev/null +++ b/pvlib/tests/test_pvefficiency.py @@ -0,0 +1,44 @@ +import numpy as np +import pandas as pd + +from .conftest import assert_series_equal +from numpy.testing import assert_allclose + +import pytest + +from pvlib import pvefficiency + + +def test_adr(): + g = [1000, 200, 1000, 200, 1000, 200] + t = [25, 25, 50, 50, 75, 75] + p = [1.0, -6.602189, 0.018582, 0.071889, 0.054049] + + e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] + + result = pvefficiency.adr(g, t, *p) + assert_allclose(result, e, rtol=1e-5) + + +def test_fit_pvefficiency_adr(): + g = [1000, 200, 1000, 200, 1000, 200] + t = [25, 25, 50, 50, 75, 75] + e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] + + p = [1.0, -6.602189, 0.018582, 0.071889, 0.054049] + + result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) + assert_allclose(result, p, rtol=1e-5) + + result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=True) + assert 'k_a' in result + + +def test_adr_round_trip(): + g = [1000, 200, 1000, 200, 1000, 200] + t = [25, 25, 50, 50, 75, 75] + e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] + + p = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) + result = pvefficiency.adr(g, t, *p) + assert_allclose(result, e, rtol=1e-5) From 704cd78fd95fb205c0f6a742d59bbec61ba8463d Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Wed, 23 Nov 2022 16:56:02 +0100 Subject: [PATCH 06/23] Stickler --- pvlib/pvefficiency.py | 32 +++++++++++++++----------------- pvlib/tests/test_pvefficiency.py | 6 ------ 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 8cdfa0c59f..fbdc623695 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -7,7 +7,6 @@ """ import numpy as np -import pandas as pd from scipy.optimize import curve_fit from scipy.special import exp10 @@ -61,18 +60,17 @@ def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, eta_max = np.max(eta) P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] - P_MAX = [+np.inf, 0, +0.1, 1, 1] - P_MIN = [0, -12, -0.1, 0, 0] - P0 = [eta_max, -6, 0.0, 0, 0] - P_SCALE = [eta_max, 100, 0.1, 1, 1] - - fit_options = dict( - p0 = P0, - bounds = [P_MIN, P_MAX], - method = 'trf', - x_scale = P_SCALE, - loss = 'soft_l1', - f_scale = eta_max * 0.05, + P_MAX = [+np.inf, 0, +0.1, 1, 1] + P_MIN = [0, -12, -0.1, 0, 0] + P0 = [eta_max, -6, 0.0, 0, 0] + P_SCALE = [eta_max, 10, 0.1, 1, 1] + + fit_options = dict(p0=P0, + bounds=[P_MIN, P_MAX], + method='trf', + x_scale=P_SCALE, + loss='soft_l1', + f_scale=eta_max * 0.05, ) fit_options.update(kwargs) @@ -191,18 +189,18 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): # obtain the difference from reference temperature T_REF = np.array(25.) - dt = temperature - T_REF + dt = temperature - T_REF # equation 29 in JPV - s_o = exp10(k_d + (dt * tc_d)) + s_o = exp10(k_d + (dt * tc_d)) # noQA: E221 s_o_ref = exp10(k_d) # equation 28 and 30 in JPV # the constant k_v does not appear here because it cancels out - v = np.log(s / s_o + 1) + v = np.log(s / s_o + 1) # noQA: E221 v /= np.log(1 / s_o_ref + 1) # equation 25 in JPV - eta = k_a * ((1 + k_rs + k_rsh) * v - k_rs * s - k_rsh * v**2) + eta = k_a * ((1 + k_rs + k_rsh) * v - k_rs * s - k_rsh * v**2) return eta diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index 87179abd78..e35667b2c3 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -1,11 +1,5 @@ -import numpy as np -import pandas as pd - -from .conftest import assert_series_equal from numpy.testing import assert_allclose -import pytest - from pvlib import pvefficiency From dd0ac02dd7574fe04e61cd42d92be6dcd46d7e7b Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Wed, 23 Nov 2022 16:57:18 +0100 Subject: [PATCH 07/23] Change rtol in tests. --- pvlib/tests/test_pvefficiency.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index e35667b2c3..5ac26b9e2b 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -11,7 +11,7 @@ def test_adr(): e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] result = pvefficiency.adr(g, t, *p) - assert_allclose(result, e, rtol=1e-5) + assert_allclose(result, e, rtol=1e-4) def test_fit_pvefficiency_adr(): @@ -22,7 +22,7 @@ def test_fit_pvefficiency_adr(): p = [1.0, -6.602189, 0.018582, 0.071889, 0.054049] result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) - assert_allclose(result, p, rtol=1e-5) + assert_allclose(result, p, rtol=1e-4) result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=True) assert 'k_a' in result @@ -35,4 +35,4 @@ def test_adr_round_trip(): p = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) result = pvefficiency.adr(g, t, *p) - assert_allclose(result, e, rtol=1e-5) + assert_allclose(result, e, rtol=1e-4) From 3a0a39d6c6a677763c08e14db7f0d2568fac5ceb Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Wed, 23 Nov 2022 18:02:04 +0100 Subject: [PATCH 08/23] Stickler --- pvlib/pvefficiency.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index fbdc623695..6eadcd170d 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -60,9 +60,9 @@ def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, eta_max = np.max(eta) P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] - P_MAX = [+np.inf, 0, +0.1, 1, 1] - P_MIN = [0, -12, -0.1, 0, 0] - P0 = [eta_max, -6, 0.0, 0, 0] + P_MAX = [+np.inf, 0, +0.1, 1, 1] # noQA: E221 + P_MIN = [0, -12, -0.1, 0, 0] # noQA: E221 + P0 = [eta_max, -6, 0.0, 0, 0] # noQA: E221 P_SCALE = [eta_max, 10, 0.1, 1, 1] fit_options = dict(p0=P0, From 29eb67dc2abd4ae0229a2bfb370279943b07e1c0 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Wed, 23 Nov 2022 21:25:10 +0100 Subject: [PATCH 09/23] First attempt at making an example for the gallery. --- .../examples/iv-modeling/plot_pvefficiency.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/examples/iv-modeling/plot_pvefficiency.py diff --git a/docs/examples/iv-modeling/plot_pvefficiency.py b/docs/examples/iv-modeling/plot_pvefficiency.py new file mode 100644 index 0000000000..62e93e26d3 --- /dev/null +++ b/docs/examples/iv-modeling/plot_pvefficiency.py @@ -0,0 +1,110 @@ +""" +Using the ADR PV efficiency model +================================= + +Examples of getting the ADR PV efficiency model parameters +and using the model for system simulation. + +(WORK IN PROGRESS) + +""" + +from io import StringIO +import pandas as pd +import matplotlib.pyplot as plt +from pvlib.pvefficiency import fit_pvefficiency_adr, adr + +iec61853data = ''' + irradiance temperature p_mp +0 100 15.0 30.159 +1 200 15.0 63.057 +2 400 15.0 129.849 +3 600 15.0 197.744 +4 800 15.0 264.825 +5 1000 15.0 330.862 +6 100 25.0 29.250 +7 200 25.0 61.137 +8 400 25.0 126.445 +9 600 25.0 192.278 +10 800 25.0 257.561 +11 1000 25.0 322.305 +12 1100 25.0 354.174 +13 100 50.0 26.854 +14 200 50.0 56.698 +15 400 50.0 117.062 +16 600 50.0 177.959 +17 800 50.0 238.626 +18 1000 50.0 298.954 +19 1100 50.0 328.413 +20 100 75.0 24.074 +21 200 75.0 51.103 +22 400 75.0 106.546 +23 600 75.0 162.966 +24 800 75.0 218.585 +25 1000 75.0 273.651 +26 1100 75.0 301.013 +''' +df = pd.read_csv(StringIO(iec61853data), delim_whitespace=True) + +P_STC = 322.305 + +def pmp2eta(g, p, p_stc): + return p / p_stc / (g / 1000) + +def eta2pmp(g, eta, p_stc): + return eta * p_stc * (g / 1000) + +eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) + +adr_parms = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) + +eta_adr = adr(df.irradiance, df.temperature, **adr_parms) + +plt.figure() +plt.plot(df.irradiance, eta_rel, 'oc') +plt.plot(df.irradiance, eta_adr, '.k') +plt.legend(['Lab measurements', 'ADR model fit']) +plt.xlabel('Irradiance [W/m²]') + +# %% Cell 1 + +import os +import pvlib + +# this system is 4000 W nominal +# system losses are 14.08 % +# therefore P_STC = 3437 W + +DATAFILE = os.path.join(pvlib.__path__[0], 'data', 'pvwatts_8760_rackmount.csv') + +df = pd.read_csv(DATAFILE, skiprows=17, nrows=8760) +df['Year'] = 2019 +df.index = pd.to_datetime(df[['Year', 'Month', 'Day', 'Hour']]) + +df['ADR modelled efficiency (-)'] = adr(df['Plane of Array Irradiance (W/m^2)'], + df['Cell Temperature (C)'], + **adr_parms) + +df['ADR modelled power (W)'] = eta2pmp(df['Plane of Array Irradiance (W/m^2)'], + df['ADR modelled efficiency (-)'], + p_stc = 3437) + +# %% Cell 2 + +DEMO_DAY = '2019-08-05' + +plt.figure() +plt.plot(df['DC Array Output (W)'][DEMO_DAY]) +plt.plot(df['ADR modelled power (W)'][DEMO_DAY]) +plt.xticks(rotation=30) +plt.legend(['PVWATTS','ADR']) +plt.ylabel('Power [W]') + +# %% Cell 3 + +plt.figure() +plt.scatter(df['DC Array Output (W)'], df['ADR modelled power (W)'], + c=df['Cell Temperature (C)'], alpha=.3, cmap='jet') +plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) +plt.xlabel('PVWATTS DC Array Output [W]') +plt.ylabel('ADR modelled power [W]') From 0027c952df06b82e2f7b302ef9ee204d3eb3dd6a Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Wed, 23 Nov 2022 23:58:56 +0100 Subject: [PATCH 10/23] Stickler in example; adjust numeric values in tests. --- .../examples/iv-modeling/plot_pvefficiency.py | 44 ++++++++++--------- pvlib/tests/test_pvefficiency.py | 10 ++--- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/examples/iv-modeling/plot_pvefficiency.py b/docs/examples/iv-modeling/plot_pvefficiency.py index 62e93e26d3..b78aaa54ac 100644 --- a/docs/examples/iv-modeling/plot_pvefficiency.py +++ b/docs/examples/iv-modeling/plot_pvefficiency.py @@ -8,10 +8,12 @@ (WORK IN PROGRESS) """ - +import os from io import StringIO import pandas as pd import matplotlib.pyplot as plt + +import pvlib from pvlib.pvefficiency import fit_pvefficiency_adr, adr iec61853data = ''' @@ -48,12 +50,15 @@ P_STC = 322.305 + def pmp2eta(g, p, p_stc): return p / p_stc / (g / 1000) + def eta2pmp(g, eta, p_stc): return eta * p_stc * (g / 1000) + eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) adr_parms = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) @@ -68,43 +73,42 @@ def eta2pmp(g, eta, p_stc): # %% Cell 1 -import os -import pvlib - # this system is 4000 W nominal # system losses are 14.08 % # therefore P_STC = 3437 W -DATAFILE = os.path.join(pvlib.__path__[0], 'data', 'pvwatts_8760_rackmount.csv') +DATADIR = os.path.join(pvlib.__path__[0], 'data') +DATAFILE = os.path.join(DATADIR, 'pvwatts_8760_rackmount.csv') df = pd.read_csv(DATAFILE, skiprows=17, nrows=8760) -df['Year'] = 2019 -df.index = pd.to_datetime(df[['Year', 'Month', 'Day', 'Hour']]) +df.columns = ['month', 'day', 'hour', + 'dni', 'dif', 't_amb', 'wind_speed', + 'poa_global', 't_cell', 'p_dc', 'p_ac'] -df['ADR modelled efficiency (-)'] = adr(df['Plane of Array Irradiance (W/m^2)'], - df['Cell Temperature (C)'], - **adr_parms) +df['year'] = 2019 +DATECOLS = ['year', 'month', 'day', 'hour'] +df.index = pd.to_datetime(df[DATECOLS]) +df = df.drop(columns=DATECOLS) -df['ADR modelled power (W)'] = eta2pmp(df['Plane of Array Irradiance (W/m^2)'], - df['ADR modelled efficiency (-)'], - p_stc = 3437) +df['eta_adr'] = adr(df['poa_global'], df['t_cell'], **adr_parms) +df['p_dc_adr'] = eta2pmp(df['poa_global'], df['eta_adr'], p_stc=3437) # %% Cell 2 DEMO_DAY = '2019-08-05' plt.figure() -plt.plot(df['DC Array Output (W)'][DEMO_DAY]) -plt.plot(df['ADR modelled power (W)'][DEMO_DAY]) +plt.plot(df['p_dc'][DEMO_DAY]) +plt.plot(df['p_dc_adr'][DEMO_DAY]) plt.xticks(rotation=30) -plt.legend(['PVWATTS','ADR']) +plt.legend(['PVWATTS', 'ADR']) plt.ylabel('Power [W]') # %% Cell 3 plt.figure() -plt.scatter(df['DC Array Output (W)'], df['ADR modelled power (W)'], - c=df['Cell Temperature (C)'], alpha=.3, cmap='jet') +plt.scatter(df['p_dc'], df['p_dc_adr'], + c=df['t_cell'], alpha=.3, cmap='jet') plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) -plt.xlabel('PVWATTS DC Array Output [W]') -plt.ylabel('ADR modelled power [W]') +plt.xlabel('PVWATTS DC array output [W]') +plt.ylabel('ADR modelled DC array output [W]') diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index 5ac26b9e2b..4fe565fcc9 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -6,9 +6,9 @@ def test_adr(): g = [1000, 200, 1000, 200, 1000, 200] t = [25, 25, 50, 50, 75, 75] - p = [1.0, -6.602189, 0.018582, 0.071889, 0.054049] + p = [1.0, -6.684898, 0.018855, 0.069917, 0.054369] - e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] + e = [1.0, 0.949154, 0.92812, 0.876472, 0.855699, 0.80325] result = pvefficiency.adr(g, t, *p) assert_allclose(result, e, rtol=1e-4) @@ -17,9 +17,9 @@ def test_adr(): def test_fit_pvefficiency_adr(): g = [1000, 200, 1000, 200, 1000, 200] t = [25, 25, 50, 50, 75, 75] - e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] + e = [1.0, 0.949154, 0.92812, 0.876472, 0.855699, 0.80325] - p = [1.0, -6.602189, 0.018582, 0.071889, 0.054049] + p = [1.0, -6.684898, 0.018855, 0.069917, 0.054369] result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) assert_allclose(result, p, rtol=1e-4) @@ -31,7 +31,7 @@ def test_fit_pvefficiency_adr(): def test_adr_round_trip(): g = [1000, 200, 1000, 200, 1000, 200] t = [25, 25, 50, 50, 75, 75] - e = [1.0, 0.949147, 0.928114, 0.876456, 0.855693, 0.80323] + e = [1.0, 0.949154, 0.92812, 0.876472, 0.855699, 0.80325] p = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) result = pvefficiency.adr(g, t, *p) From 2a799d5625869b2673184f01363c3ab86ecf0e4d Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 24 Nov 2022 14:02:19 +0100 Subject: [PATCH 11/23] More experimenting with examples for th gallery. (Not for review yet.) --- docs/examples/pvefficiency-adr/README.rst | 3 + .../plot_fit_to_matrix.py} | 69 +++++---------- .../pvefficiency-adr/plot_pvefficiency.py | 85 +++++++++++++++++++ 3 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 docs/examples/pvefficiency-adr/README.rst rename docs/examples/{iv-modeling/plot_pvefficiency.py => pvefficiency-adr/plot_fit_to_matrix.py} (54%) create mode 100644 docs/examples/pvefficiency-adr/plot_pvefficiency.py diff --git a/docs/examples/pvefficiency-adr/README.rst b/docs/examples/pvefficiency-adr/README.rst new file mode 100644 index 0000000000..0db64aa046 --- /dev/null +++ b/docs/examples/pvefficiency-adr/README.rst @@ -0,0 +1,3 @@ +ADR Model for PV Module Efficiency +---------------------------------- + diff --git a/docs/examples/iv-modeling/plot_pvefficiency.py b/docs/examples/pvefficiency-adr/plot_fit_to_matrix.py similarity index 54% rename from docs/examples/iv-modeling/plot_pvefficiency.py rename to docs/examples/pvefficiency-adr/plot_fit_to_matrix.py index b78aaa54ac..ebd039be47 100644 --- a/docs/examples/iv-modeling/plot_pvefficiency.py +++ b/docs/examples/pvefficiency-adr/plot_fit_to_matrix.py @@ -1,6 +1,6 @@ """ -Using the ADR PV efficiency model -================================= +Fitting the ADR PV module efficiency model to IEC 61853-1 matrix measurements +============================================================================= Examples of getting the ADR PV efficiency model parameters and using the model for system simulation. @@ -50,20 +50,32 @@ P_STC = 322.305 +#%% +# Going back and forth between power and efficiency is a common operation +# so here are a couple of functions for that. +# The efficiency is normalized to STC conditions, in other words, at STC +# conditions the efficiency is 1.0 (or 100 %) + def pmp2eta(g, p, p_stc): - return p / p_stc / (g / 1000) + g_rel = g / 1000 + p_rel = p / p_stc + return p_rel / g_rel -def eta2pmp(g, eta, p_stc): - return eta * p_stc * (g / 1000) +def eta2pmp(g, eta_rel, p_stc): + g_rel = g / 1000 + p_rel = g_rel * eta_rel + return p_rel * p_stc +#%% +# eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) -adr_parms = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) +adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) -eta_adr = adr(df.irradiance, df.temperature, **adr_parms) +eta_adr = adr(df.irradiance, df.temperature, **adr_params) plt.figure() plt.plot(df.irradiance, eta_rel, 'oc') @@ -71,44 +83,5 @@ def eta2pmp(g, eta, p_stc): plt.legend(['Lab measurements', 'ADR model fit']) plt.xlabel('Irradiance [W/m²]') -# %% Cell 1 - -# this system is 4000 W nominal -# system losses are 14.08 % -# therefore P_STC = 3437 W - -DATADIR = os.path.join(pvlib.__path__[0], 'data') -DATAFILE = os.path.join(DATADIR, 'pvwatts_8760_rackmount.csv') - -df = pd.read_csv(DATAFILE, skiprows=17, nrows=8760) -df.columns = ['month', 'day', 'hour', - 'dni', 'dif', 't_amb', 'wind_speed', - 'poa_global', 't_cell', 'p_dc', 'p_ac'] - -df['year'] = 2019 -DATECOLS = ['year', 'month', 'day', 'hour'] -df.index = pd.to_datetime(df[DATECOLS]) -df = df.drop(columns=DATECOLS) - -df['eta_adr'] = adr(df['poa_global'], df['t_cell'], **adr_parms) -df['p_dc_adr'] = eta2pmp(df['poa_global'], df['eta_adr'], p_stc=3437) - -# %% Cell 2 - -DEMO_DAY = '2019-08-05' - -plt.figure() -plt.plot(df['p_dc'][DEMO_DAY]) -plt.plot(df['p_dc_adr'][DEMO_DAY]) -plt.xticks(rotation=30) -plt.legend(['PVWATTS', 'ADR']) -plt.ylabel('Power [W]') - -# %% Cell 3 - -plt.figure() -plt.scatter(df['p_dc'], df['p_dc_adr'], - c=df['t_cell'], alpha=.3, cmap='jet') -plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) -plt.xlabel('PVWATTS DC array output [W]') -plt.ylabel('ADR modelled DC array output [W]') +for k, v in adr_params.items(): + print ('%-5s = %7.4f' % (k, v)) \ No newline at end of file diff --git a/docs/examples/pvefficiency-adr/plot_pvefficiency.py b/docs/examples/pvefficiency-adr/plot_pvefficiency.py new file mode 100644 index 0000000000..009e00b14b --- /dev/null +++ b/docs/examples/pvefficiency-adr/plot_pvefficiency.py @@ -0,0 +1,85 @@ +""" +Using the ADR PV efficiency model +================================= + +Examples of getting the ADR PV efficiency model parameters +and using the model for system simulation. + +(WORK IN PROGRESS) + +""" +import os +from io import StringIO +import pandas as pd +import matplotlib.pyplot as plt + +import pvlib +from pvlib.pvefficiency import fit_pvefficiency_adr, adr + +#%% +# Going back and forth between power and efficiency is a common operation +# so here are a couple of functions for that. +# The efficiency is normalized to STC conditions, in other words, at STC +# conditions the efficiency is 1.0 (or 100 %) + + +def pmp2eta(g, p, p_stc): + g_rel = g / 1000 + p_rel = p / p_stc + return p_rel / g_rel + + +def eta2pmp(g, eta_rel, p_stc): + g_rel = g / 1000 + p_rel = g_rel * eta_rel + return p_rel * p_stc + + +# %% Cell 1 + +# this system is 4000 W nominal +# system losses are 14.08 % +# therefore P_STC = 3437 W + +adr_parms = {'k_a': 0.99879, + 'k_d': -5.85188, + 'tc_d': 0.01939, + 'k_rs': 0.06962, + 'k_rsh': 0.21036 + } + +DATADIR = os.path.join(pvlib.__path__[0], 'data') +DATAFILE = os.path.join(DATADIR, 'pvwatts_8760_rackmount.csv') + +df = pd.read_csv(DATAFILE, skiprows=17, nrows=8760) +df.columns = ['month', 'day', 'hour', + 'dni', 'dif', 't_amb', 'wind_speed', + 'poa_global', 't_cell', 'p_dc', 'p_ac'] + +df['year'] = 2019 +DATECOLS = ['year', 'month', 'day', 'hour'] +df.index = pd.to_datetime(df[DATECOLS]) +df = df.drop(columns=DATECOLS) + +df['eta_adr'] = adr(df['poa_global'], df['t_cell'], **adr_parms) +df['p_dc_adr'] = eta2pmp(df['poa_global'], df['eta_adr'], p_stc=3437) + +# %% Cell 2 + +DEMO_DAY = '2019-08-05' + +plt.figure() +plt.plot(df['p_dc'][DEMO_DAY]) +plt.plot(df['p_dc_adr'][DEMO_DAY]) +plt.xticks(rotation=30) +plt.legend(['PVWATTS', 'ADR']) +plt.ylabel('Power [W]') + +# %% Cell 3 + +plt.figure() +plt.scatter(df['p_dc'], df['p_dc_adr'], + c=df['t_cell'], alpha=.3, cmap='jet') +plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) +plt.xlabel('PVWATTS DC array output [W]') +plt.ylabel('ADR modelled DC array output [W]') From abe84560d5ddac27dac876d2f81d7509ecfe9624 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 24 Nov 2022 15:33:13 +0100 Subject: [PATCH 12/23] Spruce up the examples some more and tweak the test tolerance again. --- .../.pylint.d/plot_simulate_system1.stats | Bin 0 -> 1028 bytes .../README.rst | 0 .../plot_fit_to_matrix.py | 55 ++++++++++++--- .../plot_simulate_system.py} | 63 ++++++++++++++---- pvlib/tests/test_pvefficiency.py | 9 ++- 5 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 docs/examples/adr-pv-module-efficiency/.pylint.d/plot_simulate_system1.stats rename docs/examples/{pvefficiency-adr => adr-pv-module-efficiency}/README.rst (100%) rename docs/examples/{pvefficiency-adr => adr-pv-module-efficiency}/plot_fit_to_matrix.py (60%) rename docs/examples/{pvefficiency-adr/plot_pvefficiency.py => adr-pv-module-efficiency/plot_simulate_system.py} (55%) diff --git a/docs/examples/adr-pv-module-efficiency/.pylint.d/plot_simulate_system1.stats b/docs/examples/adr-pv-module-efficiency/.pylint.d/plot_simulate_system1.stats new file mode 100644 index 0000000000000000000000000000000000000000..b27b8593d1028f824f0ce8acbf9eb1590a7918fc GIT binary patch literal 1028 zcmZWo$!^;)6m;UO&eE(+nl9;<)@f^Z-!Hx8pqo$XArL5uwg^y^o=Cdz!9anc-_d{U z7j#J3f$R9>H}jTx^QfQAF*VxZdZW>ZM^Rp+R&rVx(_(l4sFa0?w8*VwhDUm&4bKS` zOLc`dfOD=4EeB`e5(hiiZ;-e-v^Dq2I%Du5rXQbG-A zJ!po@h7+&NIQz zu;90uwxGRWvF5qSij?*z@)OClrUOLVPCQGUkg3UsfVXDgI{7Bhof-6M#L;}0dekvs zx9YSxGp3?j^Q{Y|4V}!j94dWi&ii&eotknVTz%IW?#hD)u-SF}zp^&sAu9Lgx!Ti5lYuIZ zQd>%q;MJy)+fmbFaK|Vsd6nR;UQal?0PBC==aiRRrCcQf#ZSicwCxXHGLpOL~?y*>8H@qZi)eoEi5~NfCF;k^4uT3|U>D^|D8={JSrQNTAn_dE=S4 zJ$mQY^GvrK@_t`HgGxs9-lcu;X&-y^$$CM_D3MV3`I5e5?&I9R3IkchOh&4}fxcdR OJN^Fm{FnV>yZ-=@Z8g3C literal 0 HcmV?d00001 diff --git a/docs/examples/pvefficiency-adr/README.rst b/docs/examples/adr-pv-module-efficiency/README.rst similarity index 100% rename from docs/examples/pvefficiency-adr/README.rst rename to docs/examples/adr-pv-module-efficiency/README.rst diff --git a/docs/examples/pvefficiency-adr/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py similarity index 60% rename from docs/examples/pvefficiency-adr/plot_fit_to_matrix.py rename to docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index ebd039be47..c547b4fba7 100644 --- a/docs/examples/pvefficiency-adr/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -1,21 +1,32 @@ """ -Fitting the ADR PV module efficiency model to IEC 61853-1 matrix measurements -============================================================================= +Obtaining ADR model parameters from IEC 61853 matrix measurements +================================================================= -Examples of getting the ADR PV efficiency model parameters -and using the model for system simulation. +The fitting function provided in pvlib does exactly that--and more. (WORK IN PROGRESS) +Since PV module efficiency varies with irradiance and temperature +what better way to train a model than using efficiency measurement +over a broad range of temperature and irradiance levels? +The standard IEC 61853-1 defines a standard matrix of conditions +for such measurements and this example shows how the ADR model +parameters can be determined with just a few lines of code using +functions in pvlib-python. + """ -import os + from io import StringIO import pandas as pd import matplotlib.pyplot as plt -import pvlib from pvlib.pvefficiency import fit_pvefficiency_adr, adr +# %% The text on this line is not displayed +# +# Here are some matrix measurements: +# + iec61853data = ''' irradiance temperature p_mp 0 100 15.0 30.159 @@ -50,11 +61,13 @@ P_STC = 322.305 -#%% +# %% +# # Going back and forth between power and efficiency is a common operation # so here are a couple of functions for that. # The efficiency is normalized to STC conditions, in other words, at STC # conditions the efficiency is 1.0 (or 100 %) +# def pmp2eta(g, p, p_stc): @@ -69,14 +82,26 @@ def eta2pmp(g, eta_rel, p_stc): return p_rel * p_stc -#%% +# %% # +# Now calculate the normalized or relative efficiency +# and use the handy fitting function to determine the parameters. +# + eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) eta_adr = adr(df.irradiance, df.temperature, **adr_params) +#%% +# +# The result shows only minor random differences. +# These are most likely evidence of measurement errors. +# The parameters shown below can now be used to +# simulate the module operating in a PV system. +# + plt.figure() plt.plot(df.irradiance, eta_rel, 'oc') plt.plot(df.irradiance, eta_adr, '.k') @@ -84,4 +109,16 @@ def eta2pmp(g, eta_rel, p_stc): plt.xlabel('Irradiance [W/m²]') for k, v in adr_params.items(): - print ('%-5s = %7.4f' % (k, v)) \ No newline at end of file + print('%-5s = %7.4f' % (k, v)) + +# %% +# +# References +# ---------- +# .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements +# to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + +# .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic Module +# Efficiency Model for Energy Prediction and Rating," in IEEE Journal +# of Photovoltaics, vol. 11, no. 2, pp. 527-534, March 2021, +# doi: 10.1109/JPHOTOV.2020.3045677. diff --git a/docs/examples/pvefficiency-adr/plot_pvefficiency.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py similarity index 55% rename from docs/examples/pvefficiency-adr/plot_pvefficiency.py rename to docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index 009e00b14b..95c2447c82 100644 --- a/docs/examples/pvefficiency-adr/plot_pvefficiency.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -1,26 +1,27 @@ """ -Using the ADR PV efficiency model -================================= +Simulating PV systems using the ADR module efficiency model +=========================================================== -Examples of getting the ADR PV efficiency model parameters -and using the model for system simulation. +Time series processing with the ADR model is fast and ... efficient! (WORK IN PROGRESS) """ + import os -from io import StringIO import pandas as pd import matplotlib.pyplot as plt import pvlib -from pvlib.pvefficiency import fit_pvefficiency_adr, adr +from pvlib.pvefficiency import adr -#%% +# %% +# # Going back and forth between power and efficiency is a common operation # so here are a couple of functions for that. # The efficiency is normalized to STC conditions, in other words, at STC # conditions the efficiency is 1.0 (or 100 %) +# def pmp2eta(g, p, p_stc): @@ -35,11 +36,12 @@ def eta2pmp(g, eta_rel, p_stc): return p_rel * p_stc -# %% Cell 1 - +# %% +# # this system is 4000 W nominal # system losses are 14.08 % # therefore P_STC = 3437 W +# adr_parms = {'k_a': 0.99879, 'k_d': -5.85188, @@ -48,6 +50,12 @@ def eta2pmp(g, eta_rel, p_stc): 'k_rsh': 0.21036 } +# %% +# +# Read an existing PVWATTS simulation output file +# which contains all the input data we need to run an ADR simulation. +# + DATADIR = os.path.join(pvlib.__path__[0], 'data') DATAFILE = os.path.join(DATADIR, 'pvwatts_8760_rackmount.csv') @@ -61,10 +69,22 @@ def eta2pmp(g, eta_rel, p_stc): df.index = pd.to_datetime(df[DATECOLS]) df = df.drop(columns=DATECOLS) +# %% +# +# Simulating takes just two lines of code: +# one to calculate the efficiency and one to convert efficiency to power. +# + df['eta_adr'] = adr(df['poa_global'], df['t_cell'], **adr_parms) df['p_dc_adr'] = eta2pmp(df['poa_global'], df['eta_adr'], p_stc=3437) -# %% Cell 2 +# %% +# +# Compare the ADR simulated output to PVWATS. +# +# NOTE: they are not supposed to be the same because the module simulated +# by PVWATTS is most likely different from the our ADR example module. +# DEMO_DAY = '2019-08-05' @@ -73,13 +93,30 @@ def eta2pmp(g, eta_rel, p_stc): plt.plot(df['p_dc_adr'][DEMO_DAY]) plt.xticks(rotation=30) plt.legend(['PVWATTS', 'ADR']) -plt.ylabel('Power [W]') +plt.ylabel('Power [W]'); -# %% Cell 3 +# %% +# +# The colors in the next graph show that the PVWATTS module probably has +# a larger temperature coefficient than the ADR example module. +# plt.figure() plt.scatter(df['p_dc'], df['p_dc_adr'], c=df['t_cell'], alpha=.3, cmap='jet') plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) plt.xlabel('PVWATTS DC array output [W]') -plt.ylabel('ADR modelled DC array output [W]') +plt.ylabel('ADR modelled DC array output [W]'); +plt.colorbar(label='T_cell', ax=plt.gca()); + +# %% +# +# References +# ---------- +# .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements +# to PV system simulations", Sandia Report No. SAND2020-3877, 2020. + +# .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic Module +# Efficiency Model for Energy Prediction and Rating," in IEEE Journal +# of Photovoltaics, vol. 11, no. 2, pp. 527-534, March 2021, +# doi: 10.1109/JPHOTOV.2020.3045677. diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index 4fe565fcc9..804ce092fa 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -11,7 +11,7 @@ def test_adr(): e = [1.0, 0.949154, 0.92812, 0.876472, 0.855699, 0.80325] result = pvefficiency.adr(g, t, *p) - assert_allclose(result, e, rtol=1e-4) + assert_allclose(result, e, rtol=1e-5) def test_fit_pvefficiency_adr(): @@ -22,7 +22,10 @@ def test_fit_pvefficiency_adr(): p = [1.0, -6.684898, 0.018855, 0.069917, 0.054369] result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) - assert_allclose(result, p, rtol=1e-4) + # the fitted parameters vary somewhat by platform during the testing + # so the tolerance is higher on the parameters than on the efficiencies + # in the other tests + assert_allclose(result, p, rtol=1e-3) result = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=True) assert 'k_a' in result @@ -35,4 +38,4 @@ def test_adr_round_trip(): p = pvefficiency.fit_pvefficiency_adr(g, t, e, dict_output=False) result = pvefficiency.adr(g, t, *p) - assert_allclose(result, e, rtol=1e-4) + assert_allclose(result, e, rtol=1e-5) From 118e8b80bd6527271a787bb13e45a39414baafe1 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 24 Nov 2022 16:12:50 +0100 Subject: [PATCH 13/23] Making examples is actually pretty easy and fun. --- .../plot_fit_to_matrix.py | 27 ++++++++++--------- .../plot_simulate_system.py | 16 +++++++---- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index c547b4fba7..e591bfee51 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -84,32 +84,34 @@ def eta2pmp(g, eta_rel, p_stc): # %% # -# Now calculate the normalized or relative efficiency -# and use the handy fitting function to determine the parameters. +# Now calculate the normalized or relative efficiency values +# and use the fitting function to determine the parameters. +# The parameters (shown below) can now be used to +# simulate the module operating in a PV system. # eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) -eta_adr = adr(df.irradiance, df.temperature, **adr_params) +for k, v in adr_params.items(): + print('%-5s = %7.4f' % (k, v)) -#%% +# %% # -# The result shows only minor random differences. -# These are most likely evidence of measurement errors. -# The parameters shown below can now be used to -# simulate the module operating in a PV system. +# Compare the model output to the original measurements. +# The chart below shows minor random differences +# that are most likely evidence of measurement errors. # +eta_adr = adr(df.irradiance, df.temperature, **adr_params) + plt.figure() plt.plot(df.irradiance, eta_rel, 'oc') plt.plot(df.irradiance, eta_adr, '.k') plt.legend(['Lab measurements', 'ADR model fit']) plt.xlabel('Irradiance [W/m²]') - -for k, v in adr_params.items(): - print('%-5s = %7.4f' % (k, v)) +plt.show() # %% # @@ -117,8 +119,9 @@ def eta2pmp(g, eta_rel, p_stc): # ---------- # .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements # to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - +# # .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic Module # Efficiency Model for Energy Prediction and Rating," in IEEE Journal # of Photovoltaics, vol. 11, no. 2, pp. 527-534, March 2021, # doi: 10.1109/JPHOTOV.2020.3045677. +# diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index 95c2447c82..76c857d59e 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -80,7 +80,7 @@ def eta2pmp(g, eta_rel, p_stc): # %% # -# Compare the ADR simulated output to PVWATS. +# Compare the ADR simulated output to PVWATS for one day. # # NOTE: they are not supposed to be the same because the module simulated # by PVWATTS is most likely different from the our ADR example module. @@ -93,7 +93,8 @@ def eta2pmp(g, eta_rel, p_stc): plt.plot(df['p_dc_adr'][DEMO_DAY]) plt.xticks(rotation=30) plt.legend(['PVWATTS', 'ADR']) -plt.ylabel('Power [W]'); +plt.ylabel('Power [W]') +plt.show() # %% # @@ -106,8 +107,9 @@ def eta2pmp(g, eta_rel, p_stc): c=df['t_cell'], alpha=.3, cmap='jet') plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) plt.xlabel('PVWATTS DC array output [W]') -plt.ylabel('ADR modelled DC array output [W]'); -plt.colorbar(label='T_cell', ax=plt.gca()); +plt.ylabel('ADR modelled DC array output [W]') +plt.colorbar(label='T_cell', ax=plt.gca()) +plt.show() # %% # @@ -115,8 +117,12 @@ def eta2pmp(g, eta_rel, p_stc): # ---------- # .. [1] A. Driesse and J. S. Stein, "From IEC 61853 power measurements # to PV system simulations", Sandia Report No. SAND2020-3877, 2020. - +# # .. [2] A. Driesse, M. Theristis and J. S. Stein, "A New Photovoltaic Module # Efficiency Model for Energy Prediction and Rating," in IEEE Journal # of Photovoltaics, vol. 11, no. 2, pp. 527-534, March 2021, # doi: 10.1109/JPHOTOV.2020.3045677. +# +# +# +# From 5a9b7aa972e19d6925d4f3665a063b13ee906412 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 24 Nov 2022 19:52:40 +0100 Subject: [PATCH 14/23] Add a pair of utility functions. --- pvlib/pvefficiency.py | 170 ++++++++++++++++++------------- pvlib/tests/test_pvefficiency.py | 10 ++ 2 files changed, 109 insertions(+), 71 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 6eadcd170d..e86289aee6 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -11,83 +11,32 @@ from scipy.special import exp10 -def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, - **kwargs): - """ - Determine the parameters of the adr module efficiency model by non-linear - least-squares fit to lab or field measurements. - - Parameters - ---------- - irradiance : numeric, non-negative - Effective irradiance incident on the PV module. [W/m²] - - temperature : numeric - PV module operating temperature. [°C] - - eta : numeric - Efficiency of the PV module at the specified irradiance and - temperature(s). [unitless] or [%] - - dict_output : boolean, optional - When True, return the result as a dictionary; when False, return - the result as an numpy array. - - kwargs : - Optional keyword arguments passed to `curve_fit`. - - Returns - ------- - popt : array - Optimal values for the parameters. - - pcov : 2-D array - Estimated covariance of popt. See `curve_fit` for details. - - See also - -------- - pvlib.pvefficiency.adr - - - Adapted from https://github.com/adriesse/pvpltools-python - Copyright (c) 2022, Anton Driesse, PV Performance Labs - All rights reserved. - """ - irradiance = np.asarray(irradiance, dtype=float).reshape(-1) - temperature = np.asarray(temperature, dtype=float).reshape(-1) - eta = np.asarray(eta, dtype=float).reshape(-1) +def power_from_efficiency(efficiency, irradiance, p_mp_ref): + ''' + Convert normalized or relative efficiency to power. - eta_max = np.max(eta) + If you can't figure out what the parameters mean, don't use this function! + ''' + G_REF = np.array(1000.) + g_rel = irradiance / G_REF - P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] - P_MAX = [+np.inf, 0, +0.1, 1, 1] # noQA: E221 - P_MIN = [0, -12, -0.1, 0, 0] # noQA: E221 - P0 = [eta_max, -6, 0.0, 0, 0] # noQA: E221 - P_SCALE = [eta_max, 10, 0.1, 1, 1] + p_rel = g_rel * efficiency + power = p_rel * p_mp_ref + return power - fit_options = dict(p0=P0, - bounds=[P_MIN, P_MAX], - method='trf', - x_scale=P_SCALE, - loss='soft_l1', - f_scale=eta_max * 0.05, - ) - fit_options.update(kwargs) +def efficiency_from_power(power, irradiance, p_mp_ref): + ''' + Convert power to normalized or relative efficiency. - def adr_wrapper(xdata, *params): - return adr(*xdata, *params) + If you can't figure out what the parameters mean, don't use this function! + ''' + G_REF = np.array(1000.) + g_rel = irradiance / G_REF - result = curve_fit(adr_wrapper, - xdata=[irradiance, temperature], - ydata=eta, - **fit_options, - ) - popt = result[0] - if dict_output: - return dict(zip(P_NAMES, popt)) - else: - return popt + p_rel = power / np.asanyarray(p_mp_ref, dtype=float) + eta_rel = p_rel / g_rel + return eta_rel def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): @@ -204,3 +153,82 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): eta = k_a * ((1 + k_rs + k_rsh) * v - k_rs * s - k_rsh * v**2) return eta + + +def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, + **kwargs): + """ + Determine the parameters of the adr module efficiency model by non-linear + least-squares fit to lab or field measurements. + + Parameters + ---------- + irradiance : numeric, non-negative + Effective irradiance incident on the PV module. [W/m²] + + temperature : numeric + PV module operating temperature. [°C] + + eta : numeric + Efficiency of the PV module at the specified irradiance and + temperature(s). [unitless] or [%] + + dict_output : boolean, optional + When True, return the result as a dictionary; when False, return + the result as an numpy array. + + kwargs : + Optional keyword arguments passed to `curve_fit`. + + Returns + ------- + popt : array + Optimal values for the parameters. + + pcov : 2-D array + Estimated covariance of popt. See `curve_fit` for details. + + See also + -------- + pvlib.pvefficiency.adr + + + Adapted from https://github.com/adriesse/pvpltools-python + Copyright (c) 2022, Anton Driesse, PV Performance Labs + All rights reserved. + """ + irradiance = np.asarray(irradiance, dtype=float).reshape(-1) + temperature = np.asarray(temperature, dtype=float).reshape(-1) + eta = np.asarray(eta, dtype=float).reshape(-1) + + eta_max = np.max(eta) + + P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] + P_MAX = [+np.inf, 0, +0.1, 1, 1] # noQA: E221 + P_MIN = [0, -12, -0.1, 0, 0] # noQA: E221 + P0 = [eta_max, -6, 0.0, 0, 0] # noQA: E221 + P_SCALE = [eta_max, 10, 0.1, 1, 1] + + fit_options = dict(p0=P0, + bounds=[P_MIN, P_MAX], + method='trf', + x_scale=P_SCALE, + loss='soft_l1', + f_scale=eta_max * 0.05, + ) + + fit_options.update(kwargs) + + def adr_wrapper(xdata, *params): + return adr(*xdata, *params) + + result = curve_fit(adr_wrapper, + xdata=[irradiance, temperature], + ydata=eta, + **fit_options, + ) + popt = result[0] + if dict_output: + return dict(zip(P_NAMES, popt)) + else: + return popt diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index 804ce092fa..b7a3bbf71d 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -3,6 +3,16 @@ from pvlib import pvefficiency +def test_power_from_efficiency(): + # super simple function so one test will do + assert_allclose(100, pvefficiency.power_from_efficiency(.5, 500, 400)) + + +def test_efficiency_from_power(): + # super simple function so one test will do + assert_allclose(0.5, pvefficiency.efficiency_from_power(100, 500, 400)) + + def test_adr(): g = [1000, 200, 1000, 200, 1000, 200] t = [25, 25, 50, 50, 75, 75] From 227629d1ac474baf31fd61ea09c9cb72dee2d55f Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 24 Nov 2022 19:52:40 +0100 Subject: [PATCH 15/23] Add a pair of utility functions. --- pvlib/pvefficiency.py | 170 ++++++++++++++++++------------- pvlib/tests/test_pvefficiency.py | 10 ++ 2 files changed, 109 insertions(+), 71 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 6eadcd170d..e86289aee6 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -11,83 +11,32 @@ from scipy.special import exp10 -def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, - **kwargs): - """ - Determine the parameters of the adr module efficiency model by non-linear - least-squares fit to lab or field measurements. - - Parameters - ---------- - irradiance : numeric, non-negative - Effective irradiance incident on the PV module. [W/m²] - - temperature : numeric - PV module operating temperature. [°C] - - eta : numeric - Efficiency of the PV module at the specified irradiance and - temperature(s). [unitless] or [%] - - dict_output : boolean, optional - When True, return the result as a dictionary; when False, return - the result as an numpy array. - - kwargs : - Optional keyword arguments passed to `curve_fit`. - - Returns - ------- - popt : array - Optimal values for the parameters. - - pcov : 2-D array - Estimated covariance of popt. See `curve_fit` for details. - - See also - -------- - pvlib.pvefficiency.adr - - - Adapted from https://github.com/adriesse/pvpltools-python - Copyright (c) 2022, Anton Driesse, PV Performance Labs - All rights reserved. - """ - irradiance = np.asarray(irradiance, dtype=float).reshape(-1) - temperature = np.asarray(temperature, dtype=float).reshape(-1) - eta = np.asarray(eta, dtype=float).reshape(-1) +def power_from_efficiency(efficiency, irradiance, p_mp_ref): + ''' + Convert normalized or relative efficiency to power. - eta_max = np.max(eta) + If you can't figure out what the parameters mean, don't use this function! + ''' + G_REF = np.array(1000.) + g_rel = irradiance / G_REF - P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] - P_MAX = [+np.inf, 0, +0.1, 1, 1] # noQA: E221 - P_MIN = [0, -12, -0.1, 0, 0] # noQA: E221 - P0 = [eta_max, -6, 0.0, 0, 0] # noQA: E221 - P_SCALE = [eta_max, 10, 0.1, 1, 1] + p_rel = g_rel * efficiency + power = p_rel * p_mp_ref + return power - fit_options = dict(p0=P0, - bounds=[P_MIN, P_MAX], - method='trf', - x_scale=P_SCALE, - loss='soft_l1', - f_scale=eta_max * 0.05, - ) - fit_options.update(kwargs) +def efficiency_from_power(power, irradiance, p_mp_ref): + ''' + Convert power to normalized or relative efficiency. - def adr_wrapper(xdata, *params): - return adr(*xdata, *params) + If you can't figure out what the parameters mean, don't use this function! + ''' + G_REF = np.array(1000.) + g_rel = irradiance / G_REF - result = curve_fit(adr_wrapper, - xdata=[irradiance, temperature], - ydata=eta, - **fit_options, - ) - popt = result[0] - if dict_output: - return dict(zip(P_NAMES, popt)) - else: - return popt + p_rel = power / np.asanyarray(p_mp_ref, dtype=float) + eta_rel = p_rel / g_rel + return eta_rel def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): @@ -204,3 +153,82 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): eta = k_a * ((1 + k_rs + k_rsh) * v - k_rs * s - k_rsh * v**2) return eta + + +def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, + **kwargs): + """ + Determine the parameters of the adr module efficiency model by non-linear + least-squares fit to lab or field measurements. + + Parameters + ---------- + irradiance : numeric, non-negative + Effective irradiance incident on the PV module. [W/m²] + + temperature : numeric + PV module operating temperature. [°C] + + eta : numeric + Efficiency of the PV module at the specified irradiance and + temperature(s). [unitless] or [%] + + dict_output : boolean, optional + When True, return the result as a dictionary; when False, return + the result as an numpy array. + + kwargs : + Optional keyword arguments passed to `curve_fit`. + + Returns + ------- + popt : array + Optimal values for the parameters. + + pcov : 2-D array + Estimated covariance of popt. See `curve_fit` for details. + + See also + -------- + pvlib.pvefficiency.adr + + + Adapted from https://github.com/adriesse/pvpltools-python + Copyright (c) 2022, Anton Driesse, PV Performance Labs + All rights reserved. + """ + irradiance = np.asarray(irradiance, dtype=float).reshape(-1) + temperature = np.asarray(temperature, dtype=float).reshape(-1) + eta = np.asarray(eta, dtype=float).reshape(-1) + + eta_max = np.max(eta) + + P_NAMES = ['k_a', 'k_d', 'tc_d', 'k_rs', 'k_rsh'] + P_MAX = [+np.inf, 0, +0.1, 1, 1] # noQA: E221 + P_MIN = [0, -12, -0.1, 0, 0] # noQA: E221 + P0 = [eta_max, -6, 0.0, 0, 0] # noQA: E221 + P_SCALE = [eta_max, 10, 0.1, 1, 1] + + fit_options = dict(p0=P0, + bounds=[P_MIN, P_MAX], + method='trf', + x_scale=P_SCALE, + loss='soft_l1', + f_scale=eta_max * 0.05, + ) + + fit_options.update(kwargs) + + def adr_wrapper(xdata, *params): + return adr(*xdata, *params) + + result = curve_fit(adr_wrapper, + xdata=[irradiance, temperature], + ydata=eta, + **fit_options, + ) + popt = result[0] + if dict_output: + return dict(zip(P_NAMES, popt)) + else: + return popt diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index 804ce092fa..b7a3bbf71d 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -3,6 +3,16 @@ from pvlib import pvefficiency +def test_power_from_efficiency(): + # super simple function so one test will do + assert_allclose(100, pvefficiency.power_from_efficiency(.5, 500, 400)) + + +def test_efficiency_from_power(): + # super simple function so one test will do + assert_allclose(0.5, pvefficiency.efficiency_from_power(100, 500, 400)) + + def test_adr(): g = [1000, 200, 1000, 200, 1000, 200] t = [25, 25, 50, 50, 75, 75] From 75989d80f9ab8ff803dccbb8bc077e0783a18364 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 24 Nov 2022 23:34:41 +0100 Subject: [PATCH 16/23] Minor rename --- pvlib/pvefficiency.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index e86289aee6..7eecaac3cb 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -18,9 +18,9 @@ def power_from_efficiency(efficiency, irradiance, p_mp_ref): If you can't figure out what the parameters mean, don't use this function! ''' G_REF = np.array(1000.) - g_rel = irradiance / G_REF + s = irradiance / G_REF - p_rel = g_rel * efficiency + p_rel = s * efficiency power = p_rel * p_mp_ref return power @@ -32,10 +32,10 @@ def efficiency_from_power(power, irradiance, p_mp_ref): If you can't figure out what the parameters mean, don't use this function! ''' G_REF = np.array(1000.) - g_rel = irradiance / G_REF + s = irradiance / G_REF p_rel = power / np.asanyarray(p_mp_ref, dtype=float) - eta_rel = p_rel / g_rel + eta_rel = p_rel / s return eta_rel From ee5be0383d22ab6e42cdaee8850c03e7815c72f9 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Fri, 25 Nov 2022 15:18:13 +0100 Subject: [PATCH 17/23] Simplify examples somewhat. --- .../.pylint.d/plot_simulate_system1.stats | Bin 1028 -> 0 bytes .../plot_fit_to_matrix.py | 24 +++------- .../plot_simulate_system.py | 41 +++++------------- 3 files changed, 17 insertions(+), 48 deletions(-) delete mode 100644 docs/examples/adr-pv-module-efficiency/.pylint.d/plot_simulate_system1.stats diff --git a/docs/examples/adr-pv-module-efficiency/.pylint.d/plot_simulate_system1.stats b/docs/examples/adr-pv-module-efficiency/.pylint.d/plot_simulate_system1.stats deleted file mode 100644 index b27b8593d1028f824f0ce8acbf9eb1590a7918fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1028 zcmZWo$!^;)6m;UO&eE(+nl9;<)@f^Z-!Hx8pqo$XArL5uwg^y^o=Cdz!9anc-_d{U z7j#J3f$R9>H}jTx^QfQAF*VxZdZW>ZM^Rp+R&rVx(_(l4sFa0?w8*VwhDUm&4bKS` zOLc`dfOD=4EeB`e5(hiiZ;-e-v^Dq2I%Du5rXQbG-A zJ!po@h7+&NIQz zu;90uwxGRWvF5qSij?*z@)OClrUOLVPCQGUkg3UsfVXDgI{7Bhof-6M#L;}0dekvs zx9YSxGp3?j^Q{Y|4V}!j94dWi&ii&eotknVTz%IW?#hD)u-SF}zp^&sAu9Lgx!Ti5lYuIZ zQd>%q;MJy)+fmbFaK|Vsd6nR;UQal?0PBC==aiRRrCcQf#ZSicwCxXHGLpOL~?y*>8H@qZi)eoEi5~NfCF;k^4uT3|U>D^|D8={JSrQNTAn_dE=S4 zJ$mQY^GvrK@_t`HgGxs9-lcu;X&-y^$$CM_D3MV3`I5e5?&I9R3IkchOh&4}fxcdR OJN^Fm{FnV>yZ-=@Z8g3C diff --git a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index e591bfee51..6d6e29f88d 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -20,7 +20,7 @@ import pandas as pd import matplotlib.pyplot as plt -from pvlib.pvefficiency import fit_pvefficiency_adr, adr +from pvlib.pvefficiency import adr, fit_pvefficiency_adr # %% The text on this line is not displayed # @@ -63,10 +63,10 @@ # %% # -# Going back and forth between power and efficiency is a common operation -# so here are a couple of functions for that. -# The efficiency is normalized to STC conditions, in other words, at STC -# conditions the efficiency is 1.0 (or 100 %) +# Now calculate the normalized or relative efficiency values +# and use the fitting function to determine the parameters. +# The parameters (shown below) can now be used to +# simulate the module operating in a PV system. # @@ -76,20 +76,6 @@ def pmp2eta(g, p, p_stc): return p_rel / g_rel -def eta2pmp(g, eta_rel, p_stc): - g_rel = g / 1000 - p_rel = g_rel * eta_rel - return p_rel * p_stc - - -# %% -# -# Now calculate the normalized or relative efficiency values -# and use the fitting function to determine the parameters. -# The parameters (shown below) can now be used to -# simulate the module operating in a PV system. -# - eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index 76c857d59e..523da26c0d 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -16,31 +16,7 @@ from pvlib.pvefficiency import adr # %% -# -# Going back and forth between power and efficiency is a common operation -# so here are a couple of functions for that. -# The efficiency is normalized to STC conditions, in other words, at STC -# conditions the efficiency is 1.0 (or 100 %) -# - - -def pmp2eta(g, p, p_stc): - g_rel = g / 1000 - p_rel = p / p_stc - return p_rel / g_rel - - -def eta2pmp(g, eta_rel, p_stc): - g_rel = g / 1000 - p_rel = g_rel * eta_rel - return p_rel * p_stc - - -# %% -# -# this system is 4000 W nominal -# system losses are 14.08 % -# therefore P_STC = 3437 W +# Borrow the model parameters from the other example: # adr_parms = {'k_a': 0.99879, @@ -54,6 +30,9 @@ def eta2pmp(g, eta_rel, p_stc): # # Read an existing PVWATTS simulation output file # which contains all the input data we need to run an ADR simulation. +# this system is 4000 W nominal +# system losses are 14.08 % +# therefore P_STC = 3437 W # DATADIR = os.path.join(pvlib.__path__[0], 'data') @@ -71,10 +50,17 @@ def eta2pmp(g, eta_rel, p_stc): # %% # -# Simulating takes just two lines of code: +# Simulating takes just two steps: # one to calculate the efficiency and one to convert efficiency to power. # + +def eta2pmp(g, eta_rel, p_stc): + g_rel = g / 1000 + p_rel = g_rel * eta_rel + return p_rel * p_stc + + df['eta_adr'] = adr(df['poa_global'], df['t_cell'], **adr_parms) df['p_dc_adr'] = eta2pmp(df['poa_global'], df['eta_adr'], p_stc=3437) @@ -123,6 +109,3 @@ def eta2pmp(g, eta_rel, p_stc): # of Photovoltaics, vol. 11, no. 2, pp. 527-534, March 2021, # doi: 10.1109/JPHOTOV.2020.3045677. # -# -# -# From 816e7d7b48434394ec962acc4cd162a1fcbac405 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Fri, 25 Nov 2022 15:24:45 +0100 Subject: [PATCH 18/23] Undo utility functions. --- pvlib/pvefficiency.py | 28 ---------------------------- pvlib/tests/test_pvefficiency.py | 10 ---------- 2 files changed, 38 deletions(-) diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index 7eecaac3cb..d813d9024b 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -11,34 +11,6 @@ from scipy.special import exp10 -def power_from_efficiency(efficiency, irradiance, p_mp_ref): - ''' - Convert normalized or relative efficiency to power. - - If you can't figure out what the parameters mean, don't use this function! - ''' - G_REF = np.array(1000.) - s = irradiance / G_REF - - p_rel = s * efficiency - power = p_rel * p_mp_ref - return power - - -def efficiency_from_power(power, irradiance, p_mp_ref): - ''' - Convert power to normalized or relative efficiency. - - If you can't figure out what the parameters mean, don't use this function! - ''' - G_REF = np.array(1000.) - s = irradiance / G_REF - - p_rel = power / np.asanyarray(p_mp_ref, dtype=float) - eta_rel = p_rel / s - return eta_rel - - def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): ''' Calculate PV module efficiency using the ADR model. diff --git a/pvlib/tests/test_pvefficiency.py b/pvlib/tests/test_pvefficiency.py index b7a3bbf71d..804ce092fa 100644 --- a/pvlib/tests/test_pvefficiency.py +++ b/pvlib/tests/test_pvefficiency.py @@ -3,16 +3,6 @@ from pvlib import pvefficiency -def test_power_from_efficiency(): - # super simple function so one test will do - assert_allclose(100, pvefficiency.power_from_efficiency(.5, 500, 400)) - - -def test_efficiency_from_power(): - # super simple function so one test will do - assert_allclose(0.5, pvefficiency.efficiency_from_power(100, 500, 400)) - - def test_adr(): g = [1000, 200, 1000, 200, 1000, 200] t = [25, 25, 50, 50, 75, 75] From b4304aec9aebb1178e0918d21c3c98d0d0184de9 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Fri, 25 Nov 2022 16:34:43 +0100 Subject: [PATCH 19/23] More simplification. --- .../plot_fit_to_matrix.py | 20 +++++++------------ .../plot_simulate_system.py | 17 +++++++--------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index 6d6e29f88d..3c0283355c 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -59,8 +59,6 @@ ''' df = pd.read_csv(StringIO(iec61853data), delim_whitespace=True) -P_STC = 322.305 - # %% # # Now calculate the normalized or relative efficiency values @@ -69,16 +67,12 @@ # simulate the module operating in a PV system. # +P_REF = 322.305 # (W) STC value from the table above +G_REF = 1000. # (W/m2) -def pmp2eta(g, p, p_stc): - g_rel = g / 1000 - p_rel = p / p_stc - return p_rel / g_rel - - -eta_rel = pmp2eta(df.irradiance, df.p_mp, P_STC) +df['eta_rel'] = (df.p_mp / P_REF) / (df.irradiance / G_REF) -adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, eta_rel) +adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, df.eta_rel) for k, v in adr_params.items(): print('%-5s = %7.4f' % (k, v)) @@ -90,11 +84,11 @@ def pmp2eta(g, p, p_stc): # that are most likely evidence of measurement errors. # -eta_adr = adr(df.irradiance, df.temperature, **adr_params) +eta_rel_adr = adr(df.irradiance, df.temperature, **adr_params) plt.figure() -plt.plot(df.irradiance, eta_rel, 'oc') -plt.plot(df.irradiance, eta_adr, '.k') +plt.plot(df.irradiance, df.eta_rel, 'oc') +plt.plot(df.irradiance, eta_rel_adr, '.k') plt.legend(['Lab measurements', 'ADR model fit']) plt.xlabel('Irradiance [W/m²]') plt.show() diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index 523da26c0d..5c911f1b56 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -50,26 +50,23 @@ # %% # -# Simulating takes just two steps: -# one to calculate the efficiency and one to convert efficiency to power. +# Simulating is done in two steps: +# first calculate the efficiency, then convert efficiency to power. # +P_REF = 3437. # (W) +G_REF = 1000. # (W/m2) -def eta2pmp(g, eta_rel, p_stc): - g_rel = g / 1000 - p_rel = g_rel * eta_rel - return p_rel * p_stc +df['eta_rel'] = adr(df['poa_global'], df['t_cell'], **adr_parms) - -df['eta_adr'] = adr(df['poa_global'], df['t_cell'], **adr_parms) -df['p_dc_adr'] = eta2pmp(df['poa_global'], df['eta_adr'], p_stc=3437) +df['p_dc_adr'] = P_REF * df['eta_rel'] * (df['poa_global'] / G_REF) # %% # # Compare the ADR simulated output to PVWATS for one day. # # NOTE: they are not supposed to be the same because the module simulated -# by PVWATTS is most likely different from the our ADR example module. +# by PVWATTS is different from the our ADR example module. # DEMO_DAY = '2019-08-05' From 172e5b1b39758e8c175f0876a102dabf473c302a Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Fri, 25 Nov 2022 16:59:30 +0100 Subject: [PATCH 20/23] Add new functions to the API reference under "PV Modeling/Other" (for now). --- docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py | 2 +- .../examples/adr-pv-module-efficiency/plot_simulate_system.py | 4 ++-- docs/sphinx/source/reference/pv_modeling.rst | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index 3c0283355c..8971b57a69 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -2,7 +2,7 @@ Obtaining ADR model parameters from IEC 61853 matrix measurements ================================================================= -The fitting function provided in pvlib does exactly that--and more. +There's a fitting function provided in pvlib to do exactly that. (WORK IN PROGRESS) diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index 5c911f1b56..a03488ae1b 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -54,8 +54,8 @@ # first calculate the efficiency, then convert efficiency to power. # -P_REF = 3437. # (W) -G_REF = 1000. # (W/m2) +P_REF = 3437. # (W) +G_REF = 1000. # (W/m2) df['eta_rel'] = adr(df['poa_global'], df['t_cell'], **adr_parms) diff --git a/docs/sphinx/source/reference/pv_modeling.rst b/docs/sphinx/source/reference/pv_modeling.rst index 31c380c1bb..918d237ff5 100644 --- a/docs/sphinx/source/reference/pv_modeling.rst +++ b/docs/sphinx/source/reference/pv_modeling.rst @@ -187,3 +187,5 @@ Other pvsystem.retrieve_sam pvsystem.scale_voltage_current_power + pvefficiency.adr + pvefficiency.fit_pvefficiency_adr From 8a3dae3ca14198a5917ee995a93c856eda79414a Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 27 Nov 2022 13:56:42 +0100 Subject: [PATCH 21/23] Rewrite simulation example and a few other little things. --- .../plot_fit_to_matrix.py | 17 +- .../plot_simulate_system.py | 146 ++++++++++++------ pvlib/pvefficiency.py | 26 ++-- 3 files changed, 122 insertions(+), 67 deletions(-) diff --git a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index 8971b57a69..e692d2300d 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -70,9 +70,10 @@ P_REF = 322.305 # (W) STC value from the table above G_REF = 1000. # (W/m2) -df['eta_rel'] = (df.p_mp / P_REF) / (df.irradiance / G_REF) +df['eta_rel'] = (df['p_mp'] / P_REF) / (df['irradiance'] / G_REF) -adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, df.eta_rel) +adr_params = fit_pvefficiency_adr(df['irradiance'], df['temperature'], + df['eta_rel']) for k, v in adr_params.items(): print('%-5s = %7.4f' % (k, v)) @@ -80,17 +81,19 @@ # %% # # Compare the model output to the original measurements. -# The chart below shows minor random differences -# that are most likely evidence of measurement errors. +# The chart below shows minor differences but due to their random nature +# they are most likely evidence of the limitations of measurement accuracy. # -eta_rel_adr = adr(df.irradiance, df.temperature, **adr_params) +eta_rel_adr = adr(df['irradiance'], df['temperature'], **adr_params) plt.figure() -plt.plot(df.irradiance, df.eta_rel, 'oc') -plt.plot(df.irradiance, eta_rel_adr, '.k') +plt.plot(df['irradiance'], df['eta_rel'], 'oc', ms=8) +plt.plot(df['irradiance'], eta_rel_adr, '.k') plt.legend(['Lab measurements', 'ADR model fit']) plt.xlabel('Irradiance [W/m²]') +plt.ylabel('Relative efficiency [-]') +plt.grid(alpha=0.5) plt.show() # %% diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index a03488ae1b..9a77f87b1a 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -4,7 +4,10 @@ Time series processing with the ADR model is fast and ... efficient! -(WORK IN PROGRESS) +This example reads a TMY3 weather file, and runs a basic simulation +on a fixed latitude-tilt system. +Efficiency is independent of system size, so adjusting the system +capacity is just a matter of setting the desired value, e.g. P_STC = 5000. """ @@ -13,85 +16,126 @@ import matplotlib.pyplot as plt import pvlib -from pvlib.pvefficiency import adr +from pvlib import iotools, location, pvefficiency +from pvlib.irradiance import aoi, get_total_irradiance -# %% -# Borrow the model parameters from the other example: +#%% +# +# Read a TMY3 file containing weather data and select needed columns # -adr_parms = {'k_a': 0.99879, - 'k_d': -5.85188, - 'tc_d': 0.01939, - 'k_rs': 0.06962, - 'k_rsh': 0.21036 - } +PVLIB_DIR = pvlib.__path__[0] +DATA_FILE = os.path.join(PVLIB_DIR, 'data', '723170TYA.csv') -# %% +tmy, metadata = iotools.read_tmy3(DATA_FILE, coerce_year=1990) + +df = pd.DataFrame({'ghi': tmy['GHI'], 'dhi': tmy['DHI'], + 'dni': tmy['DNI'], 'dni_extra': tmy['ETRN'], + 'temp_air': tmy['DryBulb'], 'wind_speed': tmy['Wspd'], + }) + +#%% # -# Read an existing PVWATTS simulation output file -# which contains all the input data we need to run an ADR simulation. -# this system is 4000 W nominal -# system losses are 14.08 % -# therefore P_STC = 3437 W +# Shift timestamps to middle of hour and then calculate sun positions # -DATADIR = os.path.join(pvlib.__path__[0], 'data') -DATAFILE = os.path.join(DATADIR, 'pvwatts_8760_rackmount.csv') - -df = pd.read_csv(DATAFILE, skiprows=17, nrows=8760) -df.columns = ['month', 'day', 'hour', - 'dni', 'dif', 't_amb', 'wind_speed', - 'poa_global', 't_cell', 'p_dc', 'p_ac'] +df.index = df.index - pd.Timedelta(minutes=30) -df['year'] = 2019 -DATECOLS = ['year', 'month', 'day', 'hour'] -df.index = pd.to_datetime(df[DATECOLS]) -df = df.drop(columns=DATECOLS) +loc = location.Location.from_tmy(metadata) +solpos = loc.get_solarposition(df.index) -# %% +#%% # -# Simulating is done in two steps: -# first calculate the efficiency, then convert efficiency to power. +# Determine total irradiance on a fixed-tilt array # -P_REF = 3437. # (W) -G_REF = 1000. # (W/m2) +TILT = metadata['latitude'] +ORIENT = 180 -df['eta_rel'] = adr(df['poa_global'], df['t_cell'], **adr_parms) +df['aoi'] = aoi(TILT, ORIENT, solpos.apparent_zenith, solpos.azimuth) -df['p_dc_adr'] = P_REF * df['eta_rel'] * (df['poa_global'] / G_REF) +total_irrad = get_total_irradiance(TILT, ORIENT, + solpos.apparent_zenith, solpos.azimuth, + df.dni, df.ghi, df.dhi, df.dni_extra) + +df['poa_global'] = total_irrad.poa_global # %% # -# Compare the ADR simulated output to PVWATS for one day. +# Estimate the expected operating temperature of the PV modules +# + +df['temp_pv'] = pvlib.temperature.faiman(df.poa_global, df.temp_air, + df.wind_speed) + +# %% +# +# Now we're ready to calculate PV array DC output power based +# on POA irradiance and PV module operating temperature. +# Among the models available in pvlib-python to do this are: +# - PVWatts +# - SAPM +# - single-diode model variations +# - and now also the ADR PV efficiency model +# +# Simulation is done in two steps: +# - first calculate efficiency using the ADR model, +# - then convert (scale up) efficiency to power. # -# NOTE: they are not supposed to be the same because the module simulated -# by PVWATTS is different from the our ADR example module. + +# Borrow the ADR model parameters from the other example: + +adr_params = {'k_a': 0.99879, + 'k_d': -5.85188, + 'tc_d': 0.01939, + 'k_rs': 0.06962, + 'k_rsh': 0.21036 + } + +df['eta_rel'] = pvefficiency.adr(df['poa_global'], df['temp_pv'], **adr_params) + +# Set the desired array size: +P_STC = 5000. # (W) +# and the irradiance level needed to achieve this output ( +G_STC = 1000. # (W/m2) + +df['p_mp'] = P_STC * df['eta_rel'] * (df['poa_global'] / G_STC) + +#%% +# +# Show how power and efficiency vary with both irradiance and temperature # -DEMO_DAY = '2019-08-05' +plt.figure() +pc = plt.scatter(df['poa_global'], df['eta_rel'], c=df['temp_pv'], cmap='jet') +plt.colorbar(label='Temperature [C]', ax=plt.gca()) +pc.set_alpha(0.25) +plt.grid(alpha=0.5) +plt.ylim(0.48) +plt.xlabel('Irradiance [W/m²]') +plt.ylabel('Relative efficiency [-]') +plt.show() plt.figure() -plt.plot(df['p_dc'][DEMO_DAY]) -plt.plot(df['p_dc_adr'][DEMO_DAY]) -plt.xticks(rotation=30) -plt.legend(['PVWATTS', 'ADR']) -plt.ylabel('Power [W]') +pc = plt.scatter(df['poa_global'], df['p_mp'], c=df['temp_pv'], cmap='jet') +plt.colorbar(label='Temperature [C]', ax=plt.gca()) +pc.set_alpha(0.25) +plt.grid(alpha=0.5) +plt.xlabel('Irradiance [W/m²]') +plt.ylabel('Array power [W]') plt.show() # %% # -# The colors in the next graph show that the PVWATTS module probably has -# a larger temperature coefficient than the ADR example module. +# One day: # +DEMO_DAY = '1990-08-05' + plt.figure() -plt.scatter(df['p_dc'], df['p_dc_adr'], - c=df['t_cell'], alpha=.3, cmap='jet') -plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) -plt.xlabel('PVWATTS DC array output [W]') -plt.ylabel('ADR modelled DC array output [W]') -plt.colorbar(label='T_cell', ax=plt.gca()) +plt.plot(df['p_mp'][DEMO_DAY]) +plt.xticks(rotation=30) +plt.ylabel('Power [W]') plt.show() # %% diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index d813d9024b..50f7c6e56c 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -66,8 +66,9 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): freely to adjust the scale, or to change the module class to a slightly higher or lower efficiency. - All arguments may be scalars or vectors. If multiple arguments - are vectors they must be the same length. + All arguments may be scalars or array-like. If multiple arguments + are array-like they must be the same shape or broadcastable to the + same shape. See also -------- @@ -146,24 +147,30 @@ def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, temperature(s). [unitless] or [%] dict_output : boolean, optional - When True, return the result as a dictionary; when False, return - the result as an numpy array. + When True (default), return the result as a dictionary; when False, + return the result as a numpy array. kwargs : - Optional keyword arguments passed to `curve_fit`. + Optional keyword arguments passed to `scip.optimize.curve_fit`. + These kwargs can over-ride some options set within this function, + which could be interesting for very advanced users. Returns ------- - popt : array + popt : array or dict Optimal values for the parameters. - pcov : 2-D array - Estimated covariance of popt. See `curve_fit` for details. + Notes + ----- + The best fits are obtained when the lab or field data include a wide range + of both irradiance and temperature values. A minimal data set + would consist of 6 operating points covering low, medium and high + irradiance levels at two operating temperatures. See also -------- pvlib.pvefficiency.adr - + scipy.optimize.curve_fit Adapted from https://github.com/adriesse/pvpltools-python Copyright (c) 2022, Anton Driesse, PV Performance Labs @@ -200,6 +207,7 @@ def adr_wrapper(xdata, *params): **fit_options, ) popt = result[0] + if dict_output: return dict(zip(P_NAMES, popt)) else: From 9d0bf5cd3b440e3230a7622c3c5be6526b65e8ed Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 27 Nov 2022 13:56:42 +0100 Subject: [PATCH 22/23] Rewrite simulation example with tmy3 data and a few other little things. --- .../plot_fit_to_matrix.py | 17 ++- .../plot_simulate_system.py | 140 ++++++++++++------ pvlib/pvefficiency.py | 26 ++-- 3 files changed, 119 insertions(+), 64 deletions(-) diff --git a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py index 8971b57a69..e692d2300d 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py +++ b/docs/examples/adr-pv-module-efficiency/plot_fit_to_matrix.py @@ -70,9 +70,10 @@ P_REF = 322.305 # (W) STC value from the table above G_REF = 1000. # (W/m2) -df['eta_rel'] = (df.p_mp / P_REF) / (df.irradiance / G_REF) +df['eta_rel'] = (df['p_mp'] / P_REF) / (df['irradiance'] / G_REF) -adr_params = fit_pvefficiency_adr(df.irradiance, df.temperature, df.eta_rel) +adr_params = fit_pvefficiency_adr(df['irradiance'], df['temperature'], + df['eta_rel']) for k, v in adr_params.items(): print('%-5s = %7.4f' % (k, v)) @@ -80,17 +81,19 @@ # %% # # Compare the model output to the original measurements. -# The chart below shows minor random differences -# that are most likely evidence of measurement errors. +# The chart below shows minor differences but due to their random nature +# they are most likely evidence of the limitations of measurement accuracy. # -eta_rel_adr = adr(df.irradiance, df.temperature, **adr_params) +eta_rel_adr = adr(df['irradiance'], df['temperature'], **adr_params) plt.figure() -plt.plot(df.irradiance, df.eta_rel, 'oc') -plt.plot(df.irradiance, eta_rel_adr, '.k') +plt.plot(df['irradiance'], df['eta_rel'], 'oc', ms=8) +plt.plot(df['irradiance'], eta_rel_adr, '.k') plt.legend(['Lab measurements', 'ADR model fit']) plt.xlabel('Irradiance [W/m²]') +plt.ylabel('Relative efficiency [-]') +plt.grid(alpha=0.5) plt.show() # %% diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index a03488ae1b..4273e6159f 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -4,7 +4,10 @@ Time series processing with the ADR model is fast and ... efficient! -(WORK IN PROGRESS) +This example reads a TMY3 weather file, and runs a basic simulation +on a fixed latitude-tilt system. +Efficiency is independent of system size, so adjusting the system +capacity is just a matter of setting the desired value, e.g. P_STC = 5000. """ @@ -13,85 +16,126 @@ import matplotlib.pyplot as plt import pvlib -from pvlib.pvefficiency import adr +from pvlib import iotools, location, pvefficiency +from pvlib.irradiance import aoi, get_total_irradiance # %% -# Borrow the model parameters from the other example: # +# Read a TMY3 file containing weather data and select needed columns +# + +PVLIB_DIR = pvlib.__path__[0] +DATA_FILE = os.path.join(PVLIB_DIR, 'data', '723170TYA.CSV') -adr_parms = {'k_a': 0.99879, - 'k_d': -5.85188, - 'tc_d': 0.01939, - 'k_rs': 0.06962, - 'k_rsh': 0.21036 - } +tmy, metadata = iotools.read_tmy3(DATA_FILE, coerce_year=1990) + +df = pd.DataFrame({'ghi': tmy['GHI'], 'dhi': tmy['DHI'], + 'dni': tmy['DNI'], 'dni_extra': tmy['ETRN'], + 'temp_air': tmy['DryBulb'], 'wind_speed': tmy['Wspd'], + }) # %% # -# Read an existing PVWATTS simulation output file -# which contains all the input data we need to run an ADR simulation. -# this system is 4000 W nominal -# system losses are 14.08 % -# therefore P_STC = 3437 W +# Shift timestamps to middle of hour and then calculate sun positions # -DATADIR = os.path.join(pvlib.__path__[0], 'data') -DATAFILE = os.path.join(DATADIR, 'pvwatts_8760_rackmount.csv') - -df = pd.read_csv(DATAFILE, skiprows=17, nrows=8760) -df.columns = ['month', 'day', 'hour', - 'dni', 'dif', 't_amb', 'wind_speed', - 'poa_global', 't_cell', 'p_dc', 'p_ac'] +df.index = df.index - pd.Timedelta(minutes=30) -df['year'] = 2019 -DATECOLS = ['year', 'month', 'day', 'hour'] -df.index = pd.to_datetime(df[DATECOLS]) -df = df.drop(columns=DATECOLS) +loc = location.Location.from_tmy(metadata) +solpos = loc.get_solarposition(df.index) # %% # -# Simulating is done in two steps: -# first calculate the efficiency, then convert efficiency to power. +# Determine total irradiance on a fixed-tilt array # -P_REF = 3437. # (W) -G_REF = 1000. # (W/m2) +TILT = metadata['latitude'] +ORIENT = 180 -df['eta_rel'] = adr(df['poa_global'], df['t_cell'], **adr_parms) +df['aoi'] = aoi(TILT, ORIENT, solpos.apparent_zenith, solpos.azimuth) -df['p_dc_adr'] = P_REF * df['eta_rel'] * (df['poa_global'] / G_REF) +total_irrad = get_total_irradiance(TILT, ORIENT, + solpos.apparent_zenith, solpos.azimuth, + df.dni, df.ghi, df.dhi, df.dni_extra) + +df['poa_global'] = total_irrad.poa_global # %% # -# Compare the ADR simulated output to PVWATS for one day. +# Estimate the expected operating temperature of the PV modules +# + +df['temp_pv'] = pvlib.temperature.faiman(df.poa_global, df.temp_air, + df.wind_speed) + +# %% +# +# Now we're ready to calculate PV array DC output power based +# on POA irradiance and PV module operating temperature. +# Among the models available in pvlib-python to do this are: +# - PVWatts +# - SAPM +# - single-diode model variations +# - and now also the ADR PV efficiency model +# +# Simulation is done in two steps: +# - first calculate efficiency using the ADR model, +# - then convert (scale up) efficiency to power. +# + +# Borrow the ADR model parameters from the other example: + +adr_params = {'k_a': 0.99879, + 'k_d': -5.85188, + 'tc_d': 0.01939, + 'k_rs': 0.06962, + 'k_rsh': 0.21036 + } + +df['eta_rel'] = pvefficiency.adr(df['poa_global'], df['temp_pv'], **adr_params) + +# Set the desired array size: +P_STC = 5000. # (W) +# and the irradiance level needed to achieve this output ( +G_STC = 1000. # (W/m2) + +df['p_mp'] = P_STC * df['eta_rel'] * (df['poa_global'] / G_STC) + +# %% # -# NOTE: they are not supposed to be the same because the module simulated -# by PVWATTS is different from the our ADR example module. +# Show how power and efficiency vary with both irradiance and temperature # -DEMO_DAY = '2019-08-05' +plt.figure() +pc = plt.scatter(df['poa_global'], df['eta_rel'], c=df['temp_pv'], cmap='jet') +plt.colorbar(label='Temperature [C]', ax=plt.gca()) +pc.set_alpha(0.25) +plt.grid(alpha=0.5) +plt.ylim(0.48) +plt.xlabel('Irradiance [W/m²]') +plt.ylabel('Relative efficiency [-]') +plt.show() plt.figure() -plt.plot(df['p_dc'][DEMO_DAY]) -plt.plot(df['p_dc_adr'][DEMO_DAY]) -plt.xticks(rotation=30) -plt.legend(['PVWATTS', 'ADR']) -plt.ylabel('Power [W]') +pc = plt.scatter(df['poa_global'], df['p_mp'], c=df['temp_pv'], cmap='jet') +plt.colorbar(label='Temperature [C]', ax=plt.gca()) +pc.set_alpha(0.25) +plt.grid(alpha=0.5) +plt.xlabel('Irradiance [W/m²]') +plt.ylabel('Array power [W]') plt.show() # %% # -# The colors in the next graph show that the PVWATTS module probably has -# a larger temperature coefficient than the ADR example module. +# One day: # +DEMO_DAY = '1990-08-05' + plt.figure() -plt.scatter(df['p_dc'], df['p_dc_adr'], - c=df['t_cell'], alpha=.3, cmap='jet') -plt.plot([0, 4000], [0, 4000], 'k', alpha=.5) -plt.xlabel('PVWATTS DC array output [W]') -plt.ylabel('ADR modelled DC array output [W]') -plt.colorbar(label='T_cell', ax=plt.gca()) +plt.plot(df['p_mp'][DEMO_DAY]) +plt.xticks(rotation=30) +plt.ylabel('Power [W]') plt.show() # %% diff --git a/pvlib/pvefficiency.py b/pvlib/pvefficiency.py index d813d9024b..50f7c6e56c 100644 --- a/pvlib/pvefficiency.py +++ b/pvlib/pvefficiency.py @@ -66,8 +66,9 @@ def adr(irradiance, temperature, k_a, k_d, tc_d, k_rs, k_rsh): freely to adjust the scale, or to change the module class to a slightly higher or lower efficiency. - All arguments may be scalars or vectors. If multiple arguments - are vectors they must be the same length. + All arguments may be scalars or array-like. If multiple arguments + are array-like they must be the same shape or broadcastable to the + same shape. See also -------- @@ -146,24 +147,30 @@ def fit_pvefficiency_adr(irradiance, temperature, eta, dict_output=True, temperature(s). [unitless] or [%] dict_output : boolean, optional - When True, return the result as a dictionary; when False, return - the result as an numpy array. + When True (default), return the result as a dictionary; when False, + return the result as a numpy array. kwargs : - Optional keyword arguments passed to `curve_fit`. + Optional keyword arguments passed to `scip.optimize.curve_fit`. + These kwargs can over-ride some options set within this function, + which could be interesting for very advanced users. Returns ------- - popt : array + popt : array or dict Optimal values for the parameters. - pcov : 2-D array - Estimated covariance of popt. See `curve_fit` for details. + Notes + ----- + The best fits are obtained when the lab or field data include a wide range + of both irradiance and temperature values. A minimal data set + would consist of 6 operating points covering low, medium and high + irradiance levels at two operating temperatures. See also -------- pvlib.pvefficiency.adr - + scipy.optimize.curve_fit Adapted from https://github.com/adriesse/pvpltools-python Copyright (c) 2022, Anton Driesse, PV Performance Labs @@ -200,6 +207,7 @@ def adr_wrapper(xdata, *params): **fit_options, ) popt = result[0] + if dict_output: return dict(zip(P_NAMES, popt)) else: From c0807a9ca53c95627768a4d3bda1e6d621cc510b Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 27 Nov 2022 19:35:01 +0100 Subject: [PATCH 23/23] csv -> CSV --- .../adr-pv-module-efficiency/plot_simulate_system.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py index 9a77f87b1a..4273e6159f 100644 --- a/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py +++ b/docs/examples/adr-pv-module-efficiency/plot_simulate_system.py @@ -19,13 +19,13 @@ from pvlib import iotools, location, pvefficiency from pvlib.irradiance import aoi, get_total_irradiance -#%% +# %% # # Read a TMY3 file containing weather data and select needed columns # PVLIB_DIR = pvlib.__path__[0] -DATA_FILE = os.path.join(PVLIB_DIR, 'data', '723170TYA.csv') +DATA_FILE = os.path.join(PVLIB_DIR, 'data', '723170TYA.CSV') tmy, metadata = iotools.read_tmy3(DATA_FILE, coerce_year=1990) @@ -34,7 +34,7 @@ 'temp_air': tmy['DryBulb'], 'wind_speed': tmy['Wspd'], }) -#%% +# %% # # Shift timestamps to middle of hour and then calculate sun positions # @@ -44,7 +44,7 @@ loc = location.Location.from_tmy(metadata) solpos = loc.get_solarposition(df.index) -#%% +# %% # # Determine total irradiance on a fixed-tilt array # @@ -101,7 +101,7 @@ df['p_mp'] = P_STC * df['eta_rel'] * (df['poa_global'] / G_STC) -#%% +# %% # # Show how power and efficiency vary with both irradiance and temperature #